mirror of
https://github.com/jellyfin/jellyfin.git
synced 2024-09-05 21:37:10 -04:00
commit
b7ae044e65
@ -94,7 +94,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
|
||||
var _ = StartStreaming(response, taskCompletionSource, LiveStreamCancellationTokenSource.Token);
|
||||
_ = StartStreaming(response, taskCompletionSource, LiveStreamCancellationTokenSource.Token);
|
||||
|
||||
//OpenedMediaSource.Protocol = MediaProtocol.File;
|
||||
//OpenedMediaSource.Path = tempFile;
|
||||
|
@ -282,7 +282,7 @@ namespace Jellyfin.Drawing.Skia
|
||||
var bitmap = new SKBitmap(codec.Info.Width, codec.Info.Height, !requiresTransparencyHack);
|
||||
|
||||
// decode
|
||||
var _ = codec.GetPixels(bitmap.Info, bitmap.GetPixels());
|
||||
_ = codec.GetPixels(bitmap.Info, bitmap.GetPixels());
|
||||
|
||||
origin = codec.EncodedOrigin;
|
||||
|
||||
|
@ -18,6 +18,8 @@ namespace Jellyfin.Server
|
||||
|
||||
public override bool CanSelfRestart => StartupOptions.RestartPath != null;
|
||||
|
||||
protected override bool SupportsDualModeSockets => true;
|
||||
|
||||
protected override void RestartInternal() => Program.Restart();
|
||||
|
||||
protected override IEnumerable<Assembly> GetAssembliesWithPartsInternal()
|
||||
@ -27,8 +29,6 @@ namespace Jellyfin.Server
|
||||
|
||||
protected override void ShutdownInternal() => Program.Shutdown();
|
||||
|
||||
protected override bool SupportsDualModeSockets => true;
|
||||
|
||||
protected override IHttpListener CreateHttpListener()
|
||||
=> new WebSocketSharpListener(
|
||||
Logger,
|
||||
@ -39,7 +39,6 @@ namespace Jellyfin.Server
|
||||
CryptographyProvider,
|
||||
SupportsDualModeSockets,
|
||||
FileSystemManager,
|
||||
EnvironmentInfo
|
||||
);
|
||||
EnvironmentInfo);
|
||||
}
|
||||
}
|
||||
|
@ -5,11 +5,14 @@
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- We need C# 7.1 for async main-->
|
||||
<LangVersion>latest</LangVersion>
|
||||
<!-- Disable documentation warnings (for now) -->
|
||||
<NoWarn>SA1600;CS1591</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -57,13 +57,28 @@ namespace Jellyfin.Server
|
||||
errs => Task.FromResult(0)).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public static void Shutdown()
|
||||
{
|
||||
if (!_tokenSource.IsCancellationRequested)
|
||||
{
|
||||
_tokenSource.Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
public static void Restart()
|
||||
{
|
||||
_restartOnShutdown = true;
|
||||
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
private static async Task StartApp(StartupOptions options)
|
||||
{
|
||||
ServerApplicationPaths appPaths = CreateApplicationPaths(options);
|
||||
|
||||
// $JELLYFIN_LOG_DIR needs to be set for the logger configuration manager
|
||||
Environment.SetEnvironmentVariable("JELLYFIN_LOG_DIR", appPaths.LogDirectoryPath);
|
||||
await CreateLogger(appPaths);
|
||||
await CreateLogger(appPaths).ConfigureAwait(false);
|
||||
_logger = _loggerFactory.CreateLogger("Main");
|
||||
|
||||
AppDomain.CurrentDomain.UnhandledException += (sender, e)
|
||||
@ -76,6 +91,7 @@ namespace Jellyfin.Server
|
||||
{
|
||||
return; // Already shutting down
|
||||
}
|
||||
|
||||
e.Cancel = true;
|
||||
_logger.LogInformation("Ctrl+C, shutting down");
|
||||
Environment.ExitCode = 128 + 2;
|
||||
@ -89,6 +105,7 @@ namespace Jellyfin.Server
|
||||
{
|
||||
return; // Already shutting down
|
||||
}
|
||||
|
||||
_logger.LogInformation("Received a SIGTERM signal, shutting down");
|
||||
Environment.ExitCode = 128 + 15;
|
||||
Shutdown();
|
||||
@ -102,7 +119,7 @@ namespace Jellyfin.Server
|
||||
SQLitePCL.Batteries_V2.Init();
|
||||
|
||||
// Allow all https requests
|
||||
ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(delegate { return true; });
|
||||
ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(delegate { return true; } );
|
||||
|
||||
var fileSystem = new ManagedFileSystem(_loggerFactory, environmentInfo, null, appPaths.TempDirectory, true);
|
||||
|
||||
@ -115,18 +132,18 @@ namespace Jellyfin.Server
|
||||
new NullImageEncoder(),
|
||||
new NetworkManager(_loggerFactory, environmentInfo)))
|
||||
{
|
||||
await appHost.Init(new ServiceCollection());
|
||||
await appHost.Init(new ServiceCollection()).ConfigureAwait(false);
|
||||
|
||||
appHost.ImageProcessor.ImageEncoder = GetImageEncoder(fileSystem, appPaths, appHost.LocalizationManager);
|
||||
|
||||
await appHost.RunStartupTasks();
|
||||
await appHost.RunStartupTasks().ConfigureAwait(false);
|
||||
|
||||
// TODO: read input for a stop command
|
||||
|
||||
try
|
||||
{
|
||||
// Block main thread until shutdown
|
||||
await Task.Delay(-1, _tokenSource.Token);
|
||||
await Task.Delay(-1, _tokenSource.Token).ConfigureAwait(false);
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
@ -146,7 +163,7 @@ namespace Jellyfin.Server
|
||||
/// for everything else the XDG approach is followed:
|
||||
/// https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
|
||||
/// </summary>
|
||||
/// <param name="options"></param>
|
||||
/// <param name="options">StartupOptions</param>
|
||||
/// <returns>ServerApplicationPaths</returns>
|
||||
private static ServerApplicationPaths CreateApplicationPaths(StartupOptions options)
|
||||
{
|
||||
@ -308,6 +325,7 @@ namespace Jellyfin.Server
|
||||
await rscstr.CopyToAsync(fstr).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
var configuration = new ConfigurationBuilder()
|
||||
.SetBasePath(appPaths.ConfigurationDirectoryPath)
|
||||
.AddJsonFile("logging.json")
|
||||
@ -335,7 +353,7 @@ namespace Jellyfin.Server
|
||||
}
|
||||
}
|
||||
|
||||
public static IImageEncoder GetImageEncoder(
|
||||
private static IImageEncoder GetImageEncoder(
|
||||
IFileSystem fileSystem,
|
||||
IApplicationPaths appPaths,
|
||||
ILocalizationManager localizationManager)
|
||||
@ -376,26 +394,12 @@ namespace Jellyfin.Server
|
||||
{
|
||||
return MediaBrowser.Model.System.OperatingSystem.BSD;
|
||||
}
|
||||
|
||||
throw new Exception($"Can't resolve OS with description: '{osDescription}'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Shutdown()
|
||||
{
|
||||
if (!_tokenSource.IsCancellationRequested)
|
||||
{
|
||||
_tokenSource.Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
public static void Restart()
|
||||
{
|
||||
_restartOnShutdown = true;
|
||||
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
private static void StartNewInstance(StartupOptions options)
|
||||
{
|
||||
_logger.LogInformation("Starting new instance");
|
||||
|
@ -13,7 +13,7 @@ namespace Jellyfin.Server.SocketSharp
|
||||
{
|
||||
internal static string GetParameter(string header, string attr)
|
||||
{
|
||||
int ap = header.IndexOf(attr);
|
||||
int ap = header.IndexOf(attr, StringComparison.Ordinal);
|
||||
if (ap == -1)
|
||||
{
|
||||
return null;
|
||||
@ -82,9 +82,7 @@ namespace Jellyfin.Server.SocketSharp
|
||||
}
|
||||
else
|
||||
{
|
||||
//
|
||||
// We use a substream, as in 2.x we will support large uploads streamed to disk,
|
||||
//
|
||||
var sub = new HttpPostedFile(e.Filename, e.ContentType, input, e.Start, e.Length);
|
||||
files[e.Name] = sub;
|
||||
}
|
||||
@ -127,8 +125,12 @@ namespace Jellyfin.Server.SocketSharp
|
||||
|
||||
public string Authorization => string.IsNullOrEmpty(request.Headers["Authorization"]) ? null : request.Headers["Authorization"];
|
||||
|
||||
protected bool validate_cookies, validate_query_string, validate_form;
|
||||
protected bool checked_cookies, checked_query_string, checked_form;
|
||||
protected bool validate_cookies { get; set; }
|
||||
protected bool validate_query_string { get; set; }
|
||||
protected bool validate_form { get; set; }
|
||||
protected bool checked_cookies { get; set; }
|
||||
protected bool checked_query_string { get; set; }
|
||||
protected bool checked_form { get; set; }
|
||||
|
||||
private static void ThrowValidationException(string name, string key, string value)
|
||||
{
|
||||
@ -138,8 +140,12 @@ namespace Jellyfin.Server.SocketSharp
|
||||
v = v.Substring(0, 16) + "...\"";
|
||||
}
|
||||
|
||||
string msg = string.Format("A potentially dangerous Request.{0} value was " +
|
||||
"detected from the client ({1}={2}).", name, key, v);
|
||||
string msg = string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
"A potentially dangerous Request.{0} value was detected from the client ({1}={2}).",
|
||||
name,
|
||||
key,
|
||||
v);
|
||||
|
||||
throw new Exception(msg);
|
||||
}
|
||||
@ -179,6 +185,7 @@ namespace Jellyfin.Server.SocketSharp
|
||||
for (int idx = 1; idx < len; idx++)
|
||||
{
|
||||
char next = val[idx];
|
||||
|
||||
// See http://secunia.com/advisories/14325
|
||||
if (current == '<' || current == '\xff1c')
|
||||
{
|
||||
@ -256,6 +263,7 @@ namespace Jellyfin.Server.SocketSharp
|
||||
value.Append((char)c);
|
||||
}
|
||||
}
|
||||
|
||||
if (c == -1)
|
||||
{
|
||||
AddRawKeyValue(form, key, value);
|
||||
@ -271,6 +279,7 @@ namespace Jellyfin.Server.SocketSharp
|
||||
key.Append((char)c);
|
||||
}
|
||||
}
|
||||
|
||||
if (c == -1)
|
||||
{
|
||||
AddRawKeyValue(form, key, value);
|
||||
@ -308,6 +317,7 @@ namespace Jellyfin.Server.SocketSharp
|
||||
result.Append(key);
|
||||
result.Append('=');
|
||||
}
|
||||
|
||||
result.Append(pair.Value);
|
||||
}
|
||||
|
||||
@ -429,13 +439,13 @@ namespace Jellyfin.Server.SocketSharp
|
||||
real = position + d;
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentException(nameof(origin));
|
||||
throw new ArgumentException("Unknown SeekOrigin value", nameof(origin));
|
||||
}
|
||||
|
||||
long virt = real - offset;
|
||||
if (virt < 0 || virt > Length)
|
||||
{
|
||||
throw new ArgumentException();
|
||||
throw new ArgumentException("Invalid position", nameof(d));
|
||||
}
|
||||
|
||||
position = s.Seek(real, SeekOrigin.Begin);
|
||||
@ -491,11 +501,6 @@ namespace Jellyfin.Server.SocketSharp
|
||||
public Stream InputStream => stream;
|
||||
}
|
||||
|
||||
private class Helpers
|
||||
{
|
||||
public static readonly CultureInfo InvariantCulture = CultureInfo.InvariantCulture;
|
||||
}
|
||||
|
||||
internal static class StrUtils
|
||||
{
|
||||
public static bool StartsWith(string str1, string str2, bool ignore_case)
|
||||
@ -533,12 +538,17 @@ namespace Jellyfin.Server.SocketSharp
|
||||
|
||||
public class Element
|
||||
{
|
||||
public string ContentType;
|
||||
public string Name;
|
||||
public string Filename;
|
||||
public Encoding Encoding;
|
||||
public long Start;
|
||||
public long Length;
|
||||
public string ContentType { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public string Filename { get; set; }
|
||||
|
||||
public Encoding Encoding { get; set; }
|
||||
|
||||
public long Start { get; set; }
|
||||
|
||||
public long Length { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
@ -547,15 +557,23 @@ namespace Jellyfin.Server.SocketSharp
|
||||
}
|
||||
}
|
||||
|
||||
private Stream data;
|
||||
private string boundary;
|
||||
private byte[] boundary_bytes;
|
||||
private byte[] buffer;
|
||||
private bool at_eof;
|
||||
private Encoding encoding;
|
||||
private StringBuilder sb;
|
||||
private const byte LF = (byte)'\n';
|
||||
|
||||
private const byte LF = (byte)'\n', CR = (byte)'\r';
|
||||
private const byte CR = (byte)'\r';
|
||||
|
||||
private Stream data;
|
||||
|
||||
private string boundary;
|
||||
|
||||
private byte[] boundaryBytes;
|
||||
|
||||
private byte[] buffer;
|
||||
|
||||
private bool atEof;
|
||||
|
||||
private Encoding encoding;
|
||||
|
||||
private StringBuilder sb;
|
||||
|
||||
// See RFC 2046
|
||||
// In the case of multipart entities, in which one or more different
|
||||
@ -570,18 +588,48 @@ namespace Jellyfin.Server.SocketSharp
|
||||
public HttpMultipart(Stream data, string b, Encoding encoding)
|
||||
{
|
||||
this.data = data;
|
||||
//DB: 30/01/11: cannot set or read the Position in HttpListener in Win.NET
|
||||
//var ms = new MemoryStream(32 * 1024);
|
||||
//data.CopyTo(ms);
|
||||
//this.data = ms;
|
||||
|
||||
boundary = b;
|
||||
boundary_bytes = encoding.GetBytes(b);
|
||||
buffer = new byte[boundary_bytes.Length + 2]; // CRLF or '--'
|
||||
boundaryBytes = encoding.GetBytes(b);
|
||||
buffer = new byte[boundaryBytes.Length + 2]; // CRLF or '--'
|
||||
this.encoding = encoding;
|
||||
sb = new StringBuilder();
|
||||
}
|
||||
|
||||
public Element ReadNextElement()
|
||||
{
|
||||
if (atEof || ReadBoundary())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var elem = new Element();
|
||||
string header;
|
||||
while ((header = ReadHeaders()) != null)
|
||||
{
|
||||
if (StrUtils.StartsWith(header, "Content-Disposition:", true))
|
||||
{
|
||||
elem.Name = GetContentDispositionAttribute(header, "name");
|
||||
elem.Filename = StripPath(GetContentDispositionAttributeWithEncoding(header, "filename"));
|
||||
}
|
||||
else if (StrUtils.StartsWith(header, "Content-Type:", true))
|
||||
{
|
||||
elem.ContentType = header.Substring("Content-Type:".Length).Trim();
|
||||
elem.Encoding = GetEncoding(elem.ContentType);
|
||||
}
|
||||
}
|
||||
|
||||
long start = data.Position;
|
||||
elem.Start = start;
|
||||
long pos = MoveToNextBoundary();
|
||||
if (pos == -1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
elem.Length = pos - start;
|
||||
return elem;
|
||||
}
|
||||
|
||||
private string ReadLine()
|
||||
{
|
||||
// CRLF or LF are ok as line endings.
|
||||
@ -600,6 +648,7 @@ namespace Jellyfin.Server.SocketSharp
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
got_cr = b == CR;
|
||||
sb.Append((char)b);
|
||||
}
|
||||
@ -769,7 +818,7 @@ namespace Jellyfin.Server.SocketSharp
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!CompareBytes(boundary_bytes, buffer))
|
||||
if (!CompareBytes(boundaryBytes, buffer))
|
||||
{
|
||||
state = 0;
|
||||
data.Position = retval + 2;
|
||||
@ -785,7 +834,7 @@ namespace Jellyfin.Server.SocketSharp
|
||||
|
||||
if (buffer[bl - 2] == '-' && buffer[bl - 1] == '-')
|
||||
{
|
||||
at_eof = true;
|
||||
atEof = true;
|
||||
}
|
||||
else if (buffer[bl - 2] != CR || buffer[bl - 1] != LF)
|
||||
{
|
||||
@ -800,6 +849,7 @@ namespace Jellyfin.Server.SocketSharp
|
||||
c = data.ReadByte();
|
||||
continue;
|
||||
}
|
||||
|
||||
data.Position = retval + 2;
|
||||
if (got_cr)
|
||||
{
|
||||
@ -818,42 +868,6 @@ namespace Jellyfin.Server.SocketSharp
|
||||
return retval;
|
||||
}
|
||||
|
||||
public Element ReadNextElement()
|
||||
{
|
||||
if (at_eof || ReadBoundary())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var elem = new Element();
|
||||
string header;
|
||||
while ((header = ReadHeaders()) != null)
|
||||
{
|
||||
if (StrUtils.StartsWith(header, "Content-Disposition:", true))
|
||||
{
|
||||
elem.Name = GetContentDispositionAttribute(header, "name");
|
||||
elem.Filename = StripPath(GetContentDispositionAttributeWithEncoding(header, "filename"));
|
||||
}
|
||||
else if (StrUtils.StartsWith(header, "Content-Type:", true))
|
||||
{
|
||||
elem.ContentType = header.Substring("Content-Type:".Length).Trim();
|
||||
elem.Encoding = GetEncoding(elem.ContentType);
|
||||
}
|
||||
}
|
||||
|
||||
long start = 0;
|
||||
start = data.Position;
|
||||
elem.Start = start;
|
||||
long pos = MoveToNextBoundary();
|
||||
if (pos == -1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
elem.Length = pos - start;
|
||||
return elem;
|
||||
}
|
||||
|
||||
private static string StripPath(string path)
|
||||
{
|
||||
if (path == null || path.Length == 0)
|
||||
|
@ -24,6 +24,7 @@ namespace Jellyfin.Server.SocketSharp
|
||||
|
||||
private TaskCompletionSource<bool> _taskCompletionSource = new TaskCompletionSource<bool>();
|
||||
private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
|
||||
private bool _disposed = false;
|
||||
|
||||
public SharpWebSocket(SocketHttpListener.WebSocket socket, ILogger logger)
|
||||
{
|
||||
@ -40,9 +41,9 @@ namespace Jellyfin.Server.SocketSharp
|
||||
_logger = logger;
|
||||
WebSocket = socket;
|
||||
|
||||
socket.OnMessage += socket_OnMessage;
|
||||
socket.OnClose += socket_OnClose;
|
||||
socket.OnError += socket_OnError;
|
||||
socket.OnMessage += OnSocketMessage;
|
||||
socket.OnClose += OnSocketClose;
|
||||
socket.OnError += OnSocketError;
|
||||
|
||||
WebSocket.ConnectAsServer();
|
||||
}
|
||||
@ -52,29 +53,22 @@ namespace Jellyfin.Server.SocketSharp
|
||||
return _taskCompletionSource.Task;
|
||||
}
|
||||
|
||||
void socket_OnError(object sender, SocketHttpListener.ErrorEventArgs e)
|
||||
private void OnSocketError(object sender, SocketHttpListener.ErrorEventArgs e)
|
||||
{
|
||||
_logger.LogError("Error in SharpWebSocket: {Message}", e.Message ?? string.Empty);
|
||||
//Closed?.Invoke(this, EventArgs.Empty);
|
||||
|
||||
// Closed?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
void socket_OnClose(object sender, SocketHttpListener.CloseEventArgs e)
|
||||
private void OnSocketClose(object sender, SocketHttpListener.CloseEventArgs e)
|
||||
{
|
||||
_taskCompletionSource.TrySetResult(true);
|
||||
|
||||
Closed?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
void socket_OnMessage(object sender, SocketHttpListener.MessageEventArgs e)
|
||||
private void OnSocketMessage(object sender, SocketHttpListener.MessageEventArgs e)
|
||||
{
|
||||
//if (!string.IsNullOrEmpty(e.Data))
|
||||
//{
|
||||
// if (OnReceive != null)
|
||||
// {
|
||||
// OnReceive(e.Data);
|
||||
// }
|
||||
// return;
|
||||
//}
|
||||
if (OnReceiveBytes != null)
|
||||
{
|
||||
OnReceiveBytes(e.RawData);
|
||||
@ -117,6 +111,7 @@ namespace Jellyfin.Server.SocketSharp
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -125,16 +120,23 @@ namespace Jellyfin.Server.SocketSharp
|
||||
/// <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 (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (dispose)
|
||||
{
|
||||
WebSocket.OnMessage -= socket_OnMessage;
|
||||
WebSocket.OnClose -= socket_OnClose;
|
||||
WebSocket.OnError -= socket_OnError;
|
||||
WebSocket.OnMessage -= OnSocketMessage;
|
||||
WebSocket.OnClose -= OnSocketClose;
|
||||
WebSocket.OnError -= OnSocketError;
|
||||
|
||||
_cancellationTokenSource.Cancel();
|
||||
|
||||
WebSocket.Close();
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -142,11 +144,5 @@ namespace Jellyfin.Server.SocketSharp
|
||||
/// </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; }
|
||||
}
|
||||
}
|
||||
|
@ -34,9 +34,16 @@ namespace Jellyfin.Server.SocketSharp
|
||||
private CancellationTokenSource _disposeCancellationTokenSource = new CancellationTokenSource();
|
||||
private CancellationToken _disposeCancellationToken;
|
||||
|
||||
public WebSocketSharpListener(ILogger logger, X509Certificate certificate, IStreamHelper streamHelper,
|
||||
INetworkManager networkManager, ISocketFactory socketFactory, ICryptoProvider cryptoProvider,
|
||||
bool enableDualMode, IFileSystem fileSystem, IEnvironmentInfo environment)
|
||||
public WebSocketSharpListener(
|
||||
ILogger logger,
|
||||
X509Certificate certificate,
|
||||
IStreamHelper streamHelper,
|
||||
INetworkManager networkManager,
|
||||
ISocketFactory socketFactory,
|
||||
ICryptoProvider cryptoProvider,
|
||||
bool enableDualMode,
|
||||
IFileSystem fileSystem,
|
||||
IEnvironmentInfo environment)
|
||||
{
|
||||
_logger = logger;
|
||||
_certificate = certificate;
|
||||
@ -61,7 +68,9 @@ namespace Jellyfin.Server.SocketSharp
|
||||
public void Start(IEnumerable<string> urlPrefixes)
|
||||
{
|
||||
if (_listener == null)
|
||||
{
|
||||
_listener = new HttpListener(_logger, _cryptoProvider, _socketFactory, _networkManager, _streamHelper, _fileSystem, _environment);
|
||||
}
|
||||
|
||||
_listener.EnableDualMode = _enableDualMode;
|
||||
|
||||
@ -83,15 +92,18 @@ namespace Jellyfin.Server.SocketSharp
|
||||
|
||||
private void ProcessContext(HttpListenerContext context)
|
||||
{
|
||||
var _ = Task.Run(async () => await InitTask(context, _disposeCancellationToken));
|
||||
_ = Task.Run(async () => await InitTask(context, _disposeCancellationToken).ConfigureAwait(false));
|
||||
}
|
||||
|
||||
private static void LogRequest(ILogger logger, HttpListenerRequest request)
|
||||
{
|
||||
var url = request.Url.ToString();
|
||||
|
||||
logger.LogInformation("{0} {1}. UserAgent: {2}",
|
||||
request.IsWebSocketRequest ? "WS" : "HTTP " + request.HttpMethod, url, request.UserAgent ?? string.Empty);
|
||||
logger.LogInformation(
|
||||
"{0} {1}. UserAgent: {2}",
|
||||
request.IsWebSocketRequest ? "WS" : "HTTP " + request.HttpMethod,
|
||||
url,
|
||||
request.UserAgent ?? string.Empty);
|
||||
}
|
||||
|
||||
private Task InitTask(HttpListenerContext context, CancellationToken cancellationToken)
|
||||
@ -201,7 +213,7 @@ namespace Jellyfin.Server.SocketSharp
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
//TODO Investigate and properly fix.
|
||||
// TODO: Investigate and properly fix.
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -223,38 +235,39 @@ namespace Jellyfin.Server.SocketSharp
|
||||
public Task Stop()
|
||||
{
|
||||
_disposeCancellationTokenSource.Cancel();
|
||||
|
||||
if (_listener != null)
|
||||
{
|
||||
_listener.Close();
|
||||
}
|
||||
_listener?.Close();
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases the unmanaged resources and disposes of the managed resources used.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private bool _disposed;
|
||||
private readonly object _disposeLock = new object();
|
||||
|
||||
/// <summary>
|
||||
/// Releases the unmanaged resources and disposes of the managed resources used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">Whether or not the managed resources should be disposed</param>
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (_disposed) return;
|
||||
|
||||
lock (_disposeLock)
|
||||
if (_disposed)
|
||||
{
|
||||
if (_disposed) return;
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
|
||||
//release unmanaged resources here...
|
||||
_disposed = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
Stop().GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Emby.Server.Implementations.HttpServer;
|
||||
@ -24,7 +25,7 @@ namespace Jellyfin.Server.SocketSharp
|
||||
this.request = httpContext.Request;
|
||||
this.response = new WebSocketSharpResponse(logger, httpContext.Response, this);
|
||||
|
||||
//HandlerFactoryPath = GetHandlerPathIfAny(UrlPrefixes[0]);
|
||||
// HandlerFactoryPath = GetHandlerPathIfAny(UrlPrefixes[0]);
|
||||
}
|
||||
|
||||
private static string GetHandlerPathIfAny(string listenerUrl)
|
||||
@ -41,7 +42,7 @@ namespace Jellyfin.Server.SocketSharp
|
||||
}
|
||||
|
||||
var startHostUrl = listenerUrl.Substring(pos + "://".Length);
|
||||
var endPos = startHostUrl.IndexOf('/');
|
||||
var endPos = startHostUrl.IndexOf('/', StringComparison.Ordinal);
|
||||
if (endPos == -1)
|
||||
{
|
||||
return null;
|
||||
@ -69,9 +70,11 @@ namespace Jellyfin.Server.SocketSharp
|
||||
|
||||
public string UserHostAddress => request.UserHostAddress;
|
||||
|
||||
public string XForwardedFor => string.IsNullOrEmpty(request.Headers["X-Forwarded-For"]) ? null : request.Headers["X-Forwarded-For"];
|
||||
public string XForwardedFor
|
||||
=> string.IsNullOrEmpty(request.Headers["X-Forwarded-For"]) ? null : request.Headers["X-Forwarded-For"];
|
||||
|
||||
public int? XForwardedPort => string.IsNullOrEmpty(request.Headers["X-Forwarded-Port"]) ? (int?)null : int.Parse(request.Headers["X-Forwarded-Port"]);
|
||||
public int? XForwardedPort
|
||||
=> string.IsNullOrEmpty(request.Headers["X-Forwarded-Port"]) ? (int?)null : int.Parse(request.Headers["X-Forwarded-Port"], CultureInfo.InvariantCulture);
|
||||
|
||||
public string XForwardedProtocol => string.IsNullOrEmpty(request.Headers["X-Forwarded-Proto"]) ? null : request.Headers["X-Forwarded-Proto"];
|
||||
|
||||
@ -107,6 +110,7 @@ namespace Jellyfin.Server.SocketSharp
|
||||
switch (crlf)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
if (c == '\r')
|
||||
{
|
||||
crlf = 1;
|
||||
@ -121,29 +125,39 @@ namespace Jellyfin.Server.SocketSharp
|
||||
{
|
||||
throw new ArgumentException("net_WebHeaderInvalidControlChars");
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 1:
|
||||
{
|
||||
if (c == '\n')
|
||||
{
|
||||
crlf = 2;
|
||||
break;
|
||||
}
|
||||
|
||||
throw new ArgumentException("net_WebHeaderInvalidCRLFChars");
|
||||
}
|
||||
|
||||
case 2:
|
||||
{
|
||||
if (c == ' ' || c == '\t')
|
||||
{
|
||||
crlf = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
throw new ArgumentException("net_WebHeaderInvalidCRLFChars");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (crlf != 0)
|
||||
{
|
||||
throw new ArgumentException("net_WebHeaderInvalidCRLFChars");
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
@ -156,6 +170,7 @@ namespace Jellyfin.Server.SocketSharp
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -343,6 +358,7 @@ namespace Jellyfin.Server.SocketSharp
|
||||
this.pathInfo = System.Net.WebUtility.UrlDecode(pathInfo);
|
||||
this.pathInfo = NormalizePathInfo(pathInfo, mode);
|
||||
}
|
||||
|
||||
return this.pathInfo;
|
||||
}
|
||||
}
|
||||
@ -444,7 +460,7 @@ namespace Jellyfin.Server.SocketSharp
|
||||
|
||||
public string ContentType => request.ContentType;
|
||||
|
||||
public Encoding contentEncoding;
|
||||
private Encoding contentEncoding;
|
||||
public Encoding ContentEncoding
|
||||
{
|
||||
get => contentEncoding ?? request.ContentEncoding;
|
||||
@ -502,6 +518,7 @@ namespace Jellyfin.Server.SocketSharp
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
return httpFiles;
|
||||
}
|
||||
}
|
||||
|
@ -13,12 +13,12 @@ using HttpListenerResponse = SocketHttpListener.Net.HttpListenerResponse;
|
||||
using IHttpResponse = MediaBrowser.Model.Services.IHttpResponse;
|
||||
using IRequest = MediaBrowser.Model.Services.IRequest;
|
||||
|
||||
|
||||
namespace Jellyfin.Server.SocketSharp
|
||||
{
|
||||
public class WebSocketSharpResponse : IHttpResponse
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
|
||||
private readonly HttpListenerResponse _response;
|
||||
|
||||
public WebSocketSharpResponse(ILogger logger, HttpListenerResponse response, IRequest request)
|
||||
@ -30,7 +30,9 @@ namespace Jellyfin.Server.SocketSharp
|
||||
}
|
||||
|
||||
public IRequest Request { get; private set; }
|
||||
|
||||
public Dictionary<string, object> Items { get; private set; }
|
||||
|
||||
public object OriginalResponse => _response;
|
||||
|
||||
public int StatusCode
|
||||
@ -51,7 +53,7 @@ namespace Jellyfin.Server.SocketSharp
|
||||
set => _response.ContentType = value;
|
||||
}
|
||||
|
||||
//public ICookies Cookies { get; set; }
|
||||
public QueryParamCollection Headers => _response.Headers;
|
||||
|
||||
public void AddHeader(string name, string value)
|
||||
{
|
||||
@ -64,8 +66,6 @@ namespace Jellyfin.Server.SocketSharp
|
||||
_response.AddHeader(name, value);
|
||||
}
|
||||
|
||||
public QueryParamCollection Headers => _response.Headers;
|
||||
|
||||
public string GetHeader(string name)
|
||||
{
|
||||
return _response.Headers[name];
|
||||
@ -114,9 +114,9 @@ namespace Jellyfin.Server.SocketSharp
|
||||
|
||||
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
|
||||
// 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;
|
||||
}
|
||||
|
||||
@ -147,15 +147,12 @@ namespace Jellyfin.Server.SocketSharp
|
||||
{
|
||||
sb.Append($";domain={cookie.Domain}");
|
||||
}
|
||||
//else if (restrictAllCookiesToDomain != null)
|
||||
//{
|
||||
// sb.Append($";domain={restrictAllCookiesToDomain}");
|
||||
//}
|
||||
|
||||
if (cookie.Secure)
|
||||
{
|
||||
sb.Append(";Secure");
|
||||
}
|
||||
|
||||
if (cookie.HttpOnly)
|
||||
{
|
||||
sb.Append(";HttpOnly");
|
||||
@ -164,7 +161,6 @@ namespace Jellyfin.Server.SocketSharp
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
|
||||
public bool SendChunked
|
||||
{
|
||||
get => _response.SendChunked;
|
||||
|
@ -3,11 +3,19 @@
|
||||
<Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.Analyzers">
|
||||
<!-- disable warning SA1101: Prefix local calls with 'this.' -->
|
||||
<Rule Id="SA1101" Action="None" />
|
||||
<!-- disable warning SA1130: Use lambda syntax -->
|
||||
<Rule Id="SA1130" Action="None" />
|
||||
<!-- disable warning SA1200: 'using' directive must appear within a namespace declaration -->
|
||||
<Rule Id="SA1200" Action="None" />
|
||||
<!-- disable warning SA1309: Fields must not begin with an underscore -->
|
||||
<Rule Id="SA1309" Action="None" />
|
||||
<!-- disable warning SA1512: Single-line comments must not be followed by blank line -->
|
||||
<Rule Id="SA1512" Action="None" />
|
||||
<!-- disable warning SA1633: The file header is missing or not located at the top of the file -->
|
||||
<Rule Id="SA1633" Action="None" />
|
||||
</Rules>
|
||||
<Rules AnalyzerId="Microsoft.CodeAnalysis.FxCopAnalyzers" RuleNamespace="Microsoft.Design">
|
||||
<!-- disable warning CA1054: Change the type of parameter url from string to System.Uri -->
|
||||
<Rule Id="CA1054" Action="None" />
|
||||
</Rules>
|
||||
</RuleSet>
|
||||
|
Loading…
Reference in New Issue
Block a user