From 6efd22a3d22f8d87ad17da3a1e47ca26c5bb09f2 Mon Sep 17 00:00:00 2001 From: LukePulverenti Date: Tue, 26 Feb 2013 11:10:55 -0500 Subject: added a shutdown api method, font size fix and other decouplings --- MediaBrowser.Common/Kernel/BaseKernel.cs | 54 +-- .../Kernel/BasePeriodicWebSocketListener.cs | 12 +- MediaBrowser.Common/Kernel/IApplicationHost.cs | 5 + MediaBrowser.Common/Kernel/IKernel.cs | 11 +- MediaBrowser.Common/Kernel/IServerManager.cs | 54 +++ MediaBrowser.Common/Kernel/RegisterServer.bat | 28 -- MediaBrowser.Common/Kernel/TcpManager.cs | 511 --------------------- 7 files changed, 73 insertions(+), 602 deletions(-) create mode 100644 MediaBrowser.Common/Kernel/IServerManager.cs delete mode 100644 MediaBrowser.Common/Kernel/RegisterServer.bat delete mode 100644 MediaBrowser.Common/Kernel/TcpManager.cs (limited to 'MediaBrowser.Common/Kernel') diff --git a/MediaBrowser.Common/Kernel/BaseKernel.cs b/MediaBrowser.Common/Kernel/BaseKernel.cs index 5b8da5d094..2ec1dd829d 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. /// /// The TCP manager. - public TcpManager TcpManager { get; private set; } + public IServerManager ServerManager { get; private set; } /// /// Gets the UDP server port number. @@ -202,15 +201,6 @@ namespace MediaBrowser.Common.Kernel /// The kernel context. public abstract KernelContext KernelContext { get; } - /// - /// Gets the log file path. - /// - /// The log file path. - public string LogFilePath - { - get { return ApplicationHost.LogFilePath; } - } - /// /// Gets the logger. /// @@ -238,23 +228,6 @@ namespace MediaBrowser.Common.Kernel /// isoManager 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(); + ServerManager.Start(); } /// @@ -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 /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected virtual void Dispose(bool dispose) { - if (dispose) - { - DisposeTcpManager(); - } - } - /// - /// Disposes the TCP manager. - /// - private void DisposeTcpManager() - { - if (TcpManager != null) - { - TcpManager.Dispose(); - TcpManager = null; - } } /// @@ -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 5374bb7140..6a44cf3728 100644 --- a/MediaBrowser.Common/Kernel/BasePeriodicWebSocketListener.cs +++ b/MediaBrowser.Common/Kernel/BasePeriodicWebSocketListener.cs @@ -19,8 +19,8 @@ namespace MediaBrowser.Common.Kernel /// /// The _active connections /// - protected readonly List> ActiveConnections = - new List>(); + protected readonly List> ActiveConnections = + new List>(); /// /// Gets the name. @@ -103,7 +103,7 @@ namespace MediaBrowser.Common.Kernel lock (ActiveConnections) { - ActiveConnections.Add(new Tuple(message.Connection, cancellationTokenSource, timer, state, semaphore)); + ActiveConnections.Add(new Tuple(message.Connection, cancellationTokenSource, timer, state, semaphore)); } timer.Change(TimeSpan.FromMilliseconds(dueTimeMs), TimeSpan.FromMilliseconds(periodMs)); @@ -115,9 +115,9 @@ namespace MediaBrowser.Common.Kernel /// The state. private async void TimerCallback(object state) { - var connection = (WebSocketConnection)state; + var connection = (IWebSocketConnection)state; - Tuple tuple; + Tuple tuple; lock (ActiveConnections) { @@ -187,7 +187,7 @@ namespace MediaBrowser.Common.Kernel /// Disposes the connection. /// /// The connection. - private void DisposeConnection(Tuple connection) + private void DisposeConnection(Tuple 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 af9b039bc0..1f99d10dbc 100644 --- a/MediaBrowser.Common/Kernel/IApplicationHost.cs +++ b/MediaBrowser.Common/Kernel/IApplicationHost.cs @@ -97,5 +97,10 @@ namespace MediaBrowser.Common.Kernel /// /// ``0. T TryResolve(); + + /// + /// Shuts down. + /// + void Shutdown(); } } diff --git a/MediaBrowser.Common/Kernel/IKernel.cs b/MediaBrowser.Common/Kernel/IKernel.cs index 06c2e7b64c..a28500eca7 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; @@ -71,12 +70,6 @@ namespace MediaBrowser.Common.Kernel /// The name of the web application. string WebApplicationName { get; } - /// - /// Gets the log file path. - /// - /// The log file path. - string LogFilePath { get; } - /// /// Performs the pending restart. /// @@ -104,7 +97,7 @@ namespace MediaBrowser.Common.Kernel /// Gets the TCP manager. /// /// The TCP manager. - TcpManager TcpManager { get; } + IServerManager ServerManager { get; } /// /// Gets the web socket listeners. diff --git a/MediaBrowser.Common/Kernel/IServerManager.cs b/MediaBrowser.Common/Kernel/IServerManager.cs new file mode 100644 index 0000000000..cfea6eee11 --- /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 + { + /// + /// Gets a value indicating whether [supports web socket]. + /// + /// true if [supports web socket]; otherwise, false. + bool SupportsNativeWebSocket { get; } + + /// + /// Gets the web socket port number. + /// + /// The web socket port number. + int WebSocketPortNumber { get; } + + /// + /// Starts this instance. + /// + void Start(); + + /// + /// Sends a message to all clients currently connected via a web socket + /// + /// + /// Type of the message. + /// The data. + /// Task. + void SendWebSocketMessage(string messageType, T data); + + /// + /// Sends a message to all clients currently connected via a web socket + /// + /// + /// Type of the message. + /// The function that generates the data to send, if there are any connected clients + void SendWebSocketMessage(string messageType, Func dataFunction); + + /// + /// Sends a message to all clients currently connected via a web socket + /// + /// + /// Type of the message. + /// The function that generates the data to send, if there are any connected clients + /// The cancellation token. + /// Task. + /// messageType + Task SendWebSocketMessageAsync(string messageType, Func 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 d762dfaf76..0000000000 --- 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 2dfed501af..0000000000 --- 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 -{ - /// - /// Manages the Http Server, Udp Server and WebSocket connections - /// - public class TcpManager : IDisposable - { - /// - /// This is the udp server used for server discovery by clients - /// - /// The UDP server. - private IUdpServer UdpServer { get; set; } - - /// - /// 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. - /// - /// The HTTP server. - private IHttpServer HttpServer { get; set; } - - /// - /// Gets or sets the json serializer. - /// - /// The json serializer. - private readonly IJsonSerializer _jsonSerializer; - - /// - /// This subscribes to HttpListener requests and finds the appropriate BaseHandler to process it - /// - /// The HTTP listener. - private IDisposable HttpListener { get; set; } - - /// - /// The web socket connections - /// - private readonly List _webSocketConnections = new List(); - - /// - /// Gets or sets the external web socket server. - /// - /// The external web socket server. - private IWebSocketServer ExternalWebSocketServer { get; set; } - - /// - /// The _logger - /// - private readonly ILogger _logger; - - /// - /// The _network manager - /// - private readonly INetworkManager _networkManager; - - /// - /// The _application host - /// - private readonly IApplicationHost _applicationHost; - - /// - /// The _kernel - /// - private readonly IKernel _kernel; - - /// - /// Gets a value indicating whether [supports web socket]. - /// - /// true if [supports web socket]; otherwise, false. - internal bool SupportsNativeWebSocket - { - get { return HttpServer != null && HttpServer.SupportsWebSockets; } - } - - /// - /// Gets the web socket port number. - /// - /// The web socket port number. - public int WebSocketPortNumber - { - get { return SupportsNativeWebSocket ? _kernel.Configuration.HttpServerPortNumber : _kernel.Configuration.LegacyWebSocketPortNumber; } - } - - /// - /// Initializes a new instance of the class. - /// - /// The application host. - /// The kernel. - /// The network manager. - /// The json serializer. - /// The logger. - /// applicationHost - 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(); - } - } - - /// - /// Starts the external web socket server. - /// - private void ReloadExternalWebSocketServer() - { - // Avoid windows firewall prompts in the ui - if (_kernel.KernelContext != KernelContext.Server) - { - return; - } - - DisposeExternalWebSocketServer(); - - ExternalWebSocketServer = _applicationHost.Resolve(); - - ExternalWebSocketServer.Start(_kernel.Configuration.LegacyWebSocketPortNumber); - ExternalWebSocketServer.WebSocketConnected += HttpServer_WebSocketConnected; - } - - /// - /// Restarts the Http Server, or starts it if not currently running - /// - /// if set to true [register server on failure]. - 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(); - 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; - } - - /// - /// Handles the WebSocketConnected event of the HttpServer control. - /// - /// The source of the event. - /// The instance containing the event data. - void HttpServer_WebSocketConnected(object sender, WebSocketConnectEventArgs e) - { - var connection = new WebSocketConnection(e.WebSocket, e.Endpoint, _jsonSerializer, _logger) { OnReceive = ProcessWebSocketMessageReceived }; - - _webSocketConnections.Add(connection); - } - - /// - /// Processes the web socket message received. - /// - /// The result. - 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); - } - - /// - /// Starts or re-starts the udp server - /// - 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(); - UdpServer.Start(_kernel.UdpServerPortNumber); - } - catch (SocketException ex) - { - _logger.ErrorException("Failed to start UDP Server", ex); - return; - } - - UdpServer.MessageReceived += UdpServer_MessageReceived; - } - - /// - /// Handles the MessageReceived event of the UdpServer control. - /// - /// The source of the event. - /// The instance containing the event data. - 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); - } - } - - /// - /// Sends a message to all clients currently connected via a web socket - /// - /// - /// Type of the message. - /// The data. - /// Task. - public void SendWebSocketMessage(string messageType, T data) - { - SendWebSocketMessage(messageType, () => data); - } - - /// - /// Sends a message to all clients currently connected via a web socket - /// - /// - /// Type of the message. - /// The function that generates the data to send, if there are any connected clients - public void SendWebSocketMessage(string messageType, Func dataFunction) - { - Task.Run(async () => await SendWebSocketMessageAsync(messageType, dataFunction, CancellationToken.None).ConfigureAwait(false)); - } - - /// - /// Sends a message to all clients currently connected via a web socket - /// - /// - /// Type of the message. - /// The function that generates the data to send, if there are any connected clients - /// The cancellation token. - /// Task. - /// messageType - public async Task SendWebSocketMessageAsync(string messageType, Func 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 { 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); - } - } - - /// - /// Disposes the udp server - /// - private void DisposeUdpServer() - { - if (UdpServer != null) - { - UdpServer.MessageReceived -= UdpServer_MessageReceived; - UdpServer.Dispose(); - } - } - - /// - /// Disposes the current HttpServer - /// - 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(); - } - - /// - /// Registers the server with administrator access. - /// - 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(); - } - } - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Releases unmanaged and - optionally - managed resources. - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - protected virtual void Dispose(bool dispose) - { - if (dispose) - { - DisposeUdpServer(); - DisposeHttpServer(); - } - } - - /// - /// Disposes the external web socket server. - /// - private void DisposeExternalWebSocketServer() - { - if (ExternalWebSocketServer != null) - { - ExternalWebSocketServer.Dispose(); - } - } - - /// - /// Called when [application configuration changed]. - /// - /// The old config. - /// The new config. - public void OnApplicationConfigurationChanged(BaseApplicationConfiguration oldConfig, BaseApplicationConfiguration newConfig) - { - HttpServer.EnableHttpRequestLogging = newConfig.EnableHttpLevelLogging; - - if (oldConfig.HttpServerPortNumber != newConfig.HttpServerPortNumber) - { - ReloadHttpServer(); - } - - if (!SupportsNativeWebSocket && oldConfig.LegacyWebSocketPortNumber != newConfig.LegacyWebSocketPortNumber) - { - ReloadExternalWebSocketServer(); - } - } - } -} -- cgit v1.2.3