diff --git a/websocket-sharp/Server/WebSocketServer.cs b/websocket-sharp/Server/WebSocketServer.cs index 136d98b2..f1b09cf0 100644 --- a/websocket-sharp/Server/WebSocketServer.cs +++ b/websocket-sharp/Server/WebSocketServer.cs @@ -60,7 +60,7 @@ namespace WebSocketSharp.Server private System.Net.IPAddress _address; private AuthenticationSchemes _authSchemes; - private X509Certificate2 _cert; + private X509Certificate2 _certificate; private Func _credentialsFinder; private TcpListener _listener; private Logger _logger; @@ -82,8 +82,8 @@ namespace WebSocketSharp.Server /// Initializes a new instance of the class. /// /// - /// An instance initialized by this constructor listens for the incoming connection requests on - /// port 80. + /// An instance initialized by this constructor listens for the incoming connection requests + /// on port 80. /// public WebSocketServer () : this (80) @@ -100,7 +100,7 @@ namespace WebSocketSharp.Server /// on . /// /// - /// And if is 443, that instance provides a secure connection. + /// If is 443, that instance provides a secure connection. /// /// /// @@ -121,23 +121,23 @@ namespace WebSocketSharp.Server /// /// /// An instance initialized by this constructor listens for the incoming connection requests - /// on the port (if any) in . + /// on the port in . /// /// - /// So if is without a port, either port 80 or 443 is used on which to - /// listen. It's determined by the scheme (ws or wss) in . (port 80 if - /// the scheme is ws.) + /// If doesn't include a port, either port 80 or 443 is used on which + /// to listen. It's determined by the scheme (ws or wss) in . + /// (Port 80 if the scheme is ws.) /// /// /// /// A that represents the WebSocket URL of the server. /// - /// - /// is . - /// /// /// is invalid. /// + /// + /// is . + /// public WebSocketServer (string url) { if (url == null) @@ -150,7 +150,7 @@ namespace WebSocketSharp.Server var host = _uri.DnsSafeHost; _address = host.ToIPAddress (); if (_address == null || !_address.IsLocal ()) - throw new ArgumentException ("The host part must be the local host name: " + host, "url"); + throw new ArgumentException ("The host part isn't a local host name: " + host, "url"); _port = _uri.Port; _secure = _uri.Scheme == "wss"; @@ -163,22 +163,22 @@ namespace WebSocketSharp.Server /// and . /// /// - /// An instance initialized by this constructor listens for the incoming connection requests on - /// . + /// An instance initialized by this constructor listens for the incoming connection requests + /// on . /// /// /// An that represents the port number on which to listen. /// /// - /// A that indicates providing a secure connection or not. (true - /// indicates providing a secure connection.) + /// A that indicates providing a secure connection or not. + /// (true indicates providing a secure connection.) /// - /// - /// isn't between 1 and 65535. - /// /// /// Pair of and is invalid. /// + /// + /// isn't between 1 and 65535. + /// public WebSocketServer (int port, bool secure) : this (System.Net.IPAddress.Any, port, secure) { @@ -194,7 +194,7 @@ namespace WebSocketSharp.Server /// on . /// /// - /// And if is 443, that instance provides a secure connection. + /// If is 443, that instance provides a secure connection. /// /// /// @@ -203,15 +203,15 @@ namespace WebSocketSharp.Server /// /// An that represents the port number on which to listen. /// + /// + /// isn't a local IP address. + /// /// /// is . /// /// /// isn't between 1 and 65535. /// - /// - /// isn't a local IP address. - /// public WebSocketServer (System.Net.IPAddress address, int port) : this (address, port, port == 443) { @@ -222,8 +222,8 @@ namespace WebSocketSharp.Server /// , , and . /// /// - /// An instance initialized by this constructor listens for the incoming connection requests on - /// . + /// An instance initialized by this constructor listens for the incoming connection requests + /// on . /// /// /// A that represents the local IP address of the server. @@ -232,15 +232,9 @@ namespace WebSocketSharp.Server /// An that represents the port number on which to listen. /// /// - /// A that indicates providing a secure connection or not. (true - /// indicates providing a secure connection.) + /// A that indicates providing a secure connection or not. + /// (true indicates providing a secure connection.) /// - /// - /// is . - /// - /// - /// isn't between 1 and 65535. - /// /// /// /// isn't a local IP address. @@ -252,17 +246,23 @@ namespace WebSocketSharp.Server /// Pair of and is invalid. /// /// + /// + /// is . + /// + /// + /// isn't between 1 and 65535. + /// public WebSocketServer (System.Net.IPAddress address, int port, bool secure) { if (!address.IsLocal ()) - throw new ArgumentException ("Must be the local IP address: " + address, "address"); + throw new ArgumentException ("Not a local IP address: " + address, "address"); if (!port.IsPortNumber ()) - throw new ArgumentOutOfRangeException ("port", "Must be between 1 and 65535: " + port); + throw new ArgumentOutOfRangeException ("port", "Not between 1 and 65535: " + port); if ((port == 80 && secure) || (port == 443 && !secure)) throw new ArgumentException ( - String.Format ("Invalid pair of 'port' and 'secure': {0}, {1}", port, secure)); + String.Format ("An invalid pair of 'port' and 'secure': {0}, {1}", port, secure)); _address = address; _port = port; @@ -292,9 +292,9 @@ namespace WebSocketSharp.Server /// Gets or sets the scheme used to authenticate the clients. /// /// - /// One of the enum values, indicates - /// the scheme used to authenticate the clients. - /// The default value is . + /// One of the enum values, + /// indicates the scheme used to authenticate the clients. The default value is + /// . /// public AuthenticationSchemes AuthenticationSchemes { get { @@ -313,18 +313,19 @@ namespace WebSocketSharp.Server /// Gets or sets the certificate used to authenticate the server on the secure connection. /// /// - /// A used to authenticate the server. + /// A that represents the certificate used to authenticate + /// the server. /// public X509Certificate2 Certificate { get { - return _cert; + return _certificate; } set { if (!canSet ("Certificate")) return; - _cert = value; + _certificate = value; } } @@ -403,8 +404,8 @@ namespace WebSocketSharp.Server /// Gets or sets the name of the realm associated with the server. /// /// - /// A that represents the name of the realm. The default value is - /// SECRET AREA. + /// A that represents the name of the realm. + /// The default value is "SECRET AREA". /// public string Realm { get { @@ -503,71 +504,24 @@ namespace WebSocketSharp.Server _state = ServerState.Stop; } - private void acceptRequestAsync (TcpClient client) - { - ThreadPool.QueueUserWorkItem ( - state => { - try { - var context = client.GetWebSocketContext (null, _secure, _cert, _logger); - if (_authSchemes != AuthenticationSchemes.Anonymous && - !authenticateRequest (_authSchemes, context)) - return; - - acceptWebSocket (context); - } - catch (Exception ex) { - _logger.Fatal (ex.ToString ()); - client.Close (); - } - }); - } - - private void acceptWebSocket (TcpListenerWebSocketContext context) - { - var reqUri = context.RequestUri; - if (reqUri == null) { - context.Close (HttpStatusCode.BadRequest); - return; - } - - if (_uri.IsAbsoluteUri) { - var req = reqUri.DnsSafeHost; - var expected = _uri.DnsSafeHost; - if (Uri.CheckHostName (req) == UriHostNameType.Dns && - Uri.CheckHostName (expected) == UriHostNameType.Dns && - req != expected) { - context.Close (HttpStatusCode.NotFound); - return; - } - } - - WebSocketServiceHost host; - if (!_services.TryGetServiceHostInternally (reqUri.AbsolutePath, out host)) { - context.Close (HttpStatusCode.NotImplemented); - return; - } - - host.StartSession (context); - } - private bool authenticateRequest ( AuthenticationSchemes scheme, TcpListenerWebSocketContext context) { - var challenge = scheme == AuthenticationSchemes.Basic - ? AuthenticationChallenge.CreateBasicChallenge (Realm).ToBasicString () - : scheme == AuthenticationSchemes.Digest - ? AuthenticationChallenge.CreateDigestChallenge (Realm).ToDigestString () - : null; + var chal = scheme == AuthenticationSchemes.Basic + ? AuthenticationChallenge.CreateBasicChallenge (Realm).ToBasicString () + : scheme == AuthenticationSchemes.Digest + ? AuthenticationChallenge.CreateDigestChallenge (Realm).ToDigestString () + : null; - if (challenge == null) { + if (chal == null) { context.Close (HttpStatusCode.Forbidden); return false; } var retry = -1; - var expected = scheme.ToString (); + var schm = scheme.ToString (); var realm = Realm; - var credentialsFinder = UserCredentialsFinder; + var credFinder = UserCredentialsFinder; Func auth = null; auth = () => { retry++; @@ -576,17 +530,17 @@ namespace WebSocketSharp.Server return false; } - var header = context.Headers ["Authorization"]; - if (header == null || !header.StartsWith (expected, StringComparison.OrdinalIgnoreCase)) { - context.SendAuthenticationChallenge (challenge); + var res = context.Headers["Authorization"]; + if (res == null || !res.StartsWith (schm, StringComparison.OrdinalIgnoreCase)) { + context.SendAuthenticationChallenge (chal); return auth (); } - context.SetUser (scheme, realm, credentialsFinder); + context.SetUser (scheme, realm, credFinder); if (context.IsAuthenticated) return true; - context.SendAuthenticationChallenge (challenge); + context.SendAuthenticationChallenge (chal); return auth (); }; @@ -607,9 +561,9 @@ namespace WebSocketSharp.Server return true; } - private string checkIfCertExists () + private string checkIfCertificateExists () { - return _secure && _cert == null + return _secure && _certificate == null ? "The secure connection requires a server certificate." : null; } @@ -624,11 +578,58 @@ namespace WebSocketSharp.Server _sync = new object (); } + private void processRequestAsync (TcpClient tcpClient) + { + ThreadPool.QueueUserWorkItem ( + state => { + try { + var context = tcpClient.GetWebSocketContext (null, _secure, _certificate, _logger); + if (_authSchemes != AuthenticationSchemes.Anonymous && + !authenticateRequest (_authSchemes, context)) + return; + + processWebSocketRequest (context); + } + catch (Exception ex) { + _logger.Fatal (ex.ToString ()); + tcpClient.Close (); + } + }); + } + + private void processWebSocketRequest (TcpListenerWebSocketContext context) + { + var uri = context.RequestUri; + if (uri == null) { + context.Close (HttpStatusCode.BadRequest); + return; + } + + if (_uri.IsAbsoluteUri) { + var actual = uri.DnsSafeHost; + var expected = _uri.DnsSafeHost; + if (Uri.CheckHostName (actual) == UriHostNameType.Dns && + Uri.CheckHostName (expected) == UriHostNameType.Dns && + actual != expected) { + context.Close (HttpStatusCode.NotFound); + return; + } + } + + WebSocketServiceHost host; + if (!_services.TryGetServiceHostInternally (uri.AbsolutePath, out host)) { + context.Close (HttpStatusCode.NotImplemented); + return; + } + + host.StartSession (context); + } + private void receiveRequest () { while (true) { try { - acceptRequestAsync (_listener.AcceptTcpClient ()); + processRequestAsync (_listener.AcceptTcpClient ()); } catch (SocketException ex) { _logger.Warn ("Receiving has been stopped.\nreason: " + ex.Message); @@ -664,7 +665,7 @@ namespace WebSocketSharp.Server if (result.PathAndQuery != "/") { result = null; - message = "Must not contain the path or query component: " + uriString; + message = "Includes the path or query component: " + uriString; return false; } @@ -687,9 +688,8 @@ namespace WebSocketSharp.Server /// A that represents the absolute path to the WebSocket service to add. /// /// - /// The type of the WebSocket service. - /// The TWithNew must inherit the class and must have a public - /// parameterless constructor. + /// The type of the WebSocket service. The TWithNew must inherit + /// the class and must have a public parameterless constructor. /// public void AddWebSocketService (string path) where TWithNew : WebSocketService, new () @@ -698,8 +698,8 @@ namespace WebSocketSharp.Server } /// - /// Adds the specified typed WebSocket service with the specified - /// and . + /// Adds the specified typed WebSocket service with the specified and + /// . /// /// /// @@ -707,14 +707,14 @@ namespace WebSocketSharp.Server /// from tail end of . /// /// - /// returns a initialized specified typed + /// returns an initialized specified typed /// instance. /// /// /// /// A that represents the absolute path to the WebSocket service to add. /// - /// + /// /// A Func<T> delegate that references the method used to initialize a new specified /// typed instance (a new /// instance). @@ -723,18 +723,18 @@ namespace WebSocketSharp.Server /// The type of the WebSocket service. The T must inherit the /// class. /// - public void AddWebSocketService (string path, Func constructor) + public void AddWebSocketService (string path, Func initializer) where T : WebSocketService { var msg = path.CheckIfValidServicePath () ?? - (constructor == null ? "'constructor' must not be null." : null); + (initializer == null ? "'initializer' is null." : null); if (msg != null) { _logger.Error (String.Format ("{0}\nservice path: {1}", msg, path)); return; } - var host = new WebSocketServiceHost (path, constructor, _logger); + var host = new WebSocketServiceHost (path, initializer, _logger); if (!KeepClean) host.KeepClean = false; @@ -772,7 +772,7 @@ namespace WebSocketSharp.Server public void Start () { lock (_sync) { - var msg = _state.CheckIfStartable () ?? checkIfCertExists (); + var msg = _state.CheckIfStartable () ?? checkIfCertificateExists (); if (msg != null) { _logger.Error (String.Format ("{0}\nstate: {1}\nsecure: {2}", msg, _state, _secure)); return; @@ -802,7 +802,7 @@ namespace WebSocketSharp.Server } stopListener (5000); - _services.Stop (new byte [0], true); + _services.Stop (new byte[0], true); _state = ServerState.Stop; } @@ -819,7 +819,7 @@ namespace WebSocketSharp.Server /// public void Stop (ushort code, string reason) { - byte [] data = null; + byte[] data = null; lock (_sync) { var msg = _state.CheckIfStart () ?? code.CheckIfValidCloseStatusCode () ?? @@ -846,15 +846,15 @@ namespace WebSocketSharp.Server /// and . /// /// - /// One of the enum values, represents the status code indicating - /// the reason for stop. + /// One of the enum values, represents the status code + /// indicating the reason for stop. /// /// /// A that represents the reason for stop. /// public void Stop (CloseStatusCode code, string reason) { - byte [] data = null; + byte[] data = null; lock (_sync) { var msg = _state.CheckIfStart () ?? (data = ((ushort) code).Append (reason)).CheckIfValidControlData ("reason");