diff --git a/Example3/Program.cs b/Example3/Program.cs index a99bf230..4ca347bb 100644 --- a/Example3/Program.cs +++ b/Example3/Program.cs @@ -31,16 +31,11 @@ namespace Example3 onGet (e); }; - _httpsv.OnError += (sender, e) => - { - Console.WriteLine (e.Message); - }; - _httpsv.Start (); if (_httpsv.IsListening) { Console.WriteLine ("HTTP Server listening on port: {0} service path:", _httpsv.Port); - foreach (var path in _httpsv.ServicePaths) + foreach (var path in _httpsv.WebSocketServices.ServicePaths) Console.WriteLine (" {0}", path); Console.WriteLine (); diff --git a/websocket-sharp/Server/HttpServer.cs b/websocket-sharp/Server/HttpServer.cs index 5006a1bc..a3d8f651 100644 --- a/websocket-sharp/Server/HttpServer.cs +++ b/websocket-sharp/Server/HttpServer.cs @@ -46,15 +46,15 @@ namespace WebSocketSharp.Server { #region Private Fields - private HttpListener _listener; - private bool _listening; - private Logger _logger; - private int _port; - private Thread _receiveRequestThread; - private string _rootPath; - private bool _secure; - private ServiceHostManager _serviceHosts; - private bool _windows; + private HttpListener _listener; + private bool _listening; + private Logger _logger; + private int _port; + private Thread _receiveRequestThread; + private string _rootPath; + private bool _secure; + private WebSocketServiceHostManager _serviceHosts; + private bool _windows; #endregion @@ -133,10 +133,7 @@ namespace WebSocketSharp.Server set { if (_listening) { - var msg = "The value of Certificate property cannot be changed because the server has already been started."; - _logger.Error (msg); - error (msg); - + _logger.Error ("The value of Certificate property cannot be changed because the server has already been started."); return; } @@ -235,10 +232,7 @@ namespace WebSocketSharp.Server set { if (_listening) { - var msg = "The value of RootPath property cannot be changed because the server has already been started."; - _logger.Error (msg); - error (msg); - + _logger.Error ("The value of RootPath property cannot be changed because the server has already been started."); return; } @@ -247,14 +241,14 @@ namespace WebSocketSharp.Server } /// - /// Gets the collection of paths associated with the every WebSocket services that the server provides. + /// Gets the functions for the WebSocket services that the server provides. /// /// - /// An IEnumerable<string> that contains the collection of paths. + /// A that manages the WebSocket services. /// - public IEnumerable ServicePaths { + public WebSocketServiceHostManager WebSocketServices { get { - return _serviceHosts.Paths; + return _serviceHosts; } } @@ -272,11 +266,6 @@ namespace WebSocketSharp.Server /// public event EventHandler OnDelete; - /// - /// Occurs when the server gets an error. - /// - public event EventHandler OnError; - /// /// Occurs when the server receives an HTTP GET request. /// @@ -316,17 +305,12 @@ namespace WebSocketSharp.Server #region Private Methods - private void error (string message) - { - OnError.Emit (this, new ErrorEventArgs (message)); - } - private void init () { _listener = new HttpListener (); _listening = false; _logger = new Logger (); - _serviceHosts = new ServiceHostManager (_logger); + _serviceHosts = new WebSocketServiceHostManager (_logger); _windows = false; var os = Environment.OSVersion; @@ -435,7 +419,6 @@ namespace WebSocketSharp.Server } catch (Exception ex) { _logger.Fatal (ex.Message); - error ("An exception has occured."); } }; @@ -455,8 +438,6 @@ namespace WebSocketSharp.Server } catch (Exception ex) { _logger.Fatal (ex.Message); - error ("An exception has occured."); - break; } } @@ -476,10 +457,8 @@ namespace WebSocketSharp.Server var data = code.Append (reason); if (data.Length > 125) { - var msg = "The payload length of a Close frame must be 125 bytes or less."; - _logger.Error (String.Format ("{0}\ncode: {1}\nreason: {2}", msg, code, reason)); - error (msg); - + _logger.Error ( + String.Format ("The payload length of a Close frame must be 125 bytes or less.\ncode: {0}\nreason: {1}", code, reason)); return; } } @@ -514,8 +493,6 @@ namespace WebSocketSharp.Server if (!servicePath.IsValidAbsolutePath (out msg)) { _logger.Error (msg); - error (msg); - return; } @@ -561,10 +538,7 @@ namespace WebSocketSharp.Server { if (servicePath.IsNullOrEmpty ()) { - var msg = "'servicePath' must not be null or empty."; - _logger.Error (msg); - error (msg); - + _logger.Error ("'servicePath' must not be null or empty."); return false; } @@ -584,10 +558,7 @@ namespace WebSocketSharp.Server Certificate == null ) { - var msg = "Secure connection requires a server certificate."; - _logger.Error (msg); - error (msg); - + _logger.Error ("Secure connection requires a server certificate."); return; } @@ -624,10 +595,7 @@ namespace WebSocketSharp.Server if (!code.IsCloseStatusCode ()) { - var msg = "Invalid status code for stop."; - _logger.Error (String.Format ("{0}\ncode: {1}", msg, code)); - error (msg); - + _logger.Error ("Invalid status code for stop.\ncode: " + code); return; } diff --git a/websocket-sharp/Server/ServiceHostManager.cs b/websocket-sharp/Server/ServiceHostManager.cs deleted file mode 100644 index 5d4b760d..00000000 --- a/websocket-sharp/Server/ServiceHostManager.cs +++ /dev/null @@ -1,306 +0,0 @@ -#region License -/* - * ServiceHostManager.cs - * - * The MIT License - * - * Copyright (c) 2012-2013 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -using System; -using System.Collections.Generic; - -namespace WebSocketSharp.Server -{ - internal class ServiceHostManager - { - #region Private Fields - - private volatile bool _keepClean; - private Logger _logger; - private Dictionary _serviceHosts; - private object _sync; - - #endregion - - #region Public Constructors - - public ServiceHostManager () - : this (new Logger ()) - { - } - - public ServiceHostManager (Logger logger) - { - _logger = logger; - _keepClean = true; - _serviceHosts = new Dictionary (); - _sync = new object (); - } - - #endregion - - #region Public Properties - - public int ConnectionCount { - get { - var count = 0; - foreach (var host in ServiceHosts) - count += host.ConnectionCount; - - return count; - } - } - - public int Count { - get { - lock (_sync) - { - return _serviceHosts.Count; - } - } - } - - public bool KeepClean { - get { - return _keepClean; - } - - set { - lock (_sync) - { - if (_keepClean ^ value) - { - _keepClean = value; - foreach (var host in _serviceHosts.Values) - host.KeepClean = value; - } - } - } - } - - public IEnumerable Paths { - get { - lock (_sync) - { - return _serviceHosts.Keys; - } - } - } - - public IEnumerable ServiceHosts { - get { - lock (_sync) - { - return _serviceHosts.Values; - } - } - } - - #endregion - - #region Private Methods - - private Dictionary copy () - { - lock (_sync) - { - return new Dictionary (_serviceHosts); - } - } - - #endregion - - #region Public Methods - - public void Add (string servicePath, IServiceHost serviceHost) - { - lock (_sync) - { - IServiceHost host; - if (_serviceHosts.TryGetValue (servicePath, out host)) - { - _logger.Error ( - "The WebSocket service host with the specified path found.\npath: " + servicePath); - return; - } - - _serviceHosts.Add (servicePath.UrlDecode (), serviceHost); - } - } - - public void Broadcast (byte [] data) - { - foreach (var host in ServiceHosts) - host.Broadcast (data); - } - - public void Broadcast (string data) - { - foreach (var host in ServiceHosts) - host.Broadcast (data); - } - - public bool BroadcastTo (string servicePath, byte [] data) - { - IServiceHost host; - if (TryGetServiceHost (servicePath, out host)) - { - host.Broadcast (data); - return true; - } - - _logger.Error ( - "The WebSocket service host with the specified path not found.\npath: " + servicePath); - return false; - } - - public bool BroadcastTo (string servicePath, string data) - { - IServiceHost host; - if (TryGetServiceHost (servicePath, out host)) - { - host.Broadcast (data); - return true; - } - - _logger.Error ( - "The WebSocket service host with the specified path not found.\npath: " + servicePath); - return false; - } - - public Dictionary> Broadping (string message) - { - var result = new Dictionary> (); - foreach (var service in copy ()) - result.Add (service.Key, service.Value.Broadping (message)); - - return result; - } - - public Dictionary BroadpingTo (string servicePath, string message) - { - IServiceHost host; - if (TryGetServiceHost (servicePath, out host)) - return host.Broadping (message); - - _logger.Error ( - "The WebSocket service host with the specified path not found.\npath: " + servicePath); - return null; - } - - public int GetConnectionCount (string servicePath) - { - IServiceHost host; - if (TryGetServiceHost (servicePath, out host)) - return host.ConnectionCount; - - _logger.Error ( - "The WebSocket service host with the specified path not found.\npath: " + servicePath); - return -1; - } - - public bool PingTo (string servicePath, string id, string message) - { - IServiceHost host; - if (TryGetServiceHost (servicePath, out host)) - return host.PingTo (id, message); - - _logger.Error ( - "The WebSocket service host with the specified path not found.\npath: " + servicePath); - return false; - } - - public bool Remove (string servicePath) - { - IServiceHost host; - lock (_sync) - { - if (!_serviceHosts.TryGetValue (servicePath, out host)) - { - _logger.Error ( - "The WebSocket service host with the specified path not found.\npath: " + servicePath); - return false; - } - - _serviceHosts.Remove (servicePath); - } - - host.Stop ((ushort) CloseStatusCode.AWAY, String.Empty); - return true; - } - - public bool SendTo (string servicePath, string id, byte [] data) - { - IServiceHost host; - if (TryGetServiceHost (servicePath, out host)) - return host.SendTo (id, data); - - _logger.Error ( - "The WebSocket service host with the specified path not found.\npath: " + servicePath); - return false; - } - - public bool SendTo (string servicePath, string id, string data) - { - IServiceHost host; - if (TryGetServiceHost (servicePath, out host)) - return host.SendTo (id, data); - - _logger.Error ( - "The WebSocket service host with the specified path not found.\npath: " + servicePath); - return false; - } - - public void Stop () - { - lock (_sync) - { - foreach (var host in _serviceHosts.Values) - host.Stop (); - - _serviceHosts.Clear (); - } - } - - public void Stop (ushort code, string reason) - { - lock (_sync) - { - foreach (var host in _serviceHosts.Values) - host.Stop (code, reason); - - _serviceHosts.Clear (); - } - } - - public bool TryGetServiceHost (string servicePath, out IServiceHost serviceHost) - { - lock (_sync) - { - return _serviceHosts.TryGetValue (servicePath, out serviceHost); - } - } - - #endregion - } -} diff --git a/websocket-sharp/Server/WebSocketServer.cs b/websocket-sharp/Server/WebSocketServer.cs index c5597194..1677b7f7 100644 --- a/websocket-sharp/Server/WebSocketServer.cs +++ b/websocket-sharp/Server/WebSocketServer.cs @@ -47,7 +47,7 @@ namespace WebSocketSharp.Server { #region Private Fields - private ServiceHostManager _serviceHosts; + private WebSocketServiceHostManager _serviceHosts; #endregion @@ -86,7 +86,7 @@ namespace WebSocketSharp.Server if (BaseUri.AbsolutePath != "/") throw new ArgumentException ("Must not contain the path component: " + url, "url"); - _serviceHosts = new ServiceHostManager (Log); + _serviceHosts = new WebSocketServiceHostManager (Log); } /// @@ -138,25 +138,13 @@ namespace WebSocketSharp.Server public WebSocketServer (System.Net.IPAddress address, int port, bool secure) : base (address, port, "/", secure) { - _serviceHosts = new ServiceHostManager (Log); + _serviceHosts = new WebSocketServiceHostManager (Log); } #endregion #region Public Properties - /// - /// Gets the connection count to the . - /// - /// - /// An that contains the connection count. - /// - public int ConnectionCount { - get { - return _serviceHosts.ConnectionCount; - } - } - /// /// Gets or sets a value indicating whether the server cleans up the inactive WebSocket service /// instances periodically. @@ -176,7 +164,7 @@ namespace WebSocketSharp.Server } /// - /// Gets the collection of paths associated with the every WebSocket services that the server provides. + /// Gets the collection of paths to the WebSocket services that the server provides. /// /// /// An IEnumerable<string> that contains the collection of paths. @@ -187,11 +175,23 @@ namespace WebSocketSharp.Server ? BaseUri.ToString ().TrimEnd ('/') : String.Empty; - foreach (var path in _serviceHosts.Paths) + foreach (var path in _serviceHosts.ServicePaths) yield return url + path; } } + /// + /// Gets the functions for the WebSocket services that the server provides. + /// + /// + /// A that manages the WebSocket services. + /// + public WebSocketServiceHostManager WebSocketServices { + get { + return _serviceHosts; + } + } + #endregion #region Private Methods @@ -201,10 +201,8 @@ namespace WebSocketSharp.Server var data = code.Append (reason); if (data.Length > 125) { - var msg = "The payload length of a Close frame must be 125 bytes or less."; - Log.Error (String.Format ("{0}\ncode: {1}\nreason: {2}", msg, code, reason)); - Error (msg); - + Log.Error (String.Format ( + "The payload length of a Close frame must be 125 bytes or less.\ncode: {0}\nreason: {1}", code, reason)); return; } @@ -224,19 +222,19 @@ namespace WebSocketSharp.Server /// protected override void AcceptWebSocket (TcpListenerWebSocketContext context) { - var ws = context.WebSocket; + var websocket = context.WebSocket; var path = context.Path.UrlDecode (); - ws.Log = Log; + websocket.Log = Log; IServiceHost host; if (!_serviceHosts.TryGetServiceHost (path, out host)) { - ws.Close (HttpStatusCode.NotImplemented); + websocket.Close (HttpStatusCode.NotImplemented); return; } if (BaseUri.IsAbsoluteUri) - ws.Url = new Uri (BaseUri, path); + websocket.Url = new Uri (BaseUri, path); host.BindWebSocket (context); } @@ -261,8 +259,6 @@ namespace WebSocketSharp.Server if (!servicePath.IsValidAbsolutePath (out msg)) { Log.Error (msg); - Error (msg); - return; } @@ -277,241 +273,6 @@ namespace WebSocketSharp.Server _serviceHosts.Add (servicePath, host); } - /// - /// Broadcasts the specified array of to all clients. - /// - /// - /// An array of to broadcast. - /// - public void Broadcast (byte [] data) - { - if (data == null) - { - var msg = "'data' must not be null."; - Log.Error (msg); - Error (msg); - - return; - } - - _serviceHosts.Broadcast (data); - } - - /// - /// Broadcasts the specified to all clients. - /// - /// - /// A to broadcast. - /// - public void Broadcast (string data) - { - if (data == null) - { - var msg = "'data' must not be null."; - Log.Error (msg); - Error (msg); - - return; - } - - _serviceHosts.Broadcast (data); - } - - /// - /// Broadcasts the specified array of to all clients of the WebSocket service - /// with the specified . - /// - /// - /// true if the WebSocket service is found; otherwise, false. - /// - /// - /// A that contains an absolute path to the WebSocket service to find. - /// - /// - /// An array of to broadcast. - /// - public bool BroadcastTo (string servicePath, byte [] data) - { - var msg = servicePath.IsNullOrEmpty () - ? "'servicePath' must not be null or empty." - : data == null - ? "'data' must not be null." - : String.Empty; - - if (msg.Length > 0) - { - Log.Error (msg); - Error (msg); - - return false; - } - - return _serviceHosts.BroadcastTo (servicePath, data); - } - - /// - /// Broadcasts the specified to all clients of the WebSocket service - /// with the specified . - /// - /// - /// true if the WebSocket service is found; otherwise, false. - /// - /// - /// A that contains an absolute path to the WebSocket service to find. - /// - /// - /// A to broadcast. - /// - public bool BroadcastTo (string servicePath, string data) - { - var msg = servicePath.IsNullOrEmpty () - ? "'servicePath' must not be null or empty." - : data == null - ? "'data' must not be null." - : String.Empty; - - if (msg.Length > 0) - { - Log.Error (msg); - Error (msg); - - return false; - } - - return _serviceHosts.BroadcastTo (servicePath, data); - } - - /// - /// Sends Pings with the specified to all clients. - /// - /// - /// A Dictionary<string, Dictionary<string, bool>> that contains the collection of - /// service paths and pairs of ID and value indicating whether the - /// received the Pongs from each clients in a time. - /// - /// - /// A that contains a message to send. - /// - public Dictionary> Broadping (string message) - { - if (message.IsNullOrEmpty ()) - return _serviceHosts.Broadping (String.Empty); - - var len = Encoding.UTF8.GetBytes (message).Length; - if (len > 125) - { - var msg = "The payload length of a Ping frame must be 125 bytes or less."; - Log.Error (msg); - Error (msg); - - return null; - } - - return _serviceHosts.Broadping (message); - } - - /// - /// Sends Pings with the specified to all clients of the WebSocket service - /// with the specified . - /// - /// - /// A Dictionary<string, bool> that contains the collection of session IDs and values - /// indicating whether the received the Pongs from each clients - /// in a time. If the WebSocket service is not found, returns . - /// - /// - /// A that contains an absolute path to the WebSocket service to find. - /// - /// - /// A that contains a message to send. - /// - public Dictionary BroadpingTo (string servicePath, string message) - { - if (message == null) - message = String.Empty; - - var msg = servicePath.IsNullOrEmpty () - ? "'servicePath' must not be null or empty." - : Encoding.UTF8.GetBytes (message).Length > 125 - ? "The payload length of a Ping frame must be 125 bytes or less." - : String.Empty; - - if (msg.Length > 0) - { - Log.Error (msg); - Error (msg); - - return null; - } - - return _serviceHosts.BroadpingTo (servicePath, message); - } - - /// - /// Gets the connection count to the WebSocket service with the specified . - /// - /// - /// An that contains the connection count if the WebSocket service is successfully found; - /// otherwise, -1. - /// - /// - /// A that contains an absolute path to the WebSocket service to find. - /// - public int GetConnectionCount (string servicePath) - { - if (servicePath.IsNullOrEmpty ()) - { - var msg = "'servicePath' must not be null or empty."; - Log.Error (msg); - Error (msg); - - return -1; - } - - return _serviceHosts.GetConnectionCount (servicePath); - } - - /// - /// Sends a Ping with the specified to the client associated with - /// the specified and . - /// - /// - /// true if the receives a Pong from the client in a time; - /// otherwise, false. - /// - /// - /// A that contains an absolute path to the WebSocket service to find. - /// - /// - /// A that contains an ID that represents the destination for the Ping. - /// - /// - /// A that contains a message to send. - /// - public bool PingTo (string servicePath, string id, string message) - { - if (message == null) - message = String.Empty; - - var msg = servicePath.IsNullOrEmpty () - ? "'servicePath' must not be null or empty." - : id.IsNullOrEmpty () - ? "'id' must not be null or empty." - : Encoding.UTF8.GetBytes (message).Length > 125 - ? "The payload length of a Ping frame must be 125 bytes or less." - : String.Empty; - - if (msg.Length > 0) - { - Log.Error (msg); - Error (msg); - - return false; - } - - return _serviceHosts.PingTo (servicePath, id, message); - } - /// /// Removes the WebSocket service with the specified . /// @@ -525,90 +286,13 @@ namespace WebSocketSharp.Server { if (servicePath.IsNullOrEmpty ()) { - var msg = "'servicePath' must not be null or empty."; - Log.Error (msg); - Error (msg); - + Log.Error ("'servicePath' must not be null or empty."); return false; } return _serviceHosts.Remove (servicePath); } - /// - /// Sends a binary data to the client associated with the specified and - /// . - /// - /// - /// true if the client is successfully found; otherwise, false. - /// - /// - /// A that contains an absolute path to the WebSocket service to find. - /// - /// - /// A that contains an ID that represents the destination for the data. - /// - /// - /// An array of that contains a binary data to send. - /// - public bool SendTo (string servicePath, string id, byte [] data) - { - var msg = servicePath.IsNullOrEmpty () - ? "'servicePath' must not be null or empty." - : id.IsNullOrEmpty () - ? "'id' must not be null or empty." - : data == null - ? "'data' must not be null." - : String.Empty; - - if (msg.Length > 0) - { - Log.Error (msg); - Error (msg); - - return false; - } - - return _serviceHosts.SendTo (servicePath, id, data); - } - - /// - /// Sends a text data to the client associated with the specified and - /// . - /// - /// - /// true if the client is successfully found; otherwise, false. - /// - /// - /// A that contains an absolute path to the WebSocket service to find. - /// - /// - /// A that contains an ID that represents the destination for the data. - /// - /// - /// A that contains a text data to send. - /// - public bool SendTo (string servicePath, string id, string data) - { - var msg = servicePath.IsNullOrEmpty () - ? "'servicePath' must not be null or empty." - : id.IsNullOrEmpty () - ? "'id' must not be null or empty." - : data == null - ? "'data' must not be null." - : String.Empty; - - if (msg.Length > 0) - { - Log.Error (msg); - Error (msg); - - return false; - } - - return _serviceHosts.SendTo (servicePath, id, data); - } - /// /// Stops receiving the WebSocket connection requests. /// @@ -632,10 +316,7 @@ namespace WebSocketSharp.Server { if (!code.IsCloseStatusCode ()) { - var msg = "Invalid status code for stop."; - Log.Error (String.Format ("{0}\ncode: {1}", msg, code)); - Error (msg); - + Log.Error ("Invalid status code for stop.\ncode: " + code); return; } diff --git a/websocket-sharp/Server/WebSocketServerBase.cs b/websocket-sharp/Server/WebSocketServerBase.cs index ce6a1a20..f1bea7a5 100644 --- a/websocket-sharp/Server/WebSocketServerBase.cs +++ b/websocket-sharp/Server/WebSocketServerBase.cs @@ -303,22 +303,8 @@ namespace WebSocketSharp.Server #endregion - #region Public Events - - /// - /// Occurs when the server gets an error. - /// - public event EventHandler OnError; - - #endregion - #region Private Methods - private void error (string message) - { - OnError.Emit (this, new ErrorEventArgs (message)); - } - private void init () { _listening = false; @@ -352,7 +338,6 @@ namespace WebSocketSharp.Server { client.Close (); _logger.Fatal (ex.Message); - error ("An exception has occured."); } }; @@ -372,8 +357,6 @@ namespace WebSocketSharp.Server } catch (Exception ex) { _logger.Fatal (ex.Message); - error ("An exception has occured."); - break; } } @@ -414,20 +397,6 @@ namespace WebSocketSharp.Server /// protected abstract void AcceptWebSocket (TcpListenerWebSocketContext context); - /// - /// Occurs the event with the specified . - /// - /// - /// A that contains an error message. - /// - protected virtual void Error (string message) - { - if (message.IsNullOrEmpty ()) - return; - - error (message); - } - #endregion #region Public Methods @@ -442,10 +411,7 @@ namespace WebSocketSharp.Server if (_secure && _cert == null) { - var msg = "Secure connection requires a server certificate."; - _logger.Error (msg); - error (msg); - + _logger.Error ("Secure connection requires a server certificate."); return; } diff --git a/websocket-sharp/Server/WebSocketServiceHost.cs b/websocket-sharp/Server/WebSocketServiceHost.cs index 6dbefe98..3409255e 100644 --- a/websocket-sharp/Server/WebSocketServiceHost.cs +++ b/websocket-sharp/Server/WebSocketServiceHost.cs @@ -246,10 +246,8 @@ namespace WebSocketSharp.Server var data = code.Append (reason); if (data.Length > 125) { - var msg = "The payload length of a Close frame must be 125 bytes or less."; - Log.Error (String.Format ("{0}\ncode: {1}\nreason: {2}", msg, code, reason)); - Error (msg); - + Log.Error (String.Format ( + "The payload length of a Close frame must be 125 bytes or less.\ncode: {0}\nreason: {1}", code, reason)); return; } @@ -299,10 +297,7 @@ namespace WebSocketSharp.Server { if (data == null) { - var msg = "'data' must not be null."; - Log.Error (msg); - Error (msg); - + Log.Error ("'data' must not be null."); return; } @@ -319,10 +314,7 @@ namespace WebSocketSharp.Server { if (data == null) { - var msg = "'data' must not be null."; - Log.Error (msg); - Error (msg); - + Log.Error ("'data' must not be null."); return; } @@ -347,10 +339,7 @@ namespace WebSocketSharp.Server var len = Encoding.UTF8.GetBytes (message).Length; if (len > 125) { - var msg = "The payload length of a Ping frame must be 125 bytes or less."; - Log.Error (msg); - Error (msg); - + Log.Error ("The payload length of a Ping frame must be 125 bytes or less."); return null; } @@ -379,13 +368,11 @@ namespace WebSocketSharp.Server ? "'id' must not be null or empty." : Encoding.UTF8.GetBytes (message).Length > 125 ? "The payload length of a Ping frame must be 125 bytes or less." - : String.Empty; + : null; - if (msg.Length > 0) + if (msg != null) { Log.Error (msg); - Error (msg); - return false; } @@ -411,13 +398,11 @@ namespace WebSocketSharp.Server ? "'id' must not be null or empty." : data == null ? "'data' must not be null." - : String.Empty; + : null; - if (msg.Length > 0) + if (msg != null) { Log.Error (msg); - Error (msg); - return false; } @@ -443,13 +428,11 @@ namespace WebSocketSharp.Server ? "'id' must not be null or empty." : data == null ? "'data' must not be null." - : String.Empty; + : null; - if (msg.Length > 0) + if (msg != null) { Log.Error (msg); - Error (msg); - return false; } @@ -479,10 +462,7 @@ namespace WebSocketSharp.Server { if (!code.IsCloseStatusCode ()) { - var msg = "Invalid status code for stop."; - Log.Error (String.Format ("{0}\ncode: {1}", msg, code)); - Error (msg); - + Log.Error ("Invalid status code for stop.\ncode: " + code); return; } diff --git a/websocket-sharp/Server/WebSocketServiceHostManager.cs b/websocket-sharp/Server/WebSocketServiceHostManager.cs new file mode 100644 index 00000000..c768b99c --- /dev/null +++ b/websocket-sharp/Server/WebSocketServiceHostManager.cs @@ -0,0 +1,574 @@ +#region License +/* + * WebSocketServiceHostManager.cs + * + * The MIT License + * + * Copyright (c) 2012-2013 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Text; + +namespace WebSocketSharp.Server +{ + /// + /// Manages the collection of the WebSocket service hosts. + /// + public class WebSocketServiceHostManager + { + #region Private Fields + + private volatile bool _keepClean; + private Logger _logger; + private Dictionary _serviceHosts; + private object _sync; + + #endregion + + #region Internal Constructors + + internal WebSocketServiceHostManager () + : this (new Logger ()) + { + } + + internal WebSocketServiceHostManager (Logger logger) + { + _logger = logger; + _keepClean = true; + _serviceHosts = new Dictionary (); + _sync = new object (); + } + + #endregion + + #region Public Properties + + /// + /// Gets the connection count to the WebSocket services managed by the . + /// + /// + /// An that contains the connection count. + /// + public int ConnectionCount { + get { + var count = 0; + foreach (var host in ServiceHosts) + count += host.ConnectionCount; + + return count; + } + } + + /// + /// Gets the number of the WebSocket services managed by the . + /// + /// + /// An that contains the number of the WebSocket services. + /// + public int ServiceCount { + get { + lock (_sync) + { + return _serviceHosts.Count; + } + } + } + + /// + /// Gets the collection of paths to the WebSocket services managed by the . + /// + /// + /// An IEnumerable<string> that contains the collection of paths. + /// + public IEnumerable ServicePaths { + get { + lock (_sync) + { + return _serviceHosts.Keys; + } + } + } + + #endregion + + #region Internal Properties + + internal bool KeepClean { + get { + return _keepClean; + } + + set { + lock (_sync) + { + if (_keepClean ^ value) + { + _keepClean = value; + foreach (var host in _serviceHosts.Values) + host.KeepClean = value; + } + } + } + } + + internal IEnumerable ServiceHosts { + get { + lock (_sync) + { + return _serviceHosts.Values; + } + } + } + + #endregion + + #region Private Methods + + private Dictionary copy () + { + lock (_sync) + { + return new Dictionary (_serviceHosts); + } + } + + #endregion + + #region Internal Methods + + internal void Add (string servicePath, IServiceHost serviceHost) + { + lock (_sync) + { + IServiceHost host; + if (_serviceHosts.TryGetValue (servicePath, out host)) + { + _logger.Error ( + "The WebSocket service host with the specified path already exists.\npath: " + servicePath); + return; + } + + _serviceHosts.Add (servicePath.UrlDecode (), serviceHost); + } + } + + internal bool Remove (string servicePath) + { + IServiceHost host; + lock (_sync) + { + if (!_serviceHosts.TryGetValue (servicePath, out host)) + { + _logger.Error ( + "The WebSocket service host with the specified path not found.\npath: " + servicePath); + return false; + } + + _serviceHosts.Remove (servicePath); + } + + host.Stop ((ushort) CloseStatusCode.AWAY, String.Empty); + return true; + } + + internal void Stop () + { + lock (_sync) + { + foreach (var host in _serviceHosts.Values) + host.Stop (); + + _serviceHosts.Clear (); + } + } + + internal void Stop (ushort code, string reason) + { + lock (_sync) + { + foreach (var host in _serviceHosts.Values) + host.Stop (code, reason); + + _serviceHosts.Clear (); + } + } + + internal bool TryGetServiceHost (string servicePath, out IServiceHost serviceHost) + { + lock (_sync) + { + return _serviceHosts.TryGetValue (servicePath, out serviceHost); + } + } + + #endregion + + #region Public Methods + + /// + /// Broadcasts the specified array of to all clients of the WebSocket services. + /// + /// + /// An array of to broadcast. + /// + public void Broadcast (byte [] data) + { + if (data == null) + { + _logger.Error ("'data' must not be null."); + return; + } + + foreach (var host in ServiceHosts) + host.Broadcast (data); + } + + /// + /// Broadcasts the specified to all clients of the WebSocket services. + /// + /// + /// A to broadcast. + /// + public void Broadcast (string data) + { + if (data == null) + { + _logger.Error ("'data' must not be null."); + return; + } + + foreach (var host in ServiceHosts) + host.Broadcast (data); + } + + /// + /// Broadcasts the specified array of to all clients of the WebSocket service + /// with the specified . + /// + /// + /// true if is broadcasted; otherwise, false. + /// + /// + /// An array of to broadcast. + /// + /// + /// A that contains an absolute path to the WebSocket service to find. + /// + public bool BroadcastTo (byte [] data, string servicePath) + { + var msg = data == null + ? "'data' must not be null." + : servicePath.IsNullOrEmpty () + ? "'servicePath' must not be null or empty." + : null; + + if (msg != null) + { + _logger.Error (msg); + return false; + } + + IServiceHost host; + if (!TryGetServiceHost (servicePath, out host)) + { + _logger.Error ("The WebSocket service with the specified path not found.\npath: " + servicePath); + return false; + } + + host.Broadcast (data); + return true; + } + + /// + /// Broadcasts the specified to all clients of the WebSocket service + /// with the specified . + /// + /// + /// true if is broadcasted; otherwise, false. + /// + /// + /// A to broadcast. + /// + /// + /// A that contains an absolute path to the WebSocket service to find. + /// + public bool BroadcastTo (string data, string servicePath) + { + var msg = data == null + ? "'data' must not be null." + : servicePath.IsNullOrEmpty () + ? "'servicePath' must not be null or empty." + : null; + + if (msg != null) + { + _logger.Error (msg); + return false; + } + + IServiceHost host; + if (!TryGetServiceHost (servicePath, out host)) + { + _logger.Error ("The WebSocket service with the specified path not found.\npath: " + servicePath); + return false; + } + + host.Broadcast (data); + return true; + } + + /// + /// Sends Pings with the specified to all clients of the WebSocket services. + /// + /// + /// A Dictionary<string, Dictionary<string, bool>> that contains the collection of + /// service paths and pairs of session ID and value indicating whether each WebSocket service + /// received the Pong from each client in a time. + /// + /// + /// A that contains a message to send. + /// + public Dictionary> Broadping (string message) + { + if (!message.IsNullOrEmpty ()) + { + var len = Encoding.UTF8.GetBytes (message).Length; + if (len > 125) + { + _logger.Error ("The payload length of a Ping frame must be 125 bytes or less."); + return null; + } + } + + var result = new Dictionary> (); + foreach (var service in copy ()) + result.Add (service.Key, service.Value.Broadping (message)); + + return result; + } + + /// + /// Sends Pings with the specified to all clients of the WebSocket service + /// with the specified . + /// + /// + /// A Dictionary<string, bool> that contains the collection of pairs of session ID and value + /// indicating whether the WebSocket service received the Pong from each client in a time. + /// If the WebSocket service is not found, returns . + /// + /// + /// A that contains a message to send. + /// + /// + /// A that contains an absolute path to the WebSocket service to find. + /// + public Dictionary BroadpingTo (string message, string servicePath) + { + if (message == null) + message = String.Empty; + + var msg = Encoding.UTF8.GetBytes (message).Length > 125 + ? "The payload length of a Ping frame must be 125 bytes or less." + : servicePath.IsNullOrEmpty () + ? "'servicePath' must not be null or empty." + : null; + + if (msg != null) + { + _logger.Error (msg); + return null; + } + + IServiceHost host; + if (!TryGetServiceHost (servicePath, out host)) + { + _logger.Error ("The WebSocket service with the specified path not found.\npath: " + servicePath); + return null; + } + + return host.Broadping (message); + } + + /// + /// Gets the connection count to the WebSocket service with the specified . + /// + /// + /// An that contains the connection count if the WebSocket service is successfully found; + /// otherwise, -1. + /// + /// + /// A that contains an absolute path to the WebSocket service to find. + /// + public int GetConnectionCount (string servicePath) + { + if (servicePath.IsNullOrEmpty ()) + { + _logger.Error ("'servicePath' must not be null or empty."); + return -1; + } + + IServiceHost host; + if (!TryGetServiceHost (servicePath, out host)) + { + _logger.Error ("The WebSocket service with the specified path not found.\npath: " + servicePath); + return -1; + } + + return host.ConnectionCount; + } + + /// + /// Sends a Ping with the specified to the client associated with + /// the specified and . + /// + /// + /// true if the WebSocket service with receives a Pong + /// from the client in a time; otherwise, false. + /// + /// + /// A that contains a message to send. + /// + /// + /// A that contains an ID that represents the destination for the Ping. + /// + /// + /// A that contains an absolute path to the WebSocket service to find. + /// + public bool PingTo (string message, string id, string servicePath) + { + if (message == null) + message = String.Empty; + + var msg = Encoding.UTF8.GetBytes (message).Length > 125 + ? "The payload length of a Ping frame must be 125 bytes or less." + : id.IsNullOrEmpty () + ? "'id' must not be null or empty." + : servicePath.IsNullOrEmpty () + ? "'servicePath' must not be null or empty." + : null; + + if (msg != null) + { + _logger.Error (msg); + return false; + } + + IServiceHost host; + if (!TryGetServiceHost (servicePath, out host)) + { + _logger.Error ("The WebSocket service with the specified path not found.\npath: " + servicePath); + return false; + } + + return host.PingTo (id, message); + } + + /// + /// Sends a binary data to the client associated with the specified and + /// . + /// + /// + /// true if is successfully sent; otherwise, false. + /// + /// + /// An array of that contains a binary data to send. + /// + /// + /// A that contains an ID that represents the destination for the data. + /// + /// + /// A that contains an absolute path to the WebSocket service to find. + /// + public bool SendTo (byte [] data, string id, string servicePath) + { + var msg = data == null + ? "'data' must not be null." + : id.IsNullOrEmpty () + ? "'id' must not be null or empty." + : servicePath.IsNullOrEmpty () + ? "'servicePath' must not be null or empty." + : null; + + if (msg != null) + { + _logger.Error (msg); + return false; + } + + IServiceHost host; + if (!TryGetServiceHost (servicePath, out host)) + { + _logger.Error ("The WebSocket service with the specified path not found.\npath: " + servicePath); + return false; + } + + return host.SendTo (id, data); + } + + /// + /// Sends a text data to the client associated with the specified and + /// . + /// + /// + /// true if is successfully sent; otherwise, false. + /// + /// + /// A that contains a text data to send. + /// + /// + /// A that contains an ID that represents the destination for the data. + /// + /// + /// A that contains an absolute path to the WebSocket service to find. + /// + public bool SendTo (string data, string id, string servicePath) + { + var msg = data == null + ? "'data' must not be null." + : id.IsNullOrEmpty () + ? "'id' must not be null or empty." + : servicePath.IsNullOrEmpty () + ? "'servicePath' must not be null or empty." + : null; + + if (msg != null) + { + _logger.Error (msg); + return false; + } + + IServiceHost host; + if (!TryGetServiceHost (servicePath, out host)) + { + _logger.Error ("The WebSocket service with the specified path not found.\npath: " + servicePath); + return false; + } + + return host.SendTo (id, data); + } + + #endregion + } +} diff --git a/websocket-sharp/websocket-sharp.csproj b/websocket-sharp/websocket-sharp.csproj index 277d585c..cc1147e0 100644 --- a/websocket-sharp/websocket-sharp.csproj +++ b/websocket-sharp/websocket-sharp.csproj @@ -112,7 +112,6 @@ - @@ -128,6 +127,7 @@ +