diff --git a/Example/Example.pidb b/Example/Example.pidb index c0227b1c..a19f520f 100644 Binary files a/Example/Example.pidb and b/Example/Example.pidb differ diff --git a/Example/Program.cs b/Example/Program.cs index 5e604887..8d679156 100644 --- a/Example/Program.cs +++ b/Example/Program.cs @@ -76,6 +76,8 @@ namespace Example using (WebSocket ws = new WebSocket("ws://echo.websocket.org", "echo")) //using (WebSocket ws = new WebSocket("wss://echo.websocket.org", "echo")) //using (WebSocket ws = new WebSocket("ws://localhost:4649")) + //using (WebSocket ws = new WebSocket("ws://localhost:4649/Echo")) + //using (WebSocket ws = new WebSocket("ws://localhost:4649/Chat")) { ws.OnOpen += (sender, e) => { diff --git a/Example/bin/Debug/example.exe b/Example/bin/Debug/example.exe index 03e313c2..460ba3a6 100755 Binary files a/Example/bin/Debug/example.exe and b/Example/bin/Debug/example.exe differ diff --git a/Example/bin/Debug/example.exe.mdb b/Example/bin/Debug/example.exe.mdb index 3596e266..7ebcc73d 100644 Binary files a/Example/bin/Debug/example.exe.mdb and b/Example/bin/Debug/example.exe.mdb differ diff --git a/Example/bin/Debug/websocket-sharp.dll b/Example/bin/Debug/websocket-sharp.dll index de509c6d..4b6e1a4a 100755 Binary files a/Example/bin/Debug/websocket-sharp.dll and b/Example/bin/Debug/websocket-sharp.dll differ diff --git a/Example/bin/Debug/websocket-sharp.dll.mdb b/Example/bin/Debug/websocket-sharp.dll.mdb index d8ba1d5c..d1c92fae 100644 Binary files a/Example/bin/Debug/websocket-sharp.dll.mdb and b/Example/bin/Debug/websocket-sharp.dll.mdb differ diff --git a/Example/bin/Debug_Ubuntu/example.exe b/Example/bin/Debug_Ubuntu/example.exe index 4a970e0f..9b057aae 100755 Binary files a/Example/bin/Debug_Ubuntu/example.exe and b/Example/bin/Debug_Ubuntu/example.exe differ diff --git a/Example/bin/Debug_Ubuntu/example.exe.mdb b/Example/bin/Debug_Ubuntu/example.exe.mdb index 8edb0c11..6a670133 100644 Binary files a/Example/bin/Debug_Ubuntu/example.exe.mdb and b/Example/bin/Debug_Ubuntu/example.exe.mdb differ diff --git a/Example/bin/Debug_Ubuntu/websocket-sharp.dll b/Example/bin/Debug_Ubuntu/websocket-sharp.dll index 2d652b4c..03bffdf9 100755 Binary files a/Example/bin/Debug_Ubuntu/websocket-sharp.dll and b/Example/bin/Debug_Ubuntu/websocket-sharp.dll differ diff --git a/Example/bin/Debug_Ubuntu/websocket-sharp.dll.mdb b/Example/bin/Debug_Ubuntu/websocket-sharp.dll.mdb index d86c09d4..413f6352 100644 Binary files a/Example/bin/Debug_Ubuntu/websocket-sharp.dll.mdb and b/Example/bin/Debug_Ubuntu/websocket-sharp.dll.mdb differ diff --git a/Example1/bin/Debug/example1.exe b/Example1/bin/Debug/example1.exe index 51eb29a4..37fc0ae0 100755 Binary files a/Example1/bin/Debug/example1.exe and b/Example1/bin/Debug/example1.exe differ diff --git a/Example1/bin/Debug/example1.exe.mdb b/Example1/bin/Debug/example1.exe.mdb index a26f6fcc..9e28ca8b 100644 Binary files a/Example1/bin/Debug/example1.exe.mdb and b/Example1/bin/Debug/example1.exe.mdb differ diff --git a/Example1/bin/Debug/websocket-sharp.dll b/Example1/bin/Debug/websocket-sharp.dll index de509c6d..4b6e1a4a 100755 Binary files a/Example1/bin/Debug/websocket-sharp.dll and b/Example1/bin/Debug/websocket-sharp.dll differ diff --git a/Example1/bin/Debug/websocket-sharp.dll.mdb b/Example1/bin/Debug/websocket-sharp.dll.mdb index d8ba1d5c..d1c92fae 100644 Binary files a/Example1/bin/Debug/websocket-sharp.dll.mdb and b/Example1/bin/Debug/websocket-sharp.dll.mdb differ diff --git a/Example1/bin/Debug_Ubuntu/example1.exe b/Example1/bin/Debug_Ubuntu/example1.exe index f785a051..6a24073f 100755 Binary files a/Example1/bin/Debug_Ubuntu/example1.exe and b/Example1/bin/Debug_Ubuntu/example1.exe differ diff --git a/Example1/bin/Debug_Ubuntu/example1.exe.mdb b/Example1/bin/Debug_Ubuntu/example1.exe.mdb index f0518ead..a60ddc54 100644 Binary files a/Example1/bin/Debug_Ubuntu/example1.exe.mdb and b/Example1/bin/Debug_Ubuntu/example1.exe.mdb differ diff --git a/Example1/bin/Debug_Ubuntu/websocket-sharp.dll b/Example1/bin/Debug_Ubuntu/websocket-sharp.dll index 2d652b4c..03bffdf9 100755 Binary files a/Example1/bin/Debug_Ubuntu/websocket-sharp.dll and b/Example1/bin/Debug_Ubuntu/websocket-sharp.dll differ diff --git a/Example1/bin/Debug_Ubuntu/websocket-sharp.dll.mdb b/Example1/bin/Debug_Ubuntu/websocket-sharp.dll.mdb index d86c09d4..413f6352 100644 Binary files a/Example1/bin/Debug_Ubuntu/websocket-sharp.dll.mdb and b/Example1/bin/Debug_Ubuntu/websocket-sharp.dll.mdb differ diff --git a/Example2/Example2.pidb b/Example2/Example2.pidb index 033bdcd9..b4529a60 100644 Binary files a/Example2/Example2.pidb and b/Example2/Example2.pidb differ diff --git a/Example2/Program.cs b/Example2/Program.cs index c5a46557..028f9aef 100644 --- a/Example2/Program.cs +++ b/Example2/Program.cs @@ -7,14 +7,27 @@ namespace Example2 { public static void Main(string[] args) { + /* Single service server var wssv = new WebSocketServer("ws://localhost:4649"); - //var wssv = new WebSocketServer("ws://localhost:4649"); //var wssv = new WebSocketServer(4649); + //var wssv = new WebSocketServer("ws://localhost:4649"); + //var wssv = new WebSocketServer(4649); wssv.Start(); Console.WriteLine( "WebSocket Server (url: {0})\n listening on address: {1} port: {2}\n", wssv.Uri, wssv.Address, wssv.Port); + */ + + // Multi services server + var wssv = new WebSocketServer(4649); + wssv.AddService("/Echo"); + wssv.AddService("/Chat"); + + wssv.Start(); + Console.WriteLine( + "WebSocket Server listening on port: {0}\n", wssv.Port); + Console.WriteLine("Press any key to stop server..."); Console.ReadLine(); diff --git a/Example2/bin/Debug/example2.exe b/Example2/bin/Debug/example2.exe index 27f9556a..c8bf7f10 100755 Binary files a/Example2/bin/Debug/example2.exe and b/Example2/bin/Debug/example2.exe differ diff --git a/Example2/bin/Debug/example2.exe.mdb b/Example2/bin/Debug/example2.exe.mdb index d8286522..f27ea0ec 100644 Binary files a/Example2/bin/Debug/example2.exe.mdb and b/Example2/bin/Debug/example2.exe.mdb differ diff --git a/Example2/bin/Debug/websocket-sharp.dll b/Example2/bin/Debug/websocket-sharp.dll index de509c6d..4b6e1a4a 100755 Binary files a/Example2/bin/Debug/websocket-sharp.dll and b/Example2/bin/Debug/websocket-sharp.dll differ diff --git a/Example2/bin/Debug/websocket-sharp.dll.mdb b/Example2/bin/Debug/websocket-sharp.dll.mdb index d8ba1d5c..d1c92fae 100644 Binary files a/Example2/bin/Debug/websocket-sharp.dll.mdb and b/Example2/bin/Debug/websocket-sharp.dll.mdb differ diff --git a/Example2/bin/Debug_Ubuntu/example2.exe b/Example2/bin/Debug_Ubuntu/example2.exe index 6ccf2017..ea846c4b 100755 Binary files a/Example2/bin/Debug_Ubuntu/example2.exe and b/Example2/bin/Debug_Ubuntu/example2.exe differ diff --git a/Example2/bin/Debug_Ubuntu/example2.exe.mdb b/Example2/bin/Debug_Ubuntu/example2.exe.mdb index 0ac5f698..0183c0f9 100644 Binary files a/Example2/bin/Debug_Ubuntu/example2.exe.mdb and b/Example2/bin/Debug_Ubuntu/example2.exe.mdb differ diff --git a/Example2/bin/Debug_Ubuntu/websocket-sharp.dll b/Example2/bin/Debug_Ubuntu/websocket-sharp.dll index 2d652b4c..03bffdf9 100755 Binary files a/Example2/bin/Debug_Ubuntu/websocket-sharp.dll and b/Example2/bin/Debug_Ubuntu/websocket-sharp.dll differ diff --git a/Example2/bin/Debug_Ubuntu/websocket-sharp.dll.mdb b/Example2/bin/Debug_Ubuntu/websocket-sharp.dll.mdb index d86c09d4..413f6352 100644 Binary files a/Example2/bin/Debug_Ubuntu/websocket-sharp.dll.mdb and b/Example2/bin/Debug_Ubuntu/websocket-sharp.dll.mdb differ diff --git a/Example3/bin/Debug/Example3.exe b/Example3/bin/Debug/Example3.exe index a8f76062..f2c476bc 100755 Binary files a/Example3/bin/Debug/Example3.exe and b/Example3/bin/Debug/Example3.exe differ diff --git a/Example3/bin/Debug/Example3.exe.mdb b/Example3/bin/Debug/Example3.exe.mdb index 95acfe52..a3bf5d64 100644 Binary files a/Example3/bin/Debug/Example3.exe.mdb and b/Example3/bin/Debug/Example3.exe.mdb differ diff --git a/Example3/bin/Debug/websocket-sharp.dll b/Example3/bin/Debug/websocket-sharp.dll index de509c6d..4b6e1a4a 100755 Binary files a/Example3/bin/Debug/websocket-sharp.dll and b/Example3/bin/Debug/websocket-sharp.dll differ diff --git a/Example3/bin/Debug/websocket-sharp.dll.mdb b/Example3/bin/Debug/websocket-sharp.dll.mdb index d8ba1d5c..d1c92fae 100644 Binary files a/Example3/bin/Debug/websocket-sharp.dll.mdb and b/Example3/bin/Debug/websocket-sharp.dll.mdb differ diff --git a/Example3/bin/Debug_Ubuntu/Example3.exe b/Example3/bin/Debug_Ubuntu/Example3.exe index 7cc2978a..a0639f02 100755 Binary files a/Example3/bin/Debug_Ubuntu/Example3.exe and b/Example3/bin/Debug_Ubuntu/Example3.exe differ diff --git a/Example3/bin/Debug_Ubuntu/Example3.exe.mdb b/Example3/bin/Debug_Ubuntu/Example3.exe.mdb index adbd1579..39599b76 100644 Binary files a/Example3/bin/Debug_Ubuntu/Example3.exe.mdb and b/Example3/bin/Debug_Ubuntu/Example3.exe.mdb differ diff --git a/Example3/bin/Debug_Ubuntu/websocket-sharp.dll b/Example3/bin/Debug_Ubuntu/websocket-sharp.dll index 2d652b4c..03bffdf9 100755 Binary files a/Example3/bin/Debug_Ubuntu/websocket-sharp.dll and b/Example3/bin/Debug_Ubuntu/websocket-sharp.dll differ diff --git a/Example3/bin/Debug_Ubuntu/websocket-sharp.dll.mdb b/Example3/bin/Debug_Ubuntu/websocket-sharp.dll.mdb index d86c09d4..413f6352 100644 Binary files a/Example3/bin/Debug_Ubuntu/websocket-sharp.dll.mdb and b/Example3/bin/Debug_Ubuntu/websocket-sharp.dll.mdb differ diff --git a/README.md b/README.md index 8abbbf1e..9166a471 100644 --- a/README.md +++ b/README.md @@ -147,7 +147,7 @@ Required namespace. using WebSocketSharp.Server; ``` -`WebSocketServer` class and `WebSocketService` class exist in `WebSocketSharp.Server` namespace. +`WebSocketServer`, `WebSocketServer` and `WebSocketService` classes exist in `WebSocketSharp.Server` namespace. #### Step 2 #### @@ -191,15 +191,25 @@ In addition, if you override `onOpen`, `onError` and `onClose` methods, each of #### Step 3 #### -Creating a instance of `WebSocketServer` class. +Creating a instance of `WebSocketServer` class if you want single WebSocket service server. ```cs var wssv = new WebSocketServer("ws://example.com:4649"); ``` +Creating a instance of `WebSocketServer` class if you want multi WebSocket service server. + +```cs +var wssv = new WebSocketServer(4649); +wssv.AddService("/Echo"); +wssv.AddService("/Chat"); +``` + +You can add to your `WebSocketServer` any WebSocket service and a matching path to that service by using `WebSocketServer.AddService` method. + Type of `T` inherits `WebSocketService` class, so you can use a class that was created in **Step 2**. -If you set WebSocket url without port number, `WebSocketServer` set **80** or **443** to port number automatically. +If you create a instance of WebSocket Server without port number, WebSocket Server set **80** or **443** to port number automatically. So it is necessary to run with root permission. $ sudo mono example2.exe @@ -272,6 +282,8 @@ Examples of using **websocket-sharp**. [Example3] starts the HTTP server that the connection can be upgraded to the WebSocket connection. +Please access http://localhost:4649 to do WebSocket Echo Test with your web browser after [Example3] running. + ## Supported WebSocket Protocol ## **websocket-sharp** supports **[RFC 6455]**. diff --git a/websocket-sharp/Ext.cs b/websocket-sharp/Ext.cs index 39efab79..ea29019b 100644 --- a/websocket-sharp/Ext.cs +++ b/websocket-sharp/Ext.cs @@ -37,13 +37,20 @@ using System.Collections.Generic; using System.Collections.Specialized; using System.IO; using System.Linq; +using System.Net.Sockets; using System.Text; using WebSocketSharp.Net; +using WebSocketSharp.Net.Sockets; namespace WebSocketSharp { public static class Ext { + public static TcpListenerWebSocketContext AcceptWebSocket(this TcpClient client) + { + return new TcpListenerWebSocketContext(client); + } + public static void Emit( this EventHandler eventHandler, object sender, EventArgs e) { @@ -126,7 +133,7 @@ namespace WebSocketSharp // Derived from System.Uri.IsPredefinedScheme method public static bool IsPredefinedScheme(this string scheme) { - if (scheme == null && scheme.Length < 3) + if (scheme == null && scheme.Length < 2) return false; char c = scheme[0]; @@ -136,7 +143,10 @@ namespace WebSocketSharp if (c == 'f') return (scheme == "file" || scheme == "ftp"); - + + if (c == 'w') + return (scheme == "ws" || scheme == "wss"); + if (c == 'n') { c = scheme[1]; @@ -156,6 +166,19 @@ namespace WebSocketSharp return false; } + // Derived from System.Uri.MaybeUri method + public static bool MaybeUri(this string uriString) + { + int p = uriString.IndexOf(':'); + if (p == -1) + return false; + + if (p >= 10) + return false; + + return uriString.Substring(0, p).IsPredefinedScheme(); + } + public static bool NotEqualsDo( this string expected, string actual, @@ -173,19 +196,6 @@ namespace WebSocketSharp return false; } - // Derived from System.Uri.MaybeUri method - public static bool MaybeUri(this string uriString) - { - int p = uriString.IndexOf(':'); - if (p == -1) - return false; - - if (p >= 10) - return false; - - return uriString.Substring(0, p).IsPredefinedScheme(); - } - public static byte[] ReadBytes(this TStream stream, ulong length, int bufferLength) where TStream : System.IO.Stream { diff --git a/websocket-sharp/IWsStream.cs b/websocket-sharp/IWsStream.cs index b0048f3b..a4dc3ee8 100644 --- a/websocket-sharp/IWsStream.cs +++ b/websocket-sharp/IWsStream.cs @@ -33,12 +33,14 @@ namespace WebSocketSharp { public interface IWsStream : IDisposable { - void Close(); - int Read(byte[] buffer, int offset, int size); - int ReadByte(); - WsFrame ReadFrame(); - void Write(byte[] buffer, int offset, int count); - void WriteByte(byte value); - void WriteFrame(WsFrame frame); + void Close(); + int Read(byte[] buffer, int offset, int size); + int ReadByte(); + WsFrame ReadFrame(); + string[] ReadHandshake(); + void Write(byte[] buffer, int offset, int count); + void WriteByte(byte value); + void WriteFrame(WsFrame frame); + void WriteHandshake(Handshake handshake); } } diff --git a/websocket-sharp/Net/HttpListenerWebSocketContext.cs b/websocket-sharp/Net/HttpListenerWebSocketContext.cs index afc3f83e..2de6a5b6 100644 --- a/websocket-sharp/Net/HttpListenerWebSocketContext.cs +++ b/websocket-sharp/Net/HttpListenerWebSocketContext.cs @@ -29,6 +29,7 @@ using System; using System.Collections.Generic; using System.Collections.Specialized; +using System.IO; using System.Net.Security; using System.Net.Sockets; using System.Security.Principal; @@ -39,10 +40,12 @@ namespace WebSocketSharp.Net { { private HttpListenerContext _context; private WebSocket _socket; + private IWsStream _stream; internal HttpListenerWebSocketContext(string path, HttpListenerContext context) { _context = context; + _stream = WebSocket.CreateServerStream(context); _socket = new WebSocket(path.ToUri(), this); } @@ -50,6 +53,10 @@ namespace WebSocketSharp.Net { get { return _context; } } + internal IWsStream Stream { + get { return _stream; } + } + public override CookieCollection CookieCollection { get { return _context.Request.Cookies; } } diff --git a/websocket-sharp/Net/Sockets/TcpListenerWebSocketContext.cs b/websocket-sharp/Net/Sockets/TcpListenerWebSocketContext.cs new file mode 100644 index 00000000..0d88ab58 --- /dev/null +++ b/websocket-sharp/Net/Sockets/TcpListenerWebSocketContext.cs @@ -0,0 +1,120 @@ +#region MIT License +/** + * TcpListenerWebSocketContext.cs + * + * The MIT License + * + * Copyright (c) 2012 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Net; +using System.Net.Security; +using System.Net.Sockets; +using System.Security.Principal; + +namespace WebSocketSharp.Net.Sockets { + + public class TcpListenerWebSocketContext : WebSocketContext + { + private TcpClient _client; + private bool _isSecure; + private RequestHandshake _request; + private WebSocket _socket; + private IWsStream _stream; + + internal TcpListenerWebSocketContext(TcpClient client) + { + _client = client; + init(); + } + + internal TcpClient Client { + get { return _client; } + } + + internal IWsStream Stream { + get { return _stream; } + } + + public override CookieCollection CookieCollection { + get { throw new NotImplementedException(); } + } + + public override NameValueCollection Headers { + get { return _request.Headers; } + } + + public override bool IsAuthenticated { + get { throw new NotImplementedException(); } + } + + public override bool IsSecureConnection { + get { return _isSecure; } + } + + public override bool IsLocal { + get { throw new NotImplementedException(); } + } + + public override string Origin { + get { return Headers["Origin"]; } + } + + public override Uri RequestUri { + get { return _request.RequestUri; } + } + + public override string SecWebSocketKey { + get { return Headers["Sec-WebSocket-Key"]; } + } + + public override IEnumerable SecWebSocketProtocols { + get { return Headers.GetValues("Sec-WebSocket-Protocol"); } + } + + public override string SecWebSocketVersion { + get { return Headers["Sec-WebSocket-Version"]; } + } + + public override IPrincipal User { + get { throw new NotImplementedException(); } + } + + public override WebSocket WebSocket { + get { return _socket; } + } + + private void init() + { + _stream = WebSocket.CreateServerStream(_client); + _request = RequestHandshake.Parse(_stream.ReadHandshake()); + + var port = ((IPEndPoint)_client.Client.LocalEndPoint).Port; + _isSecure = port == 443 ? true : false; + + _socket = new WebSocket(this); + } + } +} diff --git a/websocket-sharp/ResponseHandshake.cs b/websocket-sharp/ResponseHandshake.cs index a350e5a5..fbca23a4 100644 --- a/websocket-sharp/ResponseHandshake.cs +++ b/websocket-sharp/ResponseHandshake.cs @@ -35,6 +35,36 @@ namespace WebSocketSharp { public class ResponseHandshake : Handshake { + #region Public Static Fields + + public static ResponseHandshake BadRequest; + public static ResponseHandshake NotImplemented; + + #endregion + + #region Static Constructor + + static ResponseHandshake() + { + var badRequest = new ResponseHandshake { + Reason = "Bad Request", + StatusCode = ((int)HttpStatusCode.BadRequest).ToString() + }; + badRequest.Headers.Clear(); + badRequest.AddHeader("Connection", "Close"); + BadRequest = badRequest; + + var notImplemented = new ResponseHandshake { + Reason = "Not Implemented", + StatusCode = ((int)HttpStatusCode.NotImplemented).ToString() + }; + notImplemented.Headers.Clear(); + notImplemented.AddHeader("Connection", "Close"); + NotImplemented = notImplemented; + } + + #endregion + #region Public Constructor public ResponseHandshake() diff --git a/websocket-sharp/Server/WebSocketServer.cs b/websocket-sharp/Server/WebSocketServer.cs index ea65943c..4acf3152 100644 --- a/websocket-sharp/Server/WebSocketServer.cs +++ b/websocket-sharp/Server/WebSocketServer.cs @@ -31,200 +31,151 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Diagnostics; using System.Net; using System.Net.Sockets; -using System.Threading; using WebSocketSharp.Frame; +using WebSocketSharp.Net.Sockets; namespace WebSocketSharp.Server { - public class WebSocketServer : IWebSocketServer - where T : WebSocketService, new() + public class WebSocketServer : WebSocketServerBase { - #region Fields + #region Field - private Thread _acceptClientThread; - private IPAddress _address; - private bool _isSelfHost; - private int _port; - private Dictionary _services; - private TcpListener _tcpListener; - private Uri _uri; - - #endregion - - #region Internal Constructor - - internal WebSocketServer() - { - _services = new Dictionary(); - _isSelfHost = false; - } + private Dictionary _servers; #endregion #region Public Constructors - public WebSocketServer(string url) - : this() + public WebSocketServer() + : this(80) { - var uri = new Uri(url); - - string msg; - if (!isValidUri(uri, out msg)) - throw new ArgumentException(msg, "url"); - - _tcpListener = new TcpListener(_address, _port); - _isSelfHost = true; } public WebSocketServer(int port) - : this(port, "/") + : base(IPAddress.Any, port) { + _servers = new Dictionary(); } - public WebSocketServer(int port, string path) - : this() + #endregion + + #region Protected Method + + protected override void bindSocket(TcpClient client) { - var uri = path.ToUri(); - if (uri.IsAbsoluteUri) + var context = client.AcceptWebSocket(); + var path = context.RequestUri.ToString(); + if (!_servers.ContainsKey(path)) { - var msg = "Not absolute path: " + path; - throw new ArgumentException(msg, "path"); + var stream = context.Stream; + var res = ResponseHandshake.NotImplemented; + stream.WriteHandshake(res); + stream.Close(); + client.Close(); + return; } - _uri = uri; - _address = IPAddress.Any; - _port = port <= 0 ? 80 : port; - - _tcpListener = new TcpListener(_address, _port); - _isSelfHost = true; + var socket = context.WebSocket; + var server = _servers[path]; + server.BindWebSocket(socket); } #endregion - #region Properties + #region Public Methods - public IPAddress Address + public void AddService(string path) + where T : WebSocketService, new() { - get { return _address; } + var server = new WebSocketServer(); + _servers.Add(path, server); } - public bool IsSelfHost { - get { return _isSelfHost; } - } - - public int Port + public override void Stop() { - get { return _port; } - } - - public Uri Uri - { - get { return _uri; } + base.Stop(); + foreach (var server in _servers.Values) + server.Stop(); } #endregion + } - #region Events + public class WebSocketServer : WebSocketServerBase, IWebSocketServer + where T : WebSocketService, new() + { + #region Fields - public event EventHandler OnError; + private Dictionary _services; + private Uri _uri; #endregion - #region Private Methods + #region Internal Constructor - private void acceptClient() + internal WebSocketServer() { - while (true) - { - try { - var client = _tcpListener.AcceptTcpClient(); - startService(client); - } - catch (SocketException) - { - // TcpListener has been stopped. - break; - } - catch (Exception ex) - { - error(ex.Message); - break; - } - } + init(); } - private void error(string message) + #endregion + + #region Public Constructors + + public WebSocketServer(string url) + : base(url) { - #if DEBUG - var callerFrame = new StackFrame(1); - var caller = callerFrame.GetMethod(); - Console.WriteLine("WSSV: Error@{0}: {1}", caller.Name, message); - #endif - OnError.Emit(this, new ErrorEventArgs(message)); + _uri = url.ToUri(); + init(); } - private bool isValidUri(Uri uri, out string message) + public WebSocketServer(int port) + : this(port, "/") { - var scheme = uri.Scheme; - var port = uri.Port; - var host = uri.DnsSafeHost; - var ips = Dns.GetHostAddresses(host); - - if (scheme != "ws" && scheme != "wss") - { - message = "Unsupported WebSocket URI scheme: " + scheme; - return false; - } + } - if ((scheme == "wss" && port != 443) || - (scheme != "wss" && port == 443)) + public WebSocketServer(int port, string path) + : base(IPAddress.Any, port) + { + var uri = path.ToUri(); + if (uri.IsAbsoluteUri) { - message = String.Format( - "Invalid pair of WebSocket URI scheme and port: {0}, {1}", scheme, port); - return false; + var msg = "Not absolute path: " + path; + throw new ArgumentException(msg, "path"); } - if (ips.Length == 0) - { - message = "Invalid WebSocket URI host: " + host; - return false; - } + _uri = uri; + init(); + } - if (port <= 0) - port = scheme == "ws" ? 80 : 443; + #endregion - _uri = uri; - _address = ips[0]; - _port = port; + #region Property - message = String.Empty; - return true; + public Uri Uri + { + get { return _uri; } } - private void startAcceptClientThread() + #endregion + + #region Private Method + + private void init() { - _acceptClientThread = new Thread(new ThreadStart(acceptClient)); - _acceptClientThread.IsBackground = true; - _acceptClientThread.Start(); + _services = new Dictionary(); } - private void startService(TcpClient client) + #endregion + + #region Protected Method + + protected override void bindSocket(TcpClient client) { - WaitCallback startServiceCb = (state) => - { - try { - var socket = new WebSocket(_uri, client); - BindWebSocket(socket); - } - catch (Exception ex) - { - error(ex.Message); - } - }; - ThreadPool.QueueUserWorkItem(startServiceCb); + var socket = new WebSocket(_uri, client); + BindWebSocket(socket); } #endregion @@ -238,23 +189,9 @@ namespace WebSocketSharp.Server { service.Start(); } - public void Start() - { - if (!_isSelfHost) - return; - - _tcpListener.Start(); - startAcceptClientThread(); - } - - public void Stop() + public override void Stop() { - if (_isSelfHost) - { - _tcpListener.Stop(); - _acceptClientThread.Join(5 * 1000); - } - + base.Stop(); StopServices(); } diff --git a/websocket-sharp/Server/WebSocketServerBase.cs b/websocket-sharp/Server/WebSocketServerBase.cs new file mode 100644 index 00000000..93c8fe94 --- /dev/null +++ b/websocket-sharp/Server/WebSocketServerBase.cs @@ -0,0 +1,233 @@ +#region MIT License +/** + * WebSocketServerBase.cs + * + * The MIT License + * + * Copyright (c) 2012 sta.blockhead + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#endregion + +using System; +using System.Diagnostics; +using System.Net; +using System.Net.Sockets; +using System.Threading; + +namespace WebSocketSharp.Server { + + public abstract class WebSocketServerBase + { + #region Fields + + private Thread _acceptClientThread; + private IPAddress _address; + private bool _isSelfHost; + private int _port; + private TcpListener _tcpListener; + + #endregion + + #region Constructors + + protected WebSocketServerBase() + { + _isSelfHost = false; + } + + protected WebSocketServerBase(string url) + { + string msg; + if (!isValidUri(url, out msg)) + throw new ArgumentException(msg, "url"); + + init(); + } + + protected WebSocketServerBase(IPAddress address, int port) + { + _port = port <= 0 ? 80 : port; + _address = address; + init(); + } + + #endregion + + #region Property + + public IPAddress Address { + get { return _address; } + } + + public bool IsSelfHost { + get { return _isSelfHost; } + } + + public int Port { + get { return _port; } + } + + #endregion + + #region Events + + public event EventHandler OnError; + + #endregion + + #region Private Methods + + private void acceptClient() + { + while (true) + { + try + { + var client = _tcpListener.AcceptTcpClient(); + acceptSocket(client); + } + catch (SocketException) + { + // TcpListener has been stopped. + break; + } + catch (Exception ex) + { + error(ex.Message); + break; + } + } + } + + private void acceptSocket(TcpClient client) + { + WaitCallback acceptSocketCb = (state) => + { + try + { + bindSocket(client); + } + catch (Exception ex) + { + error(ex.Message); + } + }; + ThreadPool.QueueUserWorkItem(acceptSocketCb); + } + + private void error(string message) + { + #if DEBUG + var callerFrame = new StackFrame(1); + var caller = callerFrame.GetMethod(); + Console.WriteLine("WSSV: Error@{0}: {1}", caller.Name, message); + #endif + OnError.Emit(this, new ErrorEventArgs(message)); + } + + private void init() + { + _tcpListener = new TcpListener(_address, _port); + _isSelfHost = true; + } + + private bool isValidUri(string url, out string message) + { + var uri = url.ToUri(); + if (!uri.IsAbsoluteUri) + { + message = "Not absolute uri: " + url; + return false; + } + + var scheme = uri.Scheme; + var port = uri.Port; + var host = uri.DnsSafeHost; + var ips = Dns.GetHostAddresses(host); + + if (scheme != "ws" && scheme != "wss") + { + message = "Unsupported WebSocket URI scheme: " + scheme; + return false; + } + + if ((scheme == "wss" && port != 443) || + (scheme != "wss" && port == 443)) + { + message = String.Format( + "Invalid pair of WebSocket URI scheme and port: {0}, {1}", scheme, port); + return false; + } + + if (ips.Length == 0) + { + message = "Invalid WebSocket URI host: " + host; + return false; + } + + if (port <= 0) + port = scheme == "ws" ? 80 : 443; + + _address = ips[0]; + _port = port; + + message = String.Empty; + return true; + } + + private void startAcceptClientThread() + { + _acceptClientThread = new Thread(new ThreadStart(acceptClient)); + _acceptClientThread.IsBackground = true; + _acceptClientThread.Start(); + } + + #endregion + + #region Protected Method + + protected abstract void bindSocket(TcpClient client); + + #endregion + + #region Public Methods + + public virtual void Start() + { + if (!_isSelfHost) + return; + + _tcpListener.Start(); + startAcceptClientThread(); + } + + public virtual void Stop() + { + if (!_isSelfHost) + return; + + _tcpListener.Stop(); + _acceptClientThread.Join(5 * 1000); + } + + #endregion + } +} diff --git a/websocket-sharp/WebSocket.cs b/websocket-sharp/WebSocket.cs index 481568ba..27f3827d 100644 --- a/websocket-sharp/WebSocket.cs +++ b/websocket-sharp/WebSocket.cs @@ -48,6 +48,7 @@ using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using WebSocketSharp.Frame; using WebSocketSharp.Net; +using WebSocketSharp.Net.Sockets; namespace WebSocketSharp { @@ -62,27 +63,28 @@ namespace WebSocketSharp #region Private Fields - private string _base64key; - private string _binaryType; - private HttpListenerWebSocketContext _context; - private IPEndPoint _endPoint; - private AutoResetEvent _exitMessageLoop; - private string _extensions; - private Object _forClose; - private Object _forSend; - private int _fragmentLen; - private bool _isClient; - private bool _isSecure; - private NetworkStream _netStream; - private string _protocol; - private string _protocols; - private volatile WsState _readyState; - private AutoResetEvent _receivedPong; - private SslStream _sslStream; - private TcpClient _tcpClient; - private Uri _uri; - private SynchronizedCollection _unTransmittedBuffer; - private IWsStream _wsStream; + private string _base64key; + private WebSocketSharp.Net.HttpListenerContext _baseContext; + private string _binaryType; + private WebSocketContext _context; + private IPEndPoint _endPoint; + private AutoResetEvent _exitMessageLoop; + private string _extensions; + private Object _forClose; + private Object _forSend; + private int _fragmentLen; + private bool _isClient; + private bool _isSecure; + private NetworkStream _netStream; + private string _protocol; + private string _protocols; + private volatile WsState _readyState; + private AutoResetEvent _receivedPong; + private SslStream _sslStream; + private TcpClient _tcpClient; + private Uri _uri; + private SynchronizedCollection _unTransmittedBuffer; + private IWsStream _wsStream; #endregion @@ -105,13 +107,28 @@ namespace WebSocketSharp #region Internal Constructor + internal WebSocket(TcpListenerWebSocketContext context) + : this() + { + _uri = context.RequestUri; + _context = context; + _tcpClient = context.Client; + _wsStream = context.Stream; + _endPoint = (IPEndPoint)_tcpClient.Client.LocalEndPoint; + _isClient = false; + _isSecure = context.IsSecureConnection; + } + internal WebSocket(Uri uri, HttpListenerWebSocketContext context) : this() { - _uri = uri; - _context = context; - _isClient = false; - _isSecure = _context.IsSecureConnection; + _uri = uri; + _context = context; + _baseContext = context.BaseContext; + _wsStream = context.Stream; + _endPoint = _baseContext.Connection.LocalEndPoint; + _isClient = false; + _isSecure = context.IsSecureConnection; } internal WebSocket(Uri uri, TcpClient tcpClient) @@ -325,11 +342,11 @@ namespace WebSocketSharp private void closeConnection() { - if (_context != null) + if (_baseContext != null) { - _context.BaseContext.Response.Close(); - _wsStream = null; - _context = null; + _baseContext.Response.Close(); + _wsStream = null; + _baseContext = null; } if (_wsStream != null) @@ -363,13 +380,10 @@ namespace WebSocketSharp private void createClientStream() { var host = _uri.DnsSafeHost; + var port = _uri.Port; if (port <= 0) - { - port = 80; - if (IsSecure) - port = 443; - } + port = IsSecure ? 443 : 80; _tcpClient = new TcpClient(host, port); _netStream = _tcpClient.GetStream(); @@ -378,7 +392,7 @@ namespace WebSocketSharp { RemoteCertificateValidationCallback validation = (sender, certificate, chain, sslPolicyErrors) => { - // Temporary implementation + // FIXME: Always returns true return true; }; @@ -449,44 +463,20 @@ namespace WebSocketSharp private void createServerStream() { - if (_context != null) - { - _wsStream = createServerStreamFromContext(); + if (_wsStream != null) return; - } if (_tcpClient != null) { - _wsStream = createServerStreamFromTcpClient(); + _wsStream = CreateServerStream(_tcpClient); return; } - } - private IWsStream createServerStreamFromContext() - { - var stream = _context.BaseContext.Connection.Stream; - - if (IsSecure) - return new WsStream((SslStream)stream); - - return new WsStream((NetworkStream)stream); - } - - private IWsStream createServerStreamFromTcpClient() - { - _netStream = _tcpClient.GetStream(); - - if (IsSecure) + if (_baseContext != null) { - _sslStream = new SslStream(_netStream); - - var certPath = ConfigurationManager.AppSettings["ServerCertPath"]; - _sslStream.AuthenticateAsServer(new X509Certificate(certPath)); - - return new WsStream(_sslStream); + _wsStream = CreateServerStream(_baseContext); + return; } - - return new WsStream(_netStream); } private void doHandshake() @@ -695,20 +685,7 @@ namespace WebSocketSharp private string[] readHandshake() { - var buffer = new List(); - - while (true) - { - if (_wsStream.ReadByte().EqualsAndSaveTo('\r', buffer) && - _wsStream.ReadByte().EqualsAndSaveTo('\n', buffer) && - _wsStream.ReadByte().EqualsAndSaveTo('\r', buffer) && - _wsStream.ReadByte().EqualsAndSaveTo('\n', buffer)) - break; - } - - return Encoding.UTF8.GetString(buffer.ToArray()) - .Replace("\r\n", "\n").Replace("\n\n", "\n").TrimEnd('\n') - .Split('\n'); + return _wsStream.ReadHandshake(); } private MessageEventArgs receive() @@ -992,7 +969,7 @@ namespace WebSocketSharp Console.WriteLine("WS: Info@sendOpeningHandshake: Opening handshake from client:\n"); Console.WriteLine(req.ToString()); #endif - _wsStream.Write(req.ToBytes(), 0, req.ToBytes().Length); + _wsStream.WriteHandshake(req); var res = ResponseHandshake.Parse(readHandshake()); #if DEBUG @@ -1009,20 +986,14 @@ namespace WebSocketSharp Console.WriteLine("WS: Info@sendResponseHandshake: Response handshake from server:\n"); Console.WriteLine(res.ToString()); #endif - _wsStream.Write(res.ToBytes(), 0, res.ToBytes().Length); + _wsStream.WriteHandshake(res); } private void sendResponseHandshakeForInvalid() { - var code = (int)WebSocketSharp.Net.HttpStatusCode.BadRequest; - var res = new ResponseHandshake { - Reason = "Bad Request", - StatusCode = code.ToString() - }; - res.Headers.Clear(); + var res = ResponseHandshake.BadRequest; res.AddHeader("Sec-WebSocket-Version", _version); - - _wsStream.Write(res.ToBytes(), 0, res.ToBytes().Length); + _wsStream.WriteHandshake(res); } private void startMessageThread() @@ -1040,6 +1011,39 @@ namespace WebSocketSharp #endregion + #region Internal Static Methods + + internal static IWsStream CreateServerStream(TcpClient client) + { + var netStream = client.GetStream(); + + var port = ((IPEndPoint)client.Client.LocalEndPoint).Port; + if (port == 443) + { + var sslStream = new SslStream(netStream); + + var certPath = ConfigurationManager.AppSettings["ServerCertPath"]; + sslStream.AuthenticateAsServer(new X509Certificate2(certPath)); + + return new WsStream(sslStream); + } + + return new WsStream(netStream); + } + + internal static IWsStream CreateServerStream(WebSocketSharp.Net.HttpListenerContext context) + { + var conn = context.Connection; + var stream = conn.Stream; + + if (conn.IsSecure) + return new WsStream((SslStream)stream); + + return new WsStream((NetworkStream)stream); + } + + #endregion + #region Public Methods public void Close() diff --git a/websocket-sharp/WsStream.cs b/websocket-sharp/WsStream.cs index c84ffd7a..026da8fa 100644 --- a/websocket-sharp/WsStream.cs +++ b/websocket-sharp/WsStream.cs @@ -32,6 +32,7 @@ using System.IO; using System.Net.Security; using System.Net.Sockets; using System.Reflection; +using System.Text; using WebSocketSharp.Frame; namespace WebSocketSharp @@ -39,10 +40,16 @@ namespace WebSocketSharp public class WsStream : IWsStream where TStream : Stream { + #region Fields + private TStream _innerStream; private Object _forRead; private Object _forWrite; + #endregion + + #region Constructor + public WsStream(TStream innerStream) { Type streamType = typeof(TStream); @@ -62,6 +69,10 @@ namespace WebSocketSharp _forWrite = new object(); } + #endregion + + #region Public Methods + public void Close() { _innerStream.Close(); @@ -102,6 +113,27 @@ namespace WebSocketSharp } } + public string[] ReadHandshake() + { + lock (_forRead) + { + var buffer = new List(); + + while (true) + { + if (ReadByte().EqualsAndSaveTo('\r', buffer) && + ReadByte().EqualsAndSaveTo('\n', buffer) && + ReadByte().EqualsAndSaveTo('\r', buffer) && + ReadByte().EqualsAndSaveTo('\n', buffer)) + break; + } + + return Encoding.UTF8.GetString(buffer.ToArray()) + .Replace("\r\n", "\n").Replace("\n\n", "\n").TrimEnd('\n') + .Split('\n'); + } + } + public void Write(byte[] buffer, int offset, int count) { lock (_forWrite) @@ -126,5 +158,16 @@ namespace WebSocketSharp _innerStream.Write(buffer, 0, buffer.Length); } } + + public void WriteHandshake(Handshake handshake) + { + lock (_forWrite) + { + var buffer = handshake.ToBytes(); + _innerStream.Write(buffer, 0, buffer.Length); + } + } + + #endregion } } diff --git a/websocket-sharp/bin/Debug/websocket-sharp.dll b/websocket-sharp/bin/Debug/websocket-sharp.dll index de509c6d..4b6e1a4a 100755 Binary files a/websocket-sharp/bin/Debug/websocket-sharp.dll and b/websocket-sharp/bin/Debug/websocket-sharp.dll differ diff --git a/websocket-sharp/bin/Debug/websocket-sharp.dll.mdb b/websocket-sharp/bin/Debug/websocket-sharp.dll.mdb index d8ba1d5c..d1c92fae 100644 Binary files a/websocket-sharp/bin/Debug/websocket-sharp.dll.mdb and b/websocket-sharp/bin/Debug/websocket-sharp.dll.mdb differ diff --git a/websocket-sharp/bin/Debug_Ubuntu/websocket-sharp.dll b/websocket-sharp/bin/Debug_Ubuntu/websocket-sharp.dll index 2d652b4c..03bffdf9 100755 Binary files a/websocket-sharp/bin/Debug_Ubuntu/websocket-sharp.dll and b/websocket-sharp/bin/Debug_Ubuntu/websocket-sharp.dll differ diff --git a/websocket-sharp/bin/Debug_Ubuntu/websocket-sharp.dll.mdb b/websocket-sharp/bin/Debug_Ubuntu/websocket-sharp.dll.mdb index d86c09d4..413f6352 100644 Binary files a/websocket-sharp/bin/Debug_Ubuntu/websocket-sharp.dll.mdb and b/websocket-sharp/bin/Debug_Ubuntu/websocket-sharp.dll.mdb differ diff --git a/websocket-sharp/websocket-sharp.csproj b/websocket-sharp/websocket-sharp.csproj index db28bacd..a1e0d30c 100644 --- a/websocket-sharp/websocket-sharp.csproj +++ b/websocket-sharp/websocket-sharp.csproj @@ -109,11 +109,14 @@ + + + \ No newline at end of file diff --git a/websocket-sharp/websocket-sharp.pidb b/websocket-sharp/websocket-sharp.pidb index 4e75caaa..242450b5 100644 Binary files a/websocket-sharp/websocket-sharp.pidb and b/websocket-sharp/websocket-sharp.pidb differ