diff options
Diffstat (limited to 'MediaBrowser.Server.Implementations/HttpServer')
15 files changed, 257 insertions, 1462 deletions
diff --git a/MediaBrowser.Server.Implementations/HttpServer/ContainerAdapter.cs b/MediaBrowser.Server.Implementations/HttpServer/ContainerAdapter.cs index 93d224b8d..235b62f69 100644 --- a/MediaBrowser.Server.Implementations/HttpServer/ContainerAdapter.cs +++ b/MediaBrowser.Server.Implementations/HttpServer/ContainerAdapter.cs @@ -6,7 +6,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer /// <summary> /// Class ContainerAdapter /// </summary> - class ContainerAdapter : IContainerAdapter, IRelease + class ContainerAdapter : IContainerAdapter { /// <summary> /// The _app host @@ -40,14 +40,5 @@ namespace MediaBrowser.Server.Implementations.HttpServer { return _appHost.TryResolve<T>(); } - - /// <summary> - /// Releases the specified instance. - /// </summary> - /// <param name="instance">The instance.</param> - public void Release(object instance) - { - // Leave this empty so SS doesn't try to dispose our objects - } } } diff --git a/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs b/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs index 805cb0353..ebb282503 100644 --- a/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs +++ b/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs @@ -8,24 +8,31 @@ using MediaBrowser.Server.Implementations.HttpServer.SocketSharp; using ServiceStack; using ServiceStack.Host; using ServiceStack.Host.Handlers; -using ServiceStack.Logging; using ServiceStack.Web; using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Net.Security; +using System.Net.Sockets; using System.Reflection; +using System.Security.Cryptography.X509Certificates; using System.Threading; using System.Threading.Tasks; +using Emby.Common.Implementations.Net; using Emby.Server.Implementations.HttpServer; using Emby.Server.Implementations.HttpServer.SocketSharp; using MediaBrowser.Common.Net; using MediaBrowser.Common.Security; using MediaBrowser.Controller; +using MediaBrowser.Model.Cryptography; using MediaBrowser.Model.Extensions; using MediaBrowser.Model.IO; +using MediaBrowser.Model.Net; using MediaBrowser.Model.Services; -using ServiceStack.Api.Swagger; +using MediaBrowser.Model.Text; +using SocketHttpListener.Net; +using SocketHttpListener.Primitives; namespace MediaBrowser.Server.Implementations.HttpServer { @@ -49,21 +56,28 @@ namespace MediaBrowser.Server.Implementations.HttpServer private readonly IServerConfigurationManager _config; private readonly INetworkManager _networkManager; - private readonly IMemoryStreamProvider _memoryStreamProvider; + private readonly IMemoryStreamFactory _memoryStreamProvider; private readonly IServerApplicationHost _appHost; + private readonly ITextEncoding _textEncoding; + private readonly ISocketFactory _socketFactory; + private readonly ICryptoProvider _cryptoProvider; + public HttpListenerHost(IServerApplicationHost applicationHost, ILogManager logManager, IServerConfigurationManager config, string serviceName, - string defaultRedirectPath, INetworkManager networkManager, IMemoryStreamProvider memoryStreamProvider, params Assembly[] assembliesWithServices) - : base(serviceName, assembliesWithServices) + string defaultRedirectPath, INetworkManager networkManager, IMemoryStreamFactory memoryStreamProvider, ITextEncoding textEncoding, ISocketFactory socketFactory, ICryptoProvider cryptoProvider) + : base(serviceName, new Assembly[] { }) { _appHost = applicationHost; DefaultRedirectPath = defaultRedirectPath; _networkManager = networkManager; _memoryStreamProvider = memoryStreamProvider; + _textEncoding = textEncoding; + _socketFactory = socketFactory; + _cryptoProvider = cryptoProvider; _config = config; _logger = logManager.GetLogger("HttpServer"); @@ -73,10 +87,9 @@ namespace MediaBrowser.Server.Implementations.HttpServer public string GlobalResponse { get; set; } - public override void Configure(Container container) + public override void Configure() { HostConfig.Instance.DefaultRedirectPath = DefaultRedirectPath; - HostConfig.Instance.LogUnobservedTaskExceptions = false; HostConfig.Instance.MapExceptionToStatusCode = new Dictionary<Type, int> { @@ -94,19 +107,12 @@ namespace MediaBrowser.Server.Implementations.HttpServer }; HostConfig.Instance.GlobalResponseHeaders = new Dictionary<string, string>(); - HostConfig.Instance.DebugMode = false; - - HostConfig.Instance.LogFactory = LogManager.LogFactory; - HostConfig.Instance.AllowJsonpRequests = false; // The Markdown feature causes slow startup times (5 mins+) on cold boots for some users // Custom format allows images HostConfig.Instance.EnableFeatures = Feature.Html | Feature.Json | Feature.Xml | Feature.CustomFormat; - container.Adapter = _containerAdapter; - - Plugins.Add(new SwaggerFeature()); - Plugins.Add(new CorsFeature(allowedHeaders: "Content-Type, Authorization, Range, X-MediaBrowser-Token, X-Emby-Authorization")); + Container.Adapter = _containerAdapter; //Plugins.Add(new AuthFeature(() => new AuthUserSession(), new IAuthProvider[] { // new SessionAuthProvider(_containerAdapter.Resolve<ISessionContext>()), @@ -130,6 +136,14 @@ namespace MediaBrowser.Server.Implementations.HttpServer HostContext.GlobalResponseFilters.Add(new ResponseFilter(_logger).FilterResponse); } + protected override ILogger Logger + { + get + { + return _logger; + } + } + public override void OnAfterInit() { SetAppDomainData(); @@ -207,7 +221,33 @@ namespace MediaBrowser.Server.Implementations.HttpServer private IHttpListener GetListener() { - return new WebSocketSharpListener(_logger, CertificatePath, _memoryStreamProvider); + var cert = !string.IsNullOrWhiteSpace(CertificatePath) && File.Exists(CertificatePath) + ? GetCert(CertificatePath) : + null; + + return new WebSocketSharpListener(_logger, cert, _memoryStreamProvider, _textEncoding, _networkManager, _socketFactory, _cryptoProvider, new StreamFactory(), GetRequest); + } + + public static ICertificate GetCert(string certificateLocation) + { + X509Certificate2 localCert = new X509Certificate2(certificateLocation); + //localCert.PrivateKey = PrivateKey.CreateFromFile(pvk_file).RSA; + if (localCert.PrivateKey == null) + { + //throw new FileNotFoundException("Secure requested, no private key included", certificateLocation); + return null; + } + + return new Certificate(localCert); + } + + 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) @@ -259,11 +299,11 @@ namespace MediaBrowser.Server.Implementations.HttpServer var contentType = httpReq.ResponseContentType; - var serializer = HostContext.ContentTypes.GetResponseSerializer(contentType); + var serializer = ContentTypes.Instance.GetResponseSerializer(contentType); if (serializer == null) { contentType = HostContext.Config.DefaultContentType; - serializer = HostContext.ContentTypes.GetResponseSerializer(contentType); + serializer = ContentTypes.Instance.GetResponseSerializer(contentType); } var httpError = ex as IHttpError; @@ -411,171 +451,170 @@ namespace MediaBrowser.Server.Implementations.HttpServer protected async Task RequestHandler(IHttpRequest httpReq, Uri url) { var date = DateTime.Now; - var httpRes = httpReq.Response; + bool enableLog = false; + string urlToLog = null; + string remoteIp = null; - if (_disposed) + try { - httpRes.StatusCode = 503; - httpRes.Close(); - return ; - } + if (_disposed) + { + httpRes.StatusCode = 503; + return; + } - if (!ValidateHost(url)) - { - httpRes.StatusCode = 400; - httpRes.ContentType = "text/plain"; - httpRes.Write("Invalid host"); + if (!ValidateHost(url)) + { + httpRes.StatusCode = 400; + httpRes.ContentType = "text/plain"; + httpRes.Write("Invalid host"); + return; + } - httpRes.Close(); - return; - } + if (string.Equals(httpReq.Verb, "OPTIONS", StringComparison.OrdinalIgnoreCase)) + { + httpRes.StatusCode = 200; + httpRes.AddHeader("Access-Control-Allow-Origin", "*"); + httpRes.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS"); + httpRes.AddHeader("Access-Control-Allow-Headers", + "Content-Type, Authorization, Range, X-MediaBrowser-Token, X-Emby-Authorization"); + httpRes.ContentType = "text/html"; + return; + } - if (string.Equals(httpReq.Verb, "OPTIONS", StringComparison.OrdinalIgnoreCase)) - { - httpRes.StatusCode = 200; - httpRes.AddHeader("Access-Control-Allow-Origin", "*"); - httpRes.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS"); - httpRes.AddHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, Range, X-MediaBrowser-Token, X-Emby-Authorization"); - httpRes.ContentType = "text/html"; + var operationName = httpReq.OperationName; + var localPath = url.LocalPath; - httpRes.Close(); - } + var urlString = url.OriginalString; + enableLog = EnableLogging(urlString, localPath); + urlToLog = urlString; - var operationName = httpReq.OperationName; - var localPath = url.LocalPath; + if (enableLog) + { + urlToLog = GetUrlToLog(urlString); + remoteIp = httpReq.RemoteIp; - var urlString = url.OriginalString; - var enableLog = EnableLogging(urlString, localPath); - var urlToLog = urlString; + LoggerUtils.LogRequest(_logger, urlToLog, httpReq.HttpMethod, httpReq.UserAgent); + } - if (enableLog) - { - urlToLog = GetUrlToLog(urlString); - LoggerUtils.LogRequest(_logger, urlToLog, httpReq.HttpMethod, httpReq.UserAgent); - } + if (string.Equals(localPath, "/emby/", StringComparison.OrdinalIgnoreCase) || + string.Equals(localPath, "/mediabrowser/", StringComparison.OrdinalIgnoreCase)) + { + RedirectToUrl(httpRes, DefaultRedirectPath); + return; + } + if (string.Equals(localPath, "/emby", StringComparison.OrdinalIgnoreCase) || + string.Equals(localPath, "/mediabrowser", StringComparison.OrdinalIgnoreCase)) + { + RedirectToUrl(httpRes, "emby/" + DefaultRedirectPath); + return; + } - if (string.Equals(localPath, "/emby/", StringComparison.OrdinalIgnoreCase) || - string.Equals(localPath, "/mediabrowser/", StringComparison.OrdinalIgnoreCase)) - { - httpRes.RedirectToUrl(DefaultRedirectPath); - return; - } - if (string.Equals(localPath, "/emby", StringComparison.OrdinalIgnoreCase) || - string.Equals(localPath, "/mediabrowser", StringComparison.OrdinalIgnoreCase)) - { - httpRes.RedirectToUrl("emby/" + DefaultRedirectPath); - return; - } + if (string.Equals(localPath, "/mediabrowser/", StringComparison.OrdinalIgnoreCase) || + string.Equals(localPath, "/mediabrowser", StringComparison.OrdinalIgnoreCase) || + localPath.IndexOf("mediabrowser/web", StringComparison.OrdinalIgnoreCase) != -1) + { + httpRes.StatusCode = 200; + httpRes.ContentType = "text/html"; + var newUrl = urlString.Replace("mediabrowser", "emby", StringComparison.OrdinalIgnoreCase) + .Replace("/dashboard/", "/web/", StringComparison.OrdinalIgnoreCase); - if (string.Equals(localPath, "/mediabrowser/", StringComparison.OrdinalIgnoreCase) || - string.Equals(localPath, "/mediabrowser", StringComparison.OrdinalIgnoreCase) || - localPath.IndexOf("mediabrowser/web", StringComparison.OrdinalIgnoreCase) != -1) - { - httpRes.StatusCode = 200; - httpRes.ContentType = "text/html"; - var newUrl = urlString.Replace("mediabrowser", "emby", StringComparison.OrdinalIgnoreCase) - .Replace("/dashboard/", "/web/", StringComparison.OrdinalIgnoreCase); + if (!string.Equals(newUrl, urlString, StringComparison.OrdinalIgnoreCase)) + { + httpRes.Write( + "<!doctype html><html><head><title>Emby</title></head><body>Please update your Emby bookmark to <a href=\"" + + newUrl + "\">" + newUrl + "</a></body></html>"); + return; + } + } - if (!string.Equals(newUrl, urlString, StringComparison.OrdinalIgnoreCase)) + if (localPath.IndexOf("dashboard/", StringComparison.OrdinalIgnoreCase) != -1 && + localPath.IndexOf("web/dashboard", StringComparison.OrdinalIgnoreCase) == -1) { - httpRes.Write("<!doctype html><html><head><title>Emby</title></head><body>Please update your Emby bookmark to <a href=\"" + newUrl + "\">" + newUrl + "</a></body></html>"); + httpRes.StatusCode = 200; + httpRes.ContentType = "text/html"; + var newUrl = urlString.Replace("mediabrowser", "emby", StringComparison.OrdinalIgnoreCase) + .Replace("/dashboard/", "/web/", StringComparison.OrdinalIgnoreCase); - httpRes.Close(); - return; + if (!string.Equals(newUrl, urlString, StringComparison.OrdinalIgnoreCase)) + { + httpRes.Write( + "<!doctype html><html><head><title>Emby</title></head><body>Please update your Emby bookmark to <a href=\"" + + newUrl + "\">" + newUrl + "</a></body></html>"); + return; + } } - } - if (localPath.IndexOf("dashboard/", StringComparison.OrdinalIgnoreCase) != -1 && - localPath.IndexOf("web/dashboard", StringComparison.OrdinalIgnoreCase) == -1) - { - httpRes.StatusCode = 200; - httpRes.ContentType = "text/html"; - var newUrl = urlString.Replace("mediabrowser", "emby", StringComparison.OrdinalIgnoreCase) - .Replace("/dashboard/", "/web/", StringComparison.OrdinalIgnoreCase); + if (string.Equals(localPath, "/web", StringComparison.OrdinalIgnoreCase)) + { + RedirectToUrl(httpRes, DefaultRedirectPath); + return; + } + if (string.Equals(localPath, "/web/", StringComparison.OrdinalIgnoreCase)) + { + RedirectToUrl(httpRes, "../" + DefaultRedirectPath); + return; + } + if (string.Equals(localPath, "/", StringComparison.OrdinalIgnoreCase)) + { + RedirectToUrl(httpRes, DefaultRedirectPath); + return; + } + if (string.IsNullOrEmpty(localPath)) + { + RedirectToUrl(httpRes, "/" + DefaultRedirectPath); + return; + } - if (!string.Equals(newUrl, urlString, StringComparison.OrdinalIgnoreCase)) + if (string.Equals(localPath, "/emby/pin", StringComparison.OrdinalIgnoreCase)) { - httpRes.Write("<!doctype html><html><head><title>Emby</title></head><body>Please update your Emby bookmark to <a href=\"" + newUrl + "\">" + newUrl + "</a></body></html>"); + RedirectToUrl(httpRes, "web/pin.html"); + return; + } - httpRes.Close(); + if (!string.IsNullOrWhiteSpace(GlobalResponse)) + { + httpRes.StatusCode = 503; + httpRes.ContentType = "text/html"; + httpRes.Write(GlobalResponse); return; } - } - if (string.Equals(localPath, "/web", StringComparison.OrdinalIgnoreCase)) - { - httpRes.RedirectToUrl(DefaultRedirectPath); - return; - } - if (string.Equals(localPath, "/web/", StringComparison.OrdinalIgnoreCase)) - { - httpRes.RedirectToUrl("../" + DefaultRedirectPath); - return; - } - if (string.Equals(localPath, "/", StringComparison.OrdinalIgnoreCase)) - { - httpRes.RedirectToUrl(DefaultRedirectPath); - return; - } - if (string.IsNullOrEmpty(localPath)) - { - httpRes.RedirectToUrl("/" + DefaultRedirectPath); - return; - } + var handler = HttpHandlerFactory.GetHandler(httpReq); - if (string.Equals(localPath, "/emby/pin", StringComparison.OrdinalIgnoreCase)) - { - httpRes.RedirectToUrl("web/pin.html"); - return; + if (handler != null) + { + await handler.ProcessRequestAsync(httpReq, httpRes, operationName).ConfigureAwait(false); + } } - - if (!string.IsNullOrWhiteSpace(GlobalResponse)) + catch (Exception ex) { - httpRes.StatusCode = 503; - httpRes.ContentType = "text/html"; - httpRes.Write(GlobalResponse); - - httpRes.Close(); - return; + ErrorHandler(ex, httpReq); } - - var handler = HttpHandlerFactory.GetHandler(httpReq); - - var remoteIp = httpReq.RemoteIp; - - var serviceStackHandler = handler as IServiceStackHandler; - if (serviceStackHandler != null) + finally { - var restHandler = serviceStackHandler as RestHandler; - if (restHandler != null) - { - httpReq.OperationName = operationName = restHandler.RestPath.RequestType.GetOperationName(); - } + httpRes.Close(); - try - { - await serviceStackHandler.ProcessRequestAsync(httpReq, httpRes, operationName).ConfigureAwait(false); - } - finally + if (enableLog) { - httpRes.Close(); var statusCode = httpRes.StatusCode; var duration = DateTime.Now - date; - if (enableLog) - { - LoggerUtils.LogResponse(_logger, statusCode, urlToLog, remoteIp, duration); - } + LoggerUtils.LogResponse(_logger, statusCode, urlToLog, remoteIp, duration); } } - else - { - httpRes.Close(); - } } + public static void RedirectToUrl(IResponse httpRes, string url) + { + httpRes.StatusCode = 302; + httpRes.AddHeader(HttpHeaders.Location, url); + httpRes.EndRequest(); + } + + /// <summary> /// Adds the rest handlers. /// </summary> @@ -653,15 +692,6 @@ namespace MediaBrowser.Server.Implementations.HttpServer return "mediabrowser/" + path; } - /// <summary> - /// Releases the specified instance. - /// </summary> - /// <param name="instance">The instance.</param> - public override void Release(object instance) - { - // Leave this empty so SS doesn't try to dispose our objects - } - private bool _disposed; private readonly object _disposeLock = new object(); protected virtual void Dispose(bool disposing) @@ -696,4 +726,37 @@ namespace MediaBrowser.Server.Implementations.HttpServer Start(UrlPrefixes.First()); } } + + public class StreamFactory : IStreamFactory + { + public Stream CreateNetworkStream(ISocket socket, bool ownsSocket) + { + var netSocket = (NetSocket)socket; + + return new NetworkStream(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; } + } }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/HttpServer/HttpResultFactory.cs b/MediaBrowser.Server.Implementations/HttpServer/HttpResultFactory.cs index 95e1a35e6..4c251ba24 100644 --- a/MediaBrowser.Server.Implementations/HttpServer/HttpResultFactory.cs +++ b/MediaBrowser.Server.Implementations/HttpServer/HttpResultFactory.cs @@ -9,6 +9,7 @@ using System.IO; using System.Net; using System.Text; using System.Threading.Tasks; +using Emby.Server.Implementations.HttpServer; using MediaBrowser.Model.IO; using MediaBrowser.Model.Services; using ServiceStack; diff --git a/MediaBrowser.Server.Implementations/HttpServer/LoggerUtils.cs b/MediaBrowser.Server.Implementations/HttpServer/LoggerUtils.cs deleted file mode 100644 index bfbb228ed..000000000 --- a/MediaBrowser.Server.Implementations/HttpServer/LoggerUtils.cs +++ /dev/null @@ -1,43 +0,0 @@ -using MediaBrowser.Model.Logging; -using System; -using System.Globalization; -using SocketHttpListener.Net; - -namespace MediaBrowser.Server.Implementations.HttpServer -{ - public static class LoggerUtils - { - /// <summary> - /// Logs the request. - /// </summary> - /// <param name="logger">The logger.</param> - /// <param name="request">The request.</param> - public static void LogRequest(ILogger logger, HttpListenerRequest request) - { - var url = request.Url.ToString(); - - logger.Info("{0} {1}. UserAgent: {2}", request.IsWebSocketRequest ? "WS" : "HTTP " + request.HttpMethod, url, request.UserAgent ?? string.Empty); - } - - public static void LogRequest(ILogger logger, string url, string method, string userAgent) - { - logger.Info("{0} {1}. UserAgent: {2}", "HTTP " + method, url, userAgent ?? string.Empty); - } - - /// <summary> - /// Logs the response. - /// </summary> - /// <param name="logger">The logger.</param> - /// <param name="statusCode">The status code.</param> - /// <param name="url">The URL.</param> - /// <param name="endPoint">The end point.</param> - /// <param name="duration">The duration.</param> - public static void LogResponse(ILogger logger, int statusCode, string url, string endPoint, TimeSpan duration) - { - var durationMs = duration.TotalMilliseconds; - var logSuffix = durationMs >= 1000 && durationMs < 60000 ? "ms (slow)" : "ms"; - - logger.Info("HTTP Response {0} to {1}. Time: {2}{3}. {4}", statusCode, endPoint, Convert.ToInt32(durationMs).ToString(CultureInfo.InvariantCulture), logSuffix, url); - } - } -} diff --git a/MediaBrowser.Server.Implementations/HttpServer/RangeRequestWriter.cs b/MediaBrowser.Server.Implementations/HttpServer/RangeRequestWriter.cs deleted file mode 100644 index 7d4cd3b4d..000000000 --- a/MediaBrowser.Server.Implementations/HttpServer/RangeRequestWriter.cs +++ /dev/null @@ -1,230 +0,0 @@ -using MediaBrowser.Model.Logging; -using ServiceStack.Web; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Model.Services; - -namespace MediaBrowser.Server.Implementations.HttpServer -{ - public class RangeRequestWriter : IAsyncStreamWriter, IHttpResult - { - /// <summary> - /// Gets or sets the source stream. - /// </summary> - /// <value>The source stream.</value> - private Stream SourceStream { get; set; } - private string RangeHeader { get; set; } - private bool IsHeadRequest { get; set; } - - private long RangeStart { get; set; } - private long RangeEnd { get; set; } - private long RangeLength { get; set; } - private long TotalContentLength { get; set; } - - public Action OnComplete { get; set; } - private readonly ILogger _logger; - - private const int BufferSize = 81920; - - /// <summary> - /// The _options - /// </summary> - private readonly Dictionary<string, string> _options = new Dictionary<string, string>(); - - /// <summary> - /// The us culture - /// </summary> - private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); - - public Func<IDisposable> ResultScope { get; set; } - public List<Cookie> Cookies { get; private set; } - - /// <summary> - /// Additional HTTP Headers - /// </summary> - /// <value>The headers.</value> - public IDictionary<string, string> Headers - { - get { return _options; } - } - - /// <summary> - /// Initializes a new instance of the <see cref="StreamWriter" /> class. - /// </summary> - /// <param name="rangeHeader">The range header.</param> - /// <param name="source">The source.</param> - /// <param name="contentType">Type of the content.</param> - /// <param name="isHeadRequest">if set to <c>true</c> [is head request].</param> - public RangeRequestWriter(string rangeHeader, Stream source, string contentType, bool isHeadRequest, ILogger logger) - { - if (string.IsNullOrEmpty(contentType)) - { - throw new ArgumentNullException("contentType"); - } - - RangeHeader = rangeHeader; - SourceStream = source; - IsHeadRequest = isHeadRequest; - this._logger = logger; - - ContentType = contentType; - Headers["Content-Type"] = contentType; - Headers["Accept-Ranges"] = "bytes"; - StatusCode = HttpStatusCode.PartialContent; - - Cookies = new List<Cookie>(); - SetRangeValues(); - } - - /// <summary> - /// Sets the range values. - /// </summary> - private void SetRangeValues() - { - var requestedRange = RequestedRanges[0]; - - TotalContentLength = SourceStream.Length; - - // If the requested range is "0-", we can optimize by just doing a stream copy - if (!requestedRange.Value.HasValue) - { - RangeEnd = TotalContentLength - 1; - } - else - { - RangeEnd = requestedRange.Value.Value; - } - - RangeStart = requestedRange.Key; - RangeLength = 1 + RangeEnd - RangeStart; - - // Content-Length is the length of what we're serving, not the original content - Headers["Content-Length"] = RangeLength.ToString(UsCulture); - Headers["Content-Range"] = string.Format("bytes {0}-{1}/{2}", RangeStart, RangeEnd, TotalContentLength); - - if (RangeStart > 0) - { - SourceStream.Position = RangeStart; - } - } - - /// <summary> - /// The _requested ranges - /// </summary> - private List<KeyValuePair<long, long?>> _requestedRanges; - /// <summary> - /// Gets the requested ranges. - /// </summary> - /// <value>The requested ranges.</value> - protected List<KeyValuePair<long, long?>> RequestedRanges - { - get - { - if (_requestedRanges == null) - { - _requestedRanges = new List<KeyValuePair<long, long?>>(); - - // Example: bytes=0-,32-63 - var ranges = RangeHeader.Split('=')[1].Split(','); - - foreach (var range in ranges) - { - var vals = range.Split('-'); - - long start = 0; - long? end = null; - - if (!string.IsNullOrEmpty(vals[0])) - { - start = long.Parse(vals[0], UsCulture); - } - if (!string.IsNullOrEmpty(vals[1])) - { - end = long.Parse(vals[1], UsCulture); - } - - _requestedRanges.Add(new KeyValuePair<long, long?>(start, end)); - } - } - - return _requestedRanges; - } - } - - public async Task WriteToAsync(Stream responseStream, CancellationToken cancellationToken) - { - try - { - // Headers only - if (IsHeadRequest) - { - return; - } - - using (var source = SourceStream) - { - // If the requested range is "0-", we can optimize by just doing a stream copy - if (RangeEnd >= TotalContentLength - 1) - { - await source.CopyToAsync(responseStream, BufferSize).ConfigureAwait(false); - } - else - { - await CopyToInternalAsync(source, responseStream, RangeLength).ConfigureAwait(false); - } - } - } - finally - { - if (OnComplete != null) - { - OnComplete(); - } - } - } - - private async Task CopyToInternalAsync(Stream source, Stream destination, long copyLength) - { - var array = new byte[BufferSize]; - int count; - while ((count = await source.ReadAsync(array, 0, array.Length).ConfigureAwait(false)) != 0) - { - var bytesToCopy = Math.Min(count, copyLength); - - await destination.WriteAsync(array, 0, Convert.ToInt32(bytesToCopy)).ConfigureAwait(false); - - copyLength -= bytesToCopy; - - if (copyLength <= 0) - { - break; - } - } - } - - public string ContentType { get; set; } - - public IRequest RequestContext { get; set; } - - public object Response { get; set; } - - public IContentTypeWriter ResponseFilter { get; set; } - - public int Status { get; set; } - - public HttpStatusCode StatusCode - { - get { return (HttpStatusCode)Status; } - set { Status = (int)value; } - } - - public string StatusDescription { get; set; } - - public int PaddingLength { get; set; } - } -}
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/HttpServer/ResponseFilter.cs b/MediaBrowser.Server.Implementations/HttpServer/ResponseFilter.cs deleted file mode 100644 index 6247e4c17..000000000 --- a/MediaBrowser.Server.Implementations/HttpServer/ResponseFilter.cs +++ /dev/null @@ -1,127 +0,0 @@ -using MediaBrowser.Model.Logging; -using MediaBrowser.Server.Implementations.HttpServer.SocketSharp; -using System; -using System.Globalization; -using System.Net; -using System.Text; -using MediaBrowser.Model.Services; - -namespace MediaBrowser.Server.Implementations.HttpServer -{ - public class ResponseFilter - { - private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); - private readonly ILogger _logger; - - public ResponseFilter(ILogger logger) - { - _logger = logger; - } - - /// <summary> - /// Filters the response. - /// </summary> - /// <param name="req">The req.</param> - /// <param name="res">The res.</param> - /// <param name="dto">The dto.</param> - public void FilterResponse(IRequest req, IResponse res, object dto) - { - // Try to prevent compatibility view - res.AddHeader("X-UA-Compatible", "IE=Edge"); - - var exception = dto as Exception; - - if (exception != null) - { - _logger.ErrorException("Error processing request for {0}", exception, req.RawUrl); - - if (!string.IsNullOrEmpty(exception.Message)) - { - var error = exception.Message.Replace(Environment.NewLine, " "); - error = RemoveControlCharacters(error); - - res.AddHeader("X-Application-Error-Code", error); - } - } - - var vary = "Accept-Encoding"; - - var hasHeaders = dto as IHasHeaders; - var sharpResponse = res as WebSocketSharpResponse; - - if (hasHeaders != null) - { - if (!hasHeaders.Headers.ContainsKey("Server")) - { - hasHeaders.Headers["Server"] = "Mono-HTTPAPI/1.1, UPnP/1.0 DLNADOC/1.50"; - //hasHeaders.Headers["Server"] = "Mono-HTTPAPI/1.1"; - } - - // Content length has to be explicitly set on on HttpListenerResponse or it won't be happy - string contentLength; - - if (hasHeaders.Headers.TryGetValue("Content-Length", out contentLength) && !string.IsNullOrEmpty(contentLength)) - { - var length = long.Parse(contentLength, UsCulture); - - if (length > 0) - { - res.SetContentLength(length); - - var listenerResponse = res.OriginalResponse as HttpListenerResponse; - - if (listenerResponse != null) - { - // Disable chunked encoding. Technically this is only needed when using Content-Range, but - // anytime we know the content length there's no need for it - listenerResponse.SendChunked = false; - return; - } - - if (sharpResponse != null) - { - sharpResponse.SendChunked = false; - } - } - } - - string hasHeadersVary; - if (hasHeaders.Headers.TryGetValue("Vary", out hasHeadersVary)) - { - vary = hasHeadersVary; - } - - hasHeaders.Headers["Vary"] = vary; - } - - //res.KeepAlive = false; - - // Per Google PageSpeed - // This instructs the proxies to cache two versions of the resource: one compressed, and one uncompressed. - // The correct version of the resource is delivered based on the client request header. - // This is a good choice for applications that are singly homed and depend on public proxies for user locality. - res.AddHeader("Vary", vary); - } - - /// <summary> - /// Removes the control characters. - /// </summary> - /// <param name="inString">The in string.</param> - /// <returns>System.String.</returns> - public static string RemoveControlCharacters(string inString) - { - if (inString == null) return null; - - var newString = new StringBuilder(); - - foreach (var ch in inString) - { - if (!char.IsControl(ch)) - { - newString.Append(ch); - } - } - return newString.ToString(); - } - } -} diff --git a/MediaBrowser.Server.Implementations/HttpServer/ServerFactory.cs b/MediaBrowser.Server.Implementations/HttpServer/ServerFactory.cs index 4dff2d5a3..5da515900 100644 --- a/MediaBrowser.Server.Implementations/HttpServer/ServerFactory.cs +++ b/MediaBrowser.Server.Implementations/HttpServer/ServerFactory.cs @@ -2,9 +2,11 @@ using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Net; +using MediaBrowser.Model.Cryptography; using MediaBrowser.Model.IO; using MediaBrowser.Model.Logging; -using ServiceStack.Logging; +using MediaBrowser.Model.Net; +using MediaBrowser.Model.Text; namespace MediaBrowser.Server.Implementations.HttpServer { @@ -21,13 +23,14 @@ namespace MediaBrowser.Server.Implementations.HttpServer ILogManager logManager, IServerConfigurationManager config, INetworkManager networkmanager, - IMemoryStreamProvider streamProvider, + IMemoryStreamFactory streamProvider, string serverName, - string defaultRedirectpath) + string defaultRedirectpath, + ITextEncoding textEncoding, + ISocketFactory socketFactory, + ICryptoProvider cryptoProvider) { - LogManager.LogFactory = new ServerLogFactory(logManager); - - return new HttpListenerHost(applicationHost, logManager, config, serverName, defaultRedirectpath, networkmanager, streamProvider); + return new HttpListenerHost(applicationHost, logManager, config, serverName, defaultRedirectpath, networkmanager, streamProvider, textEncoding, socketFactory, cryptoProvider); } } } diff --git a/MediaBrowser.Server.Implementations/HttpServer/ServerLogFactory.cs b/MediaBrowser.Server.Implementations/HttpServer/ServerLogFactory.cs deleted file mode 100644 index 40af3f3b0..000000000 --- a/MediaBrowser.Server.Implementations/HttpServer/ServerLogFactory.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; -using MediaBrowser.Model.Logging; -using ServiceStack.Logging; - -namespace MediaBrowser.Server.Implementations.HttpServer -{ - /// <summary> - /// Class ServerLogFactory - /// </summary> - public class ServerLogFactory : ILogFactory - { - /// <summary> - /// The _log manager - /// </summary> - private readonly ILogManager _logManager; - - /// <summary> - /// Initializes a new instance of the <see cref="ServerLogFactory"/> class. - /// </summary> - /// <param name="logManager">The log manager.</param> - public ServerLogFactory(ILogManager logManager) - { - _logManager = logManager; - } - - /// <summary> - /// Gets the logger. - /// </summary> - /// <param name="typeName">Name of the type.</param> - /// <returns>ILog.</returns> - public ILog GetLogger(string typeName) - { - return new ServerLogger(_logManager.GetLogger(typeName)); - } - - /// <summary> - /// Gets the logger. - /// </summary> - /// <param name="type">The type.</param> - /// <returns>ILog.</returns> - public ILog GetLogger(Type type) - { - return GetLogger(type.Name); - } - } -}
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/HttpServer/ServerLogger.cs b/MediaBrowser.Server.Implementations/HttpServer/ServerLogger.cs deleted file mode 100644 index bf7924784..000000000 --- a/MediaBrowser.Server.Implementations/HttpServer/ServerLogger.cs +++ /dev/null @@ -1,194 +0,0 @@ -using MediaBrowser.Model.Logging; -using ServiceStack.Logging; -using System; - -namespace MediaBrowser.Server.Implementations.HttpServer -{ - /// <summary> - /// Class ServerLogger - /// </summary> - public class ServerLogger : ILog - { - /// <summary> - /// The _logger - /// </summary> - private readonly ILogger _logger; - - /// <summary> - /// Initializes a new instance of the <see cref="ServerLogger"/> class. - /// </summary> - /// <param name="logger">The logger.</param> - public ServerLogger(ILogger logger) - { - _logger = logger; - } - - /// <summary> - /// Logs a Debug message and exception. - /// </summary> - /// <param name="message">The message.</param> - /// <param name="exception">The exception.</param> - public void Debug(object message, Exception exception) - { - _logger.ErrorException(GetMesssage(message), exception); - } - - /// <summary> - /// Logs a Debug message. - /// </summary> - /// <param name="message">The message.</param> - public void Debug(object message) - { - // Way too verbose. Can always make this configurable if needed again. - //_logger.Debug(GetMesssage(message)); - } - - /// <summary> - /// Logs a Debug format message. - /// </summary> - /// <param name="format">The format.</param> - /// <param name="args">The args.</param> - public void DebugFormat(string format, params object[] args) - { - // Way too verbose. Can always make this configurable if needed again. - //_logger.Debug(format, args); - } - - /// <summary> - /// Logs a Error message and exception. - /// </summary> - /// <param name="message">The message.</param> - /// <param name="exception">The exception.</param> - public void Error(object message, Exception exception) - { - _logger.ErrorException(GetMesssage(message), exception); - } - - /// <summary> - /// Logs a Error message. - /// </summary> - /// <param name="message">The message.</param> - public void Error(object message) - { - _logger.Error(GetMesssage(message)); - } - - /// <summary> - /// Logs a Error format message. - /// </summary> - /// <param name="format">The format.</param> - /// <param name="args">The args.</param> - public void ErrorFormat(string format, params object[] args) - { - _logger.Error(format, args); - } - - /// <summary> - /// Logs a Fatal message and exception. - /// </summary> - /// <param name="message">The message.</param> - /// <param name="exception">The exception.</param> - public void Fatal(object message, Exception exception) - { - _logger.FatalException(GetMesssage(message), exception); - } - - /// <summary> - /// Logs a Fatal message. - /// </summary> - /// <param name="message">The message.</param> - public void Fatal(object message) - { - _logger.Fatal(GetMesssage(message)); - } - - /// <summary> - /// Logs a Error format message. - /// </summary> - /// <param name="format">The format.</param> - /// <param name="args">The args.</param> - public void FatalFormat(string format, params object[] args) - { - _logger.Fatal(format, args); - } - - /// <summary> - /// Logs an Info message and exception. - /// </summary> - /// <param name="message">The message.</param> - /// <param name="exception">The exception.</param> - public void Info(object message, Exception exception) - { - _logger.ErrorException(GetMesssage(message), exception); - } - - /// <summary> - /// Logs an Info message and exception. - /// </summary> - /// <param name="message">The message.</param> - public void Info(object message) - { - _logger.Info(GetMesssage(message)); - } - - /// <summary> - /// Logs an Info format message. - /// </summary> - /// <param name="format">The format.</param> - /// <param name="args">The args.</param> - public void InfoFormat(string format, params object[] args) - { - _logger.Info(format, args); - } - - /// <summary> - /// Gets or sets a value indicating whether this instance is debug enabled. - /// </summary> - /// <value><c>true</c> if this instance is debug enabled; otherwise, <c>false</c>.</value> - public bool IsDebugEnabled - { - get { return true; } - } - - /// <summary> - /// Logs a Warning message and exception. - /// </summary> - /// <param name="message">The message.</param> - /// <param name="exception">The exception.</param> - public void Warn(object message, Exception exception) - { - _logger.ErrorException(GetMesssage(message), exception); - } - - /// <summary> - /// Logs a Warning message. - /// </summary> - /// <param name="message">The message.</param> - public void Warn(object message) - { - // Hide StringMapTypeDeserializer messages - // _logger.Warn(GetMesssage(message)); - } - - /// <summary> - /// Logs a Warning format message. - /// </summary> - /// <param name="format">The format.</param> - /// <param name="args">The args.</param> - public void WarnFormat(string format, params object[] args) - { - // Hide StringMapTypeDeserializer messages - // _logger.Warn(format, args); - } - - /// <summary> - /// Gets the messsage. - /// </summary> - /// <param name="o">The o.</param> - /// <returns>System.String.</returns> - private string GetMesssage(object o) - { - return o == null ? string.Empty : o.ToString(); - } - } -} diff --git a/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/Extensions.cs b/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/Extensions.cs deleted file mode 100644 index 154313fb9..000000000 --- a/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/Extensions.cs +++ /dev/null @@ -1,28 +0,0 @@ -using MediaBrowser.Model.Logging; -using SocketHttpListener.Net; -using System; - -namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp -{ - public static class Extensions - { - public static string GetOperationName(this HttpListenerRequest request) - { - return request.Url.Segments[request.Url.Segments.Length - 1]; - } - - public static void CloseOutputStream(this HttpListenerResponse response, ILogger logger) - { - try - { - response.OutputStream.Flush(); - response.OutputStream.Close(); - response.Close(); - } - catch (Exception ex) - { - logger.ErrorException("Error in HttpListenerResponseWrapper: " + ex.Message, ex); - } - } - } -} diff --git a/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/RequestMono.cs b/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/RequestMono.cs index 13ae48cff..543eb4afe 100644 --- a/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/RequestMono.cs +++ b/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/RequestMono.cs @@ -2,11 +2,10 @@ using System.Collections.Specialized; using System.Globalization; using System.IO; +using System.Net; using System.Text; using System.Threading.Tasks; -using System.Web; using MediaBrowser.Model.Services; -using ServiceStack; namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp { @@ -128,7 +127,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp { get { - return string.IsNullOrEmpty(request.Headers[HttpHeaders.Accept]) ? null : request.Headers[HttpHeaders.Accept]; + return string.IsNullOrEmpty(request.Headers["Accept"]) ? null : request.Headers["Accept"]; } } @@ -136,7 +135,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp { get { - return string.IsNullOrEmpty(request.Headers[HttpHeaders.Authorization]) ? null : request.Headers[HttpHeaders.Authorization]; + return string.IsNullOrEmpty(request.Headers["Authorization"]) ? null : request.Headers["Authorization"]; } } @@ -152,7 +151,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp string msg = String.Format("A potentially dangerous Request.{0} value was " + "detected from the client ({1}={2}).", name, key, v); - throw new HttpRequestValidationException(msg); + throw new Exception(msg); } static void ValidateNameValueCollection(string name, QueryParamCollection coll) @@ -278,9 +277,9 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp void AddRawKeyValue(StringBuilder key, StringBuilder value) { - string decodedKey = HttpUtility.UrlDecode(key.ToString(), ContentEncoding); + string decodedKey = WebUtility.UrlDecode(key.ToString()); form.Add(decodedKey, - HttpUtility.UrlDecode(value.ToString(), ContentEncoding)); + WebUtility.UrlDecode(value.ToString())); key.Length = 0; value.Length = 0; diff --git a/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/SharpWebSocket.cs b/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/SharpWebSocket.cs deleted file mode 100644 index d363c4de6..000000000 --- a/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/SharpWebSocket.cs +++ /dev/null @@ -1,172 +0,0 @@ -using MediaBrowser.Common.Events; -using MediaBrowser.Controller.Net; -using MediaBrowser.Model.Logging; -using System; -using System.Threading; -using System.Threading.Tasks; -using WebSocketState = MediaBrowser.Model.Net.WebSocketState; - -namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp -{ - public class SharpWebSocket : IWebSocket - { - /// <summary> - /// The logger - /// </summary> - private readonly ILogger _logger; - - public event EventHandler<EventArgs> Closed; - - /// <summary> - /// Gets or sets the web socket. - /// </summary> - /// <value>The web socket.</value> - private SocketHttpListener.WebSocket WebSocket { get; set; } - - private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); - - /// <summary> - /// Initializes a new instance of the <see cref="NativeWebSocket" /> class. - /// </summary> - /// <param name="socket">The socket.</param> - /// <param name="logger">The logger.</param> - /// <exception cref="System.ArgumentNullException">socket</exception> - public SharpWebSocket(SocketHttpListener.WebSocket socket, ILogger logger) - { - if (socket == null) - { - throw new ArgumentNullException("socket"); - } - - if (logger == null) - { - throw new ArgumentNullException("logger"); - } - - _logger = logger; - WebSocket = socket; - - socket.OnMessage += socket_OnMessage; - socket.OnClose += socket_OnClose; - socket.OnError += socket_OnError; - - WebSocket.ConnectAsServer(); - } - - void socket_OnError(object sender, SocketHttpListener.ErrorEventArgs e) - { - _logger.Error("Error in SharpWebSocket: {0}", e.Message ?? string.Empty); - //EventHelper.FireEventIfNotNull(Closed, this, EventArgs.Empty, _logger); - } - - void socket_OnClose(object sender, SocketHttpListener.CloseEventArgs e) - { - EventHelper.FireEventIfNotNull(Closed, this, EventArgs.Empty, _logger); - } - - void socket_OnMessage(object sender, SocketHttpListener.MessageEventArgs e) - { - //if (!string.IsNullOrWhiteSpace(e.Data)) - //{ - // if (OnReceive != null) - // { - // OnReceive(e.Data); - // } - // return; - //} - if (OnReceiveBytes != null) - { - OnReceiveBytes(e.RawData); - } - } - - /// <summary> - /// Gets or sets the state. - /// </summary> - /// <value>The state.</value> - public WebSocketState State - { - get - { - WebSocketState commonState; - - if (!Enum.TryParse(WebSocket.ReadyState.ToString(), true, out commonState)) - { - _logger.Warn("Unrecognized WebSocketState: {0}", WebSocket.ReadyState.ToString()); - } - - return commonState; - } - } - - /// <summary> - /// Sends the async. - /// </summary> - /// <param name="bytes">The bytes.</param> - /// <param name="endOfMessage">if set to <c>true</c> [end of message].</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - public Task SendAsync(byte[] bytes, bool endOfMessage, CancellationToken cancellationToken) - { - var completionSource = new TaskCompletionSource<bool>(); - - WebSocket.SendAsync(bytes, res => completionSource.TrySetResult(true)); - - return completionSource.Task; - } - - /// <summary> - /// Sends the asynchronous. - /// </summary> - /// <param name="text">The text.</param> - /// <param name="endOfMessage">if set to <c>true</c> [end of message].</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - public Task SendAsync(string text, bool endOfMessage, CancellationToken cancellationToken) - { - var completionSource = new TaskCompletionSource<bool>(); - - WebSocket.SendAsync(text, res => completionSource.TrySetResult(true)); - - return completionSource.Task; - } - - /// <summary> - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// </summary> - public void Dispose() - { - Dispose(true); - } - - /// <summary> - /// Releases unmanaged and - optionally - managed resources. - /// </summary> - /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> - protected virtual void Dispose(bool dispose) - { - if (dispose) - { - WebSocket.OnMessage -= socket_OnMessage; - WebSocket.OnClose -= socket_OnClose; - WebSocket.OnError -= socket_OnError; - - _cancellationTokenSource.Cancel(); - - WebSocket.Close(); - } - } - - /// <summary> - /// Gets or sets the receive action. - /// </summary> - /// <value>The receive action.</value> - public Action<byte[]> OnReceiveBytes { get; set; } - - /// <summary> - /// Gets or sets the on receive. - /// </summary> - /// <value>The on receive.</value> - public Action<string> OnReceive { get; set; } - } -} diff --git a/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs b/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs deleted file mode 100644 index 56f8ab429..000000000 --- a/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs +++ /dev/null @@ -1,216 +0,0 @@ -using System.Collections.Specialized; -using MediaBrowser.Controller.Net; -using MediaBrowser.Model.Logging; -using SocketHttpListener.Net; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Emby.Server.Implementations.HttpServer; -using Emby.Server.Implementations.Logging; -using MediaBrowser.Common.IO; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.Services; -using ServiceStack; - -namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp -{ - public class WebSocketSharpListener : IHttpListener - { - private HttpListener _listener; - - private readonly ILogger _logger; - private readonly string _certificatePath; - private readonly IMemoryStreamProvider _memoryStreamProvider; - - public WebSocketSharpListener(ILogger logger, string certificatePath, IMemoryStreamProvider memoryStreamProvider) - { - _logger = logger; - _certificatePath = certificatePath; - _memoryStreamProvider = memoryStreamProvider; - } - - public Action<Exception, IRequest> ErrorHandler { get; set; } - - public Func<IHttpRequest, Uri, Task> RequestHandler { get; set; } - - public Action<WebSocketConnectingEventArgs> WebSocketConnecting { get; set; } - - public Action<WebSocketConnectEventArgs> WebSocketConnected { get; set; } - - public void Start(IEnumerable<string> urlPrefixes) - { - if (_listener == null) - _listener = new HttpListener(new PatternsLogger(_logger), _certificatePath); - - foreach (var prefix in urlPrefixes) - { - _logger.Info("Adding HttpListener prefix " + prefix); - _listener.Prefixes.Add(prefix); - } - - _listener.OnContext = ProcessContext; - - _listener.Start(); - } - - private void ProcessContext(HttpListenerContext context) - { - Task.Factory.StartNew(() => InitTask(context)); - } - - private void InitTask(HttpListenerContext context) - { - try - { - var task = this.ProcessRequestAsync(context); - task.ContinueWith(x => HandleError(x.Exception, context), TaskContinuationOptions.OnlyOnFaulted | TaskContinuationOptions.AttachedToParent); - - //if (task.Status == TaskStatus.Created) - //{ - // task.RunSynchronously(); - //} - } - catch (Exception ex) - { - HandleError(ex, context); - } - } - - private Task ProcessRequestAsync(HttpListenerContext context) - { - var request = context.Request; - - if (request.IsWebSocketRequest) - { - LoggerUtils.LogRequest(_logger, request); - - ProcessWebSocketRequest(context); - return Task.FromResult(true); - } - - if (string.IsNullOrEmpty(context.Request.RawUrl)) - return ((object)null).AsTaskResult(); - - var httpReq = GetRequest(context); - - return RequestHandler(httpReq, request.Url); - } - - private void ProcessWebSocketRequest(HttpListenerContext ctx) - { - try - { - var endpoint = ctx.Request.RemoteEndPoint.ToString(); - var url = ctx.Request.RawUrl; - var queryString = ctx.Request.QueryString ?? new NameValueCollection(); - - var queryParamCollection = new QueryParamCollection(); - - foreach (var key in queryString.AllKeys) - { - queryParamCollection[key] = queryString[key]; - } - - var connectingArgs = new WebSocketConnectingEventArgs - { - Url = url, - QueryString = queryParamCollection, - Endpoint = endpoint - }; - - if (WebSocketConnecting != null) - { - WebSocketConnecting(connectingArgs); - } - - if (connectingArgs.AllowConnection) - { - _logger.Debug("Web socket connection allowed"); - - var webSocketContext = ctx.AcceptWebSocket(null); - - if (WebSocketConnected != null) - { - WebSocketConnected(new WebSocketConnectEventArgs - { - Url = url, - QueryString = queryParamCollection, - WebSocket = new SharpWebSocket(webSocketContext.WebSocket, _logger), - Endpoint = endpoint - }); - } - } - else - { - _logger.Warn("Web socket connection not allowed"); - ctx.Response.StatusCode = 401; - ctx.Response.Close(); - } - } - catch (Exception ex) - { - _logger.ErrorException("AcceptWebSocketAsync error", ex); - ctx.Response.StatusCode = 500; - ctx.Response.Close(); - } - } - - private IHttpRequest GetRequest(HttpListenerContext httpContext) - { - var operationName = httpContext.Request.GetOperationName(); - - var req = new WebSocketSharpRequest(httpContext, operationName, RequestAttributes.None, _logger, _memoryStreamProvider); - - return req; - } - - private void HandleError(Exception ex, HttpListenerContext context) - { - var httpReq = GetRequest(context); - - if (ErrorHandler != null) - { - ErrorHandler(ex, httpReq); - } - } - - public void Stop() - { - if (_listener != null) - { - foreach (var prefix in _listener.Prefixes.ToList()) - { - _listener.Prefixes.Remove(prefix); - } - - _listener.Close(); - } - } - - public void Dispose() - { - Dispose(true); - } - - private bool _disposed; - private readonly object _disposeLock = new object(); - protected virtual void Dispose(bool disposing) - { - if (_disposed) return; - - lock (_disposeLock) - { - if (_disposed) return; - - if (disposing) - { - Stop(); - } - - //release unmanaged resources here... - _disposed = true; - } - } - } -}
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpRequest.cs b/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpRequest.cs index 72047609d..6f44fcce7 100644 --- a/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpRequest.cs +++ b/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpRequest.cs @@ -1,17 +1,14 @@ using System; using System.Collections.Generic; -using System.Collections.Specialized; using System.IO; using System.Text; using Emby.Server.Implementations.HttpServer.SocketSharp; using Funq; -using MediaBrowser.Common.IO; using MediaBrowser.Model.IO; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Services; using ServiceStack; using ServiceStack.Host; -using ServiceStack.Web; using SocketHttpListener.Net; using IHttpFile = MediaBrowser.Model.Services.IHttpFile; using IHttpRequest = MediaBrowser.Model.Services.IHttpRequest; @@ -25,9 +22,9 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp public Container Container { get; set; } private readonly HttpListenerRequest request; private readonly IHttpResponse response; - private readonly IMemoryStreamProvider _memoryStreamProvider; + private readonly IMemoryStreamFactory _memoryStreamProvider; - public WebSocketSharpRequest(HttpListenerContext httpContext, string operationName, RequestAttributes requestAttributes, ILogger logger, IMemoryStreamProvider memoryStreamProvider) + public WebSocketSharpRequest(HttpListenerContext httpContext, string operationName, ILogger logger, IMemoryStreamFactory memoryStreamProvider) { this.OperationName = operationName; _memoryStreamProvider = memoryStreamProvider; @@ -55,36 +52,10 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp get { return response; } } - public T TryResolve<T>() - { - if (typeof(T) == typeof(IHttpRequest)) - throw new Exception("You don't need to use IHttpRequest.TryResolve<IHttpRequest> to resolve itself"); - - if (typeof(T) == typeof(IHttpResponse)) - throw new Exception("Resolve IHttpResponse with 'Response' property instead of IHttpRequest.TryResolve<IHttpResponse>"); - - return Container == null - ? HostContext.TryResolve<T>() - : Container.TryResolve<T>(); - } - public string OperationName { get; set; } public object Dto { get; set; } - public string GetRawBody() - { - if (bufferedStream != null) - { - return bufferedStream.ToArray().FromUtf8Bytes(); - } - - using (var reader = new StreamReader(InputStream)) - { - return reader.ReadToEnd(); - } - } - public string RawUrl { get { return request.RawUrl; } @@ -104,7 +75,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp { get { - return String.IsNullOrEmpty(request.Headers[HttpHeaders.XForwardedFor]) ? null : request.Headers[HttpHeaders.XForwardedFor]; + return String.IsNullOrEmpty(request.Headers["X-Forwarded-For"]) ? null : request.Headers["X-Forwarded-For"]; } } @@ -112,7 +83,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp { get { - return string.IsNullOrEmpty(request.Headers[HttpHeaders.XForwardedPort]) ? (int?)null : int.Parse(request.Headers[HttpHeaders.XForwardedPort]); + return string.IsNullOrEmpty(request.Headers["X-Forwarded-Port"]) ? (int?)null : int.Parse(request.Headers["X-Forwarded-Port"]); } } @@ -120,7 +91,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp { get { - return string.IsNullOrEmpty(request.Headers[HttpHeaders.XForwardedProtocol]) ? null : request.Headers[HttpHeaders.XForwardedProtocol]; + return string.IsNullOrEmpty(request.Headers["X-Forwarded-Proto"]) ? null : request.Headers["X-Forwarded-Proto"]; } } @@ -128,7 +99,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp { get { - return String.IsNullOrEmpty(request.Headers[HttpHeaders.XRealIp]) ? null : request.Headers[HttpHeaders.XRealIp]; + return String.IsNullOrEmpty(request.Headers["X-Real-IP"]) ? null : request.Headers["X-Real-IP"]; } } @@ -140,7 +111,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp return remoteIp ?? (remoteIp = (CheckBadChars(XForwardedFor)) ?? (NormalizeIp(CheckBadChars(XRealIp)) ?? - (request.RemoteEndPoint != null ? NormalizeIp(request.RemoteEndPoint.Address.ToString()) : null))); + (request.RemoteEndPoint != null ? NormalizeIp(request.RemoteEndPoint.IpAddress.ToString()) : null))); } } @@ -280,7 +251,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp defaultContentType = HostContext.Config.DefaultContentType; } - var customContentTypes = HostContext.ContentTypes.ContentTypeFormats.Values; + var customContentTypes = ContentTypes.Instance.ContentTypeFormats.Values; var preferredContentTypes = new string[] {}; var acceptsAnything = false; @@ -328,11 +299,6 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp } } - if (httpReq.ContentType.MatchesContentType(MimeTypes.Soap12)) - { - return MimeTypes.Soap12; - } - if (acceptContentTypes == null && httpReq.ContentType == MimeTypes.Soap11) { return MimeTypes.Soap11; @@ -344,10 +310,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp private static string GetQueryStringContentType(IRequest httpReq) { - var callback = httpReq.QueryString[Keywords.Callback]; - if (!string.IsNullOrEmpty(callback)) return MimeTypes.Json; - - var format = httpReq.QueryString[Keywords.Format]; + var format = httpReq.QueryString["format"]; if (format == null) { const int formatMaxLength = 4; @@ -359,12 +322,11 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp } format = format.LeftPart('.').ToLower(); - if (format.Contains("json")) return MimeTypes.Json; + if (format.Contains("json")) return "application/json"; if (format.Contains("xml")) return MimeTypes.Xml; - if (format.Contains("jsv")) return MimeTypes.Jsv; string contentType; - HostContext.ContentTypes.ContentTypeFormats.TryGetValue(format, out contentType); + ContentTypes.Instance.ContentTypeFormats.TryGetValue(format, out contentType); return contentType; } @@ -474,10 +436,9 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp get { return request.UserAgent; } } - private QueryParamCollection headers; public QueryParamCollection Headers { - get { return headers ?? (headers = ToQueryParams(request.Headers)); } + get { return request.Headers; } } private QueryParamCollection queryString; @@ -492,18 +453,6 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp get { return formData ?? (formData = this.Form); } } - private QueryParamCollection ToQueryParams(NameValueCollection collection) - { - var result = new QueryParamCollection(); - - foreach (var key in collection.AllKeys) - { - result[key] = collection[key]; - } - - return result; - } - public bool IsLocal { get { return request.IsLocal; } @@ -563,21 +512,9 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp } } - public bool UseBufferedStream - { - get { return bufferedStream != null; } - set - { - bufferedStream = value - ? bufferedStream ?? _memoryStreamProvider.CreateNew(request.InputStream.ReadFully()) - : null; - } - } - - private MemoryStream bufferedStream; public Stream InputStream { - get { return bufferedStream ?? request.InputStream; } + get { return request.InputStream; } } public long ContentLength @@ -613,7 +550,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp } } - static Stream GetSubStream(Stream stream, IMemoryStreamProvider streamProvider) + static Stream GetSubStream(Stream stream, IMemoryStreamFactory streamProvider) { if (stream is MemoryStream) { @@ -654,4 +591,13 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp return pathInfo; } } + + public class HttpFile : IHttpFile + { + public string Name { get; set; } + public string FileName { get; set; } + public long ContentLength { get; set; } + public string ContentType { get; set; } + public Stream InputStream { get; set; } + } } diff --git a/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpResponse.cs b/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpResponse.cs deleted file mode 100644 index 3aae6c9ca..000000000 --- a/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpResponse.cs +++ /dev/null @@ -1,152 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Net; -using MediaBrowser.Model.Logging; -using ServiceStack; -using ServiceStack.Host; -using HttpListenerResponse = SocketHttpListener.Net.HttpListenerResponse; -using IHttpResponse = MediaBrowser.Model.Services.IHttpResponse; -using IRequest = MediaBrowser.Model.Services.IRequest; - -namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp -{ - public class WebSocketSharpResponse : IHttpResponse - { - private readonly ILogger _logger; - private readonly HttpListenerResponse _response; - - public WebSocketSharpResponse(ILogger logger, HttpListenerResponse response, IRequest request) - { - _logger = logger; - this._response = response; - Items = new Dictionary<string, object>(); - Request = request; - } - - public IRequest Request { get; private set; } - public bool UseBufferedStream { get; set; } - public Dictionary<string, object> Items { get; private set; } - public object OriginalResponse - { - get { return _response; } - } - - public int StatusCode - { - get { return this._response.StatusCode; } - set { this._response.StatusCode = value; } - } - - public string StatusDescription - { - get { return this._response.StatusDescription; } - set { this._response.StatusDescription = value; } - } - - public string ContentType - { - get { return _response.ContentType; } - set { _response.ContentType = value; } - } - - //public ICookies Cookies { get; set; } - - public void AddHeader(string name, string value) - { - if (string.Equals(name, "Content-Type", StringComparison.OrdinalIgnoreCase)) - { - ContentType = value; - return; - } - - _response.AddHeader(name, value); - } - - public string GetHeader(string name) - { - return _response.Headers[name]; - } - - public void Redirect(string url) - { - _response.Redirect(url); - } - - public Stream OutputStream - { - get { return _response.OutputStream; } - } - - public object Dto { get; set; } - - public void Write(string text) - { - var bOutput = System.Text.Encoding.UTF8.GetBytes(text); - _response.ContentLength64 = bOutput.Length; - - var outputStream = _response.OutputStream; - outputStream.Write(bOutput, 0, bOutput.Length); - Close(); - } - - public void Close() - { - if (!this.IsClosed) - { - this.IsClosed = true; - - try - { - this._response.CloseOutputStream(_logger); - } - catch (Exception ex) - { - _logger.ErrorException("Error closing HttpListener output stream", ex); - } - } - } - - public void End() - { - Close(); - } - - public void Flush() - { - _response.OutputStream.Flush(); - } - - public bool IsClosed - { - get; - private set; - } - - public void SetContentLength(long contentLength) - { - //you can happily set the Content-Length header in Asp.Net - //but HttpListener will complain if you do - you have to set ContentLength64 on the response. - //workaround: HttpListener throws "The parameter is incorrect" exceptions when we try to set the Content-Length header - _response.ContentLength64 = contentLength; - } - - public void SetCookie(Cookie cookie) - { - var cookieStr = cookie.AsHeaderValue(); - _response.Headers.Add(HttpHeaders.SetCookie, cookieStr); - } - - public bool SendChunked - { - get { return _response.SendChunked; } - set { _response.SendChunked = value; } - } - - public bool KeepAlive { get; set; } - - public void ClearCookies() - { - } - } -} |
