diff --git a/Example2/App.config b/Example2/App.config index 5a8a3e5d..3a02690e 100644 --- a/Example2/App.config +++ b/Example2/App.config @@ -1,7 +1,7 @@ - + diff --git a/Example3/App.config b/Example3/App.config index c1a9c3b8..b65960f8 100644 --- a/Example3/App.config +++ b/Example3/App.config @@ -1,6 +1,8 @@ - + + + diff --git a/Example3/Program.cs b/Example3/Program.cs index e18c451a..a99bf230 100644 --- a/Example3/Program.cs +++ b/Example3/Program.cs @@ -1,5 +1,6 @@ using System; using System.Configuration; +using System.Security.Cryptography.X509Certificates; using WebSocketSharp; using WebSocketSharp.Net; using WebSocketSharp.Server; @@ -13,10 +14,14 @@ namespace Example3 public static void Main (string [] args) { _httpsv = new HttpServer (4649); + //_httpsv = new HttpServer (4649, true); #if DEBUG _httpsv.Log.Level = LogLevel.TRACE; #endif _httpsv.RootPath = ConfigurationManager.AppSettings ["RootPath"]; + //var certFile = ConfigurationManager.AppSettings ["ServerCertFile"]; + //var password = ConfigurationManager.AppSettings ["CertFilePassword"]; + //_httpsv.Certificate = new X509Certificate2 (certFile, password); //_httpsv.KeepClean = false; _httpsv.AddWebSocketService ("/Echo"); _httpsv.AddWebSocketService ("/Chat"); @@ -32,12 +37,16 @@ namespace Example3 }; _httpsv.Start (); - Console.WriteLine ("HTTP Server listening on port: {0} service path:", _httpsv.Port); - foreach (var path in _httpsv.ServicePaths) - Console.WriteLine (" {0}", path); - Console.WriteLine (); + if (_httpsv.IsListening) + { + Console.WriteLine ("HTTP Server listening on port: {0} service path:", _httpsv.Port); + foreach (var path in _httpsv.ServicePaths) + Console.WriteLine (" {0}", path); - Console.WriteLine ("Press enter key to stop the server..."); + Console.WriteLine (); + } + + Console.WriteLine ("Press Enter key to stop the server..."); Console.ReadLine (); _httpsv.Stop (); diff --git a/Example3/Public/Js/echotest.js b/Example3/Public/Js/echotest.js index 23d09d56..5157ded5 100644 --- a/Example3/Public/Js/echotest.js +++ b/Example3/Public/Js/echotest.js @@ -7,6 +7,7 @@ */ var wsUri = "ws://localhost:4649/Echo"; +//var wsUri = "wss://localhost:4649/Echo"; var output; function init(){ diff --git a/websocket-sharp/Ext.cs b/websocket-sharp/Ext.cs index 4b2b7610..2ca1a666 100644 --- a/websocket-sharp/Ext.cs +++ b/websocket-sharp/Ext.cs @@ -298,6 +298,11 @@ namespace WebSocketSharp return value.StartsWith ("permessage-"); } + internal static bool IsPortNumber (this int value) + { + return value > 0 && value < 65536; + } + internal static bool IsText (this string value) { int len = value.Length; diff --git a/websocket-sharp/Net/EndPointListener.cs b/websocket-sharp/Net/EndPointListener.cs index 0ab7cce3..15758e78 100644 --- a/websocket-sharp/Net/EndPointListener.cs +++ b/websocket-sharp/Net/EndPointListener.cs @@ -6,7 +6,7 @@ // Gonzalo Paniagua Javier (gonzalo@novell.com) // // Copyright (c) 2005 Novell, Inc. (http://www.novell.com) -// Copyright (c) 2012 sta.blockhead (sta.blockhead@gmail.com) +// 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 @@ -38,110 +38,117 @@ using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Threading; -namespace WebSocketSharp.Net { +namespace WebSocketSharp.Net +{ + internal sealed class EndPointListener + { + #region Private Fields - sealed class EndPointListener { - - #region Fields - - List all; // host = '+' - X509Certificate2 cert; - IPEndPoint endpoint; - AsymmetricAlgorithm key; - Dictionary prefixes; - bool secure; - Socket sock; - List unhandled; // host = '*' - Hashtable unregistered; + List _all; // host = '+' + X509Certificate2 _cert; + IPEndPoint _endpoint; + Dictionary _prefixes; + bool _secure; + Socket _socket; + List _unhandled; // host = '*' + Dictionary _unregistered; #endregion - #region Constructor + #region Public Constructors - public EndPointListener (IPAddress addr, int port, bool secure) + public EndPointListener ( + IPAddress address, + int port, + bool secure, + string certFolderPath, + X509Certificate2 defaultCert + ) { if (secure) { - this.secure = secure; - LoadCertificateAndKey (addr, port); + _secure = secure; + _cert = getCertificate (port, certFolderPath, defaultCert); + if (_cert == null) + throw new ArgumentException ("Server certificate not found."); } - endpoint = new IPEndPoint (addr, port); - sock = new Socket (addr.AddressFamily, SocketType.Stream, ProtocolType.Tcp); - sock.Bind (endpoint); - sock.Listen (500); + _endpoint = new IPEndPoint (address, port); + _socket = new Socket (address.AddressFamily, SocketType.Stream, ProtocolType.Tcp); + _socket.Bind (_endpoint); + _socket.Listen (500); var args = new SocketAsyncEventArgs (); args.UserToken = this; - args.Completed += OnAccept; - sock.AcceptAsync (args); - prefixes = new Dictionary (); - unregistered = Hashtable.Synchronized (new Hashtable ()); + args.Completed += onAccept; + _socket.AcceptAsync (args); + _prefixes = new Dictionary (); + _unregistered = new Dictionary (); } #endregion #region Private Methods - void AddSpecial (List coll, ListenerPrefix prefix) + private static void addSpecial (List prefixes, ListenerPrefix prefix) { - if (coll == null) + if (prefixes == null) return; - foreach (ListenerPrefix p in coll) { + foreach (var p in prefixes) if (p.Path == prefix.Path) // TODO: code throw new HttpListenerException (400, "Prefix already in use."); - } - coll.Add (prefix); + + prefixes.Add (prefix); } - void CheckIfRemove () + private void checkIfRemove () { - if (prefixes.Count > 0) + if (_prefixes.Count > 0) return; - var list = unhandled; - if (list != null && list.Count > 0) + if (_unhandled != null && _unhandled.Count > 0) return; - list = all; - if (list != null && list.Count > 0) + if (_all != null && _all.Count > 0) return; - EndPointManager.RemoveEndPoint (this, endpoint); + EndPointManager.RemoveEndPoint (this, _endpoint); } - RSACryptoServiceProvider CreateRSAFromFile (string filename) + private static RSACryptoServiceProvider createRSAFromFile (string filename) { - if (filename == null) - throw new ArgumentNullException ("filename"); - var rsa = new RSACryptoServiceProvider (); byte[] pvk = null; - using (FileStream fs = File.Open (filename, FileMode.Open, FileAccess.Read, FileShare.Read)) + using (var fs = File.Open (filename, FileMode.Open, FileAccess.Read, FileShare.Read)) { pvk = new byte [fs.Length]; fs.Read (pvk, 0, pvk.Length); } + rsa.ImportCspBlob (pvk); return rsa; } - void LoadCertificateAndKey (IPAddress addr, int port) + private static X509Certificate2 getCertificate ( + int port, string certFolderPath, X509Certificate2 defaultCert) { - // Actually load the certificate try { - string dirname = Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData); - string path = Path.Combine (dirname, ".mono"); - path = Path.Combine (path, "httplistener"); - string cert_file = Path.Combine (path, String.Format ("{0}.cer", port)); - string pvk_file = Path.Combine (path, String.Format ("{0}.pvk", port)); - cert = new X509Certificate2 (cert_file); - key = CreateRSAFromFile (pvk_file); - } catch { - // ignore errors + var cer = Path.Combine (certFolderPath, String.Format ("{0}.cer", port)); + var key = Path.Combine (certFolderPath, String.Format ("{0}.key", port)); + if (File.Exists (cer) && File.Exists (key)) + { + var cert = new X509Certificate2 (cer); + cert.PrivateKey = createRSAFromFile (key); + + return cert; + } } + catch { + } + + return defaultCert; } - HttpListener MatchFromList ( + private static HttpListener matchFromList ( string host, string path, List list, out ListenerPrefix prefix) { prefix = null; @@ -149,14 +156,15 @@ namespace WebSocketSharp.Net { return null; HttpListener best_match = null; - int best_length = -1; - - foreach (ListenerPrefix p in list) { - string ppath = p.Path; + var best_length = -1; + foreach (var p in list) + { + var ppath = p.Path; if (ppath.Length < best_length) continue; - if (path.StartsWith (ppath)) { + if (path.StartsWith (ppath)) + { best_length = ppath.Length; best_match = p.Listener; prefix = p; @@ -166,101 +174,117 @@ namespace WebSocketSharp.Net { return best_match; } - static void OnAccept (object sender, EventArgs e) + private static void onAccept (object sender, EventArgs e) { - SocketAsyncEventArgs args = (SocketAsyncEventArgs) e; - EndPointListener epl = (EndPointListener) args.UserToken; + var args = (SocketAsyncEventArgs) e; + var epListener = (EndPointListener) args.UserToken; Socket accepted = null; - if (args.SocketError == SocketError.Success) { + if (args.SocketError == SocketError.Success) + { accepted = args.AcceptSocket; args.AcceptSocket = null; } try { - if (epl.sock != null) - epl.sock.AcceptAsync (args); - } catch { - if (accepted != null) { - try { - accepted.Close (); - } catch {} - accepted = null; - } - } + epListener._socket.AcceptAsync (args); + } + catch { + if (accepted != null) + accepted.Close (); + + return; + } if (accepted == null) return; - if (epl.secure && (epl.cert == null || epl.key == null)) { - accepted.Close (); - return; + HttpConnection conn = null; + try { + conn = new HttpConnection (accepted, epListener, epListener._secure, epListener._cert); + lock (((ICollection) epListener._unregistered).SyncRoot) + { + epListener._unregistered [conn] = conn; + } + + conn.BeginReadRequest (); + } + catch { + if (conn != null) + { + conn.Close (true); + return; + } + + accepted.Close (); } - HttpConnection conn = new HttpConnection (accepted, epl, epl.secure, epl.cert, epl.key); - epl.unregistered [conn] = conn; - conn.BeginReadRequest (); } - bool RemoveSpecial (List coll, ListenerPrefix prefix) + private static bool removeSpecial (List prefixes, ListenerPrefix prefix) { - if (coll == null) + if (prefixes == null) return false; - int c = coll.Count; - for (int i = 0; i < c; i++) { - ListenerPrefix p = coll [i]; - if (p.Path == prefix.Path) { - coll.RemoveAt (i); + var count = prefixes.Count; + for (int i = 0; i < count; i++) + { + if (prefixes [i].Path == prefix.Path) + { + prefixes.RemoveAt (i); return true; } } + return false; } - HttpListener SearchListener (Uri uri, out ListenerPrefix prefix) + private HttpListener searchListener (Uri uri, out ListenerPrefix prefix) { prefix = null; if (uri == null) return null; - string host = uri.Host; - int port = uri.Port; - string path = HttpUtility.UrlDecode (uri.AbsolutePath); - string path_slash = path [path.Length - 1] == '/' ? path : path + "/"; - + var host = uri.Host; + var port = uri.Port; + var path = HttpUtility.UrlDecode (uri.AbsolutePath); + var path_slash = path [path.Length - 1] == '/' ? path : path + "/"; HttpListener best_match = null; - int best_length = -1; - - if (host != null && host != "") { - var p_ro = prefixes; - foreach (ListenerPrefix p in p_ro.Keys) { - string ppath = p.Path; + var best_length = -1; + if (host != null && host.Length > 0) + { + foreach (var p in _prefixes.Keys) + { + var ppath = p.Path; if (ppath.Length < best_length) continue; if (p.Host != host || p.Port != port) continue; - if (path.StartsWith (ppath) || path_slash.StartsWith (ppath)) { + if (path.StartsWith (ppath) || path_slash.StartsWith (ppath)) + { best_length = ppath.Length; - best_match = p_ro [p]; + best_match = _prefixes [p]; prefix = p; } } + if (best_length != -1) return best_match; } - var list = unhandled; - best_match = MatchFromList (host, path, list, out prefix); + var list = _unhandled; + best_match = matchFromList (host, path, list, out prefix); if (path != path_slash && best_match == null) - best_match = MatchFromList (host, path_slash, list, out prefix); + best_match = matchFromList (host, path_slash, list, out prefix); + if (best_match != null) return best_match; - list = all; - best_match = MatchFromList (host, path, list, out prefix); + list = _all; + best_match = matchFromList (host, path, list, out prefix); if (path != path_slash && best_match == null) - best_match = MatchFromList (host, path_slash, list, out prefix); + best_match = matchFromList (host, path_slash, list, out prefix); + if (best_match != null) return best_match; @@ -269,11 +293,22 @@ namespace WebSocketSharp.Net { #endregion - #region Internal Method + #region Internal Methods - internal void RemoveConnection (HttpConnection conn) + internal static bool CertificateExists (int port, string certFolderPath) { - unregistered.Remove (conn); + var cer = Path.Combine (certFolderPath, String.Format ("{0}.cer", port)); + var key = Path.Combine (certFolderPath, String.Format ("{0}.key", port)); + + return File.Exists (cer) && File.Exists (key); + } + + internal void RemoveConnection (HttpConnection connection) + { + lock (((ICollection) _unregistered).SyncRoot) + { + _unregistered.Remove (connection); + } } #endregion @@ -282,111 +317,124 @@ namespace WebSocketSharp.Net { public void AddPrefix (ListenerPrefix prefix, HttpListener listener) { - List current; - List future; - if (prefix.Host == "*") { + List current, future; + if (prefix.Host == "*") + { do { - current = unhandled; - future = (current != null) - ? new List (current) - : new List (); + current = _unhandled; + future = current != null + ? new List (current) + : new List (); prefix.Listener = listener; - AddSpecial (future, prefix); - } while (Interlocked.CompareExchange (ref unhandled, future, current) != current); + addSpecial (future, prefix); + } while (Interlocked.CompareExchange (ref _unhandled, future, current) != current); + return; } - if (prefix.Host == "+") { + if (prefix.Host == "+") + { do { - current = all; - future = (current != null) - ? new List (current) - : new List (); + current = _all; + future = current != null + ? new List (current) + : new List (); prefix.Listener = listener; - AddSpecial (future, prefix); - } while (Interlocked.CompareExchange (ref all, future, current) != current); + addSpecial (future, prefix); + } while (Interlocked.CompareExchange (ref _all, future, current) != current); + return; } Dictionary prefs, p2; do { - prefs = prefixes; - if (prefs.ContainsKey (prefix)) { + prefs = _prefixes; + if (prefs.ContainsKey (prefix)) + { HttpListener other = prefs [prefix]; if (other != listener) // TODO: code. throw new HttpListenerException (400, "There's another listener for " + prefix); + return; } + p2 = new Dictionary (prefs); p2 [prefix] = listener; - } while (Interlocked.CompareExchange (ref prefixes, p2, prefs) != prefs); + } while (Interlocked.CompareExchange (ref _prefixes, p2, prefs) != prefs); } public bool BindContext (HttpListenerContext context) { - HttpListenerRequest req = context.Request; + var req = context.Request; ListenerPrefix prefix; - HttpListener listener = SearchListener (req.Url, out prefix); + var listener = searchListener (req.Url, out prefix); if (listener == null) return false; context.Listener = listener; context.Connection.Prefix = prefix; + return true; } public void Close () { - sock.Close (); - lock (unregistered.SyncRoot) { - Hashtable copy = (Hashtable) unregistered.Clone (); - foreach (HttpConnection c in copy.Keys) - c.Close (true); + _socket.Close (); + lock (((ICollection) _unregistered).SyncRoot) + { + var copy = new Dictionary (_unregistered); + foreach (var conn in copy.Keys) + conn.Close (true); + copy.Clear (); - unregistered.Clear (); + _unregistered.Clear (); } } public void RemovePrefix (ListenerPrefix prefix, HttpListener listener) { - List current; - List future; - if (prefix.Host == "*") { + List current, future; + if (prefix.Host == "*") + { do { - current = unhandled; - future = (current != null) - ? new List (current) - : new List (); - if (!RemoveSpecial (future, prefix)) - break; // Prefix not found - } while (Interlocked.CompareExchange (ref unhandled, future, current) != current); - CheckIfRemove (); + current = _unhandled; + future = current != null + ? new List (current) + : new List (); + if (!removeSpecial (future, prefix)) + break; // Prefix not found. + } while (Interlocked.CompareExchange (ref _unhandled, future, current) != current); + + checkIfRemove (); return; } - if (prefix.Host == "+") { + if (prefix.Host == "+") + { do { - current = all; - future = (current != null) - ? new List (current) - : new List (); - if (!RemoveSpecial (future, prefix)) - break; // Prefix not found - } while (Interlocked.CompareExchange (ref all, future, current) != current); - CheckIfRemove (); + current = _all; + future = current != null + ? new List (current) + : new List (); + if (!removeSpecial (future, prefix)) + break; // Prefix not found. + } while (Interlocked.CompareExchange (ref _all, future, current) != current); + + checkIfRemove (); return; } Dictionary prefs, p2; do { - prefs = prefixes; + prefs = _prefixes; if (!prefs.ContainsKey (prefix)) break; p2 = new Dictionary (prefs); p2.Remove (prefix); - } while (Interlocked.CompareExchange (ref prefixes, p2, prefs) != prefs); - CheckIfRemove (); + } while (Interlocked.CompareExchange (ref _prefixes, p2, prefs) != prefs); + + checkIfRemove (); } public void UnbindContext (HttpListenerContext context) diff --git a/websocket-sharp/Net/EndPointManager.cs b/websocket-sharp/Net/EndPointManager.cs index f80b40af..85abc33d 100644 --- a/websocket-sharp/Net/EndPointManager.cs +++ b/websocket-sharp/Net/EndPointManager.cs @@ -6,7 +6,7 @@ // Gonzalo Paniagua Javier (gonzalo@ximian.com) // // Copyright (c) 2005 Novell, Inc. (http://www.novell.com) -// Copyright (c) 2012 sta.blockhead (sta.blockhead@gmail.com) +// 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 @@ -33,17 +33,17 @@ using System.Collections; using System.Collections.Generic; using System.Net; -namespace WebSocketSharp.Net { +namespace WebSocketSharp.Net +{ + internal sealed class EndPointManager + { + #region Private Fields - sealed class EndPointManager { - - #region Fields - - static Dictionary> ip_to_endpoints = new Dictionary> (); + private static Dictionary> _ipToEndpoints = new Dictionary> (); #endregion - #region Constructor + #region Private Constructors private EndPointManager () { @@ -53,109 +53,122 @@ namespace WebSocketSharp.Net { #region Private Methods - static void AddPrefixInternal (string p, HttpListener listener) + private static void addPrefix (string uriPrefix, HttpListener httpListener) { - ListenerPrefix lp = new ListenerPrefix (p); - if (lp.Path.IndexOf ('%') != -1) + var prefix = new ListenerPrefix (uriPrefix); + if (prefix.Path.IndexOf ('%') != -1) throw new HttpListenerException (400, "Invalid path."); - if (lp.Path.IndexOf ("//", StringComparison.Ordinal) != -1) // TODO: Code? + if (prefix.Path.IndexOf ("//", StringComparison.Ordinal) != -1) // TODO: Code? throw new HttpListenerException (400, "Invalid path."); // Always listens on all the interfaces, no matter the host name/ip used. - EndPointListener epl = GetEPListener (IPAddress.Any, lp.Port, listener, lp.Secure); - epl.AddPrefix (lp, listener); + var epListener = getEndPointListener (IPAddress.Any, prefix.Port, httpListener, prefix.Secure); + epListener.AddPrefix (prefix, httpListener); } - static EndPointListener GetEPListener (IPAddress addr, int port, HttpListener listener, bool secure) + private static EndPointListener getEndPointListener ( + IPAddress address, int port, HttpListener httpListener, bool secure) { - Dictionary p = null; - if (ip_to_endpoints.ContainsKey (addr)) { - p = ip_to_endpoints [addr]; - } else { - p = new Dictionary (); - ip_to_endpoints [addr] = p; + Dictionary endpoints = null; + if (_ipToEndpoints.ContainsKey (address)) + { + endpoints = _ipToEndpoints [address]; + } + else + { + endpoints = new Dictionary (); + _ipToEndpoints [address] = endpoints; } - EndPointListener epl = null; - if (p.ContainsKey (port)) { - epl = p [port]; - } else { - epl = new EndPointListener (addr, port, secure); - p [port] = epl; + EndPointListener epListener = null; + if (endpoints.ContainsKey (port)) + { + epListener = endpoints [port]; + } + else + { + epListener = new EndPointListener ( + address, port, secure, httpListener.CertificateFolderPath, httpListener.DefaultCertificate); + endpoints [port] = epListener; } - return epl; + return epListener; } - static void RemovePrefixInternal (string prefix, HttpListener listener) + private static void removePrefix (string uriPrefix, HttpListener httpListener) { - ListenerPrefix lp = new ListenerPrefix (prefix); - if (lp.Path.IndexOf ('%') != -1) + var prefix = new ListenerPrefix (uriPrefix); + if (prefix.Path.IndexOf ('%') != -1) return; - if (lp.Path.IndexOf ("//", StringComparison.Ordinal) != -1) + if (prefix.Path.IndexOf ("//", StringComparison.Ordinal) != -1) return; - EndPointListener epl = GetEPListener (IPAddress.Any, lp.Port, listener, lp.Secure); - epl.RemovePrefix (lp, listener); + var epListener = getEndPointListener (IPAddress.Any, prefix.Port, httpListener, prefix.Secure); + epListener.RemovePrefix (prefix, httpListener); } #endregion #region Public Methods - public static void AddListener (HttpListener listener) + public static void AddListener (HttpListener httpListener) { - List added = new List (); - try { - lock (((ICollection)ip_to_endpoints).SyncRoot) { - foreach (string prefix in listener.Prefixes) { - AddPrefixInternal (prefix, listener); + var added = new List (); + lock (((ICollection) _ipToEndpoints).SyncRoot) + { + try { + foreach (var prefix in httpListener.Prefixes) + { + addPrefix (prefix, httpListener); added.Add (prefix); } } - } catch { - foreach (string prefix in added) { - RemovePrefix (prefix, listener); - } - throw; - } - } + catch { + foreach (var prefix in added) + removePrefix (prefix, httpListener); - public static void AddPrefix (string prefix, HttpListener listener) - { - lock (((ICollection)ip_to_endpoints).SyncRoot) { - AddPrefixInternal (prefix, listener); - } - } - - public static void RemoveEndPoint (EndPointListener epl, IPEndPoint ep) - { - lock (((ICollection)ip_to_endpoints).SyncRoot) { - Dictionary p = null; - p = ip_to_endpoints [ep.Address]; - p.Remove (ep.Port); - if (p.Count == 0) { - ip_to_endpoints.Remove (ep.Address); - } - epl.Close (); - } - } - - public static void RemoveListener (HttpListener listener) - { - lock (((ICollection)ip_to_endpoints).SyncRoot) { - foreach (string prefix in listener.Prefixes) { - RemovePrefixInternal (prefix, listener); + throw; } } } - public static void RemovePrefix (string prefix, HttpListener listener) + public static void AddPrefix (string uriPrefix, HttpListener httpListener) { - lock (((ICollection)ip_to_endpoints).SyncRoot) { - RemovePrefixInternal (prefix, listener); + lock (((ICollection) _ipToEndpoints).SyncRoot) + { + addPrefix (uriPrefix, httpListener); + } + } + + public static void RemoveEndPoint (EndPointListener epListener, IPEndPoint endpoint) + { + lock (((ICollection) _ipToEndpoints).SyncRoot) + { + var endpoints = _ipToEndpoints [endpoint.Address]; + endpoints.Remove (endpoint.Port); + if (endpoints.Count == 0) + _ipToEndpoints.Remove (endpoint.Address); + + epListener.Close (); + } + } + + public static void RemoveListener (HttpListener httpListener) + { + lock (((ICollection) _ipToEndpoints).SyncRoot) + { + foreach (var prefix in httpListener.Prefixes) + removePrefix (prefix, httpListener); + } + } + + public static void RemovePrefix (string uriPrefix, HttpListener httpListener) + { + lock (((ICollection) _ipToEndpoints).SyncRoot) + { + removePrefix (uriPrefix, httpListener); } } diff --git a/websocket-sharp/Net/HttpConnection.cs b/websocket-sharp/Net/HttpConnection.cs index 2ebf16a9..2578f4bb 100644 --- a/websocket-sharp/Net/HttpConnection.cs +++ b/websocket-sharp/Net/HttpConnection.cs @@ -7,7 +7,7 @@ // Gonzalo Paniagua Javier (gonzalo@novell.com) // // Copyright (c) 2005 Novell, Inc. (http://www.novell.com) -// Copyright (c) 2012-2013 sta.blockhead (sta.blockhead@gmail.com) +// 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 @@ -41,10 +41,10 @@ using System.Text; using System.Threading; using WebSocketSharp.Net.Security; -namespace WebSocketSharp.Net { - - internal sealed class HttpConnection { - +namespace WebSocketSharp.Net +{ + internal sealed class HttpConnection + { #region Enums enum InputState { @@ -76,7 +76,6 @@ namespace WebSocketSharp.Net { private EndPointListener _epListener; private InputState _inputState; private RequestStream _inputStream; - private AsymmetricAlgorithm _key; private HttpListener _lastListener; private LineState _lineState; private ResponseStream _outputStream; @@ -95,21 +94,23 @@ namespace WebSocketSharp.Net { #region Public Constructors public HttpConnection ( - Socket socket, - EndPointListener listener, - bool secure, - X509Certificate2 cert, - AsymmetricAlgorithm key) + Socket socket, + EndPointListener listener, + bool secure, + X509Certificate2 cert + ) { _socket = socket; _epListener = listener; _secure = secure; - _key = key; var netStream = new NetworkStream (socket, false); - if (!secure) { + if (!secure) + { _stream = netStream; - } else { + } + else + { var sslStream = new SslStream (netStream, false); sslStream.AuthenticateAsServer (cert); _stream = sslStream; @@ -180,8 +181,10 @@ namespace WebSocketSharp.Net { try { _socket.Close (); - } catch { - } finally { + } + catch { + } + finally { _socket = null; } @@ -203,11 +206,6 @@ namespace WebSocketSharp.Net { _timeout = 90000; // 90k ms for first request, 15k ms from then on. } - private AsymmetricAlgorithm OnPVKSelection (X509Certificate certificate, string targetHost) - { - return _key; - } - private static void OnRead (IAsyncResult asyncResult) { var conn = (HttpConnection) asyncResult.AsyncState; diff --git a/websocket-sharp/Net/HttpListener.cs b/websocket-sharp/Net/HttpListener.cs index 8ed306cd..d55a5b07 100644 --- a/websocket-sharp/Net/HttpListener.cs +++ b/websocket-sharp/Net/HttpListener.cs @@ -32,6 +32,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Security.Cryptography.X509Certificates; using System.Threading; // TODO: logging @@ -45,9 +46,11 @@ namespace WebSocketSharp.Net { #region Private Fields AuthenticationSchemes auth_schemes; - AuthenticationSchemeSelector auth_selector; + AuthenticationSchemeSelector auth_selector; + string cert_folder_path; Dictionary connections; List ctx_queue; + X509Certificate2 default_cert; bool disposed; bool ignore_write_exceptions; bool listening; @@ -121,6 +124,59 @@ namespace WebSocketSharp.Net { } } + /// + /// Gets or sets the path to the folder stored the certificate files used to authenticate + /// the server on the secure connection. + /// + /// + /// This property represents the path to the folder stored the certificate files associated with + /// the port number of each added URI prefix. A set of the certificate files is a pair of the + /// 'port number'.cer (DER) and 'port number'.key (DER, RSA Private Key). + /// + /// + /// A that contains the path to the certificate folder. The default value is + /// the result of Environment.GetFolderPath (). + /// + /// + /// This object has been closed. + /// + public string CertificateFolderPath { + get { + CheckDisposed (); + if (cert_folder_path.IsNullOrEmpty ()) + cert_folder_path = Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData); + + return cert_folder_path; + } + + set { + CheckDisposed (); + cert_folder_path = value; + } + } + + /// + /// Gets or sets the default certificate used to authenticate the server on the secure connection. + /// + /// + /// A used to authenticate the server if the certificate associated with + /// the port number of each added URI prefix is not found in the . + /// + /// + /// This object has been closed. + /// + public X509Certificate2 DefaultCertificate { + get { + CheckDisposed (); + return default_cert; + } + + set { + CheckDisposed (); + default_cert = value; + } + } + /// /// Gets or sets a value indicating whether the returns exceptions /// that occur when sending the response to the client. diff --git a/websocket-sharp/Net/HttpListenerPrefixCollection.cs b/websocket-sharp/Net/HttpListenerPrefixCollection.cs index 4c5a35a3..b83a254f 100644 --- a/websocket-sharp/Net/HttpListenerPrefixCollection.cs +++ b/websocket-sharp/Net/HttpListenerPrefixCollection.cs @@ -6,7 +6,7 @@ // Gonzalo Paniagua Javier (gonzalo@novell.com) // // Copyright (c) 2005 Novell, Inc. (http://www.novell.com) -// Copyright (c) 2012-2013 sta.blockhead (sta.blockhead@gmail.com) +// 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 @@ -32,40 +32,40 @@ using System; using System.Collections; using System.Collections.Generic; -namespace WebSocketSharp.Net { - +namespace WebSocketSharp.Net +{ /// /// Provides the collection used to store the URI prefixes for the . /// public class HttpListenerPrefixCollection : ICollection, IEnumerable, IEnumerable { - #region Fields + #region Private Fields - HttpListener listener; - List prefixes; + private HttpListener _listener; + private List _prefixes; #endregion - #region Private Constructor + #region Private Constructors private HttpListenerPrefixCollection () { - prefixes = new List (); + _prefixes = new List (); } #endregion - #region Internal Constructor + #region Internal Constructors internal HttpListenerPrefixCollection (HttpListener listener) : this () { - this.listener = listener; + _listener = listener; } #endregion - #region Properties + #region Public Properties /// /// Gets the number of prefixes contained in the . @@ -74,43 +74,35 @@ namespace WebSocketSharp.Net { /// A that contains the number of prefixes. /// public int Count { - get { return prefixes.Count; } + get { + return _prefixes.Count; + } } /// - /// Gets a value indicating whether access to the is read-only. + /// Gets a value indicating whether access to the + /// is read-only. /// /// /// Always returns false. /// public bool IsReadOnly { - get { return false; } + get { + return false; + } } /// - /// Gets a value indicating whether access to the is synchronized. + /// Gets a value indicating whether access to the + /// is synchronized. /// /// /// Always returns false. /// public bool IsSynchronized { - get { return false; } - } - - #endregion - - #region Explicit Interface Implementation - - /// - /// Gets an object that can be used to iterate through the . - /// - /// - /// An object that implements the interface and provides access to - /// the URI prefix strings in the . - /// - IEnumerator IEnumerable.GetEnumerator () - { - return prefixes.GetEnumerator (); + get { + return false; + } } #endregion @@ -126,33 +118,38 @@ namespace WebSocketSharp.Net { /// /// is . /// + /// + /// is invalid. + /// /// - /// The associated with this is closed. + /// The associated with this + /// is closed. /// public void Add (string uriPrefix) { - listener.CheckDisposed (); - ListenerPrefix.CheckUri (uriPrefix); - if (prefixes.Contains (uriPrefix)) + _listener.CheckDisposed (); + ListenerPrefix.CheckUriPrefix (uriPrefix); + if (_prefixes.Contains (uriPrefix)) return; - prefixes.Add (uriPrefix); - if (listener.IsListening) - EndPointManager.AddPrefix (uriPrefix, listener); + _prefixes.Add (uriPrefix); + if (_listener.IsListening) + EndPointManager.AddPrefix (uriPrefix, _listener); } /// /// Removes all URI prefixes from the . /// /// - /// The associated with this is closed. + /// The associated with this + /// is closed. /// public void Clear () { - listener.CheckDisposed (); - prefixes.Clear (); - if (listener.IsListening) - EndPointManager.RemoveListener (listener); + _listener.CheckDisposed (); + _prefixes.Clear (); + if (_listener.IsListening) + EndPointManager.RemoveListener (_listener); } /// @@ -160,7 +157,7 @@ namespace WebSocketSharp.Net { /// the specified . /// /// - /// true if the contains the specified ; + /// true if the contains ; /// otherwise, false. /// /// @@ -170,51 +167,60 @@ namespace WebSocketSharp.Net { /// is . /// /// - /// The associated with this is closed. + /// The associated with this + /// is closed. /// public bool Contains (string uriPrefix) { - listener.CheckDisposed (); + _listener.CheckDisposed (); if (uriPrefix == null) throw new ArgumentNullException ("uriPrefix"); - return prefixes.Contains (uriPrefix); + return _prefixes.Contains (uriPrefix); } /// - /// Copies the contents of the to the specified . + /// Copies the contents of the to + /// the specified . /// /// - /// An that receives the URI prefix strings in the . + /// An that receives the URI prefix strings + /// in the . /// /// - /// An that contains the zero-based index in at which copying begins. + /// An that contains the zero-based index in + /// at which copying begins. /// /// - /// The associated with this is closed. + /// The associated with this + /// is closed. /// public void CopyTo (Array array, int offset) { - listener.CheckDisposed (); - ((ICollection) prefixes).CopyTo (array, offset); + _listener.CheckDisposed (); + ((ICollection) _prefixes).CopyTo (array, offset); } /// - /// Copies the contents of the to the specified array of . + /// Copies the contents of the to + /// the specified array of . /// /// - /// An array of that receives the URI prefix strings in the . + /// An array of that receives the URI prefix strings + /// in the . /// /// - /// An that contains the zero-based index in at which copying begins. + /// An that contains the zero-based index in + /// at which copying begins. /// /// - /// The associated with this is closed. + /// The associated with this + /// is closed. /// public void CopyTo (string [] array, int offset) { - listener.CheckDisposed (); - prefixes.CopyTo (array, offset); + _listener.CheckDisposed (); + _prefixes.CopyTo (array, offset); } /// @@ -226,15 +232,16 @@ namespace WebSocketSharp.Net { /// public IEnumerator GetEnumerator () { - return prefixes.GetEnumerator (); + return _prefixes.GetEnumerator (); } /// - /// Removes the specified from the list of prefixes in the . + /// Removes the specified from the list of prefixes + /// in the . /// /// - /// true if the was found in the - /// and removed; otherwise, false. + /// true if is successfully found and removed; + /// otherwise, false. /// /// /// A that contains a URI prefix to remove. @@ -243,21 +250,39 @@ namespace WebSocketSharp.Net { /// is . /// /// - /// The associated with this is closed. + /// The associated with this + /// is closed. /// public bool Remove (string uriPrefix) { - listener.CheckDisposed (); + _listener.CheckDisposed (); if (uriPrefix == null) throw new ArgumentNullException ("uriPrefix"); - bool result = prefixes.Remove (uriPrefix); - if (result && listener.IsListening) - EndPointManager.RemovePrefix (uriPrefix, listener); + var result = _prefixes.Remove (uriPrefix); + if (result && _listener.IsListening) + EndPointManager.RemovePrefix (uriPrefix, _listener); return result; } #endregion + + #region Explicit Interface Implementation + + /// + /// Gets an object that can be used to iterate through the . + /// + /// + /// An object that implements the interface and provides access to + /// the URI prefix strings in the . + /// + IEnumerator IEnumerable.GetEnumerator () + { + return _prefixes.GetEnumerator (); + } + + #endregion + } } diff --git a/websocket-sharp/Net/ListenerPrefix.cs b/websocket-sharp/Net/ListenerPrefix.cs index 57fca953..235081f0 100644 --- a/websocket-sharp/Net/ListenerPrefix.cs +++ b/websocket-sharp/Net/ListenerPrefix.cs @@ -7,6 +7,7 @@ // Oleg Mihailik (mihailik gmail co_m) // // Copyright (c) 2005 Novell, Inc. (http://www.novell.com) +// 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 @@ -31,156 +32,177 @@ using System; using System.Net; -namespace WebSocketSharp.Net { - - sealed class ListenerPrefix { - +namespace WebSocketSharp.Net +{ + internal sealed class ListenerPrefix + { #region Private Fields - IPAddress [] addresses; - string host; - string original; - string path; - ushort port; - bool secure; + IPAddress [] _addresses; + string _host; + string _original; + string _path; + ushort _port; + bool _secure; #endregion - #region Public Field + #region Public Fields public HttpListener Listener; #endregion - #region Constructor + #region Public Constructors - // Must be called after calling ListenerPrefix.CheckUri. - public ListenerPrefix (string prefix) + // Must be called after calling ListenerPrefix.CheckUriPrefix. + public ListenerPrefix (string uriPrefix) { - original = prefix; - Parse (prefix); + _original = uriPrefix; + parse (uriPrefix); } #endregion - #region Properties + #region Public Properties public IPAddress [] Addresses { - get { return addresses; } - set { addresses = value; } + get { + return _addresses; + } + + set { + _addresses = value; + } } public string Host { - get { return host; } - } - - public int Port { - get { return (int) port; } + get { + return _host; + } } public string Path { - get { return path; } + get { + return _path; + } + } + + public int Port { + get { + return (int) _port; + } } public bool Secure { - get { return secure; } + get { + return _secure; + } } #endregion - #region Private Method + #region Private Methods - void Parse (string uri) + private void parse (string uriPrefix) { - int default_port = (uri.StartsWith ("http://")) ? 80 : 443; + int default_port = uriPrefix.StartsWith ("http://") ? 80 : 443; if (default_port == 443) - secure = true; + _secure = true; - int length = uri.Length; - int start_host = uri.IndexOf (':') + 3; - int colon = uri.IndexOf (':', start_host, length - start_host); + int length = uriPrefix.Length; + int start_host = uriPrefix.IndexOf (':') + 3; + int colon = uriPrefix.IndexOf (':', start_host, length - start_host); int root; - if (colon > 0) { - host = uri.Substring (start_host, colon - start_host); - root = uri.IndexOf ('/', colon, length - colon); - port = (ushort) Int32.Parse (uri.Substring (colon + 1, root - colon - 1)); - path = uri.Substring (root); - } else { - root = uri.IndexOf ('/', start_host, length - start_host); - host = uri.Substring (start_host, root - start_host); - path = uri.Substring (root); - port = (ushort) default_port; + if (colon > 0) + { + root = uriPrefix.IndexOf ('/', colon, length - colon); + _host = uriPrefix.Substring (start_host, colon - start_host); + _port = (ushort) Int32.Parse (uriPrefix.Substring (colon + 1, root - colon - 1)); + _path = uriPrefix.Substring (root); + } + else + { + root = uriPrefix.IndexOf ('/', start_host, length - start_host); + _host = uriPrefix.Substring (start_host, root - start_host); + _port = (ushort) default_port; + _path = uriPrefix.Substring (root); } - if (path.Length != 1) - path = path.Substring (0, path.Length - 1); + if (_path.Length != 1) + _path = _path.Substring (0, _path.Length - 1); } #endregion #region public Methods - public static void CheckUri (string uri) + public static void CheckUriPrefix (string uriPrefix) { - if (uri == null) - throw new ArgumentNullException ("uri"); + if (uriPrefix == null) + throw new ArgumentNullException ("uriPrefix"); - int default_port = (uri.StartsWith ("http://")) ? 80 : -1; + int default_port = uriPrefix.StartsWith ("http://") ? 80 : -1; if (default_port == -1) - default_port = (uri.StartsWith ("https://")) ? 443 : -1; + default_port = uriPrefix.StartsWith ("https://") ? 443 : -1; + if (default_port == -1) throw new ArgumentException ("Only 'http' and 'https' schemes are supported."); - int length = uri.Length; - int start_host = uri.IndexOf (':') + 3; + int length = uriPrefix.Length; + int start_host = uriPrefix.IndexOf (':') + 3; if (start_host >= length) throw new ArgumentException ("No host specified."); - int colon = uri.IndexOf (':', start_host, length - start_host); + int colon = uriPrefix.IndexOf (':', start_host, length - start_host); if (start_host == colon) throw new ArgumentException ("No host specified."); int root; - if (colon > 0) { - root = uri.IndexOf ('/', colon, length - colon); + if (colon > 0) + { + root = uriPrefix.IndexOf ('/', colon, length - colon); if (root == -1) throw new ArgumentException ("No path specified."); try { - int p = Int32.Parse (uri.Substring (colon + 1, root - colon - 1)); - if (p <= 0 || p >= 65536) + int port = Int32.Parse (uriPrefix.Substring (colon + 1, root - colon - 1)); + if (port <= 0 || port >= 65536) throw new Exception (); - } catch { + } + catch { throw new ArgumentException ("Invalid port."); } - } else { - root = uri.IndexOf ('/', start_host, length - start_host); + } + else + { + root = uriPrefix.IndexOf ('/', start_host, length - start_host); if (root == -1) throw new ArgumentException ("No path specified."); } - if (uri [uri.Length - 1] != '/') - throw new ArgumentException ("The prefix must end with '/'."); + if (uriPrefix [uriPrefix.Length - 1] != '/') + throw new ArgumentException ("The URI prefix must end with '/'."); } // Equals and GetHashCode are required to detect duplicates in HttpListenerPrefixCollection. - public override bool Equals (object o) + public override bool Equals (object obj) { - var other = o as ListenerPrefix; + var other = obj as ListenerPrefix; if (other == null) return false; - return (original == other.original); + return _original == other._original; } public override int GetHashCode () { - return original.GetHashCode (); + return _original.GetHashCode (); } public override string ToString () { - return original; + return _original; } #endregion diff --git a/websocket-sharp/Server/HttpServer.cs b/websocket-sharp/Server/HttpServer.cs index fdde09ee..5006a1bc 100644 --- a/websocket-sharp/Server/HttpServer.cs +++ b/websocket-sharp/Server/HttpServer.cs @@ -30,6 +30,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Security.Cryptography.X509Certificates; using System.Threading; using WebSocketSharp.Net; @@ -51,7 +52,8 @@ namespace WebSocketSharp.Server private int _port; private Thread _receiveRequestThread; private string _rootPath; - private ServiceHostManager _svcHosts; + private bool _secure; + private ServiceHostManager _serviceHosts; private bool _windows; #endregion @@ -74,9 +76,42 @@ namespace WebSocketSharp.Server /// /// An that contains a port number. /// + /// + /// is 0 or less, or 65536 or greater. + /// public HttpServer (int port) + : this (port, port == 443 ? true : false) { + } + + /// + /// Initializes a new instance of the class that listens for incoming requests + /// on the specified and . + /// + /// + /// An that contains a port number. + /// + /// + /// A that indicates providing a secure connection or not. + /// (true indicates providing a secure connection.) + /// + /// + /// is 0 or less, or 65536 or greater. + /// + /// + /// Pair of and is invalid. + /// + public HttpServer (int port, bool secure) + { + if (!port.IsPortNumber ()) + throw new ArgumentOutOfRangeException ("port", "Invalid port number: " + port); + + if (port == 80 && secure || port == 443 && !secure) + throw new ArgumentException (String.Format ( + "Invalid pair of 'port' and 'secure': {0}, {1}", port, secure)); + _port = port; + _secure = secure; init (); } @@ -84,6 +119,34 @@ namespace WebSocketSharp.Server #region Public Properties + /// + /// Gets or sets the certificate used to authenticate the server on the secure connection. + /// + /// + /// A used to authenticate the server. + /// + public X509Certificate2 Certificate { + get { + return _listener.DefaultCertificate; + } + + 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); + + return; + } + + if (EndPointListener.CertificateExists (_port, _listener.CertificateFolderPath)) + _logger.Warn ("Server certificate associated with the port number already exists."); + + _listener.DefaultCertificate = value; + } + } + /// /// Gets a value indicating whether the server has been started. /// @@ -96,6 +159,18 @@ namespace WebSocketSharp.Server } } + /// + /// Gets a value indicating whether the server provides secure connection. + /// + /// + /// true if the server provides secure connection; otherwise, false. + /// + public bool IsSecure { + get { + return _secure; + } + } + /// /// Gets or sets a value indicating whether the server cleans up the inactive WebSocket service /// instances periodically. @@ -106,11 +181,11 @@ namespace WebSocketSharp.Server /// public bool KeepClean { get { - return _svcHosts.KeepClean; + return _serviceHosts.KeepClean; } set { - _svcHosts.KeepClean = value; + _serviceHosts.KeepClean = value; } } @@ -152,12 +227,22 @@ namespace WebSocketSharp.Server /// public string RootPath { get { - return _rootPath; + return _rootPath.IsNullOrEmpty () + ? (_rootPath = "./Public") + : _rootPath; } set { - if (!_listening) - _rootPath = value; + if (_listening) + { + var msg = "The value of RootPath property cannot be changed because the server has already been started."; + _logger.Error (msg); + error (msg); + + return; + } + + _rootPath = value; } } @@ -169,7 +254,7 @@ namespace WebSocketSharp.Server /// public IEnumerable ServicePaths { get { - return _svcHosts.Paths; + return _serviceHosts.Paths; } } @@ -241,15 +326,14 @@ namespace WebSocketSharp.Server _listener = new HttpListener (); _listening = false; _logger = new Logger (); - _rootPath = "./Public"; - _svcHosts = new ServiceHostManager (); + _serviceHosts = new ServiceHostManager (_logger); _windows = false; var os = Environment.OSVersion; if (os.Platform != PlatformID.Unix && os.Platform != PlatformID.MacOSX) _windows = true; - var prefix = String.Format ("http{0}://*:{1}/", _port == 443 ? "s" : "", _port); + var prefix = String.Format ("http{0}://*:{1}/", _secure ? "s" : "", _port); _listener.Prefixes.Add (prefix); } @@ -319,15 +403,15 @@ namespace WebSocketSharp.Server var wsContext = context.AcceptWebSocket (); var path = wsContext.Path.UrlDecode (); - IServiceHost svcHost; - if (!_svcHosts.TryGetServiceHost (path, out svcHost)) + IServiceHost host; + if (!_serviceHosts.TryGetServiceHost (path, out host)) { context.Response.StatusCode = (int) HttpStatusCode.NotImplemented; return false; } wsContext.WebSocket.Log = _logger; - svcHost.BindWebSocket (wsContext); + host.BindWebSocket (wsContext); return true; } @@ -385,24 +469,49 @@ namespace WebSocketSharp.Server _receiveRequestThread.Start (); } + private void stop (ushort code, string reason, bool ignoreArgs) + { + if (!ignoreArgs) + { + 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); + + return; + } + } + + _listener.Close (); + _receiveRequestThread.Join (5 * 1000); + if (ignoreArgs) + _serviceHosts.Stop (); + else + _serviceHosts.Stop (code, reason); + + _listening = false; + } + #endregion #region Public Methods /// - /// Adds the specified typed WebSocket service. + /// Adds the specified typed WebSocket service with the specified . /// - /// - /// A that contains an absolute path associated with the WebSocket service. + /// + /// A that contains an absolute path to the WebSocket service. /// /// /// The type of the WebSocket service. The T must inherit the class. /// - public void AddWebSocketService (string absPath) + public void AddWebSocketService (string servicePath) where T : WebSocketService, new () { string msg; - if (!absPath.IsValidAbsolutePath (out msg)) + if (!servicePath.IsValidAbsolutePath (out msg)) { _logger.Error (msg); error (msg); @@ -410,12 +519,12 @@ namespace WebSocketSharp.Server return; } - var svcHost = new WebSocketServiceHost (_logger); - svcHost.Uri = absPath.ToUri (); + var host = new WebSocketServiceHost (_logger); + host.Uri = servicePath.ToUri (); if (!KeepClean) - svcHost.KeepClean = false; + host.KeepClean = false; - _svcHosts.Add (absPath, svcHost); + _serviceHosts.Add (servicePath, host); } /// @@ -430,7 +539,7 @@ namespace WebSocketSharp.Server /// public byte[] GetFile (string path) { - var filePath = _rootPath + path; + var filePath = RootPath + path; if (_windows) filePath = filePath.Replace ("/", "\\"); @@ -439,6 +548,29 @@ namespace WebSocketSharp.Server : null; } + /// + /// Removes the WebSocket service with the specified . + /// + /// + /// true if the WebSocket service is successfully found and removed; otherwise, false. + /// + /// + /// A that contains an absolute path to the WebSocket service to find. + /// + public bool RemoveWebSocketService (string servicePath) + { + if (servicePath.IsNullOrEmpty ()) + { + var msg = "'servicePath' must not be null or empty."; + _logger.Error (msg); + error (msg); + + return false; + } + + return _serviceHosts.Remove (servicePath); + } + /// /// Starts to receive the HTTP requests. /// @@ -447,6 +579,18 @@ namespace WebSocketSharp.Server if (_listening) return; + if (_secure && + !EndPointListener.CertificateExists (_port, _listener.CertificateFolderPath) && + Certificate == null + ) + { + var msg = "Secure connection requires a server certificate."; + _logger.Error (msg); + error (msg); + + return; + } + _listener.Start (); startReceiveRequestThread (); _listening = true; @@ -460,10 +604,52 @@ namespace WebSocketSharp.Server if (!_listening) return; - _listener.Close (); - _receiveRequestThread.Join (5 * 1000); - _svcHosts.Stop (); - _listening = false; + stop (0, null, true); + } + + /// + /// Stops receiving the HTTP requests with the specified and + /// used to stop the WebSocket services. + /// + /// + /// A that contains a status code indicating the reason for stop. + /// + /// + /// A that contains the reason for stop. + /// + public void Stop (ushort code, string reason) + { + if (!_listening) + return; + + if (!code.IsCloseStatusCode ()) + { + var msg = "Invalid status code for stop."; + _logger.Error (String.Format ("{0}\ncode: {1}", msg, code)); + error (msg); + + return; + } + + stop (code, reason, false); + } + + /// + /// Stops receiving the HTTP requests with the specified + /// and used to stop the WebSocket services. + /// + /// + /// A that contains a status code indicating the reason for stop. + /// + /// + /// A that contains the reason for stop. + /// + public void Stop (CloseStatusCode code, string reason) + { + if (!_listening) + return; + + stop ((ushort) code, reason, false); } #endregion diff --git a/websocket-sharp/Server/WebSocketServer.cs b/websocket-sharp/Server/WebSocketServer.cs index 6ffae487..c5597194 100644 --- a/websocket-sharp/Server/WebSocketServer.cs +++ b/websocket-sharp/Server/WebSocketServer.cs @@ -246,7 +246,7 @@ namespace WebSocketSharp.Server #region Public Methods /// - /// Adds the specified typed WebSocket service. + /// Adds the specified typed WebSocket service with the specified . /// /// /// A that contains an absolute path to the WebSocket service. diff --git a/websocket-sharp/Server/WebSocketServerBase.cs b/websocket-sharp/Server/WebSocketServerBase.cs index c8cec69c..ce6a1a20 100644 --- a/websocket-sharp/Server/WebSocketServerBase.cs +++ b/websocket-sharp/Server/WebSocketServerBase.cs @@ -350,6 +350,7 @@ namespace WebSocketSharp.Server } catch (Exception ex) { + client.Close (); _logger.Fatal (ex.Message); error ("An exception has occured."); }