aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs')
-rw-r--r--MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs339
1 files changed, 93 insertions, 246 deletions
diff --git a/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs b/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs
index 0fc9265f6..1cec4461b 100644
--- a/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs
+++ b/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs
@@ -1,11 +1,11 @@
-using System.Net.Sockets;
-using System.Runtime.Serialization;
-using Funq;
+using Funq;
using MediaBrowser.Common;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Logging;
+using MediaBrowser.Server.Implementations.HttpServer.NetListener;
+using MediaBrowser.Server.Implementations.HttpServer.SocketSharp;
using ServiceStack;
using ServiceStack.Api.Swagger;
using ServiceStack.Host;
@@ -14,11 +14,9 @@ using ServiceStack.Host.HttpListener;
using ServiceStack.Logging;
using ServiceStack.Web;
using System;
-using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using System.Net;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
@@ -27,7 +25,6 @@ namespace MediaBrowser.Server.Implementations.HttpServer
{
public class HttpListenerHost : ServiceStackHost, IHttpServer
{
- private string ServerName { get; set; }
private string HandlerPath { get; set; }
private string DefaultRedirectPath { get; set; }
@@ -36,14 +33,10 @@ namespace MediaBrowser.Server.Implementations.HttpServer
private readonly List<IRestfulService> _restServices = new List<IRestfulService>();
- private HttpListener Listener { get; set; }
- protected bool IsStarted = false;
-
- private readonly List<AutoResetEvent> _autoResetEvents = new List<AutoResetEvent>();
+ private IHttpListener _listener;
private readonly ContainerAdapter _containerAdapter;
- private readonly ConcurrentDictionary<string, string> _localEndPoints = new ConcurrentDictionary<string, string>(StringComparer.OrdinalIgnoreCase);
public event EventHandler<WebSocketConnectEventArgs> WebSocketConnected;
/// <summary>
@@ -52,24 +45,18 @@ namespace MediaBrowser.Server.Implementations.HttpServer
/// <value>The local end points.</value>
public IEnumerable<string> LocalEndPoints
{
- get { return _localEndPoints.Keys.ToList(); }
+ get { return _listener == null ? new List<string>() : _listener.LocalEndPoints; }
}
public HttpListenerHost(IApplicationHost applicationHost, ILogManager logManager, string serviceName, string handlerPath, string defaultRedirectPath, params Assembly[] assembliesWithServices)
: base(serviceName, assembliesWithServices)
{
DefaultRedirectPath = defaultRedirectPath;
- ServerName = serviceName;
HandlerPath = handlerPath;
_logger = logManager.GetLogger("HttpServer");
_containerAdapter = new ContainerAdapter(applicationHost);
-
- for (var i = 0; i < 1; i++)
- {
- _autoResetEvents.Add(new AutoResetEvent(false));
- }
}
public override void Configure(Container container)
@@ -81,7 +68,8 @@ namespace MediaBrowser.Server.Implementations.HttpServer
{typeof (InvalidOperationException), 422},
{typeof (ResourceNotFoundException), 404},
{typeof (FileNotFoundException), 404},
- {typeof (DirectoryNotFoundException), 404}
+ {typeof (DirectoryNotFoundException), 404},
+ {typeof (Implementations.Security.AuthenticationException), 401}
};
HostConfig.Instance.DebugMode = true;
@@ -95,7 +83,21 @@ namespace MediaBrowser.Server.Implementations.HttpServer
container.Adapter = _containerAdapter;
Plugins.Add(new SwaggerFeature());
- Plugins.Add(new CorsFeature(allowedHeaders: "Content-Type, Authorization"));
+ Plugins.Add(new CorsFeature(allowedHeaders: "Content-Type, Authorization, Range, X-MediaBrowser-Token"));
+
+ //Plugins.Add(new AuthFeature(() => new AuthUserSession(), new IAuthProvider[] {
+ // new SessionAuthProvider(_containerAdapter.Resolve<ISessionContext>()),
+ //}));
+
+ PreRequestFilters.Add((httpReq, httpRes) =>
+ {
+ //Handles Request and closes Responses after emitting global HTTP Headers
+ if (string.Equals(httpReq.Verb, "OPTIONS", StringComparison.OrdinalIgnoreCase))
+ {
+ httpRes.EndRequest(); //add a 'using ServiceStack;'
+ }
+ });
+
HostContext.GlobalResponseFilters.Add(new ResponseFilter(_logger).FilterResponse);
}
@@ -154,213 +156,39 @@ namespace MediaBrowser.Server.Implementations.HttpServer
/// </summary>
private void StartListener()
{
- // *** Already running - just leave it in place
- if (IsStarted)
- return;
-
- if (Listener == null)
- Listener = new HttpListener();
-
HostContext.Config.HandlerFactoryPath = ListenerRequest.GetHandlerPathIfAny(UrlPrefixes.First());
- foreach (var prefix in UrlPrefixes)
- {
- _logger.Info("Adding HttpListener prefix " + prefix);
- Listener.Prefixes.Add(prefix);
- }
+ _listener = NativeWebSocket.IsSupported
+ ? _listener = new HttpListenerServer(_logger)
+ //? _listener = new WebSocketSharpListener(_logger)
+ : _listener = new WebSocketSharpListener(_logger);
- IsStarted = true;
- _logger.Info("Starting HttpListner");
- Listener.Start();
-
- for (var i = 0; i < _autoResetEvents.Count; i++)
- {
- var index = i;
- ThreadPool.QueueUserWorkItem(o => Listen(o, index));
- }
- }
+ _listener.WebSocketHandler = WebSocketHandler;
+ _listener.ErrorHandler = ErrorHandler;
+ _listener.RequestHandler = RequestHandler;
- private bool IsListening
- {
- get { return this.IsStarted && this.Listener != null && this.Listener.IsListening; }
+ _listener.Start(UrlPrefixes);
}
- // Loop here to begin processing of new requests.
- private void Listen(object state, int index)
+ private void WebSocketHandler(WebSocketConnectEventArgs args)
{
- while (IsListening)
+ if (WebSocketConnected != null)
{
- if (Listener == null) return;
-
- try
- {
- Listener.BeginGetContext(c => ListenerCallback(c, index), Listener);
-
- _autoResetEvents[index].WaitOne();
- }
- catch (Exception ex)
- {
- _logger.Error("Listen()", ex);
- return;
- }
- if (Listener == null) return;
+ WebSocketConnected(this, args);
}
}
- // Handle the processing of a request in here.
- private void ListenerCallback(IAsyncResult asyncResult, int index)
+ private void ErrorHandler(Exception ex, IRequest httpReq)
{
- var listener = asyncResult.AsyncState as HttpListener;
- HttpListenerContext context = null;
-
- if (listener == null) return;
-
try
{
- if (!IsListening)
- {
- _logger.Debug("Ignoring ListenerCallback() as HttpListener is no longer listening");
- return;
- }
- // The EndGetContext() method, as with all Begin/End asynchronous methods in the .NET Framework,
- // blocks until there is a request to be processed or some type of data is available.
- context = listener.EndGetContext(asyncResult);
- }
- catch (Exception ex)
- {
- // You will get an exception when httpListener.Stop() is called
- // because there will be a thread stopped waiting on the .EndGetContext()
- // method, and again, that is just the way most Begin/End asynchronous
- // methods of the .NET Framework work.
- var errMsg = ex + ": " + IsListening;
- _logger.Warn(errMsg);
- return;
- }
- finally
- {
- // Once we know we have a request (or exception), we signal the other thread
- // so that it calls the BeginGetContext() (or possibly exits if we're not
- // listening any more) method to start handling the next incoming request
- // while we continue to process this request on a different thread.
- _autoResetEvents[index].Set();
- }
-
- var date = DateTime.Now;
-
- Task.Factory.StartNew(async () =>
- {
- try
- {
- var request = context.Request;
-
- LogHttpRequest(request, index);
-
- if (request.IsWebSocketRequest)
- {
- await ProcessWebSocketRequest(context).ConfigureAwait(false);
- return;
- }
-
- var localPath = request.Url.LocalPath;
-
- if (string.Equals(localPath, "/mediabrowser/", StringComparison.OrdinalIgnoreCase))
- {
- context.Response.Redirect(DefaultRedirectPath);
- context.Response.Close();
- return;
- }
- if (string.Equals(localPath, "/mediabrowser", StringComparison.OrdinalIgnoreCase))
- {
- context.Response.Redirect("mediabrowser/" + DefaultRedirectPath);
- context.Response.Close();
- return;
- }
- if (string.Equals(localPath, "/", StringComparison.OrdinalIgnoreCase))
- {
- context.Response.Redirect("mediabrowser/" + DefaultRedirectPath);
- context.Response.Close();
- return;
- }
- if (string.IsNullOrEmpty(localPath))
- {
- context.Response.Redirect("/mediabrowser/" + DefaultRedirectPath);
- context.Response.Close();
- return;
- }
-
- var url = request.Url.ToString();
- var endPoint = request.RemoteEndPoint;
-
- await ProcessRequestAsync(context).ConfigureAwait(false);
-
- var duration = DateTime.Now - date;
-
- if (EnableHttpRequestLogging)
- {
- LoggerUtils.LogResponse(_logger, context.Response, url, endPoint, duration);
- }
- }
- catch (Exception ex)
- {
- _logger.ErrorException("ProcessRequest failure", ex);
-
- HandleError(ex, context, _logger);
- }
-
- });
- }
-
- /// <summary>
- /// Logs the HTTP request.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <param name="index">The index.</param>
- private void LogHttpRequest(HttpListenerRequest request, int index)
- {
- var endpoint = request.LocalEndPoint;
-
- if (endpoint != null)
- {
- var address = endpoint.ToString();
-
- _localEndPoints.GetOrAdd(address, address);
- }
-
- if (EnableHttpRequestLogging)
- {
- LoggerUtils.LogRequest(_logger, request, index);
- }
- }
+ var httpRes = httpReq.Response;
- /// <summary>
- /// Processes the web socket request.
- /// </summary>
- /// <param name="ctx">The CTX.</param>
- /// <returns>Task.</returns>
- private async Task ProcessWebSocketRequest(HttpListenerContext ctx)
- {
-#if !__MonoCS__
- try
- {
- var webSocketContext = await ctx.AcceptWebSocketAsync(null).ConfigureAwait(false);
- if (WebSocketConnected != null)
+ if (httpRes.IsClosed)
{
- WebSocketConnected(this, new WebSocketConnectEventArgs { WebSocket = new NativeWebSocket(webSocketContext.WebSocket, _logger), Endpoint = ctx.Request.RemoteEndPoint.ToString() });
+ return;
}
- }
- catch (Exception ex)
- {
- _logger.ErrorException("AcceptWebSocketAsync error", ex);
- ctx.Response.StatusCode = 500;
- ctx.Response.Close();
- }
-#endif
- }
- public static void HandleError(Exception ex, HttpListenerContext context, ILogger logger)
- {
- try
- {
var errorResponse = new ErrorResponse
{
ResponseStatus = new ResponseStatus
@@ -371,9 +199,6 @@ namespace MediaBrowser.Server.Implementations.HttpServer
}
};
- var operationName = context.Request.GetOperationName();
- var httpReq = GetRequest(context, operationName);
- var httpRes = httpReq.Response;
var contentType = httpReq.ResponseContentType;
var serializer = HostContext.ContentTypes.GetResponseSerializer(contentType);
@@ -402,48 +227,61 @@ namespace MediaBrowser.Server.Implementations.HttpServer
}
catch (Exception errorEx)
{
- logger.ErrorException("Error this.ProcessRequest(context)(Exception while writing error to the response)", errorEx);
+ _logger.ErrorException("Error this.ProcessRequest(context)(Exception while writing error to the response)", errorEx);
}
}
- private static ListenerRequest GetRequest(HttpListenerContext httpContext, string operationName)
- {
- var req = new ListenerRequest(httpContext, operationName, RequestAttributes.None);
- req.RequestAttributes = req.GetAttributes();
- return req;
- }
-
/// <summary>
/// Shut down the Web Service
/// </summary>
public void Stop()
{
- if (Listener != null)
+ if (_listener != null)
{
- foreach (var prefix in UrlPrefixes)
- {
- Listener.Prefixes.Remove(prefix);
- }
-
- Listener.Close();
+ _listener.Stop();
}
}
/// <summary>
/// Overridable method that can be used to implement a custom hnandler
/// </summary>
- /// <param name="context"></param>
- protected Task ProcessRequestAsync(HttpListenerContext context)
+ /// <param name="httpReq">The HTTP req.</param>
+ /// <param name="url">The URL.</param>
+ /// <returns>Task.</returns>
+ protected Task RequestHandler(IHttpRequest httpReq, Uri url)
{
- if (string.IsNullOrEmpty(context.Request.RawUrl))
- return ((object)null).AsTaskResult();
-
- var operationName = context.Request.GetOperationName();
+ var date = DateTime.Now;
- var httpReq = GetRequest(context, operationName);
var httpRes = httpReq.Response;
+
+ var operationName = httpReq.OperationName;
+ var localPath = url.LocalPath;
+
+ if (string.Equals(localPath, "/" + HandlerPath + "/", StringComparison.OrdinalIgnoreCase))
+ {
+ httpRes.RedirectToUrl(DefaultRedirectPath);
+ return Task.FromResult(true);
+ }
+ if (string.Equals(localPath, "/" + HandlerPath, StringComparison.OrdinalIgnoreCase))
+ {
+ httpRes.RedirectToUrl(HandlerPath + "/" + DefaultRedirectPath);
+ return Task.FromResult(true);
+ }
+ if (string.Equals(localPath, "/", StringComparison.OrdinalIgnoreCase))
+ {
+ httpRes.RedirectToUrl(HandlerPath + "/" + DefaultRedirectPath);
+ return Task.FromResult(true);
+ }
+ if (string.IsNullOrEmpty(localPath))
+ {
+ httpRes.RedirectToUrl("/" + HandlerPath + "/" + DefaultRedirectPath);
+ return Task.FromResult(true);
+ }
+
var handler = HttpHandlerFactory.GetHandler(httpReq);
+ var remoteIp = httpReq.RemoteIp;
+
var serviceStackHandler = handler as IServiceStackHandler;
if (serviceStackHandler != null)
{
@@ -454,8 +292,21 @@ namespace MediaBrowser.Server.Implementations.HttpServer
}
var task = serviceStackHandler.ProcessRequestAsync(httpReq, httpRes, operationName);
- task.ContinueWith(x => httpRes.Close());
+ task.ContinueWith(x => httpRes.Close(), TaskContinuationOptions.OnlyOnRanToCompletion | TaskContinuationOptions.AttachedToParent);
+ //Matches Exceptions handled in HttpListenerBase.InitTask()
+
+ var urlString = url.ToString();
+
+ task.ContinueWith(x =>
+ {
+ var statusCode = httpRes.StatusCode;
+
+ var duration = DateTime.Now - date;
+
+ LoggerUtils.LogResponse(_logger, statusCode, urlString, remoteIp, duration);
+
+ }, TaskContinuationOptions.None);
return task;
}
@@ -464,12 +315,6 @@ namespace MediaBrowser.Server.Implementations.HttpServer
}
/// <summary>
- /// Gets or sets a value indicating whether [enable HTTP request logging].
- /// </summary>
- /// <value><c>true</c> if [enable HTTP request logging]; otherwise, <c>false</c>.</value>
- public bool EnableHttpRequestLogging { get; set; }
-
- /// <summary>
/// Adds the rest handlers.
/// </summary>
/// <param name="services">The services.</param>
@@ -484,6 +329,13 @@ namespace MediaBrowser.Server.Implementations.HttpServer
base.Init();
}
+ //public override RouteAttribute[] GetRouteAttributes(System.Type requestType)
+ //{
+ // var routes = base.GetRouteAttributes(requestType);
+ // routes.Each(x => x.Path = "/api" + x.Path);
+ // return routes;
+ //}
+
/// <summary>
/// Releases the specified instance.
/// </summary>
@@ -525,10 +377,5 @@ namespace MediaBrowser.Server.Implementations.HttpServer
UrlPrefixes = urlPrefixes.ToList();
Start(UrlPrefixes.First());
}
-
- public bool SupportsWebSockets
- {
- get { return NativeWebSocket.IsSupported; }
- }
}
} \ No newline at end of file