diff options
| author | stefan <stefan@hegedues.at> | 2018-09-12 19:26:21 +0200 |
|---|---|---|
| committer | stefan <stefan@hegedues.at> | 2018-09-12 19:26:21 +0200 |
| commit | 48facb797ed912e4ea6b04b17d1ff190ac2daac4 (patch) | |
| tree | 8dae77a31670a888d733484cb17dd4077d5444e8 /MediaBrowser.Server.Mono/SocketSharp/WebSocketSharpListener.cs | |
| parent | c32d8656382a0eacb301692e0084377fc433ae9b (diff) | |
Update to 3.5.2 and .net core 2.1
Diffstat (limited to 'MediaBrowser.Server.Mono/SocketSharp/WebSocketSharpListener.cs')
| -rw-r--r-- | MediaBrowser.Server.Mono/SocketSharp/WebSocketSharpListener.cs | 262 |
1 files changed, 262 insertions, 0 deletions
diff --git a/MediaBrowser.Server.Mono/SocketSharp/WebSocketSharpListener.cs b/MediaBrowser.Server.Mono/SocketSharp/WebSocketSharpListener.cs new file mode 100644 index 000000000..e75a38e38 --- /dev/null +++ b/MediaBrowser.Server.Mono/SocketSharp/WebSocketSharpListener.cs @@ -0,0 +1,262 @@ +using MediaBrowser.Controller.Net; +using MediaBrowser.Model.Logging; +using SocketHttpListener.Net; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography.X509Certificates; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Common.Net; +using MediaBrowser.Model.Cryptography; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.Net; +using MediaBrowser.Model.Services; +using MediaBrowser.Model.System; +using MediaBrowser.Model.Text; +using Emby.Server.Implementations.Net; +using Emby.Server.Implementations.HttpServer; + +namespace EmbyServer.SocketSharp +{ + public class WebSocketSharpListener : IHttpListener + { + private HttpListener _listener; + + private readonly ILogger _logger; + private readonly X509Certificate _certificate; + private readonly IStreamHelper _streamHelper; + private readonly ITextEncoding _textEncoding; + private readonly INetworkManager _networkManager; + private readonly ISocketFactory _socketFactory; + private readonly ICryptoProvider _cryptoProvider; + private readonly IFileSystem _fileSystem; + private readonly bool _enableDualMode; + private readonly IEnvironmentInfo _environment; + + private CancellationTokenSource _disposeCancellationTokenSource = new CancellationTokenSource(); + private CancellationToken _disposeCancellationToken; + + public WebSocketSharpListener(ILogger logger, X509Certificate certificate, IStreamHelper streamHelper, ITextEncoding textEncoding, INetworkManager networkManager, ISocketFactory socketFactory, ICryptoProvider cryptoProvider, bool enableDualMode, IFileSystem fileSystem, IEnvironmentInfo environment) + { + _logger = logger; + _certificate = certificate; + _streamHelper = streamHelper; + _textEncoding = textEncoding; + _networkManager = networkManager; + _socketFactory = socketFactory; + _cryptoProvider = cryptoProvider; + _enableDualMode = enableDualMode; + _fileSystem = fileSystem; + _environment = environment; + + _disposeCancellationToken = _disposeCancellationTokenSource.Token; + } + + public Func<Exception, IRequest, bool, bool, Task> ErrorHandler { get; set; } + public Func<IHttpRequest, string, string, string, CancellationToken, Task> RequestHandler { get; set; } + + public Action<WebSocketConnectingEventArgs> WebSocketConnecting { get; set; } + + public Action<WebSocketConnectEventArgs> WebSocketConnected { get; set; } + + public void Start(IEnumerable<string> urlPrefixes) + { + if (_listener == null) + _listener = new HttpListener(_logger, _cryptoProvider, _socketFactory, _networkManager, _textEncoding, _streamHelper, _fileSystem, _environment); + + _listener.EnableDualMode = _enableDualMode; + + if (_certificate != null) + { + _listener.LoadCert(_certificate); + } + + 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) + { + //InitTask(context, _disposeCancellationToken); + Task.Run(() => InitTask(context, _disposeCancellationToken)); + } + + private void LogRequest(ILogger logger, HttpListenerRequest request) + { + var url = request.Url.ToString(); + + logger.Info("{0} {1}. UserAgent: {2}", request.IsWebSocketRequest ? "WS" : "HTTP " + request.HttpMethod, url, request.UserAgent ?? string.Empty); + } + + private Task InitTask(HttpListenerContext context, CancellationToken cancellationToken) + { + IHttpRequest httpReq = null; + var request = context.Request; + + try + { + if (request.IsWebSocketRequest) + { + LogRequest(_logger, request); + + return ProcessWebSocketRequest(context); + } + + httpReq = GetRequest(context); + } + catch (Exception ex) + { + _logger.ErrorException("Error processing request", ex); + + httpReq = httpReq ?? GetRequest(context); + return ErrorHandler(ex, httpReq, true, true); + } + + var uri = request.Url; + + return RequestHandler(httpReq, uri.OriginalString, uri.Host, uri.LocalPath, cancellationToken); + } + + private async Task ProcessWebSocketRequest(HttpListenerContext ctx) + { + try + { + var endpoint = ctx.Request.RemoteEndPoint.ToString(); + var url = ctx.Request.RawUrl; + + var queryString = ctx.Request.QueryString; + + var connectingArgs = new WebSocketConnectingEventArgs + { + Url = url, + QueryString = queryString, + Endpoint = endpoint + }; + + if (WebSocketConnecting != null) + { + WebSocketConnecting(connectingArgs); + } + + if (connectingArgs.AllowConnection) + { + _logger.Debug("Web socket connection allowed"); + + var webSocketContext = await ctx.AcceptWebSocketAsync(null).ConfigureAwait(false); + + if (WebSocketConnected != null) + { + var socket = new SharpWebSocket(webSocketContext.WebSocket, _logger); + + WebSocketConnected(new WebSocketConnectEventArgs + { + Url = url, + QueryString = queryString, + WebSocket = socket, + Endpoint = endpoint + }); + + await ReceiveWebSocket(ctx, socket).ConfigureAwait(false); + } + } + else + { + _logger.Warn("Web socket connection not allowed"); + ctx.Response.StatusCode = 401; + ctx.Response.Close(); + } + } + catch (Exception ex) + { + _logger.ErrorException("AcceptWebSocketAsync error", ex); + ctx.Response.StatusCode = 500; + ctx.Response.Close(); + } + } + + private async Task ReceiveWebSocket(HttpListenerContext ctx, SharpWebSocket socket) + { + try + { + await socket.StartReceive().ConfigureAwait(false); + } + finally + { + TryClose(ctx, 200); + } + } + + private void TryClose(HttpListenerContext ctx, int statusCode) + { + try + { + ctx.Response.StatusCode = 200; + ctx.Response.Close(); + } + catch (ObjectDisposedException) + { + } + catch (Exception ex) + { + _logger.ErrorException("Error closing web socket response", ex); + } + } + + private IHttpRequest GetRequest(HttpListenerContext httpContext) + { + var urlSegments = httpContext.Request.Url.Segments; + + var operationName = urlSegments[urlSegments.Length - 1]; + + var req = new WebSocketSharpRequest(httpContext, operationName, _logger); + + return req; + } + + public Task Stop() + { + _disposeCancellationTokenSource.Cancel(); + + if (_listener != null) + { + _listener.Close(); + } + + return Task.CompletedTask; + } + + 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; + } + } + } + +}
\ No newline at end of file |
