From bc657237aa4c541fe0079fcbb7616dbe87bbf0a7 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 18 Jul 2014 21:28:40 -0400 Subject: consolidate web socket onto one port --- .../SocketSharp/WebSocketSharpListener.cs | 192 +++++++++++++++++++++ 1 file changed, 192 insertions(+) create mode 100644 MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs (limited to 'MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs') diff --git a/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs b/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs new file mode 100644 index 000000000..cf756d9f2 --- /dev/null +++ b/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs @@ -0,0 +1,192 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Amib.Threading; +using MediaBrowser.Common.Net; +using MediaBrowser.Model.Logging; +using ServiceStack; +using ServiceStack.Web; +using WebSocketSharp.Net; +using WebSocketSharp.Server; + +namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp +{ + public class WebSocketSharpListener : IHttpListener + { + private readonly ConcurrentDictionary _localEndPoints = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); + private WebSocketSharp.Server.HttpServer _httpsv; + + private readonly ILogger _logger; + private readonly SmartThreadPool _threadPoolManager; + + public WebSocketSharpListener(ILogger logger, SmartThreadPool threadPoolManager) + { + _logger = logger; + _threadPoolManager = threadPoolManager; + } + + public IEnumerable LocalEndPoints + { + get { return _localEndPoints.Keys.ToList(); } + } + + public System.Action ErrorHandler { get; set; } + + public System.Func RequestHandler { get; set; } + + public Action WebSocketHandler { get; set; } + + public void Start(IEnumerable urlPrefixes) + { + _httpsv = new WebSocketSharp.Server.HttpServer(8096, false, urlPrefixes.First()); + + _httpsv.OnRequest += _httpsv_OnRequest; + + _httpsv.Start(); + } + + void _httpsv_OnRequest(object sender, HttpRequestEventArgs e) + { + _threadPoolManager.QueueWorkItem(() => InitTask(e.Context)); + } + + private void InitTask(HttpListenerContext context) + { + try + { + var task = this.ProcessRequestAsync(context); + task.ContinueWith(x => HandleError(x.Exception, context), TaskContinuationOptions.OnlyOnFaulted | TaskContinuationOptions.AttachedToParent); + + if (task.Status == TaskStatus.Created) + { + task.RunSynchronously(); + } + } + catch (Exception ex) + { + HandleError(ex, context); + } + } + + private Task ProcessRequestAsync(HttpListenerContext context) + { + var request = context.Request; + + LogHttpRequest(request); + + if (request.IsWebSocketRequest) + { + ProcessWebSocketRequest(context); + return Task.FromResult(true); + } + + if (string.IsNullOrEmpty(context.Request.RawUrl)) + return ((object)null).AsTaskResult(); + + var httpReq = GetRequest(context); + + return RequestHandler(httpReq, request.Url); + } + + /// + /// Logs the HTTP request. + /// + /// The request. + private void LogHttpRequest(HttpListenerRequest request) + { + var endpoint = request.LocalEndPoint; + + if (endpoint != null) + { + var address = endpoint.ToString(); + + _localEndPoints.GetOrAdd(address, address); + } + + LogRequest(_logger, request); + } + + private void ProcessWebSocketRequest(HttpListenerContext ctx) + { + try + { + var webSocketContext = ctx.AcceptWebSocket(null, null); + + if (WebSocketHandler != null) + { + WebSocketHandler(new WebSocketConnectEventArgs + { + WebSocket = new SharpWebSocket(webSocketContext.WebSocket, _logger), + Endpoint = ctx.Request.RemoteEndPoint.ToString() + }); + } + } + catch (Exception ex) + { + _logger.ErrorException("AcceptWebSocketAsync error", ex); + ctx.Response.StatusCode = 500; + ctx.Response.Close(); + } + } + + private IHttpRequest GetRequest(HttpListenerContext httpContext) + { + var operationName = httpContext.Request.GetOperationName(); + + var req = new WebSocketSharpRequest(httpContext, operationName, RequestAttributes.None, _logger); + req.RequestAttributes = req.GetAttributes(); + + return req; + } + + /// + /// Logs the request. + /// + /// The logger. + /// The request. + private static void LogRequest(ILogger logger, HttpListenerRequest request) + { + var log = new StringBuilder(); + + //var headers = string.Join(",", request.Headers.AllKeys.Where(i => !string.Equals(i, "cookie", StringComparison.OrdinalIgnoreCase) && !string.Equals(i, "Referer", StringComparison.OrdinalIgnoreCase)).Select(k => k + "=" + request.Headers[k])); + + //log.AppendLine("Ip: " + request.RemoteEndPoint + ". Headers: " + headers); + + var type = request.IsWebSocketRequest ? "Web Socket" : "HTTP " + request.HttpMethod; + + logger.LogMultiline(type + " " + request.Url, LogSeverity.Debug, log); + } + + private void HandleError(Exception ex, HttpListenerContext context) + { + var httpReq = GetRequest(context); + + if (ErrorHandler != null) + { + ErrorHandler(ex, httpReq); + } + } + + public void Stop() + { + _httpsv.Stop(); + } + + private readonly object _disposeLock = new object(); + public void Dispose() + { + lock (_disposeLock) + { + if (_httpsv != null) + { + _httpsv.OnRequest -= _httpsv_OnRequest; + _httpsv.Stop(); + _httpsv = null; + } + } + } + } +} -- cgit v1.2.3