aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Common.Implementations/HttpServer
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.Common.Implementations/HttpServer')
-rw-r--r--MediaBrowser.Common.Implementations/HttpServer/BaseRestService.cs458
-rw-r--r--MediaBrowser.Common.Implementations/HttpServer/HttpServer.cs570
-rw-r--r--MediaBrowser.Common.Implementations/HttpServer/NativeWebSocket.cs165
-rw-r--r--MediaBrowser.Common.Implementations/HttpServer/ServerFactory.cs27
-rw-r--r--MediaBrowser.Common.Implementations/HttpServer/StreamWriter.cs48
-rw-r--r--MediaBrowser.Common.Implementations/HttpServer/SwaggerService.cs38
6 files changed, 0 insertions, 1306 deletions
diff --git a/MediaBrowser.Common.Implementations/HttpServer/BaseRestService.cs b/MediaBrowser.Common.Implementations/HttpServer/BaseRestService.cs
deleted file mode 100644
index 382183b58..000000000
--- a/MediaBrowser.Common.Implementations/HttpServer/BaseRestService.cs
+++ /dev/null
@@ -1,458 +0,0 @@
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.IO;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Model.Logging;
-using ServiceStack.Common;
-using ServiceStack.Common.Web;
-using ServiceStack.ServiceHost;
-using ServiceStack.ServiceInterface;
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Threading.Tasks;
-using MimeTypes = MediaBrowser.Common.Net.MimeTypes;
-
-namespace MediaBrowser.Common.Implementations.HttpServer
-{
- /// <summary>
- /// Class BaseRestService
- /// </summary>
- public class BaseRestService : Service, IRestfulService
- {
- /// <summary>
- /// Gets or sets the logger.
- /// </summary>
- /// <value>The logger.</value>
- public ILogger Logger { get; set; }
-
- /// <summary>
- /// Gets a value indicating whether this instance is range request.
- /// </summary>
- /// <value><c>true</c> if this instance is range request; otherwise, <c>false</c>.</value>
- protected bool IsRangeRequest
- {
- get
- {
- return Request.Headers.AllKeys.Contains("Range");
- }
- }
-
- /// <summary>
- /// To the optimized result.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="result">The result.</param>
- /// <returns>System.Object.</returns>
- /// <exception cref="System.ArgumentNullException">result</exception>
- protected object ToOptimizedResult<T>(T result)
- where T : class
- {
- if (result == null)
- {
- throw new ArgumentNullException("result");
- }
-
- Response.AddHeader("Vary", "Accept-Encoding");
-
- return RequestContext.ToOptimizedResult(result);
- }
-
- /// <summary>
- /// To the optimized result using cache.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="cacheKey">The cache key.</param>
- /// <param name="lastDateModified">The last date modified.</param>
- /// <param name="cacheDuration">Duration of the cache.</param>
- /// <param name="factoryFn">The factory fn.</param>
- /// <returns>System.Object.</returns>
- /// <exception cref="System.ArgumentNullException">cacheKey</exception>
- protected object ToOptimizedResultUsingCache<T>(Guid cacheKey, DateTime lastDateModified, TimeSpan? cacheDuration, Func<T> factoryFn)
- where T : class
- {
- if (cacheKey == Guid.Empty)
- {
- throw new ArgumentNullException("cacheKey");
- }
- if (factoryFn == null)
- {
- throw new ArgumentNullException("factoryFn");
- }
-
- var key = cacheKey.ToString("N");
-
- var result = PreProcessCachedResult(cacheKey, key, lastDateModified, cacheDuration, string.Empty);
-
- if (result != null)
- {
- // Return null so that service stack won't do anything
- return null;
- }
-
- return ToOptimizedResult(factoryFn());
- }
-
- /// <summary>
- /// To the cached result.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="cacheKey">The cache key.</param>
- /// <param name="lastDateModified">The last date modified.</param>
- /// <param name="cacheDuration">Duration of the cache.</param>
- /// <param name="factoryFn">The factory fn.</param>
- /// <param name="contentType">Type of the content.</param>
- /// <returns>System.Object.</returns>
- /// <exception cref="System.ArgumentNullException">cacheKey</exception>
- protected object ToCachedResult<T>(Guid cacheKey, DateTime lastDateModified, TimeSpan? cacheDuration, Func<T> factoryFn, string contentType)
- where T : class
- {
- if (cacheKey == Guid.Empty)
- {
- throw new ArgumentNullException("cacheKey");
- }
- if (factoryFn == null)
- {
- throw new ArgumentNullException("factoryFn");
- }
-
- var key = cacheKey.ToString("N");
-
- var result = PreProcessCachedResult(cacheKey, key, lastDateModified, cacheDuration, contentType);
-
- if (result != null)
- {
- // Return null so that service stack won't do anything
- return null;
- }
-
- return factoryFn();
- }
-
- /// <summary>
- /// To the static file result.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <returns>System.Object.</returns>
- /// <exception cref="System.ArgumentNullException">path</exception>
- protected object ToStaticFileResult(string path)
- {
- if (string.IsNullOrEmpty(path))
- {
- throw new ArgumentNullException("path");
- }
-
- var dateModified = File.GetLastWriteTimeUtc(path);
-
- var cacheKey = path + dateModified.Ticks;
-
- return ToStaticResult(cacheKey.GetMD5(), dateModified, null, MimeTypes.GetMimeType(path), () => Task.FromResult(GetFileStream(path)));
- }
-
- /// <summary>
- /// Gets the file stream.
- /// </summary>
- /// <param name="path">The path.</param>
- /// <returns>Stream.</returns>
- private Stream GetFileStream(string path)
- {
- return new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, FileOptions.Asynchronous);
- }
-
- /// <summary>
- /// To the static result.
- /// </summary>
- /// <param name="cacheKey">The cache key.</param>
- /// <param name="lastDateModified">The last date modified.</param>
- /// <param name="cacheDuration">Duration of the cache.</param>
- /// <param name="contentType">Type of the content.</param>
- /// <param name="factoryFn">The factory fn.</param>
- /// <returns>System.Object.</returns>
- /// <exception cref="System.ArgumentNullException">cacheKey</exception>
- protected object ToStaticResult(Guid cacheKey, DateTime? lastDateModified, TimeSpan? cacheDuration, string contentType, Func<Task<Stream>> factoryFn)
- {
- if (cacheKey == Guid.Empty)
- {
- throw new ArgumentNullException("cacheKey");
- }
- if (factoryFn == null)
- {
- throw new ArgumentNullException("factoryFn");
- }
-
- var key = cacheKey.ToString("N");
-
- var result = PreProcessCachedResult(cacheKey, key, lastDateModified, cacheDuration, contentType);
-
- if (result != null)
- {
- // Return null so that service stack won't do anything
- return null;
- }
-
- var compress = ShouldCompressResponse(contentType);
-
- if (compress)
- {
- Response.AddHeader("Vary", "Accept-Encoding");
- }
-
- return ToStaticResult(contentType, factoryFn, compress).Result;
- }
-
- /// <summary>
- /// Shoulds the compress response.
- /// </summary>
- /// <param name="contentType">Type of the content.</param>
- /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
- private bool ShouldCompressResponse(string contentType)
- {
- // It will take some work to support compression with byte range requests
- if (IsRangeRequest)
- {
- return false;
- }
-
- // Don't compress media
- if (contentType.StartsWith("audio/", StringComparison.OrdinalIgnoreCase) || contentType.StartsWith("video/", StringComparison.OrdinalIgnoreCase))
- {
- return false;
- }
-
- // Don't compress images
- if (contentType.StartsWith("image/", StringComparison.OrdinalIgnoreCase))
- {
- return false;
- }
-
- if (contentType.StartsWith("font/", StringComparison.OrdinalIgnoreCase))
- {
- return false;
- }
- if (contentType.StartsWith("application/", StringComparison.OrdinalIgnoreCase))
- {
- return false;
- }
-
- return true;
- }
-
- /// <summary>
- /// To the static result.
- /// </summary>
- /// <param name="contentType">Type of the content.</param>
- /// <param name="factoryFn">The factory fn.</param>
- /// <param name="compress">if set to <c>true</c> [compress].</param>
- /// <returns>System.Object.</returns>
- private async Task<object> ToStaticResult(string contentType, Func<Task<Stream>> factoryFn, bool compress)
- {
- if (!compress || string.IsNullOrEmpty(RequestContext.CompressionType))
- {
- Response.ContentType = contentType;
-
- var stream = await factoryFn().ConfigureAwait(false);
-
- return new StreamWriter(stream);
- }
-
- string content;
-
- using (var stream = await factoryFn().ConfigureAwait(false))
- {
- using (var reader = new StreamReader(stream))
- {
- content = await reader.ReadToEndAsync().ConfigureAwait(false);
- }
- }
-
- var contents = content.Compress(RequestContext.CompressionType);
-
- return new CompressedResult(contents, RequestContext.CompressionType, contentType);
- }
-
- /// <summary>
- /// Pres the process optimized result.
- /// </summary>
- /// <param name="cacheKey">The cache key.</param>
- /// <param name="cacheKeyString">The cache key string.</param>
- /// <param name="lastDateModified">The last date modified.</param>
- /// <param name="cacheDuration">Duration of the cache.</param>
- /// <param name="contentType">Type of the content.</param>
- /// <returns>System.Object.</returns>
- private object PreProcessCachedResult(Guid cacheKey, string cacheKeyString, DateTime? lastDateModified, TimeSpan? cacheDuration, string contentType)
- {
- Response.AddHeader("ETag", cacheKeyString);
-
- if (IsNotModified(cacheKey, lastDateModified, cacheDuration))
- {
- AddAgeHeader(lastDateModified);
- AddExpiresHeader(cacheKeyString, cacheDuration);
- //ctx.Response.SendChunked = false;
-
- if (!string.IsNullOrEmpty(contentType))
- {
- Response.ContentType = contentType;
- }
-
- Response.StatusCode = 304;
-
- return new byte[]{};
- }
-
- SetCachingHeaders(cacheKeyString, lastDateModified, cacheDuration);
-
- return null;
- }
-
- /// <summary>
- /// Determines whether [is not modified] [the specified cache key].
- /// </summary>
- /// <param name="cacheKey">The cache key.</param>
- /// <param name="lastDateModified">The last date modified.</param>
- /// <param name="cacheDuration">Duration of the cache.</param>
- /// <returns><c>true</c> if [is not modified] [the specified cache key]; otherwise, <c>false</c>.</returns>
- private bool IsNotModified(Guid? cacheKey, DateTime? lastDateModified, TimeSpan? cacheDuration)
- {
- var isNotModified = true;
-
- if (Request.Headers.AllKeys.Contains("If-Modified-Since"))
- {
- DateTime ifModifiedSince;
-
- if (DateTime.TryParse(Request.Headers["If-Modified-Since"], out ifModifiedSince))
- {
- isNotModified = IsNotModified(ifModifiedSince.ToUniversalTime(), cacheDuration, lastDateModified);
- }
- }
-
- // Validate If-None-Match
- if (isNotModified && (cacheKey.HasValue || !string.IsNullOrEmpty(Request.Headers["If-None-Match"])))
- {
- Guid ifNoneMatch;
-
- if (Guid.TryParse(Request.Headers["If-None-Match"] ?? string.Empty, out ifNoneMatch))
- {
- if (cacheKey.HasValue && cacheKey.Value == ifNoneMatch)
- {
- return true;
- }
- }
- }
-
- return false;
- }
-
- /// <summary>
- /// Determines whether [is not modified] [the specified if modified since].
- /// </summary>
- /// <param name="ifModifiedSince">If modified since.</param>
- /// <param name="cacheDuration">Duration of the cache.</param>
- /// <param name="dateModified">The date modified.</param>
- /// <returns><c>true</c> if [is not modified] [the specified if modified since]; otherwise, <c>false</c>.</returns>
- private bool IsNotModified(DateTime ifModifiedSince, TimeSpan? cacheDuration, DateTime? dateModified)
- {
- if (dateModified.HasValue)
- {
- var lastModified = NormalizeDateForComparison(dateModified.Value);
- ifModifiedSince = NormalizeDateForComparison(ifModifiedSince);
-
- return lastModified <= ifModifiedSince;
- }
-
- if (cacheDuration.HasValue)
- {
- var cacheExpirationDate = ifModifiedSince.Add(cacheDuration.Value);
-
- if (DateTime.UtcNow < cacheExpirationDate)
- {
- return true;
- }
- }
-
- return false;
- }
-
-
- /// <summary>
- /// When the browser sends the IfModifiedDate, it's precision is limited to seconds, so this will account for that
- /// </summary>
- /// <param name="date">The date.</param>
- /// <returns>DateTime.</returns>
- private DateTime NormalizeDateForComparison(DateTime date)
- {
- return new DateTime(date.Year, date.Month, date.Day, date.Hour, date.Minute, date.Second, date.Kind);
- }
-
- /// <summary>
- /// Sets the caching headers.
- /// </summary>
- /// <param name="cacheKey">The cache key.</param>
- /// <param name="lastDateModified">The last date modified.</param>
- /// <param name="cacheDuration">Duration of the cache.</param>
- private void SetCachingHeaders(string cacheKey, DateTime? lastDateModified, TimeSpan? cacheDuration)
- {
- // Don't specify both last modified and Etag, unless caching unconditionally. They are redundant
- // https://developers.google.com/speed/docs/best-practices/caching#LeverageBrowserCaching
- if (lastDateModified.HasValue && (string.IsNullOrEmpty(cacheKey) || cacheDuration.HasValue))
- {
- AddAgeHeader(lastDateModified);
- Response.AddHeader("LastModified", lastDateModified.Value.ToString("r"));
- }
-
- if (cacheDuration.HasValue)
- {
- Response.AddHeader("Cache-Control", "public, max-age=" + Convert.ToInt32(cacheDuration.Value.TotalSeconds));
- }
- else if (!string.IsNullOrEmpty(cacheKey))
- {
- Response.AddHeader("Cache-Control", "public");
- }
- else
- {
- Response.AddHeader("Cache-Control", "no-cache, no-store, must-revalidate");
- Response.AddHeader("pragma", "no-cache, no-store, must-revalidate");
- }
-
- AddExpiresHeader(cacheKey, cacheDuration);
- }
-
- /// <summary>
- /// Adds the expires header.
- /// </summary>
- /// <param name="cacheKey">The cache key.</param>
- /// <param name="cacheDuration">Duration of the cache.</param>
- private void AddExpiresHeader(string cacheKey, TimeSpan? cacheDuration)
- {
- if (cacheDuration.HasValue)
- {
- Response.AddHeader("Expires", DateTime.UtcNow.Add(cacheDuration.Value).ToString("r"));
- }
- else if (string.IsNullOrEmpty(cacheKey))
- {
- Response.AddHeader("Expires", "-1");
- }
- }
-
- /// <summary>
- /// Adds the age header.
- /// </summary>
- /// <param name="lastDateModified">The last date modified.</param>
- private void AddAgeHeader(DateTime? lastDateModified)
- {
- if (lastDateModified.HasValue)
- {
- Response.AddHeader("Age", Convert.ToInt64((DateTime.UtcNow - lastDateModified.Value).TotalSeconds).ToString(CultureInfo.InvariantCulture));
- }
- }
-
- /// <summary>
- /// Gets the routes.
- /// </summary>
- /// <returns>IEnumerable{RouteInfo}.</returns>
- public IEnumerable<RouteInfo> GetRoutes()
- {
- return new RouteInfo[] {};
- }
- }
-}
diff --git a/MediaBrowser.Common.Implementations/HttpServer/HttpServer.cs b/MediaBrowser.Common.Implementations/HttpServer/HttpServer.cs
deleted file mode 100644
index 785ca56c6..000000000
--- a/MediaBrowser.Common.Implementations/HttpServer/HttpServer.cs
+++ /dev/null
@@ -1,570 +0,0 @@
-using Funq;
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.Kernel;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Serialization;
-using ServiceStack.Api.Swagger;
-using ServiceStack.Common.Web;
-using ServiceStack.Configuration;
-using ServiceStack.Logging.NLogger;
-using ServiceStack.ServiceHost;
-using ServiceStack.ServiceInterface.Cors;
-using ServiceStack.Text;
-using ServiceStack.WebHost.Endpoints;
-using ServiceStack.WebHost.Endpoints.Extensions;
-using ServiceStack.WebHost.Endpoints.Support;
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Net;
-using System.Net.WebSockets;
-using System.Reactive.Linq;
-using System.Reflection;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Common.Implementations.HttpServer
-{
- /// <summary>
- /// Class HttpServer
- /// </summary>
- public class HttpServer : HttpListenerBase, IHttpServer
- {
- /// <summary>
- /// The logger
- /// </summary>
- private readonly ILogger _logger;
-
- /// <summary>
- /// Gets the URL prefix.
- /// </summary>
- /// <value>The URL prefix.</value>
- public string UrlPrefix { get; private set; }
-
- /// <summary>
- /// The _rest services
- /// </summary>
- private readonly List<IRestfulService> _restServices = new List<IRestfulService>();
-
- /// <summary>
- /// Gets or sets the application host.
- /// </summary>
- /// <value>The application host.</value>
- private IApplicationHost ApplicationHost { get; set; }
-
- /// <summary>
- /// This subscribes to HttpListener requests and finds the appropriate BaseHandler to process it
- /// </summary>
- /// <value>The HTTP listener.</value>
- private IDisposable HttpListener { get; set; }
-
- /// <summary>
- /// Gets or sets the protobuf serializer.
- /// </summary>
- /// <value>The protobuf serializer.</value>
- private IProtobufSerializer ProtobufSerializer { get; set; }
-
- /// <summary>
- /// Occurs when [web socket connected].
- /// </summary>
- public event EventHandler<WebSocketConnectEventArgs> WebSocketConnected;
-
- /// <summary>
- /// Gets the default redirect path.
- /// </summary>
- /// <value>The default redirect path.</value>
- private string DefaultRedirectPath { get; set; }
-
- /// <summary>
- /// Gets or sets the name of the server.
- /// </summary>
- /// <value>The name of the server.</value>
- private string ServerName { get; set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="HttpServer" /> class.
- /// </summary>
- /// <param name="applicationHost">The application host.</param>
- /// <param name="protobufSerializer">The protobuf serializer.</param>
- /// <param name="logger">The logger.</param>
- /// <param name="serverName">Name of the server.</param>
- /// <param name="defaultRedirectpath">The default redirectpath.</param>
- /// <exception cref="System.ArgumentNullException">urlPrefix</exception>
- public HttpServer(IApplicationHost applicationHost, IProtobufSerializer protobufSerializer, ILogger logger, string serverName, string defaultRedirectpath)
- : base()
- {
- if (protobufSerializer == null)
- {
- throw new ArgumentNullException("protobufSerializer");
- }
- if (logger == null)
- {
- throw new ArgumentNullException("logger");
- }
- if (applicationHost == null)
- {
- throw new ArgumentNullException("applicationHost");
- }
- if (string.IsNullOrEmpty(serverName))
- {
- throw new ArgumentNullException("serverName");
- }
- if (string.IsNullOrEmpty(defaultRedirectpath))
- {
- throw new ArgumentNullException("defaultRedirectpath");
- }
-
- ServerName = serverName;
- DefaultRedirectPath = defaultRedirectpath;
- ProtobufSerializer = protobufSerializer;
- _logger = logger;
- ApplicationHost = applicationHost;
-
- EndpointHostConfig.Instance.ServiceStackHandlerFactoryPath = null;
- EndpointHostConfig.Instance.MetadataRedirectPath = "metadata";
- }
-
- /// <summary>
- /// Configures the specified container.
- /// </summary>
- /// <param name="container">The container.</param>
- public override void Configure(Container container)
- {
- JsConfig.DateHandler = JsonDateHandler.ISO8601;
- JsConfig.ExcludeTypeInfo = true;
- JsConfig.IncludeNullValues = false;
-
- SetConfig(new EndpointHostConfig
- {
- DefaultRedirectPath = DefaultRedirectPath,
-
- // Tell SS to bubble exceptions up to here
- WriteErrorsToResponse = false,
-
- DebugMode = true
- });
-
- container.Adapter = new ContainerAdapter(ApplicationHost);
-
- Plugins.Add(new SwaggerFeature());
- Plugins.Add(new CorsFeature());
-
- ServiceStack.Logging.LogManager.LogFactory = new NLogFactory();
- }
-
- /// <summary>
- /// Starts the Web Service
- /// </summary>
- /// <param name="urlBase">A Uri that acts as the base that the server is listening on.
- /// Format should be: http://127.0.0.1:8080/ or http://127.0.0.1:8080/somevirtual/
- /// Note: the trailing slash is required! For more info see the
- /// HttpListener.Prefixes property on MSDN.</param>
- public override void Start(string urlBase)
- {
- if (string.IsNullOrEmpty(urlBase))
- {
- throw new ArgumentNullException("urlBase");
- }
-
- // *** Already running - just leave it in place
- if (IsStarted)
- {
- return;
- }
-
- if (Listener == null)
- {
- Listener = new HttpListener();
- }
-
- EndpointHost.Config.ServiceStackHandlerFactoryPath = HttpListenerRequestWrapper.GetHandlerPathIfAny(urlBase);
-
- UrlPrefix = urlBase;
-
- Listener.Prefixes.Add(urlBase);
-
- IsStarted = true;
- Listener.Start();
-
- HttpListener = CreateObservableStream().Subscribe(ProcessHttpRequestAsync);
- }
-
- /// <summary>
- /// Creates the observable stream.
- /// </summary>
- /// <returns>IObservable{HttpListenerContext}.</returns>
- private IObservable<HttpListenerContext> CreateObservableStream()
- {
- return Observable.Create<HttpListenerContext>(obs =>
- Observable.FromAsync(() => Listener.GetContextAsync())
- .Subscribe(obs))
- .Repeat()
- .Retry()
- .Publish()
- .RefCount();
- }
-
- /// <summary>
- /// Processes incoming http requests by routing them to the appropiate handler
- /// </summary>
- /// <param name="context">The CTX.</param>
- private async void ProcessHttpRequestAsync(HttpListenerContext context)
- {
- LogHttpRequest(context);
-
- if (context.Request.IsWebSocketRequest)
- {
- await ProcessWebSocketRequest(context).ConfigureAwait(false);
- return;
- }
-
-
- Task.Run(() =>
- {
- RaiseReceiveWebRequest(context);
-
- try
- {
- ProcessRequest(context);
- }
- catch (InvalidOperationException ex)
- {
- HandleException(context.Response, ex, 422);
-
- throw;
- }
- catch (ResourceNotFoundException ex)
- {
- HandleException(context.Response, ex, 404);
-
- throw;
- }
- catch (FileNotFoundException ex)
- {
- HandleException(context.Response, ex, 404);
-
- throw;
- }
- catch (DirectoryNotFoundException ex)
- {
- HandleException(context.Response, ex, 404);
-
- throw;
- }
- catch (UnauthorizedAccessException ex)
- {
- HandleException(context.Response, ex, 401);
-
- throw;
- }
- catch (ArgumentException ex)
- {
- HandleException(context.Response, ex, 400);
-
- throw;
- }
- catch (Exception ex)
- {
- HandleException(context.Response, ex, 500);
-
- throw;
- }
- });
- }
-
- /// <summary>
- /// Processes the web socket request.
- /// </summary>
- /// <param name="ctx">The CTX.</param>
- /// <returns>Task.</returns>
- private async Task ProcessWebSocketRequest(HttpListenerContext ctx)
- {
- try
- {
- var webSocketContext = await ctx.AcceptWebSocketAsync(null).ConfigureAwait(false);
-
- if (WebSocketConnected != null)
- {
- WebSocketConnected(this, new WebSocketConnectEventArgs { WebSocket = new NativeWebSocket(webSocketContext.WebSocket, _logger), Endpoint = ctx.Request.RemoteEndPoint.ToString() });
- }
- }
- catch (Exception ex)
- {
- _logger.ErrorException("AcceptWebSocketAsync error", ex);
-
- ctx.Response.StatusCode = 500;
- ctx.Response.Close();
- }
- }
-
- /// <summary>
- /// Logs the HTTP request.
- /// </summary>
- /// <param name="ctx">The CTX.</param>
- private void LogHttpRequest(HttpListenerContext ctx)
- {
- var log = new StringBuilder();
-
- log.AppendLine("Url: " + ctx.Request.Url);
- log.AppendLine("Headers: " + string.Join(",", ctx.Request.Headers.AllKeys.Select(k => k + "=" + ctx.Request.Headers[k])));
-
- var type = ctx.Request.IsWebSocketRequest ? "Web Socket" : "HTTP " + ctx.Request.HttpMethod;
-
- if (EnableHttpRequestLogging)
- {
- _logger.LogMultiline(type + " request received from " + ctx.Request.RemoteEndPoint, LogSeverity.Debug, log);
- }
- }
-
- /// <summary>
- /// Appends the error message.
- /// </summary>
- /// <param name="response">The response.</param>
- /// <param name="ex">The ex.</param>
- /// <param name="statusCode">The status code.</param>
- private void HandleException(HttpListenerResponse response, Exception ex, int statusCode)
- {
- _logger.ErrorException("Error processing request", ex);
-
- response.StatusCode = statusCode;
-
- response.Headers.Add("Status", statusCode.ToString(new CultureInfo("en-US")));
-
- response.Headers.Remove("Age");
- response.Headers.Remove("Expires");
- response.Headers.Remove("Cache-Control");
- response.Headers.Remove("Etag");
- response.Headers.Remove("Last-Modified");
-
- response.ContentType = "text/plain";
-
- if (!string.IsNullOrEmpty(ex.Message))
- {
- response.AddHeader("X-Application-Error-Code", ex.Message);
- }
-
- // This could fail, but try to add the stack trace as the body content
- try
- {
- var sb = new StringBuilder();
- sb.AppendLine("{");
- sb.AppendLine("\"ResponseStatus\":{");
- sb.AppendFormat(" \"ErrorCode\":{0},\n", ex.GetType().Name.EncodeJson());
- sb.AppendFormat(" \"Message\":{0},\n", ex.Message.EncodeJson());
- sb.AppendFormat(" \"StackTrace\":{0}\n", ex.StackTrace.EncodeJson());
- sb.AppendLine("}");
- sb.AppendLine("}");
-
- response.StatusCode = 500;
- response.ContentType = ContentType.Json;
- var sbBytes = sb.ToString().ToUtf8Bytes();
- response.OutputStream.Write(sbBytes, 0, sbBytes.Length);
- response.Close();
- }
- catch (Exception errorEx)
- {
- _logger.ErrorException("Error processing failed request", errorEx);
- }
- }
-
-
- /// <summary>
- /// Overridable method that can be used to implement a custom hnandler
- /// </summary>
- /// <param name="context">The context.</param>
- /// <exception cref="System.NotImplementedException">Cannot execute handler: + handler + at PathInfo: + httpReq.PathInfo</exception>
- protected override void ProcessRequest(HttpListenerContext context)
- {
- if (string.IsNullOrEmpty(context.Request.RawUrl)) return;
-
- var operationName = context.Request.GetOperationName();
-
- var httpReq = new HttpListenerRequestWrapper(operationName, context.Request);
- var httpRes = new HttpListenerResponseWrapper(context.Response);
- var handler = ServiceStackHttpHandlerFactory.GetHandler(httpReq);
-
- var url = context.Request.Url.ToString();
- var endPoint = context.Request.RemoteEndPoint;
-
- var serviceStackHandler = handler as IServiceStackHttpHandler;
-
- if (serviceStackHandler != null)
- {
- var restHandler = serviceStackHandler as RestHandler;
- if (restHandler != null)
- {
- httpReq.OperationName = operationName = restHandler.RestPath.RequestType.Name;
- }
- serviceStackHandler.ProcessRequest(httpReq, httpRes, operationName);
- LogResponse(context, url, endPoint);
- httpRes.Close();
- return;
- }
-
- throw new NotImplementedException("Cannot execute handler: " + handler + " at PathInfo: " + httpReq.PathInfo);
- }
-
- /// <summary>
- /// Logs the response.
- /// </summary>
- /// <param name="ctx">The CTX.</param>
- private void LogResponse(HttpListenerContext ctx, string url, IPEndPoint endPoint)
- {
- if (!EnableHttpRequestLogging)
- {
- return;
- }
-
- var statusode = ctx.Response.StatusCode;
-
- var log = new StringBuilder();
-
- log.AppendLine(string.Format("Url: {0}", url));
-
- log.AppendLine("Headers: " + string.Join(",", ctx.Response.Headers.AllKeys.Select(k => k + "=" + ctx.Response.Headers[k])));
-
- var msg = "Http Response Sent (" + statusode + ") to " + endPoint;
-
- _logger.LogMultiline(msg, LogSeverity.Debug, log);
- }
-
- /// <summary>
- /// Creates the service manager.
- /// </summary>
- /// <param name="assembliesWithServices">The assemblies with services.</param>
- /// <returns>ServiceManager.</returns>
- protected override ServiceManager CreateServiceManager(params Assembly[] assembliesWithServices)
- {
- var types = _restServices.Select(r => r.GetType()).ToArray();
-
- return new ServiceManager(new Container(), new ServiceController(() => types));
- }
-
- /// <summary>
- /// Shut down the Web Service
- /// </summary>
- public override void Stop()
- {
- if (HttpListener != null)
- {
- HttpListener.Dispose();
- HttpListener = null;
- }
-
- if (Listener != null)
- {
- Listener.Prefixes.Remove(UrlPrefix);
- }
-
- base.Stop();
- }
-
- /// <summary>
- /// The _supports native web socket
- /// </summary>
- private bool? _supportsNativeWebSocket;
-
- /// <summary>
- /// Gets a value indicating whether [supports web sockets].
- /// </summary>
- /// <value><c>true</c> if [supports web sockets]; otherwise, <c>false</c>.</value>
- public bool SupportsWebSockets
- {
- get
- {
- if (!_supportsNativeWebSocket.HasValue)
- {
- try
- {
- new ClientWebSocket();
-
- _supportsNativeWebSocket = true;
- }
- catch (PlatformNotSupportedException)
- {
- _supportsNativeWebSocket = false;
- }
- }
-
- return _supportsNativeWebSocket.Value;
- }
- }
-
-
- /// <summary>
- /// Gets or sets a value indicating whether [enable HTTP request logging].
- /// </summary>
- /// <value><c>true</c> if [enable HTTP request logging]; otherwise, <c>false</c>.</value>
- public bool EnableHttpRequestLogging { get; set; }
-
- /// <summary>
- /// Adds the rest handlers.
- /// </summary>
- /// <param name="services">The services.</param>
- public void Init(IEnumerable<IRestfulService> services)
- {
- _restServices.AddRange(services);
-
- EndpointHost.ConfigureHost(this, ServerName, CreateServiceManager());
- ContentTypeFilters.Register(ContentType.ProtoBuf, (reqCtx, res, stream) => ProtobufSerializer.SerializeToStream(res, stream), (type, stream) => ProtobufSerializer.DeserializeFromStream(stream, type));
-
- foreach (var route in services.SelectMany(i => i.GetRoutes()))
- {
- Routes.Add(route.RequestType, route.Path, route.Verbs);
- }
-
- Init();
- }
- }
-
- /// <summary>
- /// Class ContainerAdapter
- /// </summary>
- class ContainerAdapter : IContainerAdapter, IRelease
- {
- /// <summary>
- /// The _app host
- /// </summary>
- private readonly IApplicationHost _appHost;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ContainerAdapter" /> class.
- /// </summary>
- /// <param name="appHost">The app host.</param>
- public ContainerAdapter(IApplicationHost appHost)
- {
- _appHost = appHost;
- }
- /// <summary>
- /// Resolves this instance.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <returns>``0.</returns>
- public T Resolve<T>()
- {
- return _appHost.Resolve<T>();
- }
-
- /// <summary>
- /// Tries the resolve.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <returns>``0.</returns>
- public T TryResolve<T>()
- {
- 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
- }
- }
-} \ No newline at end of file
diff --git a/MediaBrowser.Common.Implementations/HttpServer/NativeWebSocket.cs b/MediaBrowser.Common.Implementations/HttpServer/NativeWebSocket.cs
deleted file mode 100644
index 97bab96f8..000000000
--- a/MediaBrowser.Common.Implementations/HttpServer/NativeWebSocket.cs
+++ /dev/null
@@ -1,165 +0,0 @@
-using MediaBrowser.Common.Net;
-using MediaBrowser.Model.Logging;
-using System;
-using System.Net.WebSockets;
-using System.Threading;
-using System.Threading.Tasks;
-using WebSocketMessageType = MediaBrowser.Common.Net.WebSocketMessageType;
-using WebSocketState = MediaBrowser.Common.Net.WebSocketState;
-
-namespace MediaBrowser.Common.Implementations.HttpServer
-{
- /// <summary>
- /// Class NativeWebSocket
- /// </summary>
- public class NativeWebSocket : IWebSocket
- {
- /// <summary>
- /// The logger
- /// </summary>
- private readonly ILogger _logger;
-
- /// <summary>
- /// Gets or sets the web socket.
- /// </summary>
- /// <value>The web socket.</value>
- private System.Net.WebSockets.WebSocket WebSocket { get; set; }
-
- /// <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 NativeWebSocket(System.Net.WebSockets.WebSocket socket, ILogger logger)
- {
- if (socket == null)
- {
- throw new ArgumentNullException("socket");
- }
-
- if (logger == null)
- {
- throw new ArgumentNullException("logger");
- }
-
- _logger = logger;
- WebSocket = socket;
-
- Receive();
- }
-
- /// <summary>
- /// Gets or sets the state.
- /// </summary>
- /// <value>The state.</value>
- public WebSocketState State
- {
- get
- {
- WebSocketState commonState;
-
- if (!Enum.TryParse(WebSocket.State.ToString(), true, out commonState))
- {
- _logger.Warn("Unrecognized WebSocketState: {0}", WebSocket.State.ToString());
- }
-
- return commonState;
- }
- }
-
- /// <summary>
- /// Receives this instance.
- /// </summary>
- private async void Receive()
- {
- while (true)
- {
- byte[] bytes;
-
- try
- {
- bytes = await ReceiveBytesAsync(CancellationToken.None).ConfigureAwait(false);
- }
- catch (WebSocketException ex)
- {
- _logger.ErrorException("Error reveiving web socket message", ex);
-
- break;
- }
-
- if (OnReceiveDelegate != null)
- {
- OnReceiveDelegate(bytes);
- }
- }
- }
-
- /// <summary>
- /// Receives the async.
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{WebSocketMessageInfo}.</returns>
- /// <exception cref="System.Net.WebSockets.WebSocketException">Connection closed</exception>
- private async Task<byte[]> ReceiveBytesAsync(CancellationToken cancellationToken)
- {
- var bytes = new byte[4096];
- var buffer = new ArraySegment<byte>(bytes);
-
- var result = await WebSocket.ReceiveAsync(buffer, cancellationToken).ConfigureAwait(false);
-
- if (result.CloseStatus.HasValue)
- {
- throw new WebSocketException("Connection closed");
- }
-
- return buffer.Array;
- }
-
- /// <summary>
- /// Sends the async.
- /// </summary>
- /// <param name="bytes">The bytes.</param>
- /// <param name="type">The type.</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, WebSocketMessageType type, bool endOfMessage, CancellationToken cancellationToken)
- {
- System.Net.WebSockets.WebSocketMessageType nativeType;
-
- if (!Enum.TryParse(type.ToString(), true, out nativeType))
- {
- _logger.Warn("Unrecognized WebSocketMessageType: {0}", type.ToString());
- }
-
- return WebSocket.SendAsync(new ArraySegment<byte>(bytes), nativeType, true, cancellationToken);
- }
-
- /// <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.Dispose();
- }
- }
-
- /// <summary>
- /// Gets or sets the receive action.
- /// </summary>
- /// <value>The receive action.</value>
- public Action<byte[]> OnReceiveDelegate { get; set; }
- }
-}
diff --git a/MediaBrowser.Common.Implementations/HttpServer/ServerFactory.cs b/MediaBrowser.Common.Implementations/HttpServer/ServerFactory.cs
deleted file mode 100644
index 743bd60c4..000000000
--- a/MediaBrowser.Common.Implementations/HttpServer/ServerFactory.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-using MediaBrowser.Common.Kernel;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Serialization;
-
-namespace MediaBrowser.Common.Implementations.HttpServer
-{
- /// <summary>
- /// Class ServerFactory
- /// </summary>
- public static class ServerFactory
- {
- /// <summary>
- /// Creates the server.
- /// </summary>
- /// <param name="applicationHost">The application host.</param>
- /// <param name="protobufSerializer">The protobuf serializer.</param>
- /// <param name="logger">The logger.</param>
- /// <param name="serverName">Name of the server.</param>
- /// <param name="defaultRedirectpath">The default redirectpath.</param>
- /// <returns>IHttpServer.</returns>
- public static IHttpServer CreateServer(IApplicationHost applicationHost, IProtobufSerializer protobufSerializer, ILogger logger, string serverName, string defaultRedirectpath)
- {
- return new HttpServer(applicationHost, protobufSerializer, logger, serverName, defaultRedirectpath);
- }
- }
-}
diff --git a/MediaBrowser.Common.Implementations/HttpServer/StreamWriter.cs b/MediaBrowser.Common.Implementations/HttpServer/StreamWriter.cs
deleted file mode 100644
index c92bfe0b9..000000000
--- a/MediaBrowser.Common.Implementations/HttpServer/StreamWriter.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-using ServiceStack.Service;
-using System.IO;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Common.Implementations.HttpServer
-{
- /// <summary>
- /// Class StreamWriter
- /// </summary>
- public class StreamWriter : IStreamWriter
- {
- /// <summary>
- /// Gets or sets the source stream.
- /// </summary>
- /// <value>The source stream.</value>
- public Stream SourceStream { get; set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="StreamWriter" /> class.
- /// </summary>
- /// <param name="source">The source.</param>
- public StreamWriter(Stream source)
- {
- SourceStream = source;
- }
-
- /// <summary>
- /// Writes to.
- /// </summary>
- /// <param name="responseStream">The response stream.</param>
- public void WriteTo(Stream responseStream)
- {
- var task = WriteToAsync(responseStream);
-
- Task.WaitAll(task);
- }
-
- /// <summary>
- /// Writes to async.
- /// </summary>
- /// <param name="responseStream">The response stream.</param>
- /// <returns>Task.</returns>
- private Task WriteToAsync(Stream responseStream)
- {
- return SourceStream.CopyToAsync(responseStream);
- }
- }
-}
diff --git a/MediaBrowser.Common.Implementations/HttpServer/SwaggerService.cs b/MediaBrowser.Common.Implementations/HttpServer/SwaggerService.cs
deleted file mode 100644
index a4808834d..000000000
--- a/MediaBrowser.Common.Implementations/HttpServer/SwaggerService.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-using ServiceStack.ServiceHost;
-using System.Diagnostics;
-using System.IO;
-
-namespace MediaBrowser.Common.Implementations.HttpServer
-{
- /// <summary>
- /// Class GetDashboardResource
- /// </summary>
- [Route("/swagger-ui/{ResourceName*}", "GET")]
- public class GetSwaggerResource
- {
- /// <summary>
- /// Gets or sets the name.
- /// </summary>
- /// <value>The name.</value>
- public string ResourceName { get; set; }
- }
-
- public class SwaggerService : BaseRestService
- {
- /// <summary>
- /// Gets the specified request.
- /// </summary>
- /// <param name="request">The request.</param>
- /// <returns>System.Object.</returns>
- public object Get(GetSwaggerResource request)
- {
- var runningDirectory = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName);
-
- var swaggerDirectory = Path.Combine(runningDirectory, "swagger-ui");
-
- var requestedFile = Path.Combine(swaggerDirectory, request.ResourceName.Replace('/', '\\'));
-
- return ToStaticFileResult(requestedFile);
- }
- }
-}