Merge pull request #2862 from MediaBrowser/dev

Dev
This commit is contained in:
Luke 2017-09-03 03:29:18 -04:00 committed by GitHub
commit 9cf5cd1e5b
29 changed files with 329 additions and 385 deletions

View File

@ -128,6 +128,7 @@ using MediaBrowser.Model.Events;
using MediaBrowser.Model.Tasks; using MediaBrowser.Model.Tasks;
using MediaBrowser.Model.Threading; using MediaBrowser.Model.Threading;
using StringExtensions = MediaBrowser.Controller.Extensions.StringExtensions; using StringExtensions = MediaBrowser.Controller.Extensions.StringExtensions;
using X509Certificate = System.Security.Cryptography.X509Certificates.X509Certificate;
namespace Emby.Server.Implementations 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; var certificateLocation = info == null ? null : info.Path;
@ -1195,7 +1196,7 @@ namespace Emby.Server.Implementations
return null; return null;
} }
return new Certificate(localCert); return localCert;
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -1584,7 +1585,7 @@ namespace Emby.Server.Implementations
} }
private CertificateInfo CertificateInfo { get; set; } private CertificateInfo CertificateInfo { get; set; }
private ICertificate Certificate { get; set; } private X509Certificate Certificate { get; set; }
private IEnumerable<string> GetUrlPrefixes() private IEnumerable<string> GetUrlPrefixes()
{ {

View File

@ -440,7 +440,6 @@
<Compile Include="Networking\NetworkManager.cs" /> <Compile Include="Networking\NetworkManager.cs" />
<Compile Include="Net\DisposableManagedObjectBase.cs" /> <Compile Include="Net\DisposableManagedObjectBase.cs" />
<Compile Include="Net\NetAcceptSocket.cs" /> <Compile Include="Net\NetAcceptSocket.cs" />
<Compile Include="Net\SocketAcceptor.cs" />
<Compile Include="Net\SocketFactory.cs" /> <Compile Include="Net\SocketFactory.cs" />
<Compile Include="Net\UdpSocket.cs" /> <Compile Include="Net\UdpSocket.cs" />
<Compile Include="News\NewsEntryPoint.cs" /> <Compile Include="News\NewsEntryPoint.cs" />

View File

@ -7,6 +7,7 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Security.Cryptography.X509Certificates;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -23,8 +24,6 @@ using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Services; using MediaBrowser.Model.Services;
using MediaBrowser.Model.System; using MediaBrowser.Model.System;
using MediaBrowser.Model.Text; using MediaBrowser.Model.Text;
using SocketHttpListener.Net;
using SocketHttpListener.Primitives;
namespace Emby.Server.Implementations.HttpServer namespace Emby.Server.Implementations.HttpServer
{ {
@ -55,9 +54,8 @@ namespace Emby.Server.Implementations.HttpServer
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
private readonly IJsonSerializer _jsonSerializer; private readonly IJsonSerializer _jsonSerializer;
private readonly IXmlSerializer _xmlSerializer; private readonly IXmlSerializer _xmlSerializer;
private readonly ICertificate _certificate; private readonly X509Certificate _certificate;
private readonly IEnvironmentInfo _environment; private readonly IEnvironmentInfo _environment;
private readonly IStreamFactory _streamFactory;
private readonly Func<Type, Func<string, object>> _funcParseFn; private readonly Func<Type, Func<string, object>> _funcParseFn;
private readonly bool _enableDualModeSockets; private readonly bool _enableDualModeSockets;
@ -71,7 +69,7 @@ namespace Emby.Server.Implementations.HttpServer
ILogger logger, ILogger logger,
IServerConfigurationManager config, IServerConfigurationManager config,
string serviceName, 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<Type, Func<string, object>> 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<Type, Func<string, object>> funcParseFn, bool enableDualModeSockets, IFileSystem fileSystem)
{ {
Instance = this; Instance = this;
@ -86,7 +84,6 @@ namespace Emby.Server.Implementations.HttpServer
_xmlSerializer = xmlSerializer; _xmlSerializer = xmlSerializer;
_environment = environment; _environment = environment;
_certificate = certificate; _certificate = certificate;
_streamFactory = streamFactory;
_funcParseFn = funcParseFn; _funcParseFn = funcParseFn;
_enableDualModeSockets = enableDualModeSockets; _enableDualModeSockets = enableDualModeSockets;
_fileSystem = fileSystem; _fileSystem = fileSystem;
@ -183,20 +180,10 @@ namespace Emby.Server.Implementations.HttpServer
return attributes; return attributes;
} }
public static string GetHandlerPathIfAny(string listenerUrl)
{
if (listenerUrl == null) return null;
var pos = listenerUrl.IndexOf("://", StringComparison.OrdinalIgnoreCase);
if (pos == -1) return null;
var startHostUrl = listenerUrl.Substring(pos + "://".Length);
var endPos = startHostUrl.IndexOf('/');
if (endPos == -1) return null;
var endHostUrl = startHostUrl.Substring(endPos + 1);
return string.IsNullOrEmpty(endHostUrl) ? null : endHostUrl.TrimEnd('/');
}
private IHttpListener GetListener() private IHttpListener GetListener()
{ {
//return new KestrelHost.KestrelListener(_logger, _environment, _fileSystem);
return new WebSocketSharpListener(_logger, return new WebSocketSharpListener(_logger,
_certificate, _certificate,
_memoryStreamProvider, _memoryStreamProvider,
@ -204,22 +191,11 @@ namespace Emby.Server.Implementations.HttpServer
_networkManager, _networkManager,
_socketFactory, _socketFactory,
_cryptoProvider, _cryptoProvider,
_streamFactory,
_enableDualModeSockets, _enableDualModeSockets,
GetRequest,
_fileSystem, _fileSystem,
_environment); _environment);
} }
private IHttpRequest GetRequest(HttpListenerContext httpContext)
{
var operationName = httpContext.Request.GetOperationName();
var req = new WebSocketSharpRequest(httpContext, operationName, _logger, _memoryStreamProvider);
return req;
}
private void OnWebSocketConnecting(WebSocketConnectingEventArgs args) private void OnWebSocketConnecting(WebSocketConnectingEventArgs args)
{ {
if (_disposed) if (_disposed)
@ -332,7 +308,8 @@ namespace Emby.Server.Implementations.HttpServer
if (_listener != null) if (_listener != null)
{ {
_logger.Info("Stopping HttpListener..."); _logger.Info("Stopping HttpListener...");
_listener.Stop(); var task = _listener.Stop();
Task.WaitAll(task);
_logger.Info("HttpListener stopped"); _logger.Info("HttpListener stopped");
} }
} }
@ -418,7 +395,7 @@ namespace Emby.Server.Implementations.HttpServer
return address.Trim('/'); return address.Trim('/');
} }
private bool ValidateHost(Uri url) private bool ValidateHost(string host)
{ {
var hosts = _config var hosts = _config
.Configuration .Configuration
@ -431,7 +408,7 @@ namespace Emby.Server.Implementations.HttpServer
return true; return true;
} }
var host = url.Host ?? string.Empty; host = host ?? string.Empty;
_logger.Debug("Validating host {0}", host); _logger.Debug("Validating host {0}", host);
@ -449,7 +426,7 @@ namespace Emby.Server.Implementations.HttpServer
/// <summary> /// <summary>
/// Overridable method that can be used to implement a custom hnandler /// Overridable method that can be used to implement a custom hnandler
/// </summary> /// </summary>
protected async Task RequestHandler(IHttpRequest httpReq, Uri url, CancellationToken cancellationToken) protected async Task RequestHandler(IHttpRequest httpReq, string urlString, string host, string localPath, CancellationToken cancellationToken)
{ {
var date = DateTime.Now; var date = DateTime.Now;
var httpRes = httpReq.Response; var httpRes = httpReq.Response;
@ -468,7 +445,7 @@ namespace Emby.Server.Implementations.HttpServer
return; return;
} }
if (!ValidateHost(url)) if (!ValidateHost(host))
{ {
httpRes.StatusCode = 400; httpRes.StatusCode = 400;
httpRes.ContentType = "text/plain"; httpRes.ContentType = "text/plain";
@ -488,9 +465,7 @@ namespace Emby.Server.Implementations.HttpServer
} }
var operationName = httpReq.OperationName; var operationName = httpReq.OperationName;
var localPath = url.LocalPath;
var urlString = url.OriginalString;
enableLog = EnableLogging(urlString, localPath); enableLog = EnableLogging(urlString, localPath);
urlToLog = urlString; urlToLog = urlString;
logHeaders = enableLog && urlToLog.IndexOf("/videos/", StringComparison.OrdinalIgnoreCase) != -1; logHeaders = enableLog && urlToLog.IndexOf("/videos/", StringComparison.OrdinalIgnoreCase) != -1;
@ -710,12 +685,19 @@ namespace Emby.Server.Implementations.HttpServer
Summary = route.Summary Summary = route.Summary
}); });
routes.Add(new RouteAttribute(DoubleNormalizeEmbyRoutePath(route.Path), route.Verbs) routes.Add(new RouteAttribute(NormalizeMediaBrowserRoutePath(route.Path), route.Verbs)
{ {
Notes = route.Notes, Notes = route.Notes,
Priority = route.Priority, Priority = route.Priority,
Summary = route.Summary Summary = route.Summary
}); });
//routes.Add(new RouteAttribute(DoubleNormalizeEmbyRoutePath(route.Path), route.Verbs)
//{
// Notes = route.Notes,
// Priority = route.Priority,
// Summary = route.Summary
//});
} }
return routes.ToArray(routes.Count); return routes.ToArray(routes.Count);
@ -756,6 +738,16 @@ namespace Emby.Server.Implementations.HttpServer
return "emby/" + path; return "emby/" + path;
} }
private string NormalizeMediaBrowserRoutePath(string path)
{
if (path.StartsWith("/", StringComparison.OrdinalIgnoreCase))
{
return "/mediabrowser" + path;
}
return "mediabrowser/" + path;
}
private string DoubleNormalizeEmbyRoutePath(string path) private string DoubleNormalizeEmbyRoutePath(string path)
{ {
if (path.StartsWith("/", StringComparison.OrdinalIgnoreCase)) if (path.StartsWith("/", StringComparison.OrdinalIgnoreCase))
@ -795,8 +787,6 @@ namespace Emby.Server.Implementations.HttpServer
{ {
UrlPrefixes = urlPrefixes; UrlPrefixes = urlPrefixes;
WebSocketSharpRequest.HandlerFactoryPath = GetHandlerPathIfAny(UrlPrefixes[0]);
_listener = GetListener(); _listener = GetListener();
_listener.WebSocketConnected = OnWebSocketConnected; _listener.WebSocketConnected = OnWebSocketConnected;

View File

@ -19,7 +19,7 @@ namespace Emby.Server.Implementations.HttpServer
/// Gets or sets the request handler. /// Gets or sets the request handler.
/// </summary> /// </summary>
/// <value>The request handler.</value> /// <value>The request handler.</value>
Func<IHttpRequest, Uri, CancellationToken, Task> RequestHandler { get; set; } Func<IHttpRequest, string, string, string, CancellationToken, Task> RequestHandler { get; set; }
/// <summary> /// <summary>
/// Gets or sets the web socket handler. /// Gets or sets the web socket handler.
@ -42,6 +42,6 @@ namespace Emby.Server.Implementations.HttpServer
/// <summary> /// <summary>
/// Stops this instance. /// Stops this instance.
/// </summary> /// </summary>
void Stop(); Task Stop();
} }
} }

View File

@ -4,6 +4,7 @@ using SocketHttpListener.Net;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Common.Net; using MediaBrowser.Common.Net;
@ -22,22 +23,20 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
private HttpListener _listener; private HttpListener _listener;
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly ICertificate _certificate; private readonly X509Certificate _certificate;
private readonly IMemoryStreamFactory _memoryStreamProvider; private readonly IMemoryStreamFactory _memoryStreamProvider;
private readonly ITextEncoding _textEncoding; private readonly ITextEncoding _textEncoding;
private readonly INetworkManager _networkManager; private readonly INetworkManager _networkManager;
private readonly ISocketFactory _socketFactory; private readonly ISocketFactory _socketFactory;
private readonly ICryptoProvider _cryptoProvider; private readonly ICryptoProvider _cryptoProvider;
private readonly IStreamFactory _streamFactory;
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
private readonly Func<HttpListenerContext, IHttpRequest> _httpRequestFactory;
private readonly bool _enableDualMode; private readonly bool _enableDualMode;
private readonly IEnvironmentInfo _environment; private readonly IEnvironmentInfo _environment;
private CancellationTokenSource _disposeCancellationTokenSource = new CancellationTokenSource(); private CancellationTokenSource _disposeCancellationTokenSource = new CancellationTokenSource();
private CancellationToken _disposeCancellationToken; private CancellationToken _disposeCancellationToken;
public WebSocketSharpListener(ILogger logger, ICertificate certificate, IMemoryStreamFactory memoryStreamProvider, ITextEncoding textEncoding, INetworkManager networkManager, ISocketFactory socketFactory, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, bool enableDualMode, Func<HttpListenerContext, IHttpRequest> httpRequestFactory, IFileSystem fileSystem, IEnvironmentInfo environment) public WebSocketSharpListener(ILogger logger, X509Certificate certificate, IMemoryStreamFactory memoryStreamProvider, ITextEncoding textEncoding, INetworkManager networkManager, ISocketFactory socketFactory, ICryptoProvider cryptoProvider, bool enableDualMode, IFileSystem fileSystem, IEnvironmentInfo environment)
{ {
_logger = logger; _logger = logger;
_certificate = certificate; _certificate = certificate;
@ -46,9 +45,7 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
_networkManager = networkManager; _networkManager = networkManager;
_socketFactory = socketFactory; _socketFactory = socketFactory;
_cryptoProvider = cryptoProvider; _cryptoProvider = cryptoProvider;
_streamFactory = streamFactory;
_enableDualMode = enableDualMode; _enableDualMode = enableDualMode;
_httpRequestFactory = httpRequestFactory;
_fileSystem = fileSystem; _fileSystem = fileSystem;
_environment = environment; _environment = environment;
@ -56,7 +53,7 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
} }
public Action<Exception, IRequest, bool> ErrorHandler { get; set; } public Action<Exception, IRequest, bool> ErrorHandler { get; set; }
public Func<IHttpRequest, Uri, CancellationToken, Task> RequestHandler { get; set; } public Func<IHttpRequest, string, string, string, CancellationToken, Task> RequestHandler { get; set; }
public Action<WebSocketConnectingEventArgs> WebSocketConnecting { get; set; } public Action<WebSocketConnectingEventArgs> WebSocketConnecting { get; set; }
@ -65,7 +62,7 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
public void Start(IEnumerable<string> urlPrefixes) public void Start(IEnumerable<string> urlPrefixes)
{ {
if (_listener == null) 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; _listener.EnableDualMode = _enableDualMode;
@ -117,7 +114,9 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
return Task.FromResult(true); return Task.FromResult(true);
} }
return RequestHandler(httpReq, request.Url, cancellationToken); var uri = request.Url;
return RequestHandler(httpReq, uri.OriginalString, uri.Host, uri.LocalPath, cancellationToken);
} }
private void ProcessWebSocketRequest(HttpListenerContext ctx) private void ProcessWebSocketRequest(HttpListenerContext ctx)
@ -173,10 +172,14 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
private IHttpRequest GetRequest(HttpListenerContext httpContext) private IHttpRequest GetRequest(HttpListenerContext httpContext)
{ {
return _httpRequestFactory(httpContext); var operationName = httpContext.Request.GetOperationName();
var req = new WebSocketSharpRequest(httpContext, operationName, _logger, _memoryStreamProvider);
return req;
} }
public void Stop() public Task Stop()
{ {
_disposeCancellationTokenSource.Cancel(); _disposeCancellationTokenSource.Cancel();
@ -189,6 +192,8 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
_listener.Close(); _listener.Close();
} }
return Task.FromResult(true);
} }
public void Dispose() public void Dispose()

View File

@ -27,6 +27,20 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
_memoryStreamProvider = memoryStreamProvider; _memoryStreamProvider = memoryStreamProvider;
this.request = httpContext.Request; this.request = httpContext.Request;
this.response = new WebSocketSharpResponse(logger, httpContext.Response, this); this.response = new WebSocketSharpResponse(logger, httpContext.Response, this);
//HandlerFactoryPath = GetHandlerPathIfAny(UrlPrefixes[0]);
}
private static string GetHandlerPathIfAny(string listenerUrl)
{
if (listenerUrl == null) return null;
var pos = listenerUrl.IndexOf("://", StringComparison.OrdinalIgnoreCase);
if (pos == -1) return null;
var startHostUrl = listenerUrl.Substring(pos + "://".Length);
var endPos = startHostUrl.IndexOf('/');
if (endPos == -1) return null;
var endHostUrl = startHostUrl.Substring(endPos + 1);
return string.IsNullOrEmpty(endHostUrl) ? null : endHostUrl.TrimEnd('/');
} }
public HttpListenerRequest HttpRequest public HttpListenerRequest HttpRequest
@ -108,7 +122,7 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
return remoteIp ?? return remoteIp ??
(remoteIp = (CheckBadChars(XForwardedFor)) ?? (remoteIp = (CheckBadChars(XForwardedFor)) ??
(NormalizeIp(CheckBadChars(XRealIp)) ?? (NormalizeIp(CheckBadChars(XRealIp)) ??
(request.RemoteEndPoint != null ? NormalizeIp(request.RemoteEndPoint.IpAddress.ToString()) : null))); (request.RemoteEndPoint != null ? NormalizeIp(request.RemoteEndPoint.Address.ToString()) : null)));
} }
} }
@ -232,13 +246,12 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
set set
{ {
this.responseContentType = value; this.responseContentType = value;
HasExplicitResponseContentType = true;
} }
} }
public const string FormUrlEncoded = "application/x-www-form-urlencoded"; public const string FormUrlEncoded = "application/x-www-form-urlencoded";
public const string MultiPartFormData = "multipart/form-data"; public const string MultiPartFormData = "multipart/form-data";
private static string GetResponseContentType(IRequest httpReq) public static string GetResponseContentType(IRequest httpReq)
{ {
var specifiedContentType = GetQueryStringContentType(httpReq); var specifiedContentType = GetQueryStringContentType(httpReq);
if (!string.IsNullOrEmpty(specifiedContentType)) return specifiedContentType; if (!string.IsNullOrEmpty(specifiedContentType)) return specifiedContentType;
@ -346,8 +359,6 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
: strVal.Substring(0, pos); : strVal.Substring(0, pos);
} }
public bool HasExplicitResponseContentType { get; private set; }
public static string HandlerFactoryPath; public static string HandlerFactoryPath;
private string pathInfo; private string pathInfo;
@ -490,13 +501,6 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
get { return HttpMethod; } get { return HttpMethod; }
} }
public string Param(string name)
{
return Headers[name]
?? QueryString[name]
?? FormData[name];
}
public string ContentType public string ContentType
{ {
get { return request.ContentType; } get { return request.ContentType; }
@ -584,18 +588,6 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
return stream; return stream;
} }
public static string GetHandlerPathIfAny(string listenerUrl)
{
if (listenerUrl == null) return null;
var pos = listenerUrl.IndexOf("://", StringComparison.OrdinalIgnoreCase);
if (pos == -1) return null;
var startHostUrl = listenerUrl.Substring(pos + "://".Length);
var endPos = startHostUrl.IndexOf('/');
if (endPos == -1) return null;
var endHostUrl = startHostUrl.Substring(endPos + 1);
return String.IsNullOrEmpty(endHostUrl) ? null : endHostUrl.TrimEnd('/');
}
public static string NormalizePathInfo(string pathInfo, string handlerPath) public static string NormalizePathInfo(string pathInfo, string handlerPath)
{ {
if (handlerPath != null && pathInfo.TrimStart('/').StartsWith( if (handlerPath != null && pathInfo.TrimStart('/').StartsWith(

View File

@ -29,7 +29,6 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
} }
public IRequest Request { get; private set; } public IRequest Request { get; private set; }
public bool UseBufferedStream { get; set; }
public Dictionary<string, object> Items { get; private set; } public Dictionary<string, object> Items { get; private set; }
public object OriginalResponse public object OriginalResponse
{ {

View File

@ -43,7 +43,7 @@ namespace Emby.Server.Implementations
IJsonSerializer json, IJsonSerializer json,
IXmlSerializer xml, IXmlSerializer xml,
IEnvironmentInfo environment, IEnvironmentInfo environment,
ICertificate certificate, X509Certificate certificate,
IFileSystem fileSystem, IFileSystem fileSystem,
bool enableDualModeSockets) bool enableDualModeSockets)
{ {
@ -63,7 +63,6 @@ namespace Emby.Server.Implementations
xml, xml,
environment, environment,
certificate, certificate,
new StreamFactory(),
GetParseFn, GetParseFn,
enableDualModeSockets, enableDualModeSockets,
fileSystem); fileSystem);
@ -74,37 +73,4 @@ namespace Emby.Server.Implementations
return s => JsvReader.GetParseFn(propertyType)(s); 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; }
}
} }

View File

@ -89,63 +89,6 @@ namespace Emby.Server.Implementations.Net
Socket.Bind(nativeEndpoint); Socket.Bind(nativeEndpoint);
} }
private SocketAcceptor _acceptor;
public void StartAccept(Action<IAcceptSocket> onAccept, Func<bool> 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<bool>();
var result = Socket.BeginSendFile(path, preBuffer, postBuffer, options, new AsyncCallback(FileSendCallback), new Tuple<Socket, string, TaskCompletionSource<bool>>(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<Socket, string, TaskCompletionSource<bool>> data = (Tuple<Socket, string, TaskCompletionSource<bool>>)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() public void Dispose()
{ {
Socket.Dispose(); Socket.Dispose();

View File

@ -45,10 +45,15 @@ namespace Emby.Server.Implementations.Services
var bytesResponse = this.Response as byte[]; var bytesResponse = this.Response as byte[];
if (bytesResponse != null) if (bytesResponse != null)
{ {
if (response != null) var contentLength = bytesResponse.Length;
response.SetContentLength(bytesResponse.Length);
await responseStream.WriteAsync(bytesResponse, 0, bytesResponse.Length).ConfigureAwait(false); if (response != null)
response.SetContentLength(contentLength);
if (contentLength > 0)
{
await responseStream.WriteAsync(bytesResponse, 0, contentLength).ConfigureAwait(false);
}
return; return;
} }

View File

@ -41,11 +41,11 @@ namespace Emby.Server.Implementations.Services
response.StatusCode = httpResult.Status; response.StatusCode = httpResult.Status;
response.StatusDescription = httpResult.StatusCode.ToString(); response.StatusDescription = httpResult.StatusCode.ToString();
if (string.IsNullOrEmpty(httpResult.ContentType)) //if (string.IsNullOrEmpty(httpResult.ContentType))
{ //{
httpResult.ContentType = defaultContentType; // httpResult.ContentType = defaultContentType;
} //}
response.ContentType = httpResult.ContentType; //response.ContentType = httpResult.ContentType;
if (httpResult.Cookies != null) if (httpResult.Cookies != null)
{ {
@ -124,7 +124,10 @@ namespace Emby.Server.Implementations.Services
response.ContentType = "application/octet-stream"; response.ContentType = "application/octet-stream";
response.SetContentLength(bytes.Length); response.SetContentLength(bytes.Length);
await response.OutputStream.WriteAsync(bytes, 0, bytes.Length, cancellationToken).ConfigureAwait(false); if (bytes.Length > 0)
{
await response.OutputStream.WriteAsync(bytes, 0, bytes.Length, cancellationToken).ConfigureAwait(false);
}
return; return;
} }
@ -133,7 +136,10 @@ namespace Emby.Server.Implementations.Services
{ {
bytes = Encoding.UTF8.GetBytes(responseText); bytes = Encoding.UTF8.GetBytes(responseText);
response.SetContentLength(bytes.Length); response.SetContentLength(bytes.Length);
await response.OutputStream.WriteAsync(bytes, 0, bytes.Length, cancellationToken).ConfigureAwait(false); if (bytes.Length > 0)
{
await response.OutputStream.WriteAsync(bytes, 0, bytes.Length, cancellationToken).ConfigureAwait(false);
}
return; return;
} }
@ -150,8 +156,15 @@ namespace Emby.Server.Implementations.Services
serializer(result, ms); serializer(result, ms);
ms.Position = 0; ms.Position = 0;
response.SetContentLength(ms.Length);
await ms.CopyToAsync(response.OutputStream).ConfigureAwait(false); var contentLength = ms.Length;
response.SetContentLength(contentLength);
if (contentLength > 0)
{
await ms.CopyToAsync(response.OutputStream).ConfigureAwait(false);
}
} }
//serializer(result, outputStream); //serializer(result, outputStream);

View File

@ -162,7 +162,11 @@ namespace Emby.Server.Implementations.Services
if (RequireqRequestStream(requestType)) if (RequireqRequestStream(requestType))
{ {
// Used by IRequiresRequestStream // Used by IRequiresRequestStream
return CreateRequiresRequestStreamRequest(host, httpReq, requestType); var request = ServiceHandler.CreateRequest(httpReq, restPath, GetRequestParams(httpReq), host.CreateInstance(requestType));
var rawReq = (IRequiresRequestStream)request;
rawReq.RequestStream = httpReq.InputStream;
return rawReq;
} }
var requestParams = GetFlattenedRequestParams(httpReq); var requestParams = GetFlattenedRequestParams(httpReq);
@ -176,16 +180,6 @@ namespace Emby.Server.Implementations.Services
return requiresRequestStreamTypeInfo.IsAssignableFrom(requestType.GetTypeInfo()); return requiresRequestStreamTypeInfo.IsAssignableFrom(requestType.GetTypeInfo());
} }
private static IRequiresRequestStream CreateRequiresRequestStreamRequest(HttpListenerHost host, IRequest req, Type requestType)
{
var restPath = GetRoute(req);
var request = ServiceHandler.CreateRequest(req, restPath, GetRequestParams(req), host.CreateInstance(requestType));
var rawReq = (IRequiresRequestStream)request;
rawReq.RequestStream = req.InputStream;
return rawReq;
}
public static object CreateRequest(HttpListenerHost host, IRequest httpReq, RestPath restPath, Dictionary<string, string> requestParams) public static object CreateRequest(HttpListenerHost host, IRequest httpReq, RestPath restPath, Dictionary<string, string> requestParams)
{ {
var requestDto = CreateContentTypeRequest(host, httpReq, restPath.RequestType, httpReq.ContentType); var requestDto = CreateContentTypeRequest(host, httpReq, restPath.RequestType, httpReq.ContentType);
@ -228,22 +222,26 @@ namespace Emby.Server.Implementations.Services
} }
} }
if ((IsMethod(request.Verb, "POST") || IsMethod(request.Verb, "PUT")) && request.FormData != null) if ((IsMethod(request.Verb, "POST") || IsMethod(request.Verb, "PUT")))
{ {
foreach (var name in request.FormData.Keys) var formData = request.FormData;
if (formData != null)
{ {
if (name == null) continue; //thank you ASP.NET foreach (var name in formData.Keys)
{
if (name == null) continue; //thank you ASP.NET
var values = request.FormData.GetValues(name); var values = formData.GetValues(name);
if (values.Count == 1) if (values.Count == 1)
{
map[name] = values[0];
}
else
{
for (var i = 0; i < values.Count; i++)
{ {
map[name + (i == 0 ? "" : "#" + i)] = values[i]; map[name] = values[0];
}
else
{
for (var i = 0; i < values.Count; i++)
{
map[name + (i == 0 ? "" : "#" + i)] = values[i];
}
} }
} }
} }
@ -270,12 +268,16 @@ namespace Emby.Server.Implementations.Services
map[name] = request.QueryString[name]; map[name] = request.QueryString[name];
} }
if ((IsMethod(request.Verb, "POST") || IsMethod(request.Verb, "PUT")) && request.FormData != null) if ((IsMethod(request.Verb, "POST") || IsMethod(request.Verb, "PUT")))
{ {
foreach (var name in request.FormData.Keys) var formData = request.FormData;
if (formData != null)
{ {
if (name == null) continue; //thank you ASP.NET foreach (var name in formData.Keys)
map[name] = request.FormData[name]; {
if (name == null) continue; //thank you ASP.NET
map[name] = formData[name];
}
} }
} }

View File

@ -12,9 +12,6 @@ namespace MediaBrowser.Model.Net
void Listen(int backlog); void Listen(int backlog);
void Bind(IpEndPointInfo endpoint); void Bind(IpEndPointInfo endpoint);
void Connect(IpEndPointInfo endPoint); void Connect(IpEndPointInfo endPoint);
void StartAccept(Action<IAcceptSocket> onAccept, Func<bool> isClosed);
IAsyncResult BeginSendFile(string path, byte[] preBuffer, byte[] postBuffer, AsyncCallback callback, object state);
void EndSendFile(IAsyncResult result);
} }
public class SocketCreateException : Exception public class SocketCreateException : Exception

View File

@ -48,11 +48,6 @@ namespace MediaBrowser.Model.Services
/// </summary> /// </summary>
string ResponseContentType { get; set; } string ResponseContentType { get; set; }
/// <summary>
/// Whether the ResponseContentType has been explicitly overrided or whether it was just the default
/// </summary>
bool HasExplicitResponseContentType { get; }
/// <summary> /// <summary>
/// Attach any data to this request that all filters and services can access. /// Attach any data to this request that all filters and services can access.
/// </summary> /// </summary>

View File

@ -1,3 +1,3 @@
using System.Reflection; using System.Reflection;
[assembly: AssemblyVersion("3.2.30.5")] [assembly: AssemblyVersion("3.2.30.6")]

View File

@ -3,6 +3,8 @@ using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Net; using System.Net;
using System.Net.Sockets;
using System.Security.Cryptography.X509Certificates;
using System.Threading; using System.Threading;
using MediaBrowser.Model.Cryptography; using MediaBrowser.Model.Cryptography;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
@ -11,37 +13,37 @@ using MediaBrowser.Model.Net;
using MediaBrowser.Model.System; using MediaBrowser.Model.System;
using MediaBrowser.Model.Text; using MediaBrowser.Model.Text;
using SocketHttpListener.Primitives; using SocketHttpListener.Primitives;
using ProtocolType = MediaBrowser.Model.Net.ProtocolType;
using SocketType = MediaBrowser.Model.Net.SocketType;
namespace SocketHttpListener.Net namespace SocketHttpListener.Net
{ {
sealed class EndPointListener sealed class EndPointListener
{ {
HttpListener listener; HttpListener listener;
IpEndPointInfo endpoint; IPEndPoint endpoint;
IAcceptSocket sock; Socket sock;
Dictionary<ListenerPrefix,HttpListener> prefixes; // Dictionary <ListenerPrefix, HttpListener> Dictionary<ListenerPrefix, HttpListener> prefixes; // Dictionary <ListenerPrefix, HttpListener>
List<ListenerPrefix> unhandled; // List<ListenerPrefix> unhandled; host = '*' List<ListenerPrefix> unhandled; // List<ListenerPrefix> unhandled; host = '*'
List<ListenerPrefix> all; // List<ListenerPrefix> all; host = '+' List<ListenerPrefix> all; // List<ListenerPrefix> all; host = '+'
ICertificate cert; X509Certificate cert;
bool secure; bool secure;
Dictionary<HttpConnection, HttpConnection> unregistered; Dictionary<HttpConnection, HttpConnection> unregistered;
private readonly ILogger _logger; private readonly ILogger _logger;
private bool _closed; private bool _closed;
private bool _enableDualMode; private bool _enableDualMode;
private readonly ICryptoProvider _cryptoProvider; private readonly ICryptoProvider _cryptoProvider;
private readonly IStreamFactory _streamFactory;
private readonly ISocketFactory _socketFactory; private readonly ISocketFactory _socketFactory;
private readonly ITextEncoding _textEncoding; private readonly ITextEncoding _textEncoding;
private readonly IMemoryStreamFactory _memoryStreamFactory; private readonly IMemoryStreamFactory _memoryStreamFactory;
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
private readonly IEnvironmentInfo _environment; 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; this.listener = listener;
_logger = logger; _logger = logger;
_cryptoProvider = cryptoProvider; _cryptoProvider = cryptoProvider;
_streamFactory = streamFactory;
_socketFactory = socketFactory; _socketFactory = socketFactory;
_memoryStreamFactory = memoryStreamFactory; _memoryStreamFactory = memoryStreamFactory;
_textEncoding = textEncoding; _textEncoding = textEncoding;
@ -51,8 +53,8 @@ namespace SocketHttpListener.Net
this.secure = secure; this.secure = secure;
this.cert = cert; this.cert = cert;
_enableDualMode = addr.Equals(IpAddressInfo.IPv6Any); _enableDualMode = addr.Equals(IPAddress.IPv6Any);
endpoint = new IpEndPointInfo(addr, port); endpoint = new IPEndPoint(addr, port);
prefixes = new Dictionary<ListenerPrefix, HttpListener>(); prefixes = new Dictionary<ListenerPrefix, HttpListener>();
unregistered = new Dictionary<HttpConnection, HttpConnection>(); unregistered = new Dictionary<HttpConnection, HttpConnection>();
@ -72,18 +74,18 @@ namespace SocketHttpListener.Net
{ {
try try
{ {
sock = _socketFactory.CreateSocket(endpoint.IpAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp, _enableDualMode); sock = CreateSocket(endpoint.Address.AddressFamily, _enableDualMode);
} }
catch (SocketCreateException ex) catch (SocketCreateException ex)
{ {
if (_enableDualMode && endpoint.IpAddress.Equals(IpAddressInfo.IPv6Any) && if (_enableDualMode && endpoint.Address.Equals(IPAddress.IPv6Any) &&
(string.Equals(ex.ErrorCode, "AddressFamilyNotSupported", StringComparison.OrdinalIgnoreCase) || (string.Equals(ex.ErrorCode, "AddressFamilyNotSupported", StringComparison.OrdinalIgnoreCase) ||
// mono on bsd is throwing this // mono on bsd is throwing this
string.Equals(ex.ErrorCode, "ProtocolNotSupported", StringComparison.OrdinalIgnoreCase))) string.Equals(ex.ErrorCode, "ProtocolNotSupported", StringComparison.OrdinalIgnoreCase)))
{ {
endpoint = new IpEndPointInfo(IpAddressInfo.Any, endpoint.Port); endpoint = new IPEndPoint(IPAddress.Any, endpoint.Port);
_enableDualMode = false; _enableDualMode = false;
sock = _socketFactory.CreateSocket(endpoint.IpAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp, _enableDualMode); sock = CreateSocket(endpoint.Address.AddressFamily, _enableDualMode);
} }
else else
{ {
@ -96,11 +98,42 @@ namespace SocketHttpListener.Net
// This is the number TcpListener uses. // This is the number TcpListener uses.
sock.Listen(2147483647); sock.Listen(2147483647);
sock.StartAccept(ProcessAccept, () => _closed); new SocketAcceptor(_logger, sock, ProcessAccept, () => _closed).StartAccept();
_closed = false; _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 try
{ {
@ -112,7 +145,7 @@ namespace SocketHttpListener.Net
return; 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); //_logger.Debug("Adding unregistered connection to {0}. Id: {1}", accepted.RemoteEndPoint, connectionId);
lock (listener.unregistered) lock (listener.unregistered)

View File

@ -66,25 +66,25 @@ namespace SocketHttpListener.Net
epl.AddPrefix(lp, listener); 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<EndPointListener> GetEPListener(ILogger logger, string host, int port, HttpListener listener, bool secure) static async Task<EndPointListener> GetEPListener(ILogger logger, string host, int port, HttpListener listener, bool secure)
{ {
var networkManager = listener.NetworkManager; var networkManager = listener.NetworkManager;
IpAddressInfo addr; IPAddress addr;
if (host == "*" || host == "+") if (host == "*" || host == "+")
addr = GetIpAnyAddress(listener); addr = GetIpAnyAddress(listener);
else if (networkManager.TryParseIpAddress(host, out addr) == false) else if (IPAddress.TryParse(host, out addr) == false)
{ {
try try
{ {
var all = (await networkManager.GetHostAddressesAsync(host).ConfigureAwait(false)); 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); GetIpAnyAddress(listener);
} }
catch catch
@ -94,10 +94,10 @@ namespace SocketHttpListener.Net
} }
Dictionary<int, EndPointListener> p = null; // Dictionary<int, EndPointListener> Dictionary<int, EndPointListener> p = null; // Dictionary<int, EndPointListener>
if (!ip_to_endpoints.TryGetValue(addr.Address, out p)) if (!ip_to_endpoints.TryGetValue(addr.ToString(), out p))
{ {
p = new Dictionary<int, EndPointListener>(); p = new Dictionary<int, EndPointListener>();
ip_to_endpoints[addr.Address] = p; ip_to_endpoints[addr.ToString()] = p;
} }
EndPointListener epl = null; EndPointListener epl = null;
@ -107,25 +107,25 @@ namespace SocketHttpListener.Net
} }
else 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; p[port] = epl;
} }
return epl; return epl;
} }
public static void RemoveEndPoint(EndPointListener epl, IpEndPointInfo ep) public static void RemoveEndPoint(EndPointListener epl, IPEndPoint ep)
{ {
lock (ip_to_endpoints) lock (ip_to_endpoints)
{ {
// Dictionary<int, EndPointListener> p // Dictionary<int, EndPointListener> p
Dictionary<int, EndPointListener> p; Dictionary<int, EndPointListener> 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); p.Remove(ep.Port);
if (p.Count == 0) if (p.Count == 0)
{ {
ip_to_endpoints.Remove(ep.IpAddress.Address); ip_to_endpoints.Remove(ep.Address.ToString());
} }
} }
epl.Close(); epl.Close();

View File

@ -1,5 +1,9 @@
using System; using System;
using System.IO; using System.IO;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Cryptography.X509Certificates;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Model.Cryptography; using MediaBrowser.Model.Cryptography;
@ -16,7 +20,7 @@ namespace SocketHttpListener.Net
{ {
private static AsyncCallback s_onreadCallback = new AsyncCallback(OnRead); private static AsyncCallback s_onreadCallback = new AsyncCallback(OnRead);
const int BufferSize = 8192; const int BufferSize = 8192;
IAcceptSocket _socket; Socket _socket;
Stream _stream; Stream _stream;
EndPointListener _epl; EndPointListener _epl;
MemoryStream _memoryStream; MemoryStream _memoryStream;
@ -31,21 +35,20 @@ namespace SocketHttpListener.Net
bool _contextBound; bool _contextBound;
bool secure; bool secure;
int _timeout = 300000; // 90k ms for first request, 15k ms from then on int _timeout = 300000; // 90k ms for first request, 15k ms from then on
IpEndPointInfo local_ep; IPEndPoint local_ep;
HttpListener _lastListener; HttpListener _lastListener;
int[] client_cert_errors; int[] client_cert_errors;
ICertificate cert; X509Certificate cert;
Stream ssl_stream; SslStream ssl_stream;
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly ICryptoProvider _cryptoProvider; private readonly ICryptoProvider _cryptoProvider;
private readonly IMemoryStreamFactory _memoryStreamFactory; private readonly IMemoryStreamFactory _memoryStreamFactory;
private readonly ITextEncoding _textEncoding; private readonly ITextEncoding _textEncoding;
private readonly IStreamFactory _streamFactory;
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
private readonly IEnvironmentInfo _environment; 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; _logger = logger;
this._socket = socket; this._socket = socket;
@ -57,14 +60,13 @@ namespace SocketHttpListener.Net
_textEncoding = textEncoding; _textEncoding = textEncoding;
_fileSystem = fileSystem; _fileSystem = fileSystem;
_environment = environment; _environment = environment;
_streamFactory = streamFactory;
} }
private async Task InitStream() private async Task InitStream()
{ {
if (secure == false) if (secure == false)
{ {
_stream = _streamFactory.CreateNetworkStream(_socket, false); _stream = new SocketStream(_socket, false);
} }
else else
{ {
@ -81,16 +83,16 @@ namespace SocketHttpListener.Net
//}); //});
//_stream = ssl_stream.AuthenticatedStream; //_stream = ssl_stream.AuthenticatedStream;
ssl_stream = _streamFactory.CreateSslStream(_streamFactory.CreateNetworkStream(_socket, false), false); ssl_stream = new SslStream(new SocketStream(_socket, false), false);
await _streamFactory.AuthenticateSslStreamAsServer(ssl_stream, cert).ConfigureAwait(false); await ssl_stream.AuthenticateAsServerAsync(cert).ConfigureAwait(false);
_stream = ssl_stream; _stream = ssl_stream;
} }
Init(); Init();
} }
public static async Task<HttpConnection> 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<HttpConnection> 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); await connection.InitStream().ConfigureAwait(false);
@ -134,21 +136,21 @@ namespace SocketHttpListener.Net
get { return _reuses; } get { return _reuses; }
} }
public IpEndPointInfo LocalEndPoint public IPEndPoint LocalEndPoint
{ {
get get
{ {
if (local_ep != null) if (local_ep != null)
return local_ep; return local_ep;
local_ep = (IpEndPointInfo)_socket.LocalEndPoint; local_ep = (IPEndPoint)_socket.LocalEndPoint;
return local_ep; return local_ep;
} }
} }
public IpEndPointInfo RemoteEndPoint public IPEndPoint RemoteEndPoint
{ {
get { return (IpEndPointInfo)_socket.RemoteEndPoint; } get { return _socket.RemoteEndPoint as IPEndPoint; }
} }
public bool IsSecure public bool IsSecure
@ -513,12 +515,12 @@ namespace SocketHttpListener.Net
return; return;
} }
IAcceptSocket s = _socket; Socket s = _socket;
_socket = null; _socket = null;
try try
{ {
if (s != null) if (s != null)
s.Shutdown(true); s.Shutdown(SocketShutdown.Both);
} }
catch catch
{ {

View File

@ -3,6 +3,7 @@ using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Net; using System.Net;
using System.Security.Cryptography.X509Certificates;
using MediaBrowser.Common.Net; using MediaBrowser.Common.Net;
using MediaBrowser.Model.Cryptography; using MediaBrowser.Model.Cryptography;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
@ -17,7 +18,6 @@ namespace SocketHttpListener.Net
public sealed class HttpListener : IDisposable public sealed class HttpListener : IDisposable
{ {
internal ICryptoProvider CryptoProvider { get; private set; } internal ICryptoProvider CryptoProvider { get; private set; }
internal IStreamFactory StreamFactory { get; private set; }
internal ISocketFactory SocketFactory { get; private set; } internal ISocketFactory SocketFactory { get; private set; }
internal IFileSystem FileSystem { get; private set; } internal IFileSystem FileSystem { get; private set; }
internal ITextEncoding TextEncoding { get; private set; } internal ITextEncoding TextEncoding { get; private set; }
@ -38,15 +38,14 @@ namespace SocketHttpListener.Net
Dictionary<HttpListenerContext, HttpListenerContext> registry; // Dictionary<HttpListenerContext,HttpListenerContext> Dictionary<HttpListenerContext, HttpListenerContext> registry; // Dictionary<HttpListenerContext,HttpListenerContext>
Dictionary<HttpConnection, HttpConnection> connections; Dictionary<HttpConnection, HttpConnection> connections;
private ILogger _logger; private ILogger _logger;
private ICertificate _certificate; private X509Certificate _certificate;
public Action<HttpListenerContext> OnContext { get; set; } public Action<HttpListenerContext> 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; _logger = logger;
CryptoProvider = cryptoProvider; CryptoProvider = cryptoProvider;
StreamFactory = streamFactory;
SocketFactory = socketFactory; SocketFactory = socketFactory;
NetworkManager = networkManager; NetworkManager = networkManager;
TextEncoding = textEncoding; TextEncoding = textEncoding;
@ -59,18 +58,18 @@ namespace SocketHttpListener.Net
auth_schemes = AuthenticationSchemes.Anonymous; auth_schemes = AuthenticationSchemes.Anonymous;
} }
public HttpListener(ICertificate certificate, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, ISocketFactory socketFactory, INetworkManager networkManager, ITextEncoding textEncoding, IMemoryStreamFactory memoryStreamFactory, IFileSystem fileSystem, IEnvironmentInfo environmentInfo) public HttpListener(X509Certificate certificate, ICryptoProvider cryptoProvider, ISocketFactory socketFactory, INetworkManager networkManager, ITextEncoding textEncoding, IMemoryStreamFactory memoryStreamFactory, IFileSystem fileSystem, IEnvironmentInfo environmentInfo)
:this(new NullLogger(), certificate, cryptoProvider, streamFactory, socketFactory, networkManager, textEncoding, memoryStreamFactory, fileSystem, 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) public HttpListener(ILogger logger, X509Certificate certificate, ICryptoProvider cryptoProvider, ISocketFactory socketFactory, INetworkManager networkManager, ITextEncoding textEncoding, IMemoryStreamFactory memoryStreamFactory, IFileSystem fileSystem, IEnvironmentInfo environmentInfo)
: this(logger, cryptoProvider, streamFactory, socketFactory, networkManager, textEncoding, memoryStreamFactory, fileSystem, environmentInfo) : this(logger, cryptoProvider, socketFactory, networkManager, textEncoding, memoryStreamFactory, fileSystem, environmentInfo)
{ {
_certificate = certificate; _certificate = certificate;
} }
public void LoadCert(ICertificate cert) public void LoadCert(X509Certificate cert)
{ {
_certificate = cert; _certificate = cert;
} }
@ -150,7 +149,7 @@ namespace SocketHttpListener.Net
// } // }
//} //}
internal ICertificate Certificate internal X509Certificate Certificate
{ {
get { return _certificate; } get { return _certificate; }
} }

View File

@ -3,6 +3,7 @@ using System.Collections.Specialized;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Net; using System.Net;
using System.Security.Cryptography.X509Certificates;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Model.Net; using MediaBrowser.Model.Net;
@ -513,7 +514,14 @@ namespace SocketHttpListener.Net
public bool IsLocal 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 public bool IsSecureConnection
@ -557,7 +565,7 @@ namespace SocketHttpListener.Net
} }
} }
public IpEndPointInfo LocalEndPoint public IPEndPoint LocalEndPoint
{ {
get { return context.Connection.LocalEndPoint; } get { return context.Connection.LocalEndPoint; }
} }
@ -577,7 +585,7 @@ namespace SocketHttpListener.Net
get { return raw_url; } get { return raw_url; }
} }
public IpEndPointInfo RemoteEndPoint public IPEndPoint RemoteEndPoint
{ {
get { return context.Connection.RemoteEndPoint; } get { return context.Connection.RemoteEndPoint; }
} }
@ -651,10 +659,5 @@ namespace SocketHttpListener.Net
return _websocketRequest; return _websocketRequest;
} }
} }
public Task<ICertificate> GetClientCertificateAsync()
{
return Task.FromResult<ICertificate>(null);
}
} }
} }

View File

@ -51,13 +51,13 @@ namespace SocketHttpListener.Net
private bool _trailer_sent; private bool _trailer_sent;
private Stream _stream; private Stream _stream;
private readonly IMemoryStreamFactory _memoryStreamFactory; private readonly IMemoryStreamFactory _memoryStreamFactory;
private readonly IAcceptSocket _socket; private readonly Socket _socket;
private readonly bool _supportsDirectSocketAccess; private readonly bool _supportsDirectSocketAccess;
private readonly IEnvironmentInfo _environment; private readonly IEnvironmentInfo _environment;
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
private readonly ILogger _logger; 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; _response = response;
_ignore_errors = ignore_errors; _ignore_errors = ignore_errors;
@ -289,64 +289,9 @@ namespace SocketHttpListener.Net
public Task TransmitFile(string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken) 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); 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<bool>();
Action<IAsyncResult> 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; const int StreamCopyToBufferSize = 81920;
private async Task TransmitFileManaged(string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken) private async Task TransmitFileManaged(string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken)
{ {

View File

@ -11,7 +11,7 @@ namespace SocketHttpListener.Net
ushort port; ushort port;
string path; string path;
bool secure; bool secure;
IpAddressInfo[] addresses; IPAddress[] addresses;
public HttpListener Listener; public HttpListener Listener;
public ListenerPrefix(string prefix) public ListenerPrefix(string prefix)
@ -25,7 +25,7 @@ namespace SocketHttpListener.Net
return original; return original;
} }
public IpAddressInfo[] Addresses public IPAddress[] Addresses
{ {
get { return addresses; } get { return addresses; }
set { addresses = value; } set { addresses = value; }

View File

@ -1,19 +1,17 @@
using System; using System;
using System.Net.Sockets; using System.Net.Sockets;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Net;
namespace Emby.Server.Implementations.Net namespace SocketHttpListener.Net
{ {
public class SocketAcceptor public class SocketAcceptor
{ {
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly Socket _originalSocket; private readonly Socket _originalSocket;
private readonly Func<bool> _isClosed; private readonly Func<bool> _isClosed;
private readonly Action<IAcceptSocket> _onAccept; private readonly Action<Socket> _onAccept;
private readonly bool _isDualMode;
public SocketAcceptor(ILogger logger, Socket originalSocket, Action<IAcceptSocket> onAccept, Func<bool> isClosed, bool isDualMode) public SocketAcceptor(ILogger logger, Socket originalSocket, Action<Socket> onAccept, Func<bool> isClosed)
{ {
if (logger == null) if (logger == null)
{ {
@ -35,7 +33,6 @@ namespace Emby.Server.Implementations.Net
_logger = logger; _logger = logger;
_originalSocket = originalSocket; _originalSocket = originalSocket;
_isClosed = isClosed; _isClosed = isClosed;
_isDualMode = isDualMode;
_onAccept = onAccept; _onAccept = onAccept;
} }
@ -117,7 +114,7 @@ namespace Emby.Server.Implementations.Net
if (acceptSocket != null) if (acceptSocket != null)
{ {
//ProcessAccept(acceptSocket); //ProcessAccept(acceptSocket);
_onAccept(new NetAcceptSocket(acceptSocket, _logger, _isDualMode)); _onAccept(acceptSocket);
} }
// Accept the next connection request // Accept the next connection request

View File

@ -254,7 +254,7 @@ namespace SocketHttpListener.Net.WebSockets
/// </summary> /// </summary>
/// <value> /// <value>
/// </value> /// </value>
public override IpEndPointInfo ServerEndPoint public override IPEndPoint ServerEndPoint
{ {
get get
{ {
@ -281,7 +281,7 @@ namespace SocketHttpListener.Net.WebSockets
/// </summary> /// </summary>
/// <value> /// <value>
/// </value> /// </value>
public override IpEndPointInfo UserEndPoint public override IPEndPoint UserEndPoint
{ {
get get
{ {

View File

@ -151,7 +151,7 @@ namespace SocketHttpListener.Net.WebSockets
/// <value> /// <value>
/// A <see cref="System.Net.IPEndPoint"/> that represents the server endpoint. /// A <see cref="System.Net.IPEndPoint"/> that represents the server endpoint.
/// </value> /// </value>
public abstract IpEndPointInfo ServerEndPoint { get; } public abstract IPEndPoint ServerEndPoint { get; }
/// <summary> /// <summary>
/// Gets the client information (identity, authentication, and security roles). /// Gets the client information (identity, authentication, and security roles).
@ -167,7 +167,7 @@ namespace SocketHttpListener.Net.WebSockets
/// <value> /// <value>
/// A <see cref="System.Net.IPEndPoint"/> that represents the client endpoint. /// A <see cref="System.Net.IPEndPoint"/> that represents the client endpoint.
/// </value> /// </value>
public abstract IpEndPointInfo UserEndPoint { get; } public abstract IPEndPoint UserEndPoint { get; }
/// <summary> /// <summary>
/// Gets the <see cref="SocketHttpListener.WebSocket"/> instance used for two-way communication /// Gets the <see cref="SocketHttpListener.WebSocket"/> instance used for two-way communication

View File

@ -1,11 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace SocketHttpListener.Primitives
{
public interface ICertificate
{
}
}

View File

@ -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);
}
}

View File

@ -82,6 +82,7 @@
<Compile Include="Net\HttpStreamAsyncResult.cs" /> <Compile Include="Net\HttpStreamAsyncResult.cs" />
<Compile Include="Net\HttpVersion.cs" /> <Compile Include="Net\HttpVersion.cs" />
<Compile Include="Net\ListenerPrefix.cs" /> <Compile Include="Net\ListenerPrefix.cs" />
<Compile Include="Net\SocketAcceptor.cs" />
<Compile Include="Net\UriScheme.cs" /> <Compile Include="Net\UriScheme.cs" />
<Compile Include="Net\WebHeaderCollection.cs" /> <Compile Include="Net\WebHeaderCollection.cs" />
<Compile Include="Net\WebHeaderEncoding.cs" /> <Compile Include="Net\WebHeaderEncoding.cs" />
@ -89,11 +90,10 @@
<Compile Include="Net\WebSockets\WebSocketContext.cs" /> <Compile Include="Net\WebSockets\WebSocketContext.cs" />
<Compile Include="Opcode.cs" /> <Compile Include="Opcode.cs" />
<Compile Include="PayloadData.cs" /> <Compile Include="PayloadData.cs" />
<Compile Include="Primitives\ICertificate.cs" />
<Compile Include="Primitives\IStreamFactory.cs" />
<Compile Include="Primitives\ITextEncoding.cs" /> <Compile Include="Primitives\ITextEncoding.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Rsv.cs" /> <Compile Include="Rsv.cs" />
<Compile Include="SocketStream.cs" />
<Compile Include="WebSocket.cs" /> <Compile Include="WebSocket.cs" />
<Compile Include="WebSocketException.cs" /> <Compile Include="WebSocketException.cs" />
<Compile Include="WebSocketFrame.cs" /> <Compile Include="WebSocketFrame.cs" />

View File

@ -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);
}
}
}