From 78165d78a23c4f8f05706619c5021754e99097f6 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sat, 2 Sep 2017 22:42:13 -0400 Subject: [PATCH] update SocketHttpListener --- .../ApplicationHost.cs | 7 +- .../Emby.Server.Implementations.csproj | 1 - .../HttpServer/HttpListenerHost.cs | 8 +- .../SocketSharp/WebSocketSharpListener.cs | 9 +- .../SocketSharp/WebSocketSharpRequest.cs | 2 +- .../HttpServerFactory.cs | 36 +------- .../Net/NetAcceptSocket.cs | 57 ------------ MediaBrowser.Model/Net/IAcceptSocket.cs | 3 - SocketHttpListener/Net/EndPointListener.cs | 67 +++++++++++---- SocketHttpListener/Net/EndPointManager.cs | 22 ++--- SocketHttpListener/Net/HttpConnection.cs | 38 ++++---- SocketHttpListener/Net/HttpListener.cs | 19 ++-- SocketHttpListener/Net/HttpListenerRequest.cs | 19 ++-- .../Net/HttpResponseStream.Managed.cs | 59 +------------ SocketHttpListener/Net/ListenerPrefix.cs | 4 +- .../Net/SocketAcceptor.cs | 11 +-- .../HttpListenerWebSocketContext.cs | 4 +- .../Net/WebSockets/WebSocketContext.cs | 4 +- SocketHttpListener/Primitives/ICertificate.cs | 11 --- .../Primitives/IStreamFactory.cs | 17 ---- SocketHttpListener/SocketHttpListener.csproj | 4 +- SocketHttpListener/SocketStream.cs | 86 +++++++++++++++++++ 22 files changed, 214 insertions(+), 274 deletions(-) rename {Emby.Server.Implementations => SocketHttpListener}/Net/SocketAcceptor.cs (90%) delete mode 100644 SocketHttpListener/Primitives/ICertificate.cs delete mode 100644 SocketHttpListener/Primitives/IStreamFactory.cs create mode 100644 SocketHttpListener/SocketStream.cs diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index b3d39a520..b264cffe6 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -128,6 +128,7 @@ using MediaBrowser.Model.Events; using MediaBrowser.Model.Tasks; using MediaBrowser.Model.Threading; using StringExtensions = MediaBrowser.Controller.Extensions.StringExtensions; +using X509Certificate = System.Security.Cryptography.X509Certificates.X509Certificate; namespace Emby.Server.Implementations { @@ -1168,7 +1169,7 @@ namespace Emby.Server.Implementations } } - private ICertificate GetCertificate(CertificateInfo info) + private X509Certificate GetCertificate(CertificateInfo info) { var certificateLocation = info == null ? null : info.Path; @@ -1195,7 +1196,7 @@ namespace Emby.Server.Implementations return null; } - return new Certificate(localCert); + return localCert; } catch (Exception ex) { @@ -1584,7 +1585,7 @@ namespace Emby.Server.Implementations } private CertificateInfo CertificateInfo { get; set; } - private ICertificate Certificate { get; set; } + private X509Certificate Certificate { get; set; } private IEnumerable GetUrlPrefixes() { diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 84ec214c9..75a9d8588 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -440,7 +440,6 @@ - diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs index 482bd9a32..5da03c73e 100644 --- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs +++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; +using System.Security.Cryptography.X509Certificates; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -55,9 +56,8 @@ namespace Emby.Server.Implementations.HttpServer private readonly IFileSystem _fileSystem; private readonly IJsonSerializer _jsonSerializer; private readonly IXmlSerializer _xmlSerializer; - private readonly ICertificate _certificate; + private readonly X509Certificate _certificate; private readonly IEnvironmentInfo _environment; - private readonly IStreamFactory _streamFactory; private readonly Func> _funcParseFn; private readonly bool _enableDualModeSockets; @@ -71,7 +71,7 @@ namespace Emby.Server.Implementations.HttpServer ILogger logger, IServerConfigurationManager config, string serviceName, - string defaultRedirectPath, INetworkManager networkManager, IMemoryStreamFactory memoryStreamProvider, ITextEncoding textEncoding, ISocketFactory socketFactory, ICryptoProvider cryptoProvider, IJsonSerializer jsonSerializer, IXmlSerializer xmlSerializer, IEnvironmentInfo environment, ICertificate certificate, IStreamFactory streamFactory, Func> funcParseFn, bool enableDualModeSockets, IFileSystem fileSystem) + string defaultRedirectPath, INetworkManager networkManager, IMemoryStreamFactory memoryStreamProvider, ITextEncoding textEncoding, ISocketFactory socketFactory, ICryptoProvider cryptoProvider, IJsonSerializer jsonSerializer, IXmlSerializer xmlSerializer, IEnvironmentInfo environment, X509Certificate certificate, Func> funcParseFn, bool enableDualModeSockets, IFileSystem fileSystem) { Instance = this; @@ -86,7 +86,6 @@ namespace Emby.Server.Implementations.HttpServer _xmlSerializer = xmlSerializer; _environment = environment; _certificate = certificate; - _streamFactory = streamFactory; _funcParseFn = funcParseFn; _enableDualModeSockets = enableDualModeSockets; _fileSystem = fileSystem; @@ -204,7 +203,6 @@ namespace Emby.Server.Implementations.HttpServer _networkManager, _socketFactory, _cryptoProvider, - _streamFactory, _enableDualModeSockets, GetRequest, _fileSystem, diff --git a/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs b/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs index e648838b2..d400258bc 100644 --- a/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs +++ b/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs @@ -4,6 +4,7 @@ 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; @@ -22,13 +23,12 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp private HttpListener _listener; private readonly ILogger _logger; - private readonly ICertificate _certificate; + private readonly X509Certificate _certificate; private readonly IMemoryStreamFactory _memoryStreamProvider; private readonly ITextEncoding _textEncoding; private readonly INetworkManager _networkManager; private readonly ISocketFactory _socketFactory; private readonly ICryptoProvider _cryptoProvider; - private readonly IStreamFactory _streamFactory; private readonly IFileSystem _fileSystem; private readonly Func _httpRequestFactory; private readonly bool _enableDualMode; @@ -37,7 +37,7 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp private CancellationTokenSource _disposeCancellationTokenSource = new CancellationTokenSource(); private CancellationToken _disposeCancellationToken; - public WebSocketSharpListener(ILogger logger, ICertificate certificate, IMemoryStreamFactory memoryStreamProvider, ITextEncoding textEncoding, INetworkManager networkManager, ISocketFactory socketFactory, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, bool enableDualMode, Func httpRequestFactory, IFileSystem fileSystem, IEnvironmentInfo environment) + public WebSocketSharpListener(ILogger logger, X509Certificate certificate, IMemoryStreamFactory memoryStreamProvider, ITextEncoding textEncoding, INetworkManager networkManager, ISocketFactory socketFactory, ICryptoProvider cryptoProvider, bool enableDualMode, Func httpRequestFactory, IFileSystem fileSystem, IEnvironmentInfo environment) { _logger = logger; _certificate = certificate; @@ -46,7 +46,6 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp _networkManager = networkManager; _socketFactory = socketFactory; _cryptoProvider = cryptoProvider; - _streamFactory = streamFactory; _enableDualMode = enableDualMode; _httpRequestFactory = httpRequestFactory; _fileSystem = fileSystem; @@ -65,7 +64,7 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp public void Start(IEnumerable urlPrefixes) { if (_listener == null) - _listener = new HttpListener(_logger, _cryptoProvider, _streamFactory, _socketFactory, _networkManager, _textEncoding, _memoryStreamProvider, _fileSystem, _environment); + _listener = new HttpListener(_logger, _cryptoProvider, _socketFactory, _networkManager, _textEncoding, _memoryStreamProvider, _fileSystem, _environment); _listener.EnableDualMode = _enableDualMode; diff --git a/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpRequest.cs b/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpRequest.cs index 2dfe6a9e3..6cdc10286 100644 --- a/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpRequest.cs +++ b/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpRequest.cs @@ -108,7 +108,7 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp return remoteIp ?? (remoteIp = (CheckBadChars(XForwardedFor)) ?? (NormalizeIp(CheckBadChars(XRealIp)) ?? - (request.RemoteEndPoint != null ? NormalizeIp(request.RemoteEndPoint.IpAddress.ToString()) : null))); + (request.RemoteEndPoint != null ? NormalizeIp(request.RemoteEndPoint.Address.ToString()) : null))); } } diff --git a/Emby.Server.Implementations/HttpServerFactory.cs b/Emby.Server.Implementations/HttpServerFactory.cs index 007f5c829..717c50e7b 100644 --- a/Emby.Server.Implementations/HttpServerFactory.cs +++ b/Emby.Server.Implementations/HttpServerFactory.cs @@ -43,7 +43,7 @@ namespace Emby.Server.Implementations IJsonSerializer json, IXmlSerializer xml, IEnvironmentInfo environment, - ICertificate certificate, + X509Certificate certificate, IFileSystem fileSystem, bool enableDualModeSockets) { @@ -63,7 +63,6 @@ namespace Emby.Server.Implementations xml, environment, certificate, - new StreamFactory(), GetParseFn, enableDualModeSockets, fileSystem); @@ -74,37 +73,4 @@ namespace Emby.Server.Implementations return s => JsvReader.GetParseFn(propertyType)(s); } } - - public class StreamFactory : IStreamFactory - { - public Stream CreateNetworkStream(IAcceptSocket acceptSocket, bool ownsSocket) - { - var netSocket = (NetAcceptSocket)acceptSocket; - - return new SocketStream(netSocket.Socket, ownsSocket); - } - - public Task AuthenticateSslStreamAsServer(Stream stream, ICertificate certificate) - { - var sslStream = (SslStream)stream; - var cert = (Certificate)certificate; - - return sslStream.AuthenticateAsServerAsync(cert.X509Certificate); - } - - public Stream CreateSslStream(Stream innerStream, bool leaveInnerStreamOpen) - { - return new SslStream(innerStream, leaveInnerStreamOpen); - } - } - - public class Certificate : ICertificate - { - public Certificate(X509Certificate x509Certificate) - { - X509Certificate = x509Certificate; - } - - public X509Certificate X509Certificate { get; private set; } - } } diff --git a/Emby.Server.Implementations/Net/NetAcceptSocket.cs b/Emby.Server.Implementations/Net/NetAcceptSocket.cs index 936a66c0b..93638a367 100644 --- a/Emby.Server.Implementations/Net/NetAcceptSocket.cs +++ b/Emby.Server.Implementations/Net/NetAcceptSocket.cs @@ -89,63 +89,6 @@ namespace Emby.Server.Implementations.Net Socket.Bind(nativeEndpoint); } - private SocketAcceptor _acceptor; - public void StartAccept(Action onAccept, Func isClosed) - { - _acceptor = new SocketAcceptor(_logger, Socket, onAccept, isClosed, DualMode); - - _acceptor.StartAccept(); - } - - public Task SendFile(string path, byte[] preBuffer, byte[] postBuffer, CancellationToken cancellationToken) - { - var options = TransmitFileOptions.UseDefaultWorkerThread; - - var completionSource = new TaskCompletionSource(); - - var result = Socket.BeginSendFile(path, preBuffer, postBuffer, options, new AsyncCallback(FileSendCallback), new Tuple>(Socket, path, completionSource)); - - return completionSource.Task; - } - - public IAsyncResult BeginSendFile(string path, byte[] preBuffer, byte[] postBuffer, AsyncCallback callback, object state) - { - var options = TransmitFileOptions.UseDefaultWorkerThread; - - return Socket.BeginSendFile(path, preBuffer, postBuffer, options, new AsyncCallback(FileSendCallback), state); - } - - public void EndSendFile(IAsyncResult result) - { - Socket.EndSendFile(result); - } - - private void FileSendCallback(IAsyncResult ar) - { - // Retrieve the socket from the state object. - Tuple> data = (Tuple>)ar.AsyncState; - - var client = data.Item1; - var path = data.Item2; - var taskCompletion = data.Item3; - - // Complete sending the data to the remote device. - try - { - client.EndSendFile(ar); - taskCompletion.TrySetResult(true); - } - catch (SocketException ex) - { - _logger.Info("Socket.SendFile failed for {0}. error code {1}", path, ex.SocketErrorCode); - taskCompletion.TrySetException(ex); - } - catch (Exception ex) - { - taskCompletion.TrySetException(ex); - } - } - public void Dispose() { Socket.Dispose(); diff --git a/MediaBrowser.Model/Net/IAcceptSocket.cs b/MediaBrowser.Model/Net/IAcceptSocket.cs index 2b5d33dce..343e12ab6 100644 --- a/MediaBrowser.Model/Net/IAcceptSocket.cs +++ b/MediaBrowser.Model/Net/IAcceptSocket.cs @@ -12,9 +12,6 @@ namespace MediaBrowser.Model.Net void Listen(int backlog); void Bind(IpEndPointInfo endpoint); void Connect(IpEndPointInfo endPoint); - void StartAccept(Action onAccept, Func isClosed); - IAsyncResult BeginSendFile(string path, byte[] preBuffer, byte[] postBuffer, AsyncCallback callback, object state); - void EndSendFile(IAsyncResult result); } public class SocketCreateException : Exception diff --git a/SocketHttpListener/Net/EndPointListener.cs b/SocketHttpListener/Net/EndPointListener.cs index 2106bbec5..2b1479e42 100644 --- a/SocketHttpListener/Net/EndPointListener.cs +++ b/SocketHttpListener/Net/EndPointListener.cs @@ -3,6 +3,8 @@ using System.Collections; using System.Collections.Generic; using System.IO; using System.Net; +using System.Net.Sockets; +using System.Security.Cryptography.X509Certificates; using System.Threading; using MediaBrowser.Model.Cryptography; using MediaBrowser.Model.IO; @@ -11,37 +13,37 @@ using MediaBrowser.Model.Net; using MediaBrowser.Model.System; using MediaBrowser.Model.Text; using SocketHttpListener.Primitives; +using ProtocolType = MediaBrowser.Model.Net.ProtocolType; +using SocketType = MediaBrowser.Model.Net.SocketType; namespace SocketHttpListener.Net { sealed class EndPointListener { HttpListener listener; - IpEndPointInfo endpoint; - IAcceptSocket sock; - Dictionary prefixes; // Dictionary + IPEndPoint endpoint; + Socket sock; + Dictionary prefixes; // Dictionary List unhandled; // List unhandled; host = '*' List all; // List all; host = '+' - ICertificate cert; + X509Certificate cert; bool secure; Dictionary unregistered; private readonly ILogger _logger; private bool _closed; private bool _enableDualMode; private readonly ICryptoProvider _cryptoProvider; - private readonly IStreamFactory _streamFactory; private readonly ISocketFactory _socketFactory; private readonly ITextEncoding _textEncoding; private readonly IMemoryStreamFactory _memoryStreamFactory; private readonly IFileSystem _fileSystem; private readonly IEnvironmentInfo _environment; - public EndPointListener(HttpListener listener, IpAddressInfo addr, int port, bool secure, ICertificate cert, ILogger logger, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, ISocketFactory socketFactory, IMemoryStreamFactory memoryStreamFactory, ITextEncoding textEncoding, IFileSystem fileSystem, IEnvironmentInfo environment) + public EndPointListener(HttpListener listener, IPAddress addr, int port, bool secure, X509Certificate cert, ILogger logger, ICryptoProvider cryptoProvider, ISocketFactory socketFactory, IMemoryStreamFactory memoryStreamFactory, ITextEncoding textEncoding, IFileSystem fileSystem, IEnvironmentInfo environment) { this.listener = listener; _logger = logger; _cryptoProvider = cryptoProvider; - _streamFactory = streamFactory; _socketFactory = socketFactory; _memoryStreamFactory = memoryStreamFactory; _textEncoding = textEncoding; @@ -51,8 +53,8 @@ namespace SocketHttpListener.Net this.secure = secure; this.cert = cert; - _enableDualMode = addr.Equals(IpAddressInfo.IPv6Any); - endpoint = new IpEndPointInfo(addr, port); + _enableDualMode = addr.Equals(IPAddress.IPv6Any); + endpoint = new IPEndPoint(addr, port); prefixes = new Dictionary(); unregistered = new Dictionary(); @@ -72,18 +74,18 @@ namespace SocketHttpListener.Net { try { - sock = _socketFactory.CreateSocket(endpoint.IpAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp, _enableDualMode); + sock = CreateSocket(endpoint.Address.AddressFamily, _enableDualMode); } catch (SocketCreateException ex) { - if (_enableDualMode && endpoint.IpAddress.Equals(IpAddressInfo.IPv6Any) && - (string.Equals(ex.ErrorCode, "AddressFamilyNotSupported", StringComparison.OrdinalIgnoreCase) || + if (_enableDualMode && endpoint.Address.Equals(IPAddress.IPv6Any) && + (string.Equals(ex.ErrorCode, "AddressFamilyNotSupported", StringComparison.OrdinalIgnoreCase) || // mono on bsd is throwing this string.Equals(ex.ErrorCode, "ProtocolNotSupported", StringComparison.OrdinalIgnoreCase))) { - endpoint = new IpEndPointInfo(IpAddressInfo.Any, endpoint.Port); + endpoint = new IPEndPoint(IPAddress.Any, endpoint.Port); _enableDualMode = false; - sock = _socketFactory.CreateSocket(endpoint.IpAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp, _enableDualMode); + sock = CreateSocket(endpoint.Address.AddressFamily, _enableDualMode); } else { @@ -96,11 +98,42 @@ namespace SocketHttpListener.Net // This is the number TcpListener uses. sock.Listen(2147483647); - sock.StartAccept(ProcessAccept, () => _closed); + new SocketAcceptor(_logger, sock, ProcessAccept, () => _closed).StartAccept(); _closed = false; } - private async void ProcessAccept(IAcceptSocket accepted) + private Socket CreateSocket(AddressFamily addressFamily, bool dualMode) + { + try + { + var socket = new Socket(addressFamily, System.Net.Sockets.SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp); + + if (dualMode) + { + socket.DualMode = true; + } + + return socket; + } + catch (SocketException ex) + { + throw new SocketCreateException(ex.SocketErrorCode.ToString(), ex); + } + catch (ArgumentException ex) + { + if (dualMode) + { + // Mono for BSD incorrectly throws ArgumentException instead of SocketException + throw new SocketCreateException("AddressFamilyNotSupported", ex); + } + else + { + throw; + } + } + } + + private async void ProcessAccept(Socket accepted) { try { @@ -112,7 +145,7 @@ namespace SocketHttpListener.Net return; } - HttpConnection conn = await HttpConnection.Create(_logger, accepted, listener, listener.secure, listener.cert, _cryptoProvider, _streamFactory, _memoryStreamFactory, _textEncoding, _fileSystem, _environment).ConfigureAwait(false); + HttpConnection conn = await HttpConnection.Create(_logger, accepted, listener, listener.secure, listener.cert, _cryptoProvider, _memoryStreamFactory, _textEncoding, _fileSystem, _environment).ConfigureAwait(false); //_logger.Debug("Adding unregistered connection to {0}. Id: {1}", accepted.RemoteEndPoint, connectionId); lock (listener.unregistered) diff --git a/SocketHttpListener/Net/EndPointManager.cs b/SocketHttpListener/Net/EndPointManager.cs index ddb110ec1..557caa59a 100644 --- a/SocketHttpListener/Net/EndPointManager.cs +++ b/SocketHttpListener/Net/EndPointManager.cs @@ -66,25 +66,25 @@ namespace SocketHttpListener.Net epl.AddPrefix(lp, listener); } - private static IpAddressInfo GetIpAnyAddress(HttpListener listener) + private static IPAddress GetIpAnyAddress(HttpListener listener) { - return listener.EnableDualMode ? IpAddressInfo.IPv6Any : IpAddressInfo.Any; + return listener.EnableDualMode ? IPAddress.IPv6Any : IPAddress.Any; } static async Task GetEPListener(ILogger logger, string host, int port, HttpListener listener, bool secure) { var networkManager = listener.NetworkManager; - IpAddressInfo addr; + IPAddress addr; if (host == "*" || host == "+") addr = GetIpAnyAddress(listener); - else if (networkManager.TryParseIpAddress(host, out addr) == false) + else if (IPAddress.TryParse(host, out addr) == false) { try { var all = (await networkManager.GetHostAddressesAsync(host).ConfigureAwait(false)); - addr = (all.Length == 0 ? null : all[0]) ?? + addr = (all.Length == 0 ? null : IPAddress.Parse(all[0].Address)) ?? GetIpAnyAddress(listener); } catch @@ -94,10 +94,10 @@ namespace SocketHttpListener.Net } Dictionary p = null; // Dictionary - if (!ip_to_endpoints.TryGetValue(addr.Address, out p)) + if (!ip_to_endpoints.TryGetValue(addr.ToString(), out p)) { p = new Dictionary(); - ip_to_endpoints[addr.Address] = p; + ip_to_endpoints[addr.ToString()] = p; } EndPointListener epl = null; @@ -107,25 +107,25 @@ namespace SocketHttpListener.Net } else { - epl = new EndPointListener(listener, addr, port, secure, listener.Certificate, logger, listener.CryptoProvider, listener.StreamFactory, listener.SocketFactory, listener.MemoryStreamFactory, listener.TextEncoding, listener.FileSystem, listener.EnvironmentInfo); + epl = new EndPointListener(listener, addr, port, secure, listener.Certificate, logger, listener.CryptoProvider, listener.SocketFactory, listener.MemoryStreamFactory, listener.TextEncoding, listener.FileSystem, listener.EnvironmentInfo); p[port] = epl; } return epl; } - public static void RemoveEndPoint(EndPointListener epl, IpEndPointInfo ep) + public static void RemoveEndPoint(EndPointListener epl, IPEndPoint ep) { lock (ip_to_endpoints) { // Dictionary p Dictionary p; - if (ip_to_endpoints.TryGetValue(ep.IpAddress.Address, out p)) + if (ip_to_endpoints.TryGetValue(ep.Address.ToString(), out p)) { p.Remove(ep.Port); if (p.Count == 0) { - ip_to_endpoints.Remove(ep.IpAddress.Address); + ip_to_endpoints.Remove(ep.Address.ToString()); } } epl.Close(); diff --git a/SocketHttpListener/Net/HttpConnection.cs b/SocketHttpListener/Net/HttpConnection.cs index e66443c59..4e8158964 100644 --- a/SocketHttpListener/Net/HttpConnection.cs +++ b/SocketHttpListener/Net/HttpConnection.cs @@ -1,5 +1,9 @@ using System; using System.IO; +using System.Net; +using System.Net.Security; +using System.Net.Sockets; +using System.Security.Cryptography.X509Certificates; using System.Text; using System.Threading.Tasks; using MediaBrowser.Model.Cryptography; @@ -16,7 +20,7 @@ namespace SocketHttpListener.Net { private static AsyncCallback s_onreadCallback = new AsyncCallback(OnRead); const int BufferSize = 8192; - IAcceptSocket _socket; + Socket _socket; Stream _stream; EndPointListener _epl; MemoryStream _memoryStream; @@ -31,21 +35,20 @@ namespace SocketHttpListener.Net bool _contextBound; bool secure; int _timeout = 300000; // 90k ms for first request, 15k ms from then on - IpEndPointInfo local_ep; + IPEndPoint local_ep; HttpListener _lastListener; int[] client_cert_errors; - ICertificate cert; - Stream ssl_stream; + X509Certificate cert; + SslStream ssl_stream; private readonly ILogger _logger; private readonly ICryptoProvider _cryptoProvider; private readonly IMemoryStreamFactory _memoryStreamFactory; private readonly ITextEncoding _textEncoding; - private readonly IStreamFactory _streamFactory; private readonly IFileSystem _fileSystem; private readonly IEnvironmentInfo _environment; - private HttpConnection(ILogger logger, IAcceptSocket socket, EndPointListener epl, bool secure, ICertificate cert, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, IMemoryStreamFactory memoryStreamFactory, ITextEncoding textEncoding, IFileSystem fileSystem, IEnvironmentInfo environment) + private HttpConnection(ILogger logger, Socket socket, EndPointListener epl, bool secure, X509Certificate cert, ICryptoProvider cryptoProvider, IMemoryStreamFactory memoryStreamFactory, ITextEncoding textEncoding, IFileSystem fileSystem, IEnvironmentInfo environment) { _logger = logger; this._socket = socket; @@ -57,14 +60,13 @@ namespace SocketHttpListener.Net _textEncoding = textEncoding; _fileSystem = fileSystem; _environment = environment; - _streamFactory = streamFactory; } private async Task InitStream() { if (secure == false) { - _stream = _streamFactory.CreateNetworkStream(_socket, false); + _stream = new SocketStream(_socket, false); } else { @@ -81,16 +83,16 @@ namespace SocketHttpListener.Net //}); //_stream = ssl_stream.AuthenticatedStream; - ssl_stream = _streamFactory.CreateSslStream(_streamFactory.CreateNetworkStream(_socket, false), false); - await _streamFactory.AuthenticateSslStreamAsServer(ssl_stream, cert).ConfigureAwait(false); + ssl_stream = new SslStream(new SocketStream(_socket, false), false); + await ssl_stream.AuthenticateAsServerAsync(cert).ConfigureAwait(false); _stream = ssl_stream; } Init(); } - public static async Task Create(ILogger logger, IAcceptSocket sock, EndPointListener epl, bool secure, ICertificate cert, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, IMemoryStreamFactory memoryStreamFactory, ITextEncoding textEncoding, IFileSystem fileSystem, IEnvironmentInfo environment) + public static async Task Create(ILogger logger, Socket sock, EndPointListener epl, bool secure, X509Certificate cert, ICryptoProvider cryptoProvider, IMemoryStreamFactory memoryStreamFactory, ITextEncoding textEncoding, IFileSystem fileSystem, IEnvironmentInfo environment) { - var connection = new HttpConnection(logger, sock, epl, secure, cert, cryptoProvider, streamFactory, memoryStreamFactory, textEncoding, fileSystem, environment); + var connection = new HttpConnection(logger, sock, epl, secure, cert, cryptoProvider, memoryStreamFactory, textEncoding, fileSystem, environment); await connection.InitStream().ConfigureAwait(false); @@ -134,21 +136,21 @@ namespace SocketHttpListener.Net get { return _reuses; } } - public IpEndPointInfo LocalEndPoint + public IPEndPoint LocalEndPoint { get { if (local_ep != null) return local_ep; - local_ep = (IpEndPointInfo)_socket.LocalEndPoint; + local_ep = (IPEndPoint)_socket.LocalEndPoint; return local_ep; } } - public IpEndPointInfo RemoteEndPoint + public IPEndPoint RemoteEndPoint { - get { return (IpEndPointInfo)_socket.RemoteEndPoint; } + get { return _socket.RemoteEndPoint as IPEndPoint; } } public bool IsSecure @@ -513,12 +515,12 @@ namespace SocketHttpListener.Net return; } - IAcceptSocket s = _socket; + Socket s = _socket; _socket = null; try { if (s != null) - s.Shutdown(true); + s.Shutdown(SocketShutdown.Both); } catch { diff --git a/SocketHttpListener/Net/HttpListener.cs b/SocketHttpListener/Net/HttpListener.cs index b3e01425c..4ae472f9e 100644 --- a/SocketHttpListener/Net/HttpListener.cs +++ b/SocketHttpListener/Net/HttpListener.cs @@ -3,6 +3,7 @@ using System.Collections; using System.Collections.Generic; using System.IO; using System.Net; +using System.Security.Cryptography.X509Certificates; using MediaBrowser.Common.Net; using MediaBrowser.Model.Cryptography; using MediaBrowser.Model.IO; @@ -17,7 +18,6 @@ namespace SocketHttpListener.Net public sealed class HttpListener : IDisposable { internal ICryptoProvider CryptoProvider { get; private set; } - internal IStreamFactory StreamFactory { get; private set; } internal ISocketFactory SocketFactory { get; private set; } internal IFileSystem FileSystem { get; private set; } internal ITextEncoding TextEncoding { get; private set; } @@ -38,15 +38,14 @@ namespace SocketHttpListener.Net Dictionary registry; // Dictionary Dictionary connections; private ILogger _logger; - private ICertificate _certificate; + private X509Certificate _certificate; public Action OnContext { get; set; } - public HttpListener(ILogger logger, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, ISocketFactory socketFactory, INetworkManager networkManager, ITextEncoding textEncoding, IMemoryStreamFactory memoryStreamFactory, IFileSystem fileSystem, IEnvironmentInfo environmentInfo) + public HttpListener(ILogger logger, ICryptoProvider cryptoProvider, ISocketFactory socketFactory, INetworkManager networkManager, ITextEncoding textEncoding, IMemoryStreamFactory memoryStreamFactory, IFileSystem fileSystem, IEnvironmentInfo environmentInfo) { _logger = logger; CryptoProvider = cryptoProvider; - StreamFactory = streamFactory; SocketFactory = socketFactory; NetworkManager = networkManager; TextEncoding = textEncoding; @@ -59,18 +58,18 @@ namespace SocketHttpListener.Net auth_schemes = AuthenticationSchemes.Anonymous; } - public HttpListener(ICertificate certificate, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, ISocketFactory socketFactory, INetworkManager networkManager, ITextEncoding textEncoding, IMemoryStreamFactory memoryStreamFactory, IFileSystem fileSystem, IEnvironmentInfo environmentInfo) - :this(new NullLogger(), certificate, cryptoProvider, streamFactory, socketFactory, networkManager, textEncoding, memoryStreamFactory, fileSystem, environmentInfo) + public HttpListener(X509Certificate certificate, ICryptoProvider cryptoProvider, ISocketFactory socketFactory, INetworkManager networkManager, ITextEncoding textEncoding, IMemoryStreamFactory memoryStreamFactory, IFileSystem fileSystem, IEnvironmentInfo environmentInfo) + :this(new NullLogger(), certificate, cryptoProvider, socketFactory, networkManager, textEncoding, memoryStreamFactory, fileSystem, environmentInfo) { } - public HttpListener(ILogger logger, ICertificate certificate, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, ISocketFactory socketFactory, INetworkManager networkManager, ITextEncoding textEncoding, IMemoryStreamFactory memoryStreamFactory, IFileSystem fileSystem, IEnvironmentInfo environmentInfo) - : this(logger, cryptoProvider, streamFactory, socketFactory, networkManager, textEncoding, memoryStreamFactory, fileSystem, environmentInfo) + public HttpListener(ILogger logger, X509Certificate certificate, ICryptoProvider cryptoProvider, ISocketFactory socketFactory, INetworkManager networkManager, ITextEncoding textEncoding, IMemoryStreamFactory memoryStreamFactory, IFileSystem fileSystem, IEnvironmentInfo environmentInfo) + : this(logger, cryptoProvider, socketFactory, networkManager, textEncoding, memoryStreamFactory, fileSystem, environmentInfo) { _certificate = certificate; } - public void LoadCert(ICertificate cert) + public void LoadCert(X509Certificate cert) { _certificate = cert; } @@ -150,7 +149,7 @@ namespace SocketHttpListener.Net // } //} - internal ICertificate Certificate + internal X509Certificate Certificate { get { return _certificate; } } diff --git a/SocketHttpListener/Net/HttpListenerRequest.cs b/SocketHttpListener/Net/HttpListenerRequest.cs index f9df52593..5e391424f 100644 --- a/SocketHttpListener/Net/HttpListenerRequest.cs +++ b/SocketHttpListener/Net/HttpListenerRequest.cs @@ -3,6 +3,7 @@ using System.Collections.Specialized; using System.Globalization; using System.IO; using System.Net; +using System.Security.Cryptography.X509Certificates; using System.Text; using System.Threading.Tasks; using MediaBrowser.Model.Net; @@ -513,7 +514,14 @@ namespace SocketHttpListener.Net public bool IsLocal { - get { return RemoteEndPoint.IpAddress.Equals(IpAddressInfo.Loopback) || RemoteEndPoint.IpAddress.Equals(IpAddressInfo.IPv6Loopback) || LocalEndPoint.IpAddress.Equals(RemoteEndPoint.IpAddress); } + get + { + var remoteEndPoint = RemoteEndPoint; + + return remoteEndPoint.Address.Equals(IPAddress.Loopback) || + remoteEndPoint.Address.Equals(IPAddress.IPv6Loopback) || + LocalEndPoint.Address.Equals(remoteEndPoint.Address); + } } public bool IsSecureConnection @@ -557,7 +565,7 @@ namespace SocketHttpListener.Net } } - public IpEndPointInfo LocalEndPoint + public IPEndPoint LocalEndPoint { get { return context.Connection.LocalEndPoint; } } @@ -577,7 +585,7 @@ namespace SocketHttpListener.Net get { return raw_url; } } - public IpEndPointInfo RemoteEndPoint + public IPEndPoint RemoteEndPoint { get { return context.Connection.RemoteEndPoint; } } @@ -651,10 +659,5 @@ namespace SocketHttpListener.Net return _websocketRequest; } } - - public Task GetClientCertificateAsync() - { - return Task.FromResult(null); - } } } diff --git a/SocketHttpListener/Net/HttpResponseStream.Managed.cs b/SocketHttpListener/Net/HttpResponseStream.Managed.cs index b700c293d..d5ef0b320 100644 --- a/SocketHttpListener/Net/HttpResponseStream.Managed.cs +++ b/SocketHttpListener/Net/HttpResponseStream.Managed.cs @@ -51,13 +51,13 @@ namespace SocketHttpListener.Net private bool _trailer_sent; private Stream _stream; private readonly IMemoryStreamFactory _memoryStreamFactory; - private readonly IAcceptSocket _socket; + private readonly Socket _socket; private readonly bool _supportsDirectSocketAccess; private readonly IEnvironmentInfo _environment; private readonly IFileSystem _fileSystem; private readonly ILogger _logger; - internal HttpResponseStream(Stream stream, HttpListenerResponse response, bool ignore_errors, IMemoryStreamFactory memoryStreamFactory, IAcceptSocket socket, bool supportsDirectSocketAccess, IEnvironmentInfo environment, IFileSystem fileSystem, ILogger logger) + internal HttpResponseStream(Stream stream, HttpListenerResponse response, bool ignore_errors, IMemoryStreamFactory memoryStreamFactory, Socket socket, bool supportsDirectSocketAccess, IEnvironmentInfo environment, IFileSystem fileSystem, ILogger logger) { _response = response; _ignore_errors = ignore_errors; @@ -289,64 +289,9 @@ namespace SocketHttpListener.Net public Task TransmitFile(string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken) { - if (_supportsDirectSocketAccess && offset == 0 && count == 0 && !_response.SendChunked && _response.ContentLength64 > 8192) - { - if (EnableSendFileWithSocket) - { - return TransmitFileOverSocket(path, offset, count, fileShareMode, cancellationToken); - } - } return TransmitFileManaged(path, offset, count, fileShareMode, cancellationToken); } - private readonly byte[] _emptyBuffer = new byte[] { }; - private Task TransmitFileOverSocket(string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken) - { - var ms = GetHeaders(false); - - byte[] preBuffer; - if (ms != null) - { - using (var msCopy = new MemoryStream()) - { - ms.CopyTo(msCopy); - preBuffer = msCopy.ToArray(); - } - } - else - { - return TransmitFileManaged(path, offset, count, fileShareMode, cancellationToken); - } - - //_logger.Info("Socket sending file {0} {1}", path, response.ContentLength64); - - var taskCompletion = new TaskCompletionSource(); - - Action callback = callbackResult => - { - try - { - _socket.EndSendFile(callbackResult); - taskCompletion.TrySetResult(true); - } - catch (Exception ex) - { - taskCompletion.TrySetException(ex); - } - }; - - var result = _socket.BeginSendFile(path, preBuffer, _emptyBuffer, new AsyncCallback(callback), null); - - if (result.CompletedSynchronously) - { - callback(result); - } - - cancellationToken.Register(() => taskCompletion.TrySetCanceled()); - - return taskCompletion.Task; - } - const int StreamCopyToBufferSize = 81920; private async Task TransmitFileManaged(string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken) { diff --git a/SocketHttpListener/Net/ListenerPrefix.cs b/SocketHttpListener/Net/ListenerPrefix.cs index 2c314da50..605b7b88c 100644 --- a/SocketHttpListener/Net/ListenerPrefix.cs +++ b/SocketHttpListener/Net/ListenerPrefix.cs @@ -11,7 +11,7 @@ namespace SocketHttpListener.Net ushort port; string path; bool secure; - IpAddressInfo[] addresses; + IPAddress[] addresses; public HttpListener Listener; public ListenerPrefix(string prefix) @@ -25,7 +25,7 @@ namespace SocketHttpListener.Net return original; } - public IpAddressInfo[] Addresses + public IPAddress[] Addresses { get { return addresses; } set { addresses = value; } diff --git a/Emby.Server.Implementations/Net/SocketAcceptor.cs b/SocketHttpListener/Net/SocketAcceptor.cs similarity index 90% rename from Emby.Server.Implementations/Net/SocketAcceptor.cs rename to SocketHttpListener/Net/SocketAcceptor.cs index 288ba93ad..36332f52b 100644 --- a/Emby.Server.Implementations/Net/SocketAcceptor.cs +++ b/SocketHttpListener/Net/SocketAcceptor.cs @@ -1,19 +1,17 @@ using System; using System.Net.Sockets; using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Net; -namespace Emby.Server.Implementations.Net +namespace SocketHttpListener.Net { public class SocketAcceptor { private readonly ILogger _logger; private readonly Socket _originalSocket; private readonly Func _isClosed; - private readonly Action _onAccept; - private readonly bool _isDualMode; + private readonly Action _onAccept; - public SocketAcceptor(ILogger logger, Socket originalSocket, Action onAccept, Func isClosed, bool isDualMode) + public SocketAcceptor(ILogger logger, Socket originalSocket, Action onAccept, Func isClosed) { if (logger == null) { @@ -35,7 +33,6 @@ namespace Emby.Server.Implementations.Net _logger = logger; _originalSocket = originalSocket; _isClosed = isClosed; - _isDualMode = isDualMode; _onAccept = onAccept; } @@ -117,7 +114,7 @@ namespace Emby.Server.Implementations.Net if (acceptSocket != null) { //ProcessAccept(acceptSocket); - _onAccept(new NetAcceptSocket(acceptSocket, _logger, _isDualMode)); + _onAccept(acceptSocket); } // Accept the next connection request diff --git a/SocketHttpListener/Net/WebSockets/HttpListenerWebSocketContext.cs b/SocketHttpListener/Net/WebSockets/HttpListenerWebSocketContext.cs index 034ac17d2..803c67b83 100644 --- a/SocketHttpListener/Net/WebSockets/HttpListenerWebSocketContext.cs +++ b/SocketHttpListener/Net/WebSockets/HttpListenerWebSocketContext.cs @@ -254,7 +254,7 @@ namespace SocketHttpListener.Net.WebSockets /// /// /// - public override IpEndPointInfo ServerEndPoint + public override IPEndPoint ServerEndPoint { get { @@ -281,7 +281,7 @@ namespace SocketHttpListener.Net.WebSockets /// /// /// - public override IpEndPointInfo UserEndPoint + public override IPEndPoint UserEndPoint { get { diff --git a/SocketHttpListener/Net/WebSockets/WebSocketContext.cs b/SocketHttpListener/Net/WebSockets/WebSocketContext.cs index 3ffa6e639..9665ab789 100644 --- a/SocketHttpListener/Net/WebSockets/WebSocketContext.cs +++ b/SocketHttpListener/Net/WebSockets/WebSocketContext.cs @@ -151,7 +151,7 @@ namespace SocketHttpListener.Net.WebSockets /// /// A that represents the server endpoint. /// - public abstract IpEndPointInfo ServerEndPoint { get; } + public abstract IPEndPoint ServerEndPoint { get; } /// /// Gets the client information (identity, authentication, and security roles). @@ -167,7 +167,7 @@ namespace SocketHttpListener.Net.WebSockets /// /// A that represents the client endpoint. /// - public abstract IpEndPointInfo UserEndPoint { get; } + public abstract IPEndPoint UserEndPoint { get; } /// /// Gets the instance used for two-way communication diff --git a/SocketHttpListener/Primitives/ICertificate.cs b/SocketHttpListener/Primitives/ICertificate.cs deleted file mode 100644 index ec21e9445..000000000 --- a/SocketHttpListener/Primitives/ICertificate.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Threading.Tasks; - -namespace SocketHttpListener.Primitives -{ - public interface ICertificate - { - } -} diff --git a/SocketHttpListener/Primitives/IStreamFactory.cs b/SocketHttpListener/Primitives/IStreamFactory.cs deleted file mode 100644 index 4b623b940..000000000 --- a/SocketHttpListener/Primitives/IStreamFactory.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; -using System.Threading.Tasks; -using MediaBrowser.Model.Net; - -namespace SocketHttpListener.Primitives -{ - public interface IStreamFactory - { - Stream CreateNetworkStream(IAcceptSocket acceptSocket, bool ownsSocket); - Stream CreateSslStream(Stream innerStream, bool leaveInnerStreamOpen); - - Task AuthenticateSslStreamAsServer(Stream stream, ICertificate certificate); - } -} diff --git a/SocketHttpListener/SocketHttpListener.csproj b/SocketHttpListener/SocketHttpListener.csproj index 1aa788931..6ed42ea88 100644 --- a/SocketHttpListener/SocketHttpListener.csproj +++ b/SocketHttpListener/SocketHttpListener.csproj @@ -82,6 +82,7 @@ + @@ -89,11 +90,10 @@ - - + diff --git a/SocketHttpListener/SocketStream.cs b/SocketHttpListener/SocketStream.cs new file mode 100644 index 000000000..a4f31eab9 --- /dev/null +++ b/SocketHttpListener/SocketStream.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net.Sockets; +using System.Text; +using System.Threading.Tasks; + +namespace SocketHttpListener +{ + public class SocketStream : Stream + { + private readonly Socket _socket; + + public SocketStream(Socket socket, bool ownsSocket) + { + _socket = socket; + } + + public override void Flush() + { + } + + public override bool CanRead + { + get { return true; } + } + public override bool CanSeek + { + get { return false; } + } + public override bool CanWrite + { + get { return true; } + } + public override long Length + { + get { throw new NotImplementedException(); } + } + public override long Position + { + get { throw new NotImplementedException(); } + set { throw new NotImplementedException(); } + } + + public override void Write(byte[] buffer, int offset, int count) + { + _socket.Send(buffer, offset, count, SocketFlags.None); + } + + public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) + { + return _socket.BeginSend(buffer, offset, count, SocketFlags.None, callback, state); + } + + public override void EndWrite(IAsyncResult asyncResult) + { + _socket.EndSend(asyncResult); + } + + public override void SetLength(long value) + { + throw new NotImplementedException(); + } + + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotImplementedException(); + } + + public override int Read(byte[] buffer, int offset, int count) + { + return _socket.Receive(buffer, offset, count, SocketFlags.None); + } + + public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state) + { + return _socket.BeginReceive(buffer, offset, count, SocketFlags.None, callback, state); + } + + public override int EndRead(IAsyncResult asyncResult) + { + return _socket.EndReceive(asyncResult); + } + } +}