aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Common
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.Common')
-rw-r--r--MediaBrowser.Common/Kernel/BaseKernel.cs54
-rw-r--r--MediaBrowser.Common/Kernel/BasePeriodicWebSocketListener.cs12
-rw-r--r--MediaBrowser.Common/Kernel/IApplicationHost.cs5
-rw-r--r--MediaBrowser.Common/Kernel/IKernel.cs11
-rw-r--r--MediaBrowser.Common/Kernel/IServerManager.cs54
-rw-r--r--MediaBrowser.Common/Kernel/RegisterServer.bat28
-rw-r--r--MediaBrowser.Common/Kernel/TcpManager.cs511
-rw-r--r--MediaBrowser.Common/MediaBrowser.Common.csproj5
-rw-r--r--MediaBrowser.Common/Net/IWebSocketConnection.cs85
-rw-r--r--MediaBrowser.Common/Net/IWebSocketServer.cs6
-rw-r--r--MediaBrowser.Common/Net/WebSocketConnection.cs254
11 files changed, 166 insertions, 859 deletions
diff --git a/MediaBrowser.Common/Kernel/BaseKernel.cs b/MediaBrowser.Common/Kernel/BaseKernel.cs
index 5b8da5d09..2ec1dd829 100644
--- a/MediaBrowser.Common/Kernel/BaseKernel.cs
+++ b/MediaBrowser.Common/Kernel/BaseKernel.cs
@@ -1,5 +1,4 @@
using MediaBrowser.Common.Events;
-using MediaBrowser.Common.Net;
using MediaBrowser.Common.Plugins;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Logging;
@@ -165,7 +164,7 @@ namespace MediaBrowser.Common.Kernel
/// Gets or sets the TCP manager.
/// </summary>
/// <value>The TCP manager.</value>
- public TcpManager TcpManager { get; private set; }
+ public IServerManager ServerManager { get; private set; }
/// <summary>
/// Gets the UDP server port number.
@@ -203,15 +202,6 @@ namespace MediaBrowser.Common.Kernel
public abstract KernelContext KernelContext { get; }
/// <summary>
- /// Gets the log file path.
- /// </summary>
- /// <value>The log file path.</value>
- public string LogFilePath
- {
- get { return ApplicationHost.LogFilePath; }
- }
-
- /// <summary>
/// Gets the logger.
/// </summary>
/// <value>The logger.</value>
@@ -238,23 +228,6 @@ namespace MediaBrowser.Common.Kernel
/// <exception cref="System.ArgumentNullException">isoManager</exception>
protected BaseKernel(IApplicationHost appHost, TApplicationPathsType appPaths, IXmlSerializer xmlSerializer, ILogger logger)
{
- if (appHost == null)
- {
- throw new ArgumentNullException("appHost");
- }
- if (appPaths == null)
- {
- throw new ArgumentNullException("appPaths");
- }
- if (xmlSerializer == null)
- {
- throw new ArgumentNullException("xmlSerializer");
- }
- if (logger == null)
- {
- throw new ArgumentNullException("logger");
- }
-
ApplicationPaths = appPaths;
ApplicationHost = appHost;
_xmlSerializer = xmlSerializer;
@@ -291,8 +264,8 @@ namespace MediaBrowser.Common.Kernel
await OnComposablePartsLoaded().ConfigureAwait(false);
- DisposeTcpManager();
- TcpManager = (TcpManager)ApplicationHost.CreateInstance(typeof(TcpManager));
+ ServerManager = ApplicationHost.Resolve<IServerManager>();
+ ServerManager.Start();
}
/// <summary>
@@ -357,7 +330,7 @@ namespace MediaBrowser.Common.Kernel
{
HasPendingRestart = true;
- TcpManager.SendWebSocketMessage("HasPendingRestartChanged", GetSystemInfo());
+ ServerManager.SendWebSocketMessage("HasPendingRestartChanged", GetSystemInfo());
EventHelper.QueueEventIfNotNull(HasPendingRestartChanged, this, EventArgs.Empty, Logger);
}
@@ -377,22 +350,7 @@ namespace MediaBrowser.Common.Kernel
/// <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)
- {
- DisposeTcpManager();
- }
- }
- /// <summary>
- /// Disposes the TCP manager.
- /// </summary>
- private void DisposeTcpManager()
- {
- if (TcpManager != null)
- {
- TcpManager.Dispose();
- TcpManager = null;
- }
}
/// <summary>
@@ -424,8 +382,8 @@ namespace MediaBrowser.Common.Kernel
HasPendingRestart = HasPendingRestart,
Version = ApplicationHost.ApplicationVersion.ToString(),
IsNetworkDeployed = ApplicationHost.CanSelfUpdate,
- WebSocketPortNumber = TcpManager.WebSocketPortNumber,
- SupportsNativeWebSocket = TcpManager.SupportsNativeWebSocket,
+ WebSocketPortNumber = ServerManager.WebSocketPortNumber,
+ SupportsNativeWebSocket = ServerManager.SupportsNativeWebSocket,
FailedPluginAssemblies = ApplicationHost.FailedAssemblies.ToArray()
};
}
diff --git a/MediaBrowser.Common/Kernel/BasePeriodicWebSocketListener.cs b/MediaBrowser.Common/Kernel/BasePeriodicWebSocketListener.cs
index 5374bb714..6a44cf372 100644
--- a/MediaBrowser.Common/Kernel/BasePeriodicWebSocketListener.cs
+++ b/MediaBrowser.Common/Kernel/BasePeriodicWebSocketListener.cs
@@ -19,8 +19,8 @@ namespace MediaBrowser.Common.Kernel
/// <summary>
/// The _active connections
/// </summary>
- protected readonly List<Tuple<WebSocketConnection, CancellationTokenSource, Timer, TStateType, SemaphoreSlim>> ActiveConnections =
- new List<Tuple<WebSocketConnection, CancellationTokenSource, Timer, TStateType, SemaphoreSlim>>();
+ protected readonly List<Tuple<IWebSocketConnection, CancellationTokenSource, Timer, TStateType, SemaphoreSlim>> ActiveConnections =
+ new List<Tuple<IWebSocketConnection, CancellationTokenSource, Timer, TStateType, SemaphoreSlim>>();
/// <summary>
/// Gets the name.
@@ -103,7 +103,7 @@ namespace MediaBrowser.Common.Kernel
lock (ActiveConnections)
{
- ActiveConnections.Add(new Tuple<WebSocketConnection, CancellationTokenSource, Timer, TStateType, SemaphoreSlim>(message.Connection, cancellationTokenSource, timer, state, semaphore));
+ ActiveConnections.Add(new Tuple<IWebSocketConnection, CancellationTokenSource, Timer, TStateType, SemaphoreSlim>(message.Connection, cancellationTokenSource, timer, state, semaphore));
}
timer.Change(TimeSpan.FromMilliseconds(dueTimeMs), TimeSpan.FromMilliseconds(periodMs));
@@ -115,9 +115,9 @@ namespace MediaBrowser.Common.Kernel
/// <param name="state">The state.</param>
private async void TimerCallback(object state)
{
- var connection = (WebSocketConnection)state;
+ var connection = (IWebSocketConnection)state;
- Tuple<WebSocketConnection, CancellationTokenSource, Timer, TStateType, SemaphoreSlim> tuple;
+ Tuple<IWebSocketConnection, CancellationTokenSource, Timer, TStateType, SemaphoreSlim> tuple;
lock (ActiveConnections)
{
@@ -187,7 +187,7 @@ namespace MediaBrowser.Common.Kernel
/// Disposes the connection.
/// </summary>
/// <param name="connection">The connection.</param>
- private void DisposeConnection(Tuple<WebSocketConnection, CancellationTokenSource, Timer, TStateType, SemaphoreSlim> connection)
+ private void DisposeConnection(Tuple<IWebSocketConnection, CancellationTokenSource, Timer, TStateType, SemaphoreSlim> connection)
{
Logger.Info("{1} stop transmitting over websocket to {0}", connection.Item1.RemoteEndPoint, GetType().Name);
diff --git a/MediaBrowser.Common/Kernel/IApplicationHost.cs b/MediaBrowser.Common/Kernel/IApplicationHost.cs
index af9b039bc..1f99d10db 100644
--- a/MediaBrowser.Common/Kernel/IApplicationHost.cs
+++ b/MediaBrowser.Common/Kernel/IApplicationHost.cs
@@ -97,5 +97,10 @@ namespace MediaBrowser.Common.Kernel
/// <typeparam name="T"></typeparam>
/// <returns>``0.</returns>
T TryResolve<T>();
+
+ /// <summary>
+ /// Shuts down.
+ /// </summary>
+ void Shutdown();
}
}
diff --git a/MediaBrowser.Common/Kernel/IKernel.cs b/MediaBrowser.Common/Kernel/IKernel.cs
index 06c2e7b64..a28500eca 100644
--- a/MediaBrowser.Common/Kernel/IKernel.cs
+++ b/MediaBrowser.Common/Kernel/IKernel.cs
@@ -1,5 +1,4 @@
-using MediaBrowser.Common.Net;
-using MediaBrowser.Common.Plugins;
+using MediaBrowser.Common.Plugins;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.System;
using System;
@@ -72,12 +71,6 @@ namespace MediaBrowser.Common.Kernel
string WebApplicationName { get; }
/// <summary>
- /// Gets the log file path.
- /// </summary>
- /// <value>The log file path.</value>
- string LogFilePath { get; }
-
- /// <summary>
/// Performs the pending restart.
/// </summary>
void PerformPendingRestart();
@@ -104,7 +97,7 @@ namespace MediaBrowser.Common.Kernel
/// Gets the TCP manager.
/// </summary>
/// <value>The TCP manager.</value>
- TcpManager TcpManager { get; }
+ IServerManager ServerManager { get; }
/// <summary>
/// Gets the web socket listeners.
diff --git a/MediaBrowser.Common/Kernel/IServerManager.cs b/MediaBrowser.Common/Kernel/IServerManager.cs
new file mode 100644
index 000000000..cfea6eee1
--- /dev/null
+++ b/MediaBrowser.Common/Kernel/IServerManager.cs
@@ -0,0 +1,54 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Common.Kernel
+{
+ public interface IServerManager : IDisposable
+ {
+ /// <summary>
+ /// Gets a value indicating whether [supports web socket].
+ /// </summary>
+ /// <value><c>true</c> if [supports web socket]; otherwise, <c>false</c>.</value>
+ bool SupportsNativeWebSocket { get; }
+
+ /// <summary>
+ /// Gets the web socket port number.
+ /// </summary>
+ /// <value>The web socket port number.</value>
+ int WebSocketPortNumber { get; }
+
+ /// <summary>
+ /// Starts this instance.
+ /// </summary>
+ void Start();
+
+ /// <summary>
+ /// Sends a message to all clients currently connected via a web socket
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <param name="messageType">Type of the message.</param>
+ /// <param name="data">The data.</param>
+ /// <returns>Task.</returns>
+ void SendWebSocketMessage<T>(string messageType, T data);
+
+ /// <summary>
+ /// Sends a message to all clients currently connected via a web socket
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <param name="messageType">Type of the message.</param>
+ /// <param name="dataFunction">The function that generates the data to send, if there are any connected clients</param>
+ void SendWebSocketMessage<T>(string messageType, Func<T> dataFunction);
+
+ /// <summary>
+ /// Sends a message to all clients currently connected via a web socket
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <param name="messageType">Type of the message.</param>
+ /// <param name="dataFunction">The function that generates the data to send, if there are any connected clients</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task.</returns>
+ /// <exception cref="System.ArgumentNullException">messageType</exception>
+ Task SendWebSocketMessageAsync<T>(string messageType, Func<T> dataFunction, CancellationToken cancellationToken);
+ }
+} \ No newline at end of file
diff --git a/MediaBrowser.Common/Kernel/RegisterServer.bat b/MediaBrowser.Common/Kernel/RegisterServer.bat
deleted file mode 100644
index d762dfaf7..000000000
--- a/MediaBrowser.Common/Kernel/RegisterServer.bat
+++ /dev/null
@@ -1,28 +0,0 @@
-rem %1 = http server port
-rem %2 = http server url
-rem %3 = udp server port
-rem %4 = tcp server port (web socket)
-
-if [%1]==[] GOTO DONE
-
-netsh advfirewall firewall delete rule name="Port %1" protocol=TCP localport=%1
-netsh advfirewall firewall add rule name="Port %1" dir=in action=allow protocol=TCP localport=%1
-
-if [%2]==[] GOTO DONE
-
-netsh http del urlacl url="%2" user="NT AUTHORITY\Authenticated Users"
-netsh http add urlacl url="%2" user="NT AUTHORITY\Authenticated Users"
-
-if [%3]==[] GOTO DONE
-
-netsh advfirewall firewall delete rule name="Port %3" protocol=UDP localport=%3
-netsh advfirewall firewall add rule name="Port %3" dir=in action=allow protocol=UDP localport=%3
-
-if [%4]==[] GOTO DONE
-
-netsh advfirewall firewall delete rule name="Port %4" protocol=TCP localport=%4
-netsh advfirewall firewall add rule name="Port %4" dir=in action=allow protocol=TCP localport=%4
-
-
-:DONE
-Exit \ No newline at end of file
diff --git a/MediaBrowser.Common/Kernel/TcpManager.cs b/MediaBrowser.Common/Kernel/TcpManager.cs
deleted file mode 100644
index 2dfed501a..000000000
--- a/MediaBrowser.Common/Kernel/TcpManager.cs
+++ /dev/null
@@ -1,511 +0,0 @@
-using MediaBrowser.Common.Net;
-using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Serialization;
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.IO;
-using System.Linq;
-using System.Net;
-using System.Net.Sockets;
-using System.Reflection;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Common.Kernel
-{
- /// <summary>
- /// Manages the Http Server, Udp Server and WebSocket connections
- /// </summary>
- public class TcpManager : IDisposable
- {
- /// <summary>
- /// This is the udp server used for server discovery by clients
- /// </summary>
- /// <value>The UDP server.</value>
- private IUdpServer UdpServer { get; set; }
-
- /// <summary>
- /// Both the Ui and server will have a built-in HttpServer.
- /// People will inevitably want remote control apps so it's needed in the Ui too.
- /// </summary>
- /// <value>The HTTP server.</value>
- private IHttpServer HttpServer { get; set; }
-
- /// <summary>
- /// Gets or sets the json serializer.
- /// </summary>
- /// <value>The json serializer.</value>
- private readonly IJsonSerializer _jsonSerializer;
-
- /// <summary>
- /// This subscribes to HttpListener requests and finds the appropriate BaseHandler to process it
- /// </summary>
- /// <value>The HTTP listener.</value>
- private IDisposable HttpListener { get; set; }
-
- /// <summary>
- /// The web socket connections
- /// </summary>
- private readonly List<WebSocketConnection> _webSocketConnections = new List<WebSocketConnection>();
-
- /// <summary>
- /// Gets or sets the external web socket server.
- /// </summary>
- /// <value>The external web socket server.</value>
- private IWebSocketServer ExternalWebSocketServer { get; set; }
-
- /// <summary>
- /// The _logger
- /// </summary>
- private readonly ILogger _logger;
-
- /// <summary>
- /// The _network manager
- /// </summary>
- private readonly INetworkManager _networkManager;
-
- /// <summary>
- /// The _application host
- /// </summary>
- private readonly IApplicationHost _applicationHost;
-
- /// <summary>
- /// The _kernel
- /// </summary>
- private readonly IKernel _kernel;
-
- /// <summary>
- /// Gets a value indicating whether [supports web socket].
- /// </summary>
- /// <value><c>true</c> if [supports web socket]; otherwise, <c>false</c>.</value>
- internal bool SupportsNativeWebSocket
- {
- get { return HttpServer != null && HttpServer.SupportsWebSockets; }
- }
-
- /// <summary>
- /// Gets the web socket port number.
- /// </summary>
- /// <value>The web socket port number.</value>
- public int WebSocketPortNumber
- {
- get { return SupportsNativeWebSocket ? _kernel.Configuration.HttpServerPortNumber : _kernel.Configuration.LegacyWebSocketPortNumber; }
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="TcpManager" /> class.
- /// </summary>
- /// <param name="applicationHost">The application host.</param>
- /// <param name="kernel">The kernel.</param>
- /// <param name="networkManager">The network manager.</param>
- /// <param name="jsonSerializer">The json serializer.</param>
- /// <param name="logger">The logger.</param>
- /// <exception cref="System.ArgumentNullException">applicationHost</exception>
- public TcpManager(IApplicationHost applicationHost, IKernel kernel, INetworkManager networkManager, IJsonSerializer jsonSerializer, ILogger logger)
- {
- if (applicationHost == null)
- {
- throw new ArgumentNullException("applicationHost");
- }
- if (kernel == null)
- {
- throw new ArgumentNullException("kernel");
- }
- if (networkManager == null)
- {
- throw new ArgumentNullException("networkManager");
- }
- if (jsonSerializer == null)
- {
- throw new ArgumentNullException("jsonSerializer");
- }
- if (logger == null)
- {
- throw new ArgumentNullException("logger");
- }
-
- _logger = logger;
- _jsonSerializer = jsonSerializer;
- _kernel = kernel;
- _applicationHost = applicationHost;
- _networkManager = networkManager;
-
- if (applicationHost.IsFirstRun)
- {
- RegisterServerWithAdministratorAccess();
- }
-
- ReloadUdpServer();
- ReloadHttpServer();
-
- if (!SupportsNativeWebSocket)
- {
- ReloadExternalWebSocketServer();
- }
- }
-
- /// <summary>
- /// Starts the external web socket server.
- /// </summary>
- private void ReloadExternalWebSocketServer()
- {
- // Avoid windows firewall prompts in the ui
- if (_kernel.KernelContext != KernelContext.Server)
- {
- return;
- }
-
- DisposeExternalWebSocketServer();
-
- ExternalWebSocketServer = _applicationHost.Resolve<IWebSocketServer>();
-
- ExternalWebSocketServer.Start(_kernel.Configuration.LegacyWebSocketPortNumber);
- ExternalWebSocketServer.WebSocketConnected += HttpServer_WebSocketConnected;
- }
-
- /// <summary>
- /// Restarts the Http Server, or starts it if not currently running
- /// </summary>
- /// <param name="registerServerOnFailure">if set to <c>true</c> [register server on failure].</param>
- public void ReloadHttpServer(bool registerServerOnFailure = true)
- {
- // Only reload if the port has changed, so that we don't disconnect any active users
- if (HttpServer != null && HttpServer.UrlPrefix.Equals(_kernel.HttpServerUrlPrefix, StringComparison.OrdinalIgnoreCase))
- {
- return;
- }
-
- DisposeHttpServer();
-
- _logger.Info("Loading Http Server");
-
- try
- {
- HttpServer = _applicationHost.Resolve<IHttpServer>();
- HttpServer.EnableHttpRequestLogging = _kernel.Configuration.EnableHttpLevelLogging;
- HttpServer.Start(_kernel.HttpServerUrlPrefix);
- }
- catch (HttpListenerException ex)
- {
- _logger.ErrorException("Error starting Http Server", ex);
-
- if (registerServerOnFailure)
- {
- RegisterServerWithAdministratorAccess();
-
- // Don't get stuck in a loop
- ReloadHttpServer(false);
-
- return;
- }
-
- throw;
- }
-
- HttpServer.WebSocketConnected += HttpServer_WebSocketConnected;
- }
-
- /// <summary>
- /// Handles the WebSocketConnected event of the HttpServer control.
- /// </summary>
- /// <param name="sender">The source of the event.</param>
- /// <param name="e">The <see cref="WebSocketConnectEventArgs" /> instance containing the event data.</param>
- void HttpServer_WebSocketConnected(object sender, WebSocketConnectEventArgs e)
- {
- var connection = new WebSocketConnection(e.WebSocket, e.Endpoint, _jsonSerializer, _logger) { OnReceive = ProcessWebSocketMessageReceived };
-
- _webSocketConnections.Add(connection);
- }
-
- /// <summary>
- /// Processes the web socket message received.
- /// </summary>
- /// <param name="result">The result.</param>
- private async void ProcessWebSocketMessageReceived(WebSocketMessageInfo result)
- {
- var tasks = _kernel.WebSocketListeners.Select(i => Task.Run(async () =>
- {
- try
- {
- await i.ProcessMessage(result).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("{0} failed processing WebSocket message {1}", ex, i.GetType().Name, result.MessageType);
- }
- }));
-
- await Task.WhenAll(tasks).ConfigureAwait(false);
- }
-
- /// <summary>
- /// Starts or re-starts the udp server
- /// </summary>
- private void ReloadUdpServer()
- {
- // For now, there's no reason to keep reloading this over and over
- if (UdpServer != null)
- {
- return;
- }
-
- // Avoid windows firewall prompts in the ui
- if (_kernel.KernelContext != KernelContext.Server)
- {
- return;
- }
-
- DisposeUdpServer();
-
- try
- {
- // The port number can't be in configuration because we don't want it to ever change
- UdpServer = _applicationHost.Resolve<IUdpServer>();
- UdpServer.Start(_kernel.UdpServerPortNumber);
- }
- catch (SocketException ex)
- {
- _logger.ErrorException("Failed to start UDP Server", ex);
- return;
- }
-
- UdpServer.MessageReceived += UdpServer_MessageReceived;
- }
-
- /// <summary>
- /// Handles the MessageReceived event of the UdpServer control.
- /// </summary>
- /// <param name="sender">The source of the event.</param>
- /// <param name="e">The <see cref="UdpMessageReceivedEventArgs" /> instance containing the event data.</param>
- async void UdpServer_MessageReceived(object sender, UdpMessageReceivedEventArgs e)
- {
- var expectedMessage = String.Format("who is MediaBrowser{0}?", _kernel.KernelContext);
- var expectedMessageBytes = Encoding.UTF8.GetBytes(expectedMessage);
-
- if (expectedMessageBytes.SequenceEqual(e.Bytes))
- {
- _logger.Info("Received UDP server request from " + e.RemoteEndPoint);
-
- // Send a response back with our ip address and port
- var response = String.Format("MediaBrowser{0}|{1}:{2}", _kernel.KernelContext, _networkManager.GetLocalIpAddress(), _kernel.UdpServerPortNumber);
-
- await UdpServer.SendAsync(Encoding.UTF8.GetBytes(response), e.RemoteEndPoint);
- }
- }
-
- /// <summary>
- /// Sends a message to all clients currently connected via a web socket
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="messageType">Type of the message.</param>
- /// <param name="data">The data.</param>
- /// <returns>Task.</returns>
- public void SendWebSocketMessage<T>(string messageType, T data)
- {
- SendWebSocketMessage(messageType, () => data);
- }
-
- /// <summary>
- /// Sends a message to all clients currently connected via a web socket
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="messageType">Type of the message.</param>
- /// <param name="dataFunction">The function that generates the data to send, if there are any connected clients</param>
- public void SendWebSocketMessage<T>(string messageType, Func<T> dataFunction)
- {
- Task.Run(async () => await SendWebSocketMessageAsync(messageType, dataFunction, CancellationToken.None).ConfigureAwait(false));
- }
-
- /// <summary>
- /// Sends a message to all clients currently connected via a web socket
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="messageType">Type of the message.</param>
- /// <param name="dataFunction">The function that generates the data to send, if there are any connected clients</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- /// <exception cref="System.ArgumentNullException">messageType</exception>
- public async Task SendWebSocketMessageAsync<T>(string messageType, Func<T> dataFunction, CancellationToken cancellationToken)
- {
- if (string.IsNullOrEmpty(messageType))
- {
- throw new ArgumentNullException("messageType");
- }
-
- if (dataFunction == null)
- {
- throw new ArgumentNullException("dataFunction");
- }
-
- if (cancellationToken == null)
- {
- throw new ArgumentNullException("cancellationToken");
- }
-
- cancellationToken.ThrowIfCancellationRequested();
-
- var connections = _webSocketConnections.Where(s => s.State == WebSocketState.Open).ToList();
-
- if (connections.Count > 0)
- {
- _logger.Info("Sending web socket message {0}", messageType);
-
- var message = new WebSocketMessage<T> { MessageType = messageType, Data = dataFunction() };
- var bytes = _jsonSerializer.SerializeToBytes(message);
-
- var tasks = connections.Select(s => Task.Run(() =>
- {
- try
- {
- s.SendAsync(bytes, cancellationToken);
- }
- catch (OperationCanceledException)
- {
- throw;
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error sending web socket message {0} to {1}", ex, messageType, s.RemoteEndPoint);
- }
- }));
-
- await Task.WhenAll(tasks).ConfigureAwait(false);
- }
- }
-
- /// <summary>
- /// Disposes the udp server
- /// </summary>
- private void DisposeUdpServer()
- {
- if (UdpServer != null)
- {
- UdpServer.MessageReceived -= UdpServer_MessageReceived;
- UdpServer.Dispose();
- }
- }
-
- /// <summary>
- /// Disposes the current HttpServer
- /// </summary>
- private void DisposeHttpServer()
- {
- foreach (var socket in _webSocketConnections)
- {
- // Dispose the connection
- socket.Dispose();
- }
-
- _webSocketConnections.Clear();
-
- if (HttpServer != null)
- {
- _logger.Info("Disposing Http Server");
-
- HttpServer.WebSocketConnected -= HttpServer_WebSocketConnected;
- HttpServer.Dispose();
- }
-
- if (HttpListener != null)
- {
- HttpListener.Dispose();
- }
-
- DisposeExternalWebSocketServer();
- }
-
- /// <summary>
- /// Registers the server with administrator access.
- /// </summary>
- private void RegisterServerWithAdministratorAccess()
- {
- // Create a temp file path to extract the bat file to
- var tmpFile = Path.Combine(_kernel.ApplicationPaths.TempDirectory, Guid.NewGuid() + ".bat");
-
- // Extract the bat file
- using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("MediaBrowser.Common.Kernel.RegisterServer.bat"))
- {
- using (var fileStream = File.Create(tmpFile))
- {
- stream.CopyTo(fileStream);
- }
- }
-
- var startInfo = new ProcessStartInfo
- {
- FileName = tmpFile,
-
- Arguments = string.Format("{0} {1} {2} {3}", _kernel.Configuration.HttpServerPortNumber,
- _kernel.HttpServerUrlPrefix,
- _kernel.UdpServerPortNumber,
- _kernel.Configuration.LegacyWebSocketPortNumber),
-
- CreateNoWindow = true,
- WindowStyle = ProcessWindowStyle.Hidden,
- Verb = "runas",
- ErrorDialog = false
- };
-
- using (var process = Process.Start(startInfo))
- {
- process.WaitForExit();
- }
- }
-
- /// <summary>
- /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
- /// </summary>
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- /// <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)
- {
- DisposeUdpServer();
- DisposeHttpServer();
- }
- }
-
- /// <summary>
- /// Disposes the external web socket server.
- /// </summary>
- private void DisposeExternalWebSocketServer()
- {
- if (ExternalWebSocketServer != null)
- {
- ExternalWebSocketServer.Dispose();
- }
- }
-
- /// <summary>
- /// Called when [application configuration changed].
- /// </summary>
- /// <param name="oldConfig">The old config.</param>
- /// <param name="newConfig">The new config.</param>
- public void OnApplicationConfigurationChanged(BaseApplicationConfiguration oldConfig, BaseApplicationConfiguration newConfig)
- {
- HttpServer.EnableHttpRequestLogging = newConfig.EnableHttpLevelLogging;
-
- if (oldConfig.HttpServerPortNumber != newConfig.HttpServerPortNumber)
- {
- ReloadHttpServer();
- }
-
- if (!SupportsNativeWebSocket && oldConfig.LegacyWebSocketPortNumber != newConfig.LegacyWebSocketPortNumber)
- {
- ReloadExternalWebSocketServer();
- }
- }
- }
-}
diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj
index 333c20beb..b136eb43c 100644
--- a/MediaBrowser.Common/MediaBrowser.Common.csproj
+++ b/MediaBrowser.Common/MediaBrowser.Common.csproj
@@ -91,10 +91,10 @@
<Compile Include="IO\StreamDefaults.cs" />
<Compile Include="Kernel\BasePeriodicWebSocketListener.cs" />
<Compile Include="Kernel\IApplicationPaths.cs" />
+ <Compile Include="Kernel\IServerManager.cs" />
<Compile Include="Kernel\IWebSocketListener.cs" />
<Compile Include="Kernel\IApplicationHost.cs" />
<Compile Include="Kernel\IKernel.cs" />
- <Compile Include="Kernel\TcpManager.cs" />
<Compile Include="Net\Handlers\IHttpServerHandler.cs" />
<Compile Include="Net\Handlers\StaticFileHandler.cs" />
<Compile Include="Net\IHttpClient.cs" />
@@ -103,12 +103,12 @@
<Compile Include="Net\IRestfulService.cs" />
<Compile Include="Net\IUdpServer.cs" />
<Compile Include="Net\IWebSocket.cs" />
+ <Compile Include="Net\IWebSocketConnection.cs" />
<Compile Include="Net\IWebSocketServer.cs" />
<Compile Include="Net\MimeTypes.cs" />
<Compile Include="Net\StreamWriter.cs" />
<Compile Include="Net\UdpMessageReceivedEventArgs.cs" />
<Compile Include="Net\WebSocketConnectEventArgs.cs" />
- <Compile Include="Net\WebSocketConnection.cs" />
<Compile Include="Net\WebSocketMessageType.cs" />
<Compile Include="Net\WebSocketState.cs" />
<Compile Include="Plugins\BaseUiPlugin.cs" />
@@ -138,7 +138,6 @@
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
- <EmbeddedResource Include="Kernel\RegisterServer.bat" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
diff --git a/MediaBrowser.Common/Net/IWebSocketConnection.cs b/MediaBrowser.Common/Net/IWebSocketConnection.cs
new file mode 100644
index 000000000..8e1b13ceb
--- /dev/null
+++ b/MediaBrowser.Common/Net/IWebSocketConnection.cs
@@ -0,0 +1,85 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Common.Net
+{
+ public interface IWebSocketConnection : IDisposable
+ {
+ /// <summary>
+ /// Gets or sets the receive action.
+ /// </summary>
+ /// <value>The receive action.</value>
+ Action<WebSocketMessageInfo> OnReceive { get; set; }
+
+ /// <summary>
+ /// Gets the state.
+ /// </summary>
+ /// <value>The state.</value>
+ WebSocketState State { get; }
+
+ /// <summary>
+ /// Gets the remote end point.
+ /// </summary>
+ /// <value>The remote end point.</value>
+ string RemoteEndPoint { get; }
+
+ /// <summary>
+ /// Sends a message asynchronously.
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <param name="message">The message.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task.</returns>
+ /// <exception cref="System.ArgumentNullException">message</exception>
+ Task SendAsync<T>(WebSocketMessage<T> message, CancellationToken cancellationToken);
+
+ /// <summary>
+ /// Sends a message asynchronously.
+ /// </summary>
+ /// <param name="buffer">The buffer.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task.</returns>
+ Task SendAsync(byte[] buffer, CancellationToken cancellationToken);
+
+ /// <summary>
+ /// Sends a message asynchronously.
+ /// </summary>
+ /// <param name="buffer">The buffer.</param>
+ /// <param name="type">The type.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task.</returns>
+ /// <exception cref="System.ArgumentNullException">buffer</exception>
+ Task SendAsync(byte[] buffer, WebSocketMessageType type, CancellationToken cancellationToken);
+ }
+
+ /// <summary>
+ /// Class WebSocketMessage
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ public class WebSocketMessage<T>
+ {
+ /// <summary>
+ /// Gets or sets the type of the message.
+ /// </summary>
+ /// <value>The type of the message.</value>
+ public string MessageType { get; set; }
+ /// <summary>
+ /// Gets or sets the data.
+ /// </summary>
+ /// <value>The data.</value>
+ public T Data { get; set; }
+ }
+
+ /// <summary>
+ /// Class WebSocketMessageInfo
+ /// </summary>
+ public class WebSocketMessageInfo : WebSocketMessage<string>
+ {
+ /// <summary>
+ /// Gets or sets the connection.
+ /// </summary>
+ /// <value>The connection.</value>
+ public IWebSocketConnection Connection { get; set; }
+ }
+} \ No newline at end of file
diff --git a/MediaBrowser.Common/Net/IWebSocketServer.cs b/MediaBrowser.Common/Net/IWebSocketServer.cs
index 5ce571fbb..187e03e09 100644
--- a/MediaBrowser.Common/Net/IWebSocketServer.cs
+++ b/MediaBrowser.Common/Net/IWebSocketServer.cs
@@ -22,5 +22,11 @@ namespace MediaBrowser.Common.Net
/// Occurs when [web socket connected].
/// </summary>
event EventHandler<WebSocketConnectEventArgs> WebSocketConnected;
+
+ /// <summary>
+ /// Gets the port.
+ /// </summary>
+ /// <value>The port.</value>
+ int Port { get; }
}
}
diff --git a/MediaBrowser.Common/Net/WebSocketConnection.cs b/MediaBrowser.Common/Net/WebSocketConnection.cs
deleted file mode 100644
index 1ad0e8f06..000000000
--- a/MediaBrowser.Common/Net/WebSocketConnection.cs
+++ /dev/null
@@ -1,254 +0,0 @@
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Serialization;
-using System;
-using System.IO;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Common.Net
-{
- /// <summary>
- /// Class WebSocketConnection
- /// </summary>
- public class WebSocketConnection : IDisposable
- {
- /// <summary>
- /// The _socket
- /// </summary>
- private readonly IWebSocket _socket;
-
- /// <summary>
- /// The _remote end point
- /// </summary>
- public readonly string RemoteEndPoint;
-
- /// <summary>
- /// The _cancellation token source
- /// </summary>
- private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
-
- /// <summary>
- /// The _send semaphore
- /// </summary>
- private readonly SemaphoreSlim _sendSemaphore = new SemaphoreSlim(1,1);
-
- /// <summary>
- /// The logger
- /// </summary>
- private readonly ILogger _logger;
-
- /// <summary>
- /// The _json serializer
- /// </summary>
- private readonly IJsonSerializer _jsonSerializer;
-
- /// <summary>
- /// Gets or sets the receive action.
- /// </summary>
- /// <value>The receive action.</value>
- public Action<WebSocketMessageInfo> OnReceive { get; set; }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="WebSocketConnection" /> class.
- /// </summary>
- /// <param name="socket">The socket.</param>
- /// <param name="remoteEndPoint">The remote end point.</param>
- /// <param name="jsonSerializer">The json serializer.</param>
- /// <param name="logger">The logger.</param>
- /// <exception cref="System.ArgumentNullException">socket</exception>
- public WebSocketConnection(IWebSocket socket, string remoteEndPoint, IJsonSerializer jsonSerializer, ILogger logger)
- {
- if (socket == null)
- {
- throw new ArgumentNullException("socket");
- }
- if (string.IsNullOrEmpty(remoteEndPoint))
- {
- throw new ArgumentNullException("remoteEndPoint");
- }
- if (jsonSerializer == null)
- {
- throw new ArgumentNullException("jsonSerializer");
- }
- if (logger == null)
- {
- throw new ArgumentNullException("logger");
- }
-
- _jsonSerializer = jsonSerializer;
- _socket = socket;
- _socket.OnReceiveDelegate = OnReceiveInternal;
- RemoteEndPoint = remoteEndPoint;
- _logger = logger;
- }
-
- /// <summary>
- /// Called when [receive].
- /// </summary>
- /// <param name="bytes">The bytes.</param>
- private void OnReceiveInternal(byte[] bytes)
- {
- if (OnReceive == null)
- {
- return;
- }
- try
- {
- WebSocketMessageInfo info;
-
- using (var memoryStream = new MemoryStream(bytes))
- {
- info = (WebSocketMessageInfo)_jsonSerializer.DeserializeFromStream(memoryStream, typeof(WebSocketMessageInfo));
- }
-
- info.Connection = this;
-
- OnReceive(info);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error processing web socket message", ex);
- }
- }
-
- /// <summary>
- /// Sends a message asynchronously.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="message">The message.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- /// <exception cref="System.ArgumentNullException">message</exception>
- public Task SendAsync<T>(WebSocketMessage<T> message, CancellationToken cancellationToken)
- {
- if (message == null)
- {
- throw new ArgumentNullException("message");
- }
-
- var bytes = _jsonSerializer.SerializeToBytes(message);
-
- return SendAsync(bytes, cancellationToken);
- }
-
- /// <summary>
- /// Sends a message asynchronously.
- /// </summary>
- /// <param name="buffer">The buffer.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- public Task SendAsync(byte[] buffer, CancellationToken cancellationToken)
- {
- return SendAsync(buffer, WebSocketMessageType.Text, cancellationToken);
- }
-
- /// <summary>
- /// Sends a message asynchronously.
- /// </summary>
- /// <param name="buffer">The buffer.</param>
- /// <param name="type">The type.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- /// <exception cref="System.ArgumentNullException">buffer</exception>
- public async Task SendAsync(byte[] buffer, WebSocketMessageType type, CancellationToken cancellationToken)
- {
- if (buffer == null)
- {
- throw new ArgumentNullException("buffer");
- }
-
- if (cancellationToken == null)
- {
- throw new ArgumentNullException("cancellationToken");
- }
-
- cancellationToken.ThrowIfCancellationRequested();
-
- // Per msdn docs, attempting to send simultaneous messages will result in one failing.
- // This should help us workaround that and ensure all messages get sent
- await _sendSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- try
- {
- await _socket.SendAsync(buffer, type, true, cancellationToken);
- }
- catch (OperationCanceledException)
- {
- _logger.Info("WebSocket message to {0} was cancelled", RemoteEndPoint);
-
- throw;
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error sending WebSocket message {0}", ex, RemoteEndPoint);
-
- throw;
- }
- finally
- {
- _sendSemaphore.Release();
- }
- }
-
- /// <summary>
- /// Gets the state.
- /// </summary>
- /// <value>The state.</value>
- public WebSocketState State
- {
- get { return _socket.State; }
- }
-
- /// <summary>
- /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
- /// </summary>
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- /// <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)
- {
- _cancellationTokenSource.Dispose();
- _socket.Dispose();
- }
- }
- }
-
- /// <summary>
- /// Class WebSocketMessage
- /// </summary>
- /// <typeparam name="T"></typeparam>
- public class WebSocketMessage<T>
- {
- /// <summary>
- /// Gets or sets the type of the message.
- /// </summary>
- /// <value>The type of the message.</value>
- public string MessageType { get; set; }
- /// <summary>
- /// Gets or sets the data.
- /// </summary>
- /// <value>The data.</value>
- public T Data { get; set; }
- }
-
- /// <summary>
- /// Class WebSocketMessageInfo
- /// </summary>
- public class WebSocketMessageInfo : WebSocketMessage<string>
- {
- /// <summary>
- /// Gets or sets the connection.
- /// </summary>
- /// <value>The connection.</value>
- public WebSocketConnection Connection { get; set; }
- }
-}