using MediaBrowser.Controller.Net; using MediaBrowser.Model.Logging; using MediaBrowser.Server.Implementations.Logging; using ServiceStack; using ServiceStack.Web; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using SocketHttpListener.Net; namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp { public class WebSocketSharpListener : IHttpListener { private HttpListener _listener; private readonly ILogger _logger; private readonly Action _endpointListener; public WebSocketSharpListener(ILogger logger, Action endpointListener) { _logger = logger; _endpointListener = endpointListener; } public Action ErrorHandler { get; set; } public Func RequestHandler { get; set; } public Action WebSocketHandler { get; set; } public void Start(IEnumerable urlPrefixes) { if (_listener == null) _listener = new HttpListener(new PatternsLogger(_logger), null); foreach (var prefix in urlPrefixes) { _logger.Info("Adding HttpListener prefix " + prefix); _listener.Prefixes.Add(prefix); } _listener.OnContext = ProcessContext; _listener.Start(); } private void ProcessContext(HttpListenerContext context) { Task.Factory.StartNew(() => InitTask(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(); _endpointListener(address); } LogRequest(_logger, request); } private void ProcessWebSocketRequest(HttpListenerContext ctx) { try { var webSocketContext = ctx.AcceptWebSocket(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() { if (_listener != null) { foreach (var prefix in _listener.Prefixes.ToList()) { _listener.Prefixes.Remove(prefix); } _listener.Close(); } } public void Dispose() { Dispose(true); } private bool _disposed; private readonly object _disposeLock = new object(); protected virtual void Dispose(bool disposing) { if (_disposed) return; lock (_disposeLock) { if (_disposed) return; if (disposing) { Stop(); } //release unmanaged resources here... _disposed = true; } } } }