mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-02-21 12:02:46 +00:00
update portable projects
This commit is contained in:
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 (!ValidateHost(url))
|
||||
{
|
||||
httpRes.StatusCode = 400;
|
||||
httpRes.ContentType = "text/plain";
|
||||
httpRes.Write("Invalid host");
|
||||
|
||||
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";
|
||||
|
||||
httpRes.Close();
|
||||
}
|
||||
|
||||
var operationName = httpReq.OperationName;
|
||||
var localPath = url.LocalPath;
|
||||
|
||||
var urlString = url.OriginalString;
|
||||
var enableLog = EnableLogging(urlString, localPath);
|
||||
var urlToLog = urlString;
|
||||
|
||||
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))
|
||||
{
|
||||
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(newUrl, urlString, StringComparison.OrdinalIgnoreCase))
|
||||
if (_disposed)
|
||||
{
|
||||
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.Close();
|
||||
httpRes.StatusCode = 503;
|
||||
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(newUrl, urlString, StringComparison.OrdinalIgnoreCase))
|
||||
if (!ValidateHost(url))
|
||||
{
|
||||
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.Close();
|
||||
httpRes.StatusCode = 400;
|
||||
httpRes.ContentType = "text/plain";
|
||||
httpRes.Write("Invalid host");
|
||||
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;
|
||||
}
|
||||
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(localPath, "/emby/pin", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
httpRes.RedirectToUrl("web/pin.html");
|
||||
return;
|
||||
var operationName = httpReq.OperationName;
|
||||
var localPath = url.LocalPath;
|
||||
|
||||
var urlString = url.OriginalString;
|
||||
enableLog = EnableLogging(urlString, localPath);
|
||||
urlToLog = urlString;
|
||||
|
||||
if (enableLog)
|
||||
{
|
||||
urlToLog = GetUrlToLog(urlString);
|
||||
remoteIp = httpReq.RemoteIp;
|
||||
|
||||
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, "/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 (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(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(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(localPath, "/emby/pin", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
RedirectToUrl(httpRes, "web/pin.html");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(GlobalResponse))
|
||||
{
|
||||
httpRes.StatusCode = 503;
|
||||
httpRes.ContentType = "text/html";
|
||||
httpRes.Write(GlobalResponse);
|
||||
return;
|
||||
}
|
||||
|
||||
var handler = HttpHandlerFactory.GetHandler(httpReq);
|
||||
|
||||
if (handler != null)
|
||||
{
|
||||
await handler.ProcessRequestAsync(httpReq, httpRes, operationName).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(GlobalResponse))
|
||||
catch (Exception ex)
|
||||
{
|
||||
ErrorHandler(ex, httpReq);
|
||||
}
|
||||
finally
|
||||
{
|
||||
httpRes.StatusCode = 503;
|
||||
httpRes.ContentType = "text/html";
|
||||
httpRes.Write(GlobalResponse);
|
||||
|
||||
httpRes.Close();
|
||||
return;
|
||||
}
|
||||
|
||||
var handler = HttpHandlerFactory.GetHandler(httpReq);
|
||||
|
||||
var remoteIp = httpReq.RemoteIp;
|
||||
|
||||
var serviceStackHandler = handler as IServiceStackHandler;
|
||||
if (serviceStackHandler != null)
|
||||
{
|
||||
var restHandler = serviceStackHandler as RestHandler;
|
||||
if (restHandler != null)
|
||||
if (enableLog)
|
||||
{
|
||||
httpReq.OperationName = operationName = restHandler.RestPath.RequestType.GetOperationName();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await serviceStackHandler.ProcessRequestAsync(httpReq, httpRes, operationName).ConfigureAwait(false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
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; }
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user