2019-02-26 07:09:42 +00:00
|
|
|
using System;
|
2019-02-25 22:34:32 +00:00
|
|
|
using System.Collections.Generic;
|
2019-02-26 19:13:48 +00:00
|
|
|
using System.Linq;
|
|
|
|
using System.Net;
|
2019-02-26 14:13:06 +00:00
|
|
|
using System.Net.WebSockets;
|
2019-02-25 22:34:32 +00:00
|
|
|
using System.Threading;
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
using Emby.Server.Implementations.HttpServer;
|
|
|
|
using Emby.Server.Implementations.Net;
|
|
|
|
using MediaBrowser.Controller.Net;
|
|
|
|
using MediaBrowser.Model.Services;
|
|
|
|
using Microsoft.AspNetCore.Http;
|
2019-02-26 07:09:42 +00:00
|
|
|
using Microsoft.AspNetCore.Http.Extensions;
|
2019-02-25 22:34:32 +00:00
|
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
|
|
|
|
namespace Emby.Server.Implementations.SocketSharp
|
|
|
|
{
|
|
|
|
public class WebSocketSharpListener : IHttpListener
|
|
|
|
{
|
|
|
|
private readonly ILogger _logger;
|
|
|
|
|
|
|
|
private CancellationTokenSource _disposeCancellationTokenSource = new CancellationTokenSource();
|
|
|
|
private CancellationToken _disposeCancellationToken;
|
|
|
|
|
|
|
|
public WebSocketSharpListener(
|
|
|
|
ILogger logger)
|
|
|
|
{
|
|
|
|
_logger = logger;
|
|
|
|
|
|
|
|
_disposeCancellationToken = _disposeCancellationTokenSource.Token;
|
|
|
|
}
|
|
|
|
|
|
|
|
public Func<Exception, IRequest, bool, bool, Task> ErrorHandler { get; set; }
|
|
|
|
public Func<IHttpRequest, string, string, string, CancellationToken, Task> RequestHandler { get; set; }
|
|
|
|
|
|
|
|
public Action<WebSocketConnectingEventArgs> WebSocketConnecting { get; set; }
|
|
|
|
|
|
|
|
public Action<WebSocketConnectEventArgs> WebSocketConnected { get; set; }
|
|
|
|
|
2019-02-26 19:13:48 +00:00
|
|
|
private static void LogRequest(ILogger logger, HttpRequest request)
|
2019-02-25 22:34:32 +00:00
|
|
|
{
|
2019-02-26 19:13:48 +00:00
|
|
|
var url = request.GetDisplayUrl();
|
2019-02-25 22:34:32 +00:00
|
|
|
|
2019-02-26 19:13:48 +00:00
|
|
|
logger.LogInformation("{0} {1}. UserAgent: {2}", "WS", url, request.Headers["User-Agent"].ToString());
|
2019-02-25 22:34:32 +00:00
|
|
|
}
|
|
|
|
|
2019-02-26 07:09:42 +00:00
|
|
|
public async Task ProcessWebSocketRequest(HttpContext ctx)
|
2019-02-25 22:34:32 +00:00
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
2019-02-26 19:13:48 +00:00
|
|
|
LogRequest(_logger, ctx.Request);
|
2019-02-26 07:09:42 +00:00
|
|
|
var endpoint = ctx.Connection.RemoteIpAddress.ToString();
|
|
|
|
var url = ctx.Request.GetDisplayUrl();
|
2019-02-25 22:34:32 +00:00
|
|
|
|
|
|
|
var connectingArgs = new WebSocketConnectingEventArgs
|
|
|
|
{
|
|
|
|
Url = url,
|
2019-02-27 13:23:39 +00:00
|
|
|
QueryString = ctx.Request.Query,
|
2019-02-25 22:34:32 +00:00
|
|
|
Endpoint = endpoint
|
|
|
|
};
|
|
|
|
|
|
|
|
WebSocketConnecting?.Invoke(connectingArgs);
|
|
|
|
|
|
|
|
if (connectingArgs.AllowConnection)
|
|
|
|
{
|
|
|
|
_logger.LogDebug("Web socket connection allowed");
|
|
|
|
|
2019-02-26 07:09:42 +00:00
|
|
|
var webSocketContext = await ctx.WebSockets.AcceptWebSocketAsync(null).ConfigureAwait(false);
|
2019-02-26 14:13:06 +00:00
|
|
|
var socket = new SharpWebSocket(webSocketContext, _logger);
|
2019-02-25 22:34:32 +00:00
|
|
|
|
2019-02-26 14:13:06 +00:00
|
|
|
WebSocketConnected(new WebSocketConnectEventArgs
|
2019-02-25 22:34:32 +00:00
|
|
|
{
|
2019-02-26 14:13:06 +00:00
|
|
|
Url = url,
|
2019-02-27 13:23:39 +00:00
|
|
|
QueryString = ctx.Request.Query,
|
2019-02-26 14:13:06 +00:00
|
|
|
WebSocket = socket,
|
|
|
|
Endpoint = endpoint
|
|
|
|
});
|
|
|
|
|
2019-02-26 19:13:48 +00:00
|
|
|
var buffer = WebSocket.CreateClientBuffer(4096, 4096);
|
|
|
|
WebSocketReceiveResult result;
|
|
|
|
var message = new List<byte>();
|
2019-02-26 18:48:18 +00:00
|
|
|
|
2019-02-26 19:13:48 +00:00
|
|
|
do
|
2019-02-26 18:48:18 +00:00
|
|
|
{
|
2019-02-26 21:57:59 +00:00
|
|
|
result = await webSocketContext.ReceiveAsync(buffer, _disposeCancellationToken);
|
2019-02-26 18:48:18 +00:00
|
|
|
socket.OnReceiveBytes(buffer.Array);
|
2019-02-26 19:13:48 +00:00
|
|
|
message.AddRange(buffer.Array.Take(result.Count));
|
|
|
|
} while (!result.EndOfMessage && result.MessageType != WebSocketMessageType.Close);
|
2019-02-26 18:48:18 +00:00
|
|
|
|
2019-02-26 19:13:48 +00:00
|
|
|
socket.OnReceiveBytes(message.ToArray());
|
|
|
|
await webSocketContext.CloseAsync(result.CloseStatus ?? WebSocketCloseStatus.NormalClosure,
|
2019-02-26 21:57:59 +00:00
|
|
|
result.CloseStatusDescription, _disposeCancellationToken);
|
2019-02-26 19:13:48 +00:00
|
|
|
socket.Dispose();
|
2019-02-25 22:34:32 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
_logger.LogWarning("Web socket connection not allowed");
|
|
|
|
ctx.Response.StatusCode = 401;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (Exception ex)
|
|
|
|
{
|
|
|
|
_logger.LogError(ex, "AcceptWebSocketAsync error");
|
|
|
|
ctx.Response.StatusCode = 500;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public Task Stop()
|
|
|
|
{
|
|
|
|
_disposeCancellationTokenSource.Cancel();
|
|
|
|
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;
|
|
|
|
|
|
|
|
/// <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;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (disposing)
|
|
|
|
{
|
|
|
|
Stop().GetAwaiter().GetResult();
|
|
|
|
}
|
|
|
|
|
|
|
|
_disposed = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|