diff --git a/websocket-sharp/Ext.cs b/websocket-sharp/Ext.cs index 67b93795..fc8eabb9 100644 --- a/websocket-sharp/Ext.cs +++ b/websocket-sharp/Ext.cs @@ -164,6 +164,59 @@ namespace WebSocketSharp } } + internal static string CheckIfValidCloseData (this byte [] data) + { + return data.Length > 125 + ? "The payload length of a Close frame must be 125 bytes or less." + : null; + } + + internal static string CheckIfValidCloseStatusCode (this ushort code) + { + return !code.IsCloseStatusCode () + ? "Invalid close status code." + : null; + } + + internal static string CheckIfValidPingMessage (this string message) + { + return message != null && message.Length > 0 && Encoding.UTF8.GetBytes (message).Length > 125 + ? "The payload length of a Ping frame must be 125 bytes or less." + : null; + } + + internal static string CheckIfValidSendData (this byte [] data) + { + return data == null + ? "'data' must not be null." + : null; + } + + internal static string CheckIfValidSendData (this string data) + { + return data == null + ? "'data' must not be null." + : null; + } + + internal static string CheckIfValidServicePath (this string servicePath) + { + return servicePath == null || servicePath.Length == 0 + ? "'servicePath' must not be null or empty." + : servicePath [0] != '/' + ? "'servicePath' not absolute path." + : servicePath.IndexOfAny (new [] {'?', '#'}) != -1 + ? "'servicePath' must not contain either or both query and fragment components." + : null; + } + + internal static string CheckIfValidSessionID (this string id) + { + return id == null || id.Length == 0 + ? "'id' must not be null or empty." + : null; + } + internal static byte [] Compress (this byte [] value, CompressionMethod method) { return method == CompressionMethod.DEFLATE @@ -230,26 +283,26 @@ namespace WebSocketSharp return value == method.ToCompressionExtension (); } - // - // Determines whether the specified equals the specified , - // and invokes the specified Action<int> delegate at the same time. - // - // - // true if equals ; otherwise, false. - // - // - // An to compare. - // - // - // A to compare. - // - // - // An Action<int> delegate that references the method(s) called at the same time as when comparing. - // An parameter to pass to the method(s) is . - // - // - // is less than 0, or greater than 255. - // + /// + /// Determines whether the specified equals the specified , + /// and invokes the specified Action<int> delegate at the same time. + /// + /// + /// true if equals ; otherwise, false. + /// + /// + /// An to compare. + /// + /// + /// A to compare. + /// + /// + /// An Action<int> delegate that references the method(s) called at the same time as when comparing. + /// An parameter to pass to the method(s) is . + /// + /// + /// is less than 0, or greater than 255. + /// internal static bool EqualsWith (this int value, char c, Action action) { if (value < 0 || value > 255) @@ -259,6 +312,31 @@ namespace WebSocketSharp return value == c - 0; } + /// + /// Gets the absolute path from the specified . + /// + /// + /// A that contains the absolute path if it is successfully found; + /// otherwise, . + /// + /// + /// A that contains a URI to get the absolute path from. + /// + internal static string GetAbsolutePath (this Uri uri) + { + if (uri.IsAbsoluteUri) + return uri.AbsolutePath; + + var original = uri.OriginalString; + if (original [0] != '/') + return null; + + var i = original.IndexOfAny (new [] {'?', '#'}); + return i > 0 + ? original.Substring (0, i) + : original; + } + internal static string GetNameInternal (this string nameAndValue, string separator) { int i = nameAndValue.IndexOf (separator); @@ -498,6 +576,14 @@ namespace WebSocketSharp return CompressionMethod.NONE; } + internal static string TrimEndSlash (this string value) + { + value = value.TrimEnd ('/'); + return value.Length > 0 + ? value + : "/"; + } + internal static string Unquote (this string value) { var start = value.IndexOf ('\"'); @@ -536,7 +622,7 @@ namespace WebSocketSharp /// public static bool Contains (this string value, params char [] chars) { - return chars.Length == 0 + return chars == null || chars.Length == 0 ? true : value == null || value.Length == 0 ? false @@ -639,35 +725,6 @@ namespace WebSocketSharp eventHandler (sender, e); } - /// - /// Gets the absolute path from the specified . - /// - /// - /// A that contains the absolute path if gets successfully; - /// otherwise, . - /// - /// - /// A that contains a URI to get the absolute path from. - /// - public static string GetAbsolutePath (this Uri uri) - { - if (uri == null) - return null; - - if (uri.IsAbsoluteUri) - return uri.AbsolutePath; - - var uriString = uri.OriginalString; - var i = uriString.IndexOf ('/'); - if (i != 0) - return null; - - i = uriString.IndexOfAny (new [] {'?', '#'}); - return i > 0 - ? uriString.Substring (0, i) - : uriString; - } - /// /// Gets the collection of cookies from the specified . /// @@ -851,25 +908,23 @@ namespace WebSocketSharp /// /// /// - /// true if is in the allowable range of the WebSocket close status code; otherwise, false. + /// true if is in the allowable range of the WebSocket close status code; + /// otherwise, false. /// - /// + /// /// A to test. /// - public static bool IsCloseStatusCode (this ushort code) + public static bool IsCloseStatusCode (this ushort value) { - return code < 1000 - ? false - : code > 4999 - ? false - : true; + return value > 999 && value < 5000; } /// /// Determines whether the specified is enclosed in the specified . /// /// - /// true if is enclosed in ; otherwise, false. + /// true if is enclosed in ; + /// otherwise, false. /// /// /// A to test. @@ -879,9 +934,10 @@ namespace WebSocketSharp /// public static bool IsEnclosedIn (this string value, char c) { - return value == null || value.Length == 0 - ? false - : value [0] == c && value [value.Length - 1] == c; + return value != null && + value.Length > 1 && + value [0] == c && + value [value.Length - 1] == c; } /// @@ -1021,44 +1077,6 @@ namespace WebSocketSharp request.Headers.Contains ("Connection", "Upgrade"); } - /// - /// Determines whether the specified is valid absolute path. - /// - /// - /// true if is valid absolute path; otherwise, false. - /// - /// - /// A to test. - /// - /// - /// A that receives a message if is invalid. - /// - public static bool IsValidAbsolutePath (this string absPath, out string message) - { - if (absPath == null || absPath.Length == 0) - { - message = "Must not be null or empty."; - return false; - } - - var i = absPath.IndexOf ('/'); - if (i != 0) - { - message = "Not absolute path: " + absPath; - return false; - } - - i = absPath.IndexOfAny (new [] {'?', '#'}); - if (i != -1) - { - message = "Must not contain either or both query and fragment components: " + absPath; - return false; - } - - message = String.Empty; - return true; - } - /// /// Determines whether the specified is a URI string. /// diff --git a/websocket-sharp/Server/HttpServer.cs b/websocket-sharp/Server/HttpServer.cs index d9f0c200..bff346be 100644 --- a/websocket-sharp/Server/HttpServer.cs +++ b/websocket-sharp/Server/HttpServer.cs @@ -134,7 +134,8 @@ namespace WebSocketSharp.Server set { if (_listening) { - _logger.Error ("The value of Certificate property cannot be changed because the server has already been started."); + _logger.Error ( + "The value of Certificate property cannot be changed because the server has already been started."); return; } @@ -233,7 +234,8 @@ namespace WebSocketSharp.Server set { if (_listening) { - _logger.Error ("The value of RootPath property cannot be changed because the server has already been started."); + _logger.Error ( + "The value of RootPath property cannot be changed because the server has already been started."); return; } @@ -386,10 +388,9 @@ namespace WebSocketSharp.Server private bool processWebSocketRequest (HttpListenerContext context) { var wsContext = context.AcceptWebSocket (); - var path = wsContext.Path.UrlDecode (); IWebSocketServiceHost host; - if (!_serviceHosts.TryGetServiceHost (path, out host)) + if (!_serviceHosts.TryGetServiceHost (wsContext.Path, out host)) { context.Response.StatusCode = (int) HttpStatusCode.NotImplemented; return false; @@ -451,26 +452,19 @@ namespace WebSocketSharp.Server _receiveRequestThread.Start (); } - private void stop (ushort code, string reason, bool ignoreArgs) + private void stop (ushort code, string reason) { - if (!ignoreArgs) + var data = code.Append (reason); + var msg = data.CheckIfValidCloseData (); + if (msg != null) { - var data = code.Append (reason); - if (data.Length > 125) - { - _logger.Error ( - String.Format ("The payload length of a Close frame must be 125 bytes or less.\ncode: {0}\nreason: {1}", code, reason)); - return; - } + _logger.Error (String.Format ("{0}\ncode: {1}\nreason: {2}", msg, code, reason)); + return; } _listener.Close (); _receiveRequestThread.Join (5 * 1000); - if (ignoreArgs) - _serviceHosts.Stop (); - else - _serviceHosts.Stop (code, reason); - + _serviceHosts.Stop (code, reason); _listening = false; } @@ -481,6 +475,10 @@ namespace WebSocketSharp.Server /// /// Adds the specified typed WebSocket service with the specified . /// + /// + /// This method converts to URL-decoded string and + /// removes '/' from tail end of . + /// /// /// A that contains an absolute path to the WebSocket service. /// @@ -490,10 +488,10 @@ namespace WebSocketSharp.Server public void AddWebSocketService (string servicePath) where T : WebSocketService, new () { - string msg; - if (!servicePath.IsValidAbsolutePath (out msg)) + var msg = servicePath.CheckIfValidServicePath (); + if (msg != null) { - _logger.Error (msg); + _logger.Error (String.Format ("{0}\nservice path: {1}", msg, servicePath ?? "")); return; } @@ -529,6 +527,10 @@ namespace WebSocketSharp.Server /// /// Removes the WebSocket service with the specified . /// + /// + /// This method converts to URL-decoded string and + /// removes '/' from tail end of . + /// /// /// true if the WebSocket service is successfully found and removed; otherwise, false. /// @@ -537,9 +539,10 @@ namespace WebSocketSharp.Server /// public bool RemoveWebSocketService (string servicePath) { - if (servicePath.IsNullOrEmpty ()) + var msg = servicePath.CheckIfValidServicePath (); + if (msg != null) { - _logger.Error ("'servicePath' must not be null or empty."); + _logger.Error (String.Format ("{0}\nservice path: {1}", msg, servicePath ?? "")); return false; } @@ -576,7 +579,10 @@ namespace WebSocketSharp.Server if (!_listening) return; - stop (0, null, true); + _listener.Close (); + _receiveRequestThread.Join (5 * 1000); + _serviceHosts.Stop (); + _listening = false; } /// @@ -594,13 +600,14 @@ namespace WebSocketSharp.Server if (!_listening) return; - if (!code.IsCloseStatusCode ()) + var msg = code.CheckIfValidCloseStatusCode (); + if (msg != null) { - _logger.Error ("Invalid status code for stop.\ncode: " + code); + _logger.Error (String.Format ("{0}\ncode: {1}", msg, code)); return; } - stop (code, reason, false); + stop (code, reason); } /// @@ -618,7 +625,7 @@ namespace WebSocketSharp.Server if (!_listening) return; - stop ((ushort) code, reason, false); + stop ((ushort) code, reason); } #endregion diff --git a/websocket-sharp/Server/IWebSocketServiceHost.cs b/websocket-sharp/Server/IWebSocketServiceHost.cs index d7c1c00a..ad98dc00 100644 --- a/websocket-sharp/Server/IWebSocketServiceHost.cs +++ b/websocket-sharp/Server/IWebSocketServiceHost.cs @@ -79,22 +79,81 @@ namespace WebSocketSharp.Server /// void Broadcast (string data); + /// + /// Sends Pings to all clients of the WebSocket service host. + /// + /// + /// A Dictionary<string, bool> that contains the collection of pairs of session ID and value + /// indicating whether the WebSocket service host received a Pong from each client in a time. + /// + Dictionary Broadping (); + /// /// Sends Pings with the specified to all clients of /// the WebSocket service host. /// /// /// A Dictionary<string, bool> that contains the collection of pairs of session ID and value - /// indicating whether the WebSocket service host received the Pong from each client in a time. + /// indicating whether the WebSocket service host received a Pong from each client in a time. /// /// /// A that contains a message to send. /// Dictionary Broadping (string message); + /// + /// Close the WebSocket session with the specified . + /// + /// + /// A that contains a session ID to find. + /// + void CloseSession (string id); + + /// + /// Close the WebSocket session with the specified , + /// and . + /// + /// + /// A that contains a status code indicating the reason for closure. + /// + /// + /// A that contains the reason for closure. + /// + /// + /// A that contains a session ID to find. + /// + void CloseSession (ushort code, string reason, string id); + + /// + /// Close the WebSocket session with the specified , + /// and . + /// + /// + /// A that contains a status code indicating the reason for closure. + /// + /// + /// A that contains the reason for closure. + /// + /// + /// A that contains a session ID to find. + /// + void CloseSession (CloseStatusCode code, string reason, string id); + + /// + /// Sends a Ping to the client associated with the specified . + /// + /// + /// true if the WebSocket service host receives a Pong from the client in a time; + /// otherwise, false. + /// + /// + /// A that contains a session ID that represents the destination for the Ping. + /// + bool PingTo (string id); + /// /// Sends a Ping with the specified to the client associated with - /// the specified ID. + /// the specified . /// /// /// true if the WebSocket service host receives a Pong from the client in a time; @@ -104,12 +163,12 @@ namespace WebSocketSharp.Server /// A that contains a message to send. /// /// - /// A that contains an ID that represents the destination for the Ping. + /// A that contains a session ID that represents the destination for the Ping. /// bool PingTo (string message, string id); /// - /// Sends a binary data to the client associated with the specified ID. + /// Sends a binary data to the client associated with the specified . /// /// /// true if is successfully sent; otherwise, false. @@ -118,12 +177,12 @@ namespace WebSocketSharp.Server /// 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 a session ID that represents the destination for the data. /// bool SendTo (byte [] data, string id); /// - /// Sends a text data to the client associated with the specified ID. + /// Sends a text data to the client associated with the specified . /// /// /// true if is successfully sent; otherwise, false. @@ -132,15 +191,10 @@ namespace WebSocketSharp.Server /// A that contains a text data to send. /// /// - /// A that contains an ID that represents the destination for the data. + /// A that contains a session ID that represents the destination for the data. /// bool SendTo (string data, string id); - /// - /// Starts the WebSocket service host. - /// - void Start (); - /// /// Stops the WebSocket service host. /// diff --git a/websocket-sharp/Server/WebSocketServer.cs b/websocket-sharp/Server/WebSocketServer.cs index f0e9d91f..2917ab04 100644 --- a/websocket-sharp/Server/WebSocketServer.cs +++ b/websocket-sharp/Server/WebSocketServer.cs @@ -199,10 +199,10 @@ namespace WebSocketSharp.Server private void stop (ushort code, string reason) { var data = code.Append (reason); - if (data.Length > 125) + var msg = data.CheckIfValidCloseData (); + if (msg != null) { - Log.Error (String.Format ( - "The payload length of a Close frame must be 125 bytes or less.\ncode: {0}\nreason: {1}", code, reason)); + Log.Error (String.Format ("{0}\ncode: {1}\nreason: {2}", msg, code, reason)); return; } @@ -223,9 +223,9 @@ namespace WebSocketSharp.Server protected override void AcceptWebSocket (TcpListenerWebSocketContext context) { var websocket = context.WebSocket; - var path = context.Path.UrlDecode (); - websocket.Log = Log; + + var path = context.Path; IWebSocketServiceHost host; if (!_serviceHosts.TryGetServiceHost (path, out host)) { @@ -246,6 +246,10 @@ namespace WebSocketSharp.Server /// /// Adds the specified typed WebSocket service with the specified . /// + /// + /// This method converts to URL-decoded string and + /// removes '/' from tail end of . + /// /// /// A that contains an absolute path to the WebSocket service. /// @@ -255,10 +259,10 @@ namespace WebSocketSharp.Server public void AddWebSocketService (string servicePath) where T : WebSocketService, new () { - string msg; - if (!servicePath.IsValidAbsolutePath (out msg)) + var msg = servicePath.CheckIfValidServicePath (); + if (msg != null) { - Log.Error (msg); + Log.Error (String.Format ("{0}\nservice path: {1}", msg, servicePath ?? "")); return; } @@ -276,6 +280,10 @@ namespace WebSocketSharp.Server /// /// Removes the WebSocket service with the specified . /// + /// + /// This method converts to URL-decoded string and + /// removes '/' from tail end of . + /// /// /// true if the WebSocket service is successfully found and removed; otherwise, false. /// @@ -284,9 +292,10 @@ namespace WebSocketSharp.Server /// public bool RemoveWebSocketService (string servicePath) { - if (servicePath.IsNullOrEmpty ()) + var msg = servicePath.CheckIfValidServicePath (); + if (msg != null) { - Log.Error ("'servicePath' must not be null or empty."); + Log.Error (String.Format ("{0}\nservice path: {1}", msg, servicePath ?? "")); return false; } @@ -314,9 +323,10 @@ namespace WebSocketSharp.Server /// public void Stop (ushort code, string reason) { - if (!code.IsCloseStatusCode ()) + var msg = code.CheckIfValidCloseStatusCode (); + if (msg != null) { - Log.Error ("Invalid status code for stop.\ncode: " + code); + Log.Error (String.Format ("{0}\ncode: {1}", msg, code)); return; } diff --git a/websocket-sharp/Server/WebSocketServerBase.cs b/websocket-sharp/Server/WebSocketServerBase.cs index 7b84faee..711347f7 100644 --- a/websocket-sharp/Server/WebSocketServerBase.cs +++ b/websocket-sharp/Server/WebSocketServerBase.cs @@ -117,7 +117,7 @@ namespace WebSocketSharp.Server /// /// Initializes a new instance of the class /// that listens for incoming connection attempts on the specified , - /// , and . + /// , and . /// /// /// A that contains a local IP address. @@ -125,7 +125,7 @@ namespace WebSocketSharp.Server /// /// An that contains a port number. /// - /// + /// /// A that contains an absolute path. /// /// @@ -133,14 +133,14 @@ namespace WebSocketSharp.Server /// (true indicates providing a secure connection.) /// /// - /// Either or is . + /// Either or is . /// /// /// is 0 or less, or 65536 or greater. /// /// /// - /// is invalid. + /// is invalid. /// /// /// -or- @@ -149,20 +149,20 @@ namespace WebSocketSharp.Server /// Pair of and is invalid. /// /// - protected WebSocketServerBase (IPAddress address, int port, string absPath, bool secure) + protected WebSocketServerBase (IPAddress address, int port, string servicePath, bool secure) { if (address == null) throw new ArgumentNullException ("address"); - if (absPath == null) - throw new ArgumentNullException ("absPath"); + if (servicePath == null) + throw new ArgumentNullException ("servicePath"); if (!port.IsPortNumber ()) throw new ArgumentOutOfRangeException ("port", "Invalid port number: " + port); - string msg; - if (!absPath.IsValidAbsolutePath (out msg)) - throw new ArgumentException (msg, "absPath"); + var msg = servicePath.CheckIfValidServicePath (); + if (msg != null) + throw new ArgumentException (msg, "servicePath"); if ((port == 80 && secure) || (port == 443 && !secure)) throw new ArgumentException (String.Format ( @@ -170,7 +170,7 @@ namespace WebSocketSharp.Server _address = address; _port = port; - _uri = absPath.ToUri (); + _uri = servicePath.ToUri (); _secure = secure; init (); diff --git a/websocket-sharp/Server/WebSocketService.cs b/websocket-sharp/Server/WebSocketService.cs index cd48680f..a380bf2a 100644 --- a/websocket-sharp/Server/WebSocketService.cs +++ b/websocket-sharp/Server/WebSocketService.cs @@ -37,7 +37,7 @@ using WebSocketSharp.Net.WebSockets; namespace WebSocketSharp.Server { /// - /// Provides the basic functions of the WebSocket service. + /// Provides the basic functions of the WebSocket service managed by the WebSocket service host. /// /// /// The WebSocketService class is an abstract class. @@ -46,9 +46,9 @@ namespace WebSocketSharp.Server { #region Private Fields + private WebSocket _websocket; private WebSocketContext _context; private WebSocketServiceManager _sessions; - private WebSocket _websocket; #endregion @@ -115,11 +115,10 @@ namespace WebSocketSharp.Server } /// - /// Gets the sessions to the instances. + /// Gets the collection of the WebSocket sessions managed by the WebSocket service host. /// /// - /// A that contains the sessions to - /// the instances. + /// A that contains a collection of the WebSocket sessions. /// protected WebSocketServiceManager Sessions { get { @@ -134,10 +133,10 @@ namespace WebSocketSharp.Server #region Public Properties /// - /// Gets the ID of the current instance. + /// Gets the session ID of the current instance. /// /// - /// A that contains an ID. + /// A that contains a session ID. /// public string ID { get; private set; @@ -216,86 +215,6 @@ namespace WebSocketSharp.Server #region Protected Methods - /// - /// Calls the method with the specified . - /// - /// - /// A that contains an error message. - /// - protected virtual void Error (string message) - { - if (!message.IsNullOrEmpty ()) - OnError (new ErrorEventArgs (message)); - } - - /// - /// Is called when the WebSocket connection has been closed. - /// - /// - /// A that contains an event data associated with - /// an inner event. - /// - protected virtual void OnClose (CloseEventArgs e) - { - } - - /// - /// Is called when the inner or current - /// gets an error. - /// - /// - /// An that contains an event data associated with - /// an inner event. - /// - protected virtual void OnError (ErrorEventArgs e) - { - } - - /// - /// Is called when the inner receives a data frame. - /// - /// - /// A that contains an event data associated with - /// an inner event. - /// - protected virtual void OnMessage (MessageEventArgs e) - { - } - - /// - /// Is called when the WebSocket connection has been established. - /// - protected virtual void OnOpen () - { - } - - /// - /// Validates the cookies used in the WebSocket connection request. - /// - /// - /// This method is called when the inner validates - /// the WebSocket connection request. - /// - /// - /// true if the cookies is valid; otherwise, false. - /// The default returns true. - /// - /// - /// A that contains a collection of the HTTP Cookies - /// to validate. - /// - /// - /// A that receives the HTTP Cookies to send to the client. - /// - protected virtual bool ValidateCookies (CookieCollection request, CookieCollection response) - { - return true; - } - - #endregion - - #region Public Methods - /// /// Broadcasts the specified array of to the clients of every /// instances in the . @@ -303,14 +222,14 @@ namespace WebSocketSharp.Server /// /// An array of to broadcast. /// - public virtual void Broadcast (byte [] data) + protected virtual void Broadcast (byte [] data) { if (!IsBound) return; - if (data == null) + var msg = data.CheckIfValidSendData (); + if (msg != null) { - var msg = "'data' must not be null."; Log.Error (msg); Error (msg); @@ -327,14 +246,14 @@ namespace WebSocketSharp.Server /// /// A to broadcast. /// - public virtual void Broadcast (string data) + protected virtual void Broadcast (string data) { if (!IsBound) return; - if (data == null) + var msg = data.CheckIfValidSendData (); + if (msg != null) { - var msg = "'data' must not be null."; Log.Error (msg); Error (msg); @@ -352,10 +271,10 @@ namespace WebSocketSharp.Server /// A Dictionary<string, bool> that contains the collection of pairs of session ID and value /// indicating whether the each instance received a Pong in a time. /// - public virtual Dictionary Broadping () + protected virtual Dictionary Broadping () { return IsBound - ? _sessions.Broadping (String.Empty) + ? _sessions.Broadping () : null; } @@ -370,18 +289,14 @@ namespace WebSocketSharp.Server /// /// A that contains a message to send. /// - public virtual Dictionary Broadping (string message) + protected virtual Dictionary Broadping (string message) { if (!IsBound) return null; - if (message.IsNullOrEmpty ()) - return _sessions.Broadping (String.Empty); - - var len = Encoding.UTF8.GetBytes (message).Length; - if (len > 125) + var msg = message.CheckIfValidPingMessage (); + if (msg != null) { - var msg = "The payload length of a Ping frame must be 125 bytes or less."; Log.Error (msg); Error (msg); @@ -392,56 +307,89 @@ namespace WebSocketSharp.Server } /// - /// Sends a Ping to the client of the current instance. + /// Calls the method with the specified . /// - /// - /// true if the current instance receives a Pong in a time; - /// otherwise, false. - /// - public virtual bool Ping () + /// + /// A that contains an error message. + /// + protected virtual void Error (string message) { - return IsBound - ? _websocket.Ping () - : false; + if (!message.IsNullOrEmpty ()) + OnError (new ErrorEventArgs (message)); } /// - /// Sends a Ping with the specified to the client of - /// the current instance. + /// Is called when the WebSocket connection has been closed. /// - /// - /// true if the current instance receives a Pong in a time; - /// otherwise, false. - /// - /// - /// A that contains a message to send. + /// + /// A that contains an event data associated with + /// an inner event. /// - public virtual bool Ping (string message) + protected virtual void OnClose (CloseEventArgs e) + { + } + + /// + /// Is called when the inner or current + /// gets an error. + /// + /// + /// An that contains an event data associated with + /// an inner event. + /// + protected virtual void OnError (ErrorEventArgs e) + { + } + + /// + /// Is called when the inner receives a data frame. + /// + /// + /// A that contains an event data associated with + /// an inner event. + /// + protected virtual void OnMessage (MessageEventArgs e) + { + } + + /// + /// Is called when the WebSocket connection has been established. + /// + protected virtual void OnOpen () { - return IsBound - ? _websocket.Ping (message) - : false; } /// /// Sends a Ping to the client of the instance - /// with the specified ID. + /// with the specified . /// /// /// true if the instance with receives /// a Pong in a time; otherwise, false. /// /// - /// A that contains an ID that represents the destination for the Ping. + /// A that contains a session ID that represents the destination for the Ping. /// - public virtual bool PingTo (string id) + protected virtual bool PingTo (string id) { - return PingTo (String.Empty, id); + if (!IsBound) + return false; + + var msg = id.CheckIfValidSessionID (); + if (msg != null) + { + Log.Error (msg); + Error (msg); + + return false; + } + + return _sessions.PingTo (id); } /// /// Sends a Ping with the specified to the client of - /// the instance with the specified ID. + /// the instance with the specified . /// /// /// true if the instance with receives @@ -451,16 +399,16 @@ namespace WebSocketSharp.Server /// A that contains a message to send. /// /// - /// A that contains an ID that represents the destination for the Ping. + /// A that contains a session ID that represents the destination for the Ping. /// - public virtual bool PingTo (string message, string id) + protected virtual bool PingTo (string message, string id) { if (!IsBound) return false; - if (id.IsNullOrEmpty ()) + var msg = id.CheckIfValidSessionID (); + if (msg != null) { - var msg = "'id' must not be null or empty."; Log.Error (msg); Error (msg); @@ -470,33 +418,9 @@ namespace WebSocketSharp.Server return _sessions.PingTo (message, id); } - /// - /// Sends a binary data to the client of the current instance. - /// - /// - /// An array of that contains a binary data to send. - /// - public virtual void Send (byte [] data) - { - if (IsBound) - _websocket.Send (data); - } - - /// - /// Sends a text data to the client of the current instance. - /// - /// - /// A that contains a text data to send. - /// - public virtual void Send (string data) - { - if (IsBound) - _websocket.Send (data); - } - /// /// Sends a binary data to the client of the instance - /// with the specified ID. + /// with the specified . /// /// /// true if is successfully sent; otherwise, false. @@ -505,19 +429,14 @@ namespace WebSocketSharp.Server /// 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 a session ID that represents the destination for the data. /// - public virtual bool SendTo (byte [] data, string id) + protected virtual bool SendTo (byte [] data, string id) { if (!IsBound) return false; - var msg = data == null - ? "'data' must not be null." - : id.IsNullOrEmpty () - ? "'id' must not be null or empty." - : null; - + var msg = id.CheckIfValidSessionID (); if (msg != null) { Log.Error (msg); @@ -531,7 +450,7 @@ namespace WebSocketSharp.Server /// /// Sends a text data to the client of the instance - /// with the specified ID. + /// with the specified . /// /// /// true if is successfully sent; otherwise, false. @@ -540,19 +459,14 @@ namespace WebSocketSharp.Server /// A that contains a text data to send. /// /// - /// A that contains an ID that represents the destination for the data. + /// A that contains a session ID that represents the destination for the data. /// - public virtual bool SendTo (string data, string id) + protected virtual bool SendTo (string data, string id) { if (!IsBound) return false; - var msg = data == null - ? "'data' must not be null." - : id.IsNullOrEmpty () - ? "'id' must not be null or empty." - : null; - + var msg = id.CheckIfValidSessionID (); if (msg != null) { Log.Error (msg); @@ -564,6 +478,89 @@ namespace WebSocketSharp.Server return _sessions.SendTo (data, id); } + /// + /// Validates the cookies used in the WebSocket connection request. + /// + /// + /// This method is called when the inner validates + /// the WebSocket connection request. + /// + /// + /// true if the cookies is valid; otherwise, false. + /// The default returns true. + /// + /// + /// A that contains a collection of the HTTP Cookies + /// to validate. + /// + /// + /// A that receives the HTTP Cookies to send to the client. + /// + protected virtual bool ValidateCookies (CookieCollection request, CookieCollection response) + { + return true; + } + + #endregion + + #region Public Methods + + /// + /// Sends a Ping to the client of the current instance. + /// + /// + /// true if the current instance receives a Pong in a time; + /// otherwise, false. + /// + public virtual bool Ping () + { + return IsBound + ? _websocket.Ping () + : false; + } + + /// + /// Sends a Ping with the specified to the client of + /// the current instance. + /// + /// + /// true if the current instance receives a Pong in a time; + /// otherwise, false. + /// + /// + /// A that contains a message to send. + /// + public virtual bool Ping (string message) + { + return IsBound + ? _websocket.Ping (message) + : false; + } + + /// + /// Sends a binary data to the client of the current instance. + /// + /// + /// An array of that contains a binary data to send. + /// + public virtual void Send (byte [] data) + { + if (IsBound) + _websocket.Send (data); + } + + /// + /// Sends a text data to the client of the current instance. + /// + /// + /// A that contains a text data to send. + /// + public virtual void Send (string data) + { + if (IsBound) + _websocket.Send (data); + } + /// /// Starts the current instance. /// diff --git a/websocket-sharp/Server/WebSocketServiceHost.cs b/websocket-sharp/Server/WebSocketServiceHost.cs index 8c293fe1..818874c2 100644 --- a/websocket-sharp/Server/WebSocketServiceHost.cs +++ b/websocket-sharp/Server/WebSocketServiceHost.cs @@ -52,6 +52,7 @@ namespace WebSocketSharp.Server { #region Private Fields + private string _servicePath; private WebSocketServiceManager _sessions; #endregion @@ -111,43 +112,43 @@ namespace WebSocketSharp.Server /// /// Initializes a new instance of the WebSocketServiceHost<T> class that listens for - /// incoming connection attempts on the specified and . + /// incoming connection attempts on the specified and . /// /// /// An that contains a port number. /// - /// + /// /// A that contains an absolute path. /// - public WebSocketServiceHost (int port, string absPath) - : this (System.Net.IPAddress.Any, port, absPath) + public WebSocketServiceHost (int port, string servicePath) + : this (System.Net.IPAddress.Any, port, servicePath) { } /// /// Initializes a new instance of the WebSocketServiceHost<T> class that listens for - /// incoming connection attempts on the specified , + /// incoming connection attempts on the specified , /// and . /// /// /// An that contains a port number. /// - /// + /// /// A that contains an absolute path. /// /// /// A that indicates providing a secure connection or not. /// (true indicates providing a secure connection.) /// - public WebSocketServiceHost (int port, string absPath, bool secure) - : this (System.Net.IPAddress.Any, port, absPath, secure) + public WebSocketServiceHost (int port, string servicePath, bool secure) + : this (System.Net.IPAddress.Any, port, servicePath, secure) { } /// /// Initializes a new instance of the WebSocketServiceHost<T> class that listens for /// incoming connection attempts on the specified , - /// and . + /// and . /// /// /// A that contains a local IP address. @@ -155,18 +156,18 @@ namespace WebSocketSharp.Server /// /// An that contains a port number. /// - /// + /// /// A that contains an absolute path. /// - public WebSocketServiceHost (System.Net.IPAddress address, int port, string absPath) - : this (address, port, absPath, port == 443 ? true : false) + public WebSocketServiceHost (System.Net.IPAddress address, int port, string servicePath) + : this (address, port, servicePath, port == 443 ? true : false) { } /// /// Initializes a new instance of the WebSocketServiceHost<T> class that listens for /// incoming connection attempts on the specified , , - /// and . + /// and . /// /// /// A that contains a local IP address. @@ -174,15 +175,15 @@ namespace WebSocketSharp.Server /// /// An that contains a port number. /// - /// + /// /// A that contains an absolute path. /// /// /// A that indicates providing a secure connection or not. /// (true indicates providing a secure connection.) /// - public WebSocketServiceHost (System.Net.IPAddress address, int port, string absPath, bool secure) - : base (address, port, absPath, secure) + public WebSocketServiceHost (System.Net.IPAddress address, int port, string servicePath, bool secure) + : base (address, port, servicePath, secure) { _sessions = new WebSocketServiceManager (Log); } @@ -208,8 +209,8 @@ namespace WebSocketSharp.Server /// the inactive instances periodically. /// /// - /// true if the WebSocket service host cleans up the inactive WebSocket service instances every 60 seconds; - /// otherwise, false. The default value is true. + /// true if the WebSocket service host cleans up the inactive WebSocket service instances + /// every 60 seconds; otherwise, false. The default value is true. /// public bool KeepClean { get { @@ -221,6 +222,33 @@ namespace WebSocketSharp.Server } } + /// + /// Gets the collection of the WebSocket sessions managed by the WebSocket service host. + /// + /// + /// A that contains a collection of the WebSocket sessions. + /// + public WebSocketServiceManager Sessions { + get { + return _sessions; + } + } + + /// + /// Gets the path to the WebSocket service that the WebSocket service host provides. + /// + /// + /// A that contains an absolute path to the WebSocket service. + /// + public string ServicePath { + get { + if (_servicePath == null) + _servicePath = HttpUtility.UrlDecode (BaseUri.GetAbsolutePath ()).TrimEndSlash (); + + return _servicePath; + } + } + /// /// Gets the WebSocket URL on which to listen for incoming connection attempts. /// @@ -244,10 +272,10 @@ namespace WebSocketSharp.Server private void stop (ushort code, string reason) { var data = code.Append (reason); - if (data.Length > 125) + var msg = data.CheckIfValidCloseData (); + if (msg != null) { - Log.Error (String.Format ( - "The payload length of a Close frame must be 125 bytes or less.\ncode: {0}\nreason: {1}", code, reason)); + Log.Error (String.Format ("{0}\ncode: {1}\nreason: {2}", msg, code, reason)); return; } @@ -268,17 +296,17 @@ namespace WebSocketSharp.Server protected override void AcceptWebSocket (TcpListenerWebSocketContext context) { var websocket = context.WebSocket; - var path = context.Path.UrlDecode (); - websocket.Log = Log; - if (path != Uri.GetAbsolutePath ().UrlDecode ()) + + var path = HttpUtility.UrlDecode (context.Path).TrimEndSlash (); + if (path != ServicePath) { websocket.Close (HttpStatusCode.NotImplemented); return; } - if (Uri.IsAbsoluteUri) - websocket.Url = Uri; + if (BaseUri.IsAbsoluteUri) + websocket.Url = BaseUri; ((IWebSocketServiceHost) this).BindWebSocket (context); } @@ -295,9 +323,10 @@ namespace WebSocketSharp.Server /// public void Broadcast (byte [] data) { - if (data == null) + var msg = data.CheckIfValidSendData (); + if (msg != null) { - Log.Error ("'data' must not be null."); + Log.Error (msg); return; } @@ -312,58 +341,160 @@ namespace WebSocketSharp.Server /// public void Broadcast (string data) { - if (data == null) + var msg = data.CheckIfValidSendData (); + if (msg != null) { - Log.Error ("'data' must not be null."); + Log.Error (msg); return; } _sessions.Broadcast (data); } + /// + /// Sends Pings to all clients. + /// + /// + /// A Dictionary<string, bool> that contains the collection of pairs of session ID and value + /// indicating whether the WebSocket service host received a Pong from each client in a time. + /// + public Dictionary Broadping () + { + return _sessions.Broadping (); + } + /// /// Sends Pings with the specified to all clients. /// /// /// A Dictionary<string, bool> that contains the collection of pairs of session ID and value - /// indicating whether the service host received the Pong from each client in a time. + /// indicating whether the WebSocket service host received a Pong from each client in a time. /// /// /// A that contains a message to send. /// public Dictionary Broadping (string message) { - if (message.IsNullOrEmpty ()) - return _sessions.Broadping (String.Empty); - - var len = Encoding.UTF8.GetBytes (message).Length; - if (len > 125) + var msg = message.CheckIfValidPingMessage (); + if (msg != null) { - Log.Error ("The payload length of a Ping frame must be 125 bytes or less."); + Log.Error (msg); return null; } return _sessions.Broadping (message); } + /// + /// Close the WebSocket session with the specified . + /// + /// + /// A that contains a session ID to find. + /// + public void CloseSession (string id) + { + var msg = id.CheckIfValidSessionID (); + if (msg != null) + { + Log.Error (msg); + return; + } + + _sessions.StopServiceInstance (id); + } + + /// + /// Close the WebSocket session with the specified , + /// and . + /// + /// + /// A that contains a status code indicating the reason for closure. + /// + /// + /// A that contains the reason for closure. + /// + /// + /// A that contains a session ID to find. + /// + public void CloseSession (ushort code, string reason, string id) + { + var msg = id.CheckIfValidSessionID (); + if (msg != null) + { + Log.Error (msg); + return; + } + + _sessions.StopServiceInstance (code, reason, id); + } + + /// + /// Close the WebSocket session with the specified , + /// and . + /// + /// + /// A that contains a status code indicating the reason for closure. + /// + /// + /// A that contains the reason for closure. + /// + /// + /// A that contains a session ID to find. + /// + public void CloseSession (CloseStatusCode code, string reason, string id) + { + var msg = id.CheckIfValidSessionID (); + if (msg != null) + { + Log.Error (msg); + return; + } + + _sessions.StopServiceInstance (code, reason, id); + } + + /// + /// Sends a Ping to the client associated with the specified . + /// + /// + /// true if the WebSocket service host receives a Pong from the client in a time; + /// otherwise, false. + /// + /// + /// A that contains a session ID that represents the destination for the Ping. + /// + public bool PingTo (string id) + { + var msg = id.CheckIfValidSessionID (); + if (msg != null) + { + Log.Error (msg); + return false; + } + + return _sessions.PingTo (id); + } + /// /// Sends a Ping with the specified to the client associated with - /// the specified ID. + /// the specified . /// /// - /// true if the service host receives a Pong from the client in a time; otherwise, false. + /// true if the WebSocket service host 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 a session ID that represents the destination for the Ping. /// public bool PingTo (string message, string id) { - if (id.IsNullOrEmpty ()) + var msg = id.CheckIfValidSessionID (); + if (msg != null) { - Log.Error ("'id' must not be null or empty."); + Log.Error (msg); return false; } @@ -371,7 +502,7 @@ namespace WebSocketSharp.Server } /// - /// Sends a binary data to the client associated with the specified ID. + /// Sends a binary data to the client associated with the specified . /// /// /// true if is successfully sent; otherwise, false. @@ -380,16 +511,11 @@ namespace WebSocketSharp.Server /// 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 a session ID that represents the destination for the data. /// public bool SendTo (byte [] data, string id) { - var msg = data == null - ? "'data' must not be null." - : id.IsNullOrEmpty () - ? "'id' must not be null or empty." - : null; - + var msg = id.CheckIfValidSessionID (); if (msg != null) { Log.Error (msg); @@ -400,7 +526,7 @@ namespace WebSocketSharp.Server } /// - /// Sends a text data to the client associated with the specified ID. + /// Sends a text data to the client associated with the specified . /// /// /// true if is successfully sent; otherwise, false. @@ -409,16 +535,11 @@ namespace WebSocketSharp.Server /// A that contains a text data to send. /// /// - /// A that contains an ID that represents the destination for the data. + /// A that contains a session ID that represents the destination for the data. /// public bool SendTo (string data, string id) { - var msg = data == null - ? "'data' must not be null." - : id.IsNullOrEmpty () - ? "'id' must not be null or empty." - : null; - + var msg = id.CheckIfValidSessionID (); if (msg != null) { Log.Error (msg); @@ -449,9 +570,10 @@ namespace WebSocketSharp.Server /// public void Stop (ushort code, string reason) { - if (!code.IsCloseStatusCode ()) + var msg = code.CheckIfValidCloseStatusCode (); + if (msg != null) { - Log.Error ("Invalid status code for stop.\ncode: " + code); + Log.Error (String.Format ("{0}\ncode: {1}", msg, code)); return; } @@ -527,18 +649,81 @@ namespace WebSocketSharp.Server return _sessions.Broadping (message); } + /// + /// Close the WebSocket session with the specified . + /// + /// + /// A that contains a session ID to find. + /// + void IWebSocketServiceHost.CloseSession (string id) + { + _sessions.StopServiceInstance (id); + } + + /// + /// Close the WebSocket session with the specified , + /// and . + /// + /// + /// A that contains a status code indicating the reason for closure. + /// + /// + /// A that contains the reason for closure. + /// + /// + /// A that contains a session ID to find. + /// + void IWebSocketServiceHost.CloseSession (ushort code, string reason, string id) + { + _sessions.StopServiceInstance (code, reason, id); + } + + /// + /// Close the WebSocket session with the specified , + /// and . + /// + /// + /// A that contains a status code indicating the reason for closure. + /// + /// + /// A that contains the reason for closure. + /// + /// + /// A that contains a session ID to find. + /// + void IWebSocketServiceHost.CloseSession (CloseStatusCode code, string reason, string id) + { + _sessions.StopServiceInstance (code, reason, id); + } + + /// + /// Sends a Ping to the client associated with the specified . + /// + /// + /// true if the WebSocket service host receives a Pong from the client in a time; + /// otherwise, false. + /// + /// + /// A that contains a session ID that represents the destination for the Ping. + /// + bool IWebSocketServiceHost.PingTo (string id) + { + return _sessions.PingTo (id); + } + /// /// Sends a Ping with the specified to the client associated with - /// the specified ID. + /// the specified . /// /// - /// true if the service host receives a Pong from the client in a time; otherwise, false. + /// true if the WebSocket service host 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 a session ID that represents the destination for the Ping. /// bool IWebSocketServiceHost.PingTo (string message, string id) { @@ -546,7 +731,7 @@ namespace WebSocketSharp.Server } /// - /// Sends a binary data to the client associated with the specified ID. + /// Sends a binary data to the client associated with the specified . /// /// /// true if is successfully sent; otherwise, false. @@ -555,7 +740,7 @@ namespace WebSocketSharp.Server /// 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 a session ID that represents the destination for the data. /// bool IWebSocketServiceHost.SendTo (byte [] data, string id) { @@ -563,7 +748,7 @@ namespace WebSocketSharp.Server } /// - /// Sends a text data to the client associated with the specified ID. + /// Sends a text data to the client associated with the specified . /// /// /// true if is successfully sent; otherwise, false. @@ -572,7 +757,7 @@ namespace WebSocketSharp.Server /// A that contains a text data to send. /// /// - /// A that contains an ID that represents the destination for the data. + /// A that contains a session ID that represents the destination for the data. /// bool IWebSocketServiceHost.SendTo (string data, string id) { diff --git a/websocket-sharp/Server/WebSocketServiceHostManager.cs b/websocket-sharp/Server/WebSocketServiceHostManager.cs index 0bd1fcf0..2bc238ff 100644 --- a/websocket-sharp/Server/WebSocketServiceHostManager.cs +++ b/websocket-sharp/Server/WebSocketServiceHostManager.cs @@ -29,6 +29,7 @@ using System; using System.Collections.Generic; using System.Text; +using WebSocketSharp.Net; namespace WebSocketSharp.Server { @@ -63,6 +64,37 @@ namespace WebSocketSharp.Server #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 Public Properties /// @@ -87,7 +119,7 @@ namespace WebSocketSharp.Server /// /// An that contains the number of the WebSocket services. /// - public int ServiceCount { + public int Count { get { lock (_sync) { @@ -113,37 +145,6 @@ namespace WebSocketSharp.Server #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 () @@ -160,6 +161,7 @@ namespace WebSocketSharp.Server internal void Add (string servicePath, IWebSocketServiceHost serviceHost) { + servicePath = HttpUtility.UrlDecode (servicePath).TrimEndSlash (); lock (_sync) { IWebSocketServiceHost host; @@ -170,12 +172,14 @@ namespace WebSocketSharp.Server return; } - _serviceHosts.Add (servicePath.UrlDecode (), serviceHost); + _serviceHosts.Add (servicePath, serviceHost); } } internal bool Remove (string servicePath) { + servicePath = HttpUtility.UrlDecode (servicePath).TrimEndSlash (); + IWebSocketServiceHost host; lock (_sync) { @@ -217,6 +221,7 @@ namespace WebSocketSharp.Server internal bool TryGetServiceHost (string servicePath, out IWebSocketServiceHost serviceHost) { + servicePath = HttpUtility.UrlDecode (servicePath).TrimEndSlash (); lock (_sync) { return _serviceHosts.TryGetValue (servicePath, out serviceHost); @@ -228,16 +233,18 @@ namespace WebSocketSharp.Server #region Public Methods /// - /// Broadcasts the specified array of to all clients of the WebSocket services. + /// Broadcasts the specified array of to all clients of the WebSocket services + /// managed by the . /// /// /// An array of to broadcast. /// public void Broadcast (byte [] data) { - if (data == null) + var msg = data.CheckIfValidSendData (); + if (msg != null) { - _logger.Error ("'data' must not be null."); + _logger.Error (msg); return; } @@ -246,16 +253,18 @@ namespace WebSocketSharp.Server } /// - /// Broadcasts the specified to all clients of the WebSocket services. + /// Broadcasts the specified to all clients of the WebSocket services + /// managed by the . /// /// /// A to broadcast. /// public void Broadcast (string data) { - if (data == null) + var msg = data.CheckIfValidSendData (); + if (msg != null) { - _logger.Error ("'data' must not be null."); + _logger.Error (msg); return; } @@ -278,12 +287,7 @@ namespace WebSocketSharp.Server /// 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; - + var msg = data.CheckIfValidSendData () ?? servicePath.CheckIfValidServicePath (); if (msg != null) { _logger.Error (msg); @@ -316,12 +320,7 @@ namespace WebSocketSharp.Server /// 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; - + var msg = data.CheckIfValidSendData () ?? servicePath.CheckIfValidServicePath (); if (msg != null) { _logger.Error (msg); @@ -340,26 +339,41 @@ namespace WebSocketSharp.Server } /// - /// Sends Pings with the specified to all clients of the WebSocket services. + /// Sends Pings to all clients of the WebSocket services managed by the . + /// + /// + /// 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 a Pong from each client in a time. + /// + public Dictionary> Broadping () + { + var result = new Dictionary> (); + foreach (var service in copy ()) + result.Add (service.Key, service.Value.Broadping ()); + + return result; + } + + /// + /// Sends Pings with the specified to all clients of the WebSocket services + /// managed by the . /// /// /// 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. + /// received a Pong from each client in a time. /// /// /// A that contains a message to send. /// public Dictionary> Broadping (string message) { - if (!message.IsNullOrEmpty ()) + var msg = message.CheckIfValidPingMessage (); + if (msg != null) { - 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; - } + _logger.Error (msg); + return null; } var result = new Dictionary> (); @@ -369,13 +383,43 @@ namespace WebSocketSharp.Server return result; } + /// + /// Sends Pings 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 a Pong from each client in a time. + /// If the WebSocket service is not found, returns . + /// + /// + /// A that contains an absolute path to the WebSocket service to find. + /// + public Dictionary BroadpingTo (string servicePath) + { + var msg = servicePath.CheckIfValidServicePath (); + if (msg != null) + { + _logger.Error (msg); + return null; + } + + IWebSocketServiceHost host; + if (!TryGetServiceHost (servicePath, out host)) + { + _logger.Error ("The WebSocket service with the specified path not found.\npath: " + servicePath); + return null; + } + + return host.Broadping (); + } + /// /// 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. + /// indicating whether the WebSocket service received a Pong from each client in a time. /// If the WebSocket service is not found, returns . /// /// @@ -386,15 +430,7 @@ namespace WebSocketSharp.Server /// 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; - + var msg = message.CheckIfValidPingMessage () ?? servicePath.CheckIfValidServicePath (); if (msg != null) { _logger.Error (msg); @@ -411,21 +447,121 @@ namespace WebSocketSharp.Server return host.Broadping (message); } + /// + /// Close the WebSocket session with the specified and + /// . + /// + /// + /// A that contains a session ID to find. + /// + /// + /// A that contains an absolute path to the WebSocket service to find. + /// + public void CloseSession (string id, string servicePath) + { + var msg = id.CheckIfValidSessionID () ?? servicePath.CheckIfValidServicePath (); + if (msg != null) + { + _logger.Error (msg); + return; + } + + IWebSocketServiceHost host; + if (!TryGetServiceHost (servicePath, out host)) + { + _logger.Error ("The WebSocket service with the specified path not found.\npath: " + servicePath); + return; + } + + host.CloseSession (id); + } + + /// + /// Close the WebSocket session with the specified , , + /// and . + /// + /// + /// A that contains a status code indicating the reason for closure. + /// + /// + /// A that contains the reason for closure. + /// + /// + /// A that contains a session ID to find. + /// + /// + /// A that contains an absolute path to the WebSocket service to find. + /// + public void CloseSession (ushort code, string reason, string id, string servicePath) + { + var msg = id.CheckIfValidSessionID () ?? servicePath.CheckIfValidServicePath (); + if (msg != null) + { + _logger.Error (msg); + return; + } + + IWebSocketServiceHost host; + if (!TryGetServiceHost (servicePath, out host)) + { + _logger.Error ("The WebSocket service with the specified path not found.\npath: " + servicePath); + return; + } + + host.CloseSession (code, reason, id); + } + + /// + /// Close the WebSocket session with the specified , , + /// and . + /// + /// + /// A that contains a status code indicating the reason for closure. + /// + /// + /// A that contains the reason for closure. + /// + /// + /// A that contains a session ID to find. + /// + /// + /// A that contains an absolute path to the WebSocket service to find. + /// + public void CloseSession (CloseStatusCode code, string reason, string id, string servicePath) + { + var msg = id.CheckIfValidSessionID () ?? servicePath.CheckIfValidServicePath (); + if (msg != null) + { + _logger.Error (msg); + return; + } + + IWebSocketServiceHost host; + if (!TryGetServiceHost (servicePath, out host)) + { + _logger.Error ("The WebSocket service with the specified path not found.\npath: " + servicePath); + return; + } + + host.CloseSession (code, reason, id); + } + /// /// 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. + /// 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.CheckIfValidServicePath (); + if (msg != null) { - _logger.Error ("'servicePath' must not be null or empty."); + _logger.Error (msg); return -1; } @@ -439,6 +575,39 @@ namespace WebSocketSharp.Server return host.ConnectionCount; } + /// + /// Sends a Ping 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 session ID that represents the destination for the Ping. + /// + /// + /// A that contains an absolute path to the WebSocket service to find. + /// + public bool PingTo (string id, string servicePath) + { + var msg = id.CheckIfValidSessionID () ?? servicePath.CheckIfValidServicePath (); + if (msg != null) + { + _logger.Error (msg); + return false; + } + + IWebSocketServiceHost 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); + } + /// /// Sends a Ping with the specified to the client associated with /// the specified and . @@ -451,18 +620,15 @@ namespace WebSocketSharp.Server /// A that contains a message to send. /// /// - /// A that contains an ID that represents the destination for the Ping. + /// A that contains a session 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) { - var msg = id.IsNullOrEmpty () - ? "'id' must not be null or empty." - : servicePath.IsNullOrEmpty () - ? "'servicePath' must not be null or empty." - : null; + var msg = (message.CheckIfValidPingMessage () ?? id.CheckIfValidSessionID ()) ?? + servicePath.CheckIfValidServicePath (); if (msg != null) { @@ -491,20 +657,15 @@ namespace WebSocketSharp.Server /// 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 a session 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; + var msg = (data.CheckIfValidSendData () ?? id.CheckIfValidSessionID ()) ?? + servicePath.CheckIfValidServicePath (); if (msg != null) { @@ -533,20 +694,15 @@ namespace WebSocketSharp.Server /// A that contains a text data to send. /// /// - /// A that contains an ID that represents the destination for the data. + /// A that contains a session 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; + var msg = (data.CheckIfValidSendData () ?? id.CheckIfValidSessionID ()) ?? + servicePath.CheckIfValidServicePath (); if (msg != null) { diff --git a/websocket-sharp/Server/WebSocketServiceManager.cs b/websocket-sharp/Server/WebSocketServiceManager.cs index a49078a5..a0f7892d 100644 --- a/websocket-sharp/Server/WebSocketServiceManager.cs +++ b/websocket-sharp/Server/WebSocketServiceManager.cs @@ -84,7 +84,7 @@ namespace WebSocketSharp.Server /// public IEnumerable ActiveIDs { get { - return from result in Broadping (String.Empty) + return from result in Broadping () where result.Value select result.Key; } @@ -134,14 +134,14 @@ namespace WebSocketSharp.Server /// public IEnumerable InactiveIDs { get { - return from result in Broadping (String.Empty) + return from result in Broadping () where !result.Value select result.Key; } } /// - /// Gets the instance with the specified ID + /// Gets the instance with the specified /// from the . /// /// @@ -153,9 +153,10 @@ namespace WebSocketSharp.Server /// public WebSocketService this [string id] { get { - if (id.IsNullOrEmpty ()) + var msg = id.CheckIfValidSessionID (); + if (msg != null) { - _logger.Error ("'id' must not be null or empty."); + _logger.Error (msg); return null; } @@ -289,23 +290,6 @@ namespace WebSocketSharp.Server _sweepTimer.Start (); } - private void stop (ushort code, string reason, bool ignoreArgs) - { - stopSweepTimer (); - lock (_sync) - { - if (_stopped) - return; - - _stopped = true; - foreach (var service in copy ().Values) - if (ignoreArgs) - service.Stop (); - else - service.Stop (code, reason); - } - } - private void stopSweepTimer () { if (_sweepTimer.Enabled) @@ -331,8 +315,8 @@ namespace WebSocketSharp.Server } /// - /// Broadcasts the specified array of to the clients of every - /// instances managed by the . + /// Broadcasts the specified array of to the clients of every + /// instances managed by the . /// /// /// An array of to broadcast. @@ -346,8 +330,8 @@ namespace WebSocketSharp.Server } /// - /// Broadcasts the specified to the clients of every - /// instances managed by the . + /// Broadcasts the specified to the clients of every + /// instances managed by the . /// /// /// A to broadcast. @@ -361,12 +345,29 @@ namespace WebSocketSharp.Server } /// - /// Sends Pings with the specified to the clients of every - /// instances managed by the . + /// Sends Pings to the clients of every instances managed by + /// the . + /// + /// + /// A Dictionary<string, bool> that contains the collection of pairs of ID and value indicating + /// whether each instance received a Pong from the client in a time. + /// + internal Dictionary Broadping () + { + var result = new Dictionary (); + foreach (var session in copy ()) + result.Add (session.Key, session.Value.Ping ()); + + return result; + } + + /// + /// Sends Pings with the specified to the clients of every + /// instances managed by the . /// /// - /// A Dictionary<string, bool> that contains the collection of pairs of session ID and value - /// indicating whether the each instance received a Pong in a time. + /// A Dictionary<string, bool> that contains the collection of pairs of ID and value indicating + /// whether each instance received a Pong from the client in a time. /// /// /// A that contains a message to send. @@ -381,12 +382,36 @@ namespace WebSocketSharp.Server } /// - /// Sends a Ping with the specified to the client of - /// the instance with the specified ID. + /// Sends a Ping to the client of the instance + /// with the specified . /// /// - /// true if the instance with receives - /// a Pong in a time; otherwise, false. + /// true if the instance receives a Pong from the client + /// in a time; otherwise, false. + /// + /// + /// A that contains an ID that represents the destination for the Ping. + /// + internal bool PingTo (string id) + { + WebSocketService service; + if (!TryGetServiceInstance (id, out service)) + { + _logger.Error ( + "The WebSocket service instance with the specified ID not found.\nID: " + id); + return false; + } + + return service.Ping (); + } + + /// + /// Sends a Ping with the specified to the client of the + /// instance with the specified . + /// + /// + /// true if the instance receives a Pong from the client + /// in a time; otherwise, false. /// /// /// A that contains a message to send. @@ -417,7 +442,7 @@ namespace WebSocketSharp.Server /// /// Sends a binary data to the client of the instance - /// with the specified ID. + /// with the specified . /// /// /// true if is successfully sent; otherwise, false. @@ -444,7 +469,7 @@ namespace WebSocketSharp.Server /// /// Sends a text data to the client of the instance - /// with the specified ID. + /// with the specified . /// /// /// true if is successfully sent; otherwise, false. @@ -471,12 +496,69 @@ namespace WebSocketSharp.Server internal void Stop () { - stop (0, null, true); + stopSweepTimer (); + lock (_sync) + { + if (_stopped) + return; + + _stopped = true; + foreach (var service in copy ().Values) + service.Stop (); + } } internal void Stop (ushort code, string reason) { - stop (code, reason, false); + stopSweepTimer (); + lock (_sync) + { + if (_stopped) + return; + + _stopped = true; + foreach (var service in copy ().Values) + service.Stop (code, reason); + } + } + + internal void StopServiceInstance (string id) + { + WebSocketService service; + if (!TryGetServiceInstance (id, out service)) + { + _logger.Error ( + "The WebSocket service instance with the specified ID not found.\nID: " + id); + return; + } + + service.Stop (); + } + + internal void StopServiceInstance (ushort code, string reason, string id) + { + WebSocketService service; + if (!TryGetServiceInstance (id, out service)) + { + _logger.Error ( + "The WebSocket service instance with the specified ID not found.\nID: " + id); + return; + } + + service.Stop (code, reason); + } + + internal void StopServiceInstance (CloseStatusCode code, string reason, string id) + { + WebSocketService service; + if (!TryGetServiceInstance (id, out service)) + { + _logger.Error ( + "The WebSocket service instance with the specified ID not found.\nID: " + id); + return; + } + + service.Stop (code, reason); } #endregion @@ -520,7 +602,7 @@ namespace WebSocketSharp.Server } /// - /// Tries to get the instance with the specified ID. + /// Tries to get the instance with the specified . /// /// /// true if the instance with