diff --git a/Example/Example.pidb b/Example/Example.pidb index db856f7a..1ab1e052 100644 Binary files a/Example/Example.pidb and b/Example/Example.pidb differ diff --git a/Example/Program.cs b/Example/Program.cs index a1605fe2..df1a5cfe 100644 --- a/Example/Program.cs +++ b/Example/Program.cs @@ -77,7 +77,11 @@ namespace Example //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/Echo?name=nobita")) + //using (WebSocket ws = new WebSocket("ws://localhost:4649/エコー?name=のび太")) //using (WebSocket ws = new WebSocket("ws://localhost:4649/Chat")) + //using (WebSocket ws = new WebSocket("ws://localhost:4649/Chat?name=nobita")) + //using (WebSocket ws = new WebSocket("ws://localhost:4649/チャット?name=のび太")) { ws.OnOpen += (sender, e) => { diff --git a/Example/bin/Debug/example.exe b/Example/bin/Debug/example.exe index cb601859..c1a95eef 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 4383dfa6..98579731 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 59be2565..2c81ac0e 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 9c587ea2..f0adb074 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 cd333388..97b1ae4e 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 6722714c..4f2ac68a 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 defd4397..a7c9c8aa 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 870943a9..91b3c991 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/Example/bin/Release/example.exe b/Example/bin/Release/example.exe index e01cc0b3..f119b36a 100755 Binary files a/Example/bin/Release/example.exe and b/Example/bin/Release/example.exe differ diff --git a/Example/bin/Release/websocket-sharp.dll b/Example/bin/Release/websocket-sharp.dll index 41b06474..e96204cf 100755 Binary files a/Example/bin/Release/websocket-sharp.dll and b/Example/bin/Release/websocket-sharp.dll differ diff --git a/Example/bin/Release_Ubuntu/example.exe b/Example/bin/Release_Ubuntu/example.exe index 913d68f9..e3ea75df 100755 Binary files a/Example/bin/Release_Ubuntu/example.exe and b/Example/bin/Release_Ubuntu/example.exe differ diff --git a/Example/bin/Release_Ubuntu/websocket-sharp.dll b/Example/bin/Release_Ubuntu/websocket-sharp.dll index 975bf118..f5d4ff6c 100755 Binary files a/Example/bin/Release_Ubuntu/websocket-sharp.dll and b/Example/bin/Release_Ubuntu/websocket-sharp.dll differ diff --git a/Example1/bin/Debug/example1.exe b/Example1/bin/Debug/example1.exe index 8fdaae89..95a297e5 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 1657a064..f0aae89b 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 59be2565..2c81ac0e 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 9c587ea2..f0adb074 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 21f7f942..25b73afe 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 8c97a4dc..c9e9f3e3 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 defd4397..a7c9c8aa 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 870943a9..91b3c991 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/Example1/bin/Release/example1.exe b/Example1/bin/Release/example1.exe index 46c11ed1..97d21b47 100755 Binary files a/Example1/bin/Release/example1.exe and b/Example1/bin/Release/example1.exe differ diff --git a/Example1/bin/Release/websocket-sharp.dll b/Example1/bin/Release/websocket-sharp.dll index 41b06474..e96204cf 100755 Binary files a/Example1/bin/Release/websocket-sharp.dll and b/Example1/bin/Release/websocket-sharp.dll differ diff --git a/Example1/bin/Release_Ubuntu/example1.exe b/Example1/bin/Release_Ubuntu/example1.exe index 9cf0e4d7..34e83457 100755 Binary files a/Example1/bin/Release_Ubuntu/example1.exe and b/Example1/bin/Release_Ubuntu/example1.exe differ diff --git a/Example1/bin/Release_Ubuntu/websocket-sharp.dll b/Example1/bin/Release_Ubuntu/websocket-sharp.dll index 975bf118..f5d4ff6c 100755 Binary files a/Example1/bin/Release_Ubuntu/websocket-sharp.dll and b/Example1/bin/Release_Ubuntu/websocket-sharp.dll differ diff --git a/Example2/Chat.cs b/Example2/Chat.cs index 48b444c8..dda58a54 100644 --- a/Example2/Chat.cs +++ b/Example2/Chat.cs @@ -6,9 +6,37 @@ namespace Example2 { public class Chat : WebSocketService { - protected override void onMessage(object sender, MessageEventArgs e) + private static object _forId = new object(); + private static uint _id = 0; + + private string _name; + + private string getName() + { + lock (_forId) + { + return QueryString.Exists("name") + ? QueryString["name"] + : "anon#" + (++_id); + } + } + + protected override void OnOpen(object sender, EventArgs e) + { + _name = getName(); + } + + protected override void OnMessage(object sender, MessageEventArgs e) + { + + var msg = String.Format("{0}: {1}", _name, e.Data); + Publish(msg); + } + + protected override void OnClose(object sender, CloseEventArgs e) { - Publish(e.Data); + var msg = String.Format("{0} got logged off...", _name); + Publish(msg); } } } diff --git a/Example2/Echo.cs b/Example2/Echo.cs index f31ca174..8d87c2d5 100644 --- a/Example2/Echo.cs +++ b/Example2/Echo.cs @@ -2,18 +2,16 @@ using System; using WebSocketSharp; using WebSocketSharp.Server; -namespace Example2 -{ +namespace Example2 { + public class Echo : WebSocketService { - protected override void onMessage(object sender, MessageEventArgs e) - { - Send(e.Data); - } - - protected override void onClose(object sender, CloseEventArgs e) + protected override void OnMessage(object sender, MessageEventArgs e) { - Console.WriteLine("[Echo] Close({0})", e.Code); + var msg = QueryString.Exists("name") + ? String.Format("'{0}' returns to {1}", e.Data, QueryString["name"]) + : e.Data; + Send(msg); } } } diff --git a/Example2/Example2.pidb b/Example2/Example2.pidb index 16285d05..30c9abf8 100644 Binary files a/Example2/Example2.pidb and b/Example2/Example2.pidb differ diff --git a/Example2/Program.cs b/Example2/Program.cs index 028f9aef..49804c03 100644 --- a/Example2/Program.cs +++ b/Example2/Program.cs @@ -8,10 +8,18 @@ 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("ws://localhost:4649/Echo"); + //var wssv = new WebSocketServer("ws://localhost:4649/エコー"); //var wssv = new WebSocketServer(4649); + //var wssv = new WebSocketServer(4649, "/Echo"); + //var wssv = new WebSocketServer(4649, "/エコー"); //var wssv = new WebSocketServer("ws://localhost:4649"); + //var wssv = new WebSocketServer("ws://localhost:4649/Chat"); + //var wssv = new WebSocketServer("ws://localhost:4649/チャット"); //var wssv = new WebSocketServer(4649); + //var wssv = new WebSocketServer(4649, "/Chat"); + //var wssv = new WebSocketServer(4649, "/チャット"); wssv.Start(); Console.WriteLine( @@ -22,7 +30,9 @@ namespace Example2 // Multi services server var wssv = new WebSocketServer(4649); wssv.AddService("/Echo"); + wssv.AddService("/エコー"); wssv.AddService("/Chat"); + wssv.AddService("/チャット"); wssv.Start(); Console.WriteLine( diff --git a/Example2/bin/Debug/example2.exe b/Example2/bin/Debug/example2.exe index 1ab6140f..2fba62f8 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 9be918e3..8836aa84 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 59be2565..2c81ac0e 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 9c587ea2..f0adb074 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 052e10aa..cf7b5d8d 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 8e88e04f..e7c919b9 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 defd4397..a7c9c8aa 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 870943a9..91b3c991 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/Example2/bin/Release/example2.exe b/Example2/bin/Release/example2.exe index e03b4067..433a2a0b 100755 Binary files a/Example2/bin/Release/example2.exe and b/Example2/bin/Release/example2.exe differ diff --git a/Example2/bin/Release/websocket-sharp.dll b/Example2/bin/Release/websocket-sharp.dll index 41b06474..e96204cf 100755 Binary files a/Example2/bin/Release/websocket-sharp.dll and b/Example2/bin/Release/websocket-sharp.dll differ diff --git a/Example2/bin/Release_Ubuntu/example2.exe b/Example2/bin/Release_Ubuntu/example2.exe index a4e2384d..fff2d150 100755 Binary files a/Example2/bin/Release_Ubuntu/example2.exe and b/Example2/bin/Release_Ubuntu/example2.exe differ diff --git a/Example2/bin/Release_Ubuntu/websocket-sharp.dll b/Example2/bin/Release_Ubuntu/websocket-sharp.dll index 975bf118..f5d4ff6c 100755 Binary files a/Example2/bin/Release_Ubuntu/websocket-sharp.dll and b/Example2/bin/Release_Ubuntu/websocket-sharp.dll differ diff --git a/Example3/Chat.cs b/Example3/Chat.cs index 55a14374..7ba53cb0 100644 --- a/Example3/Chat.cs +++ b/Example3/Chat.cs @@ -6,9 +6,37 @@ namespace Example3 { public class Chat : WebSocketService { - protected override void onMessage(object sender, MessageEventArgs e) + private static object _forId = new object(); + private static uint _id = 0; + + private string _name; + + private string getName() + { + lock (_forId) + { + return QueryString.Exists("name") + ? QueryString["name"] + : "anon#" + (++_id); + } + } + + protected override void OnOpen(object sender, EventArgs e) + { + _name = getName(); + } + + protected override void OnMessage(object sender, MessageEventArgs e) + { + + var msg = String.Format("{0}: {1}", _name, e.Data); + Publish(msg); + } + + protected override void OnClose(object sender, CloseEventArgs e) { - Publish(e.Data); + var msg = String.Format("{0} got logged off...", _name); + Publish(msg); } } } diff --git a/Example3/Echo.cs b/Example3/Echo.cs index 3f824d55..71f55fae 100644 --- a/Example3/Echo.cs +++ b/Example3/Echo.cs @@ -6,14 +6,12 @@ namespace Example3 { public class Echo : WebSocketService { - protected override void onMessage(object sender, MessageEventArgs e) + protected override void OnMessage(object sender, MessageEventArgs e) { - Send(e.Data); - } - - protected override void onClose(object sender, CloseEventArgs e) - { - Console.WriteLine("[Echo] Close({0})", e.Code); + var msg = QueryString.Exists("name") + ? String.Format("'{0}' returns to {1}", e.Data, QueryString["name"]) + : e.Data; + Send(msg); } } } diff --git a/Example3/Example3.pidb b/Example3/Example3.pidb index 77f78cb5..4e2ef0c0 100644 Binary files a/Example3/Example3.pidb and b/Example3/Example3.pidb differ diff --git a/Example3/Program.cs b/Example3/Program.cs index 7c971a13..0368abb1 100644 --- a/Example3/Program.cs +++ b/Example3/Program.cs @@ -12,7 +12,8 @@ namespace Example3 public static void Main(string[] args) { _httpsv = new HttpServer(4649); - _httpsv.AddService("/"); + _httpsv.AddService("/Echo"); + _httpsv.AddService("/Chat"); _httpsv.OnGet += (sender, e) => { diff --git a/Example3/Public/Js/echotest.js b/Example3/Public/Js/echotest.js index 3f61e039..23d09d56 100644 --- a/Example3/Public/Js/echotest.js +++ b/Example3/Public/Js/echotest.js @@ -6,7 +6,7 @@ * */ -var wsUri = "ws://localhost:4649/"; +var wsUri = "ws://localhost:4649/Echo"; var output; function init(){ diff --git a/Example3/bin/Debug/Example3.exe b/Example3/bin/Debug/Example3.exe index 51ed2c34..92d8f12f 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 fcf357ad..d78a5546 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 59be2565..2c81ac0e 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 9c587ea2..f0adb074 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 1ffaaa75..d3e781e4 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 36ef1b04..a1c1d4db 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 defd4397..a7c9c8aa 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 870943a9..91b3c991 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/Example3/bin/Release/Example3.exe b/Example3/bin/Release/Example3.exe index 741ce5ce..7098e3cd 100755 Binary files a/Example3/bin/Release/Example3.exe and b/Example3/bin/Release/Example3.exe differ diff --git a/Example3/bin/Release/websocket-sharp.dll b/Example3/bin/Release/websocket-sharp.dll index 41b06474..e96204cf 100755 Binary files a/Example3/bin/Release/websocket-sharp.dll and b/Example3/bin/Release/websocket-sharp.dll differ diff --git a/Example3/bin/Release_Ubuntu/Example3.exe b/Example3/bin/Release_Ubuntu/Example3.exe index ee3dab93..11695f83 100755 Binary files a/Example3/bin/Release_Ubuntu/Example3.exe and b/Example3/bin/Release_Ubuntu/Example3.exe differ diff --git a/Example3/bin/Release_Ubuntu/websocket-sharp.dll b/Example3/bin/Release_Ubuntu/websocket-sharp.dll index 975bf118..f5d4ff6c 100755 Binary files a/Example3/bin/Release_Ubuntu/websocket-sharp.dll and b/Example3/bin/Release_Ubuntu/websocket-sharp.dll differ diff --git a/README.md b/README.md index e7d7782a..f6098da8 100644 --- a/README.md +++ b/README.md @@ -164,7 +164,7 @@ using WebSocketSharp.Server; public class Echo : WebSocketService { - protected override void onMessage(object sender, MessageEventArgs e) + protected override void OnMessage(object sender, MessageEventArgs e) { Send(e.Data); } @@ -180,16 +180,16 @@ using WebSocketSharp.Server; public class Chat : WebSocketService { - protected override void onMessage(object sender, MessageEventArgs e) + protected override void OnMessage(object sender, MessageEventArgs e) { Publish(e.Data); } } ``` -If you override the `onMessage` method, it is bound to the server side `WebSocket.OnMessage` event. +If you override the `OnMessage` method, it is bound to the server side `WebSocket.OnMessage` event. -In addition, if you override the `onOpen`, `onError` and `onClose` methods, each of them is bound to the `WebSocket.OnOpen`, `WebSocket.OnError` and `WebSocket.OnClose` events. +In addition, if you override the `OnOpen`, `OnError` and `OnClose` methods, each of them is bound to the `WebSocket.OnOpen`, `WebSocket.OnError` and `WebSocket.OnClose` events. #### Step 3 #### diff --git a/websocket-sharp/Ext.cs b/websocket-sharp/Ext.cs index d07ce2b5..0228eeec 100644 --- a/websocket-sharp/Ext.cs +++ b/websocket-sharp/Ext.cs @@ -52,23 +52,46 @@ namespace WebSocketSharp { return new TcpListenerWebSocketContext(client); } + /// + /// Emit the specified delegate if is not . + /// + /// + /// An to be emitted. + /// + /// + /// An that emits this . + /// + /// + /// An that contains no event data. + /// public static void Emit( this EventHandler eventHandler, object sender, EventArgs e) { - if (eventHandler != null) - { + if (!eventHandler.IsNull()) eventHandler(sender, e); - } } + /// + /// Emit the specified delegate if is not . + /// + /// + /// An to be emitted. + /// + /// + /// An that emits this . + /// + /// + /// An that contains the event data. + /// + /// + /// The type of the event data generated by the event. + /// public static void Emit( this EventHandler eventHandler, object sender, TEventArgs e) where TEventArgs : EventArgs { - if (eventHandler != null) - { + if (!eventHandler.IsNull()) eventHandler(sender, e); - } } public static bool EqualsAndSaveTo(this int value, char c, List dest) @@ -81,17 +104,17 @@ namespace WebSocketSharp { return b == Convert.ToByte(c); } - public static bool Exists(this NameValueCollection headers, string name) + public static bool Exists(this NameValueCollection collections, string name) { - return headers[name] != null + return collections[name] != null ? true : false; } - public static bool Exists(this NameValueCollection headers, string name, string value) + public static bool Exists(this NameValueCollection collections, string name, string value) { - var values = headers[name]; - if (values == null) + var values = collections[name]; + if (values.IsNull()) return false; foreach (string v in values.Split(',')) @@ -101,6 +124,27 @@ namespace WebSocketSharp { return false; } + public static string GetAbsolutePath(this Uri uri) + { + if (uri.IsAbsoluteUri) + return uri.AbsolutePath; + + var uriString = uri.OriginalString; + + var i = uriString.IndexOf('/'); + if (i != 0) + { + var msg = "Not absolute path: " + uriString; + throw new ArgumentException(msg, "uri"); + } + + var j = uriString.IndexOfAny(new []{'?', '#'}); + if (j > 0) + return uriString.Substring(0, j); + + return uriString; + } + public static string GetDescription(this HttpStatusCode code) { return ((int)code).GetStatusDescription(); @@ -162,11 +206,32 @@ namespace WebSocketSharp { return String.Empty; } + public static string GetName(this string nameAndValue, string separator) + { + var i = nameAndValue.IndexOf(separator); + if (i <= 0) + return null; + + return nameAndValue.Substring(0, i).Trim(); + } + + public static KeyValuePair GetNameAndValue(this string nameAndValue, string separator) + { + var i = nameAndValue.IndexOf(separator); + if (i <= 0 || i == nameAndValue.Length - 1) + return new KeyValuePair(null, null); + + var name = nameAndValue.Substring(0, i).Trim(); + var value = nameAndValue.Substring(i + 1).Trim(); + + return new KeyValuePair(name, value); + } + /// /// Gets the value from a that contains a pair of name and value are separated by a separator string. /// /// - /// A that contains the value if can get; otherwise, null. + /// A that contains the value if any; otherwise, null. /// /// /// A that contains a pair of name and value are separated by a separator string. @@ -177,7 +242,7 @@ namespace WebSocketSharp { public static string GetValue(this string nameAndValue, string separator) { var i = nameAndValue.IndexOf(separator); - if (i <= 0) + if (i == -1 || i == nameAndValue.Length - 1) return null; return nameAndValue.Substring(i + 1).Trim(); @@ -195,10 +260,42 @@ namespace WebSocketSharp { } } - public static bool IsNullDo(this T value, Action act) + /// + /// Determines whether the specified is . + /// + /// + /// true if the is ; otherwise, false. + /// + /// + /// A to test. + /// + public static bool IsEmpty(this string value) + { + return value == String.Empty ? true : false; + } + + /// + /// Determines whether the specified object is . + /// + /// + /// true if the specified object is ; otherwise, false. + /// + /// + /// An to test. + /// + /// + /// The type of this . + /// + public static bool IsNull(this T obj) + where T : class + { + return obj == null ? true : false; + } + + public static bool IsNullDo(this T obj, Action act) where T : class { - if (value == null) + if (obj.IsNull()) { act(); return true; @@ -207,10 +304,50 @@ namespace WebSocketSharp { return false; } + /// + /// Determines whether the specified is or . + /// + /// + /// true if the specified is or ; otherwise, false. + /// + /// + /// A to test. + /// + public static bool IsNullOrEmpty(this string value) + { + return String.IsNullOrEmpty(value); + } + + public static bool IsValidAbsolutePath(this string absPath, out string message) + { + if (absPath.IsEmpty()) + { + message = "Must not be empty."; + return false; + } + + var i = absPath.IndexOf('/'); + if (i != 0) + { + message = "Not absolute path: " + absPath; + return false; + } + + var j = absPath.IndexOfAny(new []{'?', '#'}); + if (j != -1) + { + message = "Must not contain either or both query and fragment components: " + absPath; + return false; + } + + message = String.Empty; + return true; + } + // Derived from System.Uri.IsPredefinedScheme method public static bool IsPredefinedScheme(this string scheme) { - if (scheme == null && scheme.Length < 2) + if (scheme.IsNull() && scheme.Length < 2) return false; char c = scheme[0]; @@ -243,56 +380,6 @@ namespace WebSocketSharp { return false; } - /// - /// Determines whether is valid WebSocket URI. - /// - /// - /// true if is valid WebSocket URI; otherwise, false. - /// - /// - /// A that contains a WebSocket URI. - /// - /// - /// A that contains a error message if is invalid WebSocket URI; otherwise, String.Empty. - /// - public static bool IsValidWebSocketUri(this Uri uri, out string message) - { - if (!uri.IsAbsoluteUri) - { - message = "Not absolute URI: " + uri.ToString(); - return false; - } - - var scheme = uri.Scheme; - if (scheme != "ws" && scheme != "wss") - { - message = "Unsupported WebSocket URI scheme: " + scheme; - return false; - } - - var original = uri.OriginalString; - if (original.Contains('#')) - { - message = "WebSocket URI must not contain a fragment component: " + original; - return false; - } - - var port = uri.Port; - if (port > 0) - { - if ((scheme == "ws" && port == 443) || - (scheme == "wss" && port == 80)) - { - message = String.Format( - "Invalid pair of WebSocket URI scheme and port: {0}, {1}", scheme, port); - return false; - } - } - - message = String.Empty; - return true; - } - // Derived from System.Uri.MaybeUri method public static bool MaybeUri(this string uriString) { @@ -372,12 +459,11 @@ namespace WebSocketSharp { public static T[] SubArray(this T[] array, int startIndex, int length) { if (startIndex == 0 && array.Length == length) - { return array; - } T[] subArray = new T[length]; - Array.Copy(array, startIndex, subArray, 0, length); + Array.Copy(array, startIndex, subArray, 0, length); + return subArray; } @@ -564,6 +650,85 @@ namespace WebSocketSharp { return new Uri(uriString); } + /// + /// Tries to create a new WebSocket using . + /// + /// + /// true if the WebSocket was successfully created; otherwise, false. + /// + /// + /// A that contains a WebSocket URI. + /// + /// + /// When this method returns, contains a created WebSocket if is valid WebSocket URI; otherwise, . + /// + /// + /// When this method returns, contains a error message if is invalid WebSocket URI; otherwise, String.Empty. + /// + /// + /// Is thrown when passed to a method is invalid because it is . + /// + public static bool TryCreateWebSocketUri(this string uriString, out Uri result, out string message) + { + if (uriString == null) + throw new ArgumentNullException("uriString"); + + result = null; + if (uriString == String.Empty) + { + message = "Must not be empty."; + return false; + } + + var uri = uriString.ToUri(); + if (!uri.IsAbsoluteUri) + { + message = "Not absolute URI: " + uriString; + return false; + } + + var scheme = uri.Scheme; + if (scheme != "ws" && scheme != "wss") + { + message = "Unsupported scheme: " + scheme; + return false; + } + + var fragment = uri.Fragment; + if (!String.IsNullOrEmpty(fragment)) + { + message = "Must not contain the fragment component: " + uriString; + return false; + } + + var port = uri.Port; + if (port > 0) + { + if ((scheme == "ws" && port == 443) || + (scheme == "wss" && port == 80)) + { + message = String.Format( + "Invalid pair of scheme and port: {0}, {1}", scheme, port); + return false; + } + } + + result = uri; + message = String.Empty; + + return true; + } + + public static string UrlDecode(this string s) + { + return HttpUtility.UrlDecode(s); + } + + public static string UrlEncode(this string s) + { + return HttpUtility.UrlEncode(s); + } + public static void WriteContent(this HttpListenerResponse response, byte[] content) { var output = response.OutputStream; diff --git a/websocket-sharp/Net/HttpListenerContext.cs b/websocket-sharp/Net/HttpListenerContext.cs index 8e74b30b..f8258323 100644 --- a/websocket-sharp/Net/HttpListenerContext.cs +++ b/websocket-sharp/Net/HttpListenerContext.cs @@ -169,9 +169,9 @@ namespace WebSocketSharp.Net { #region Public Method - public HttpListenerWebSocketContext AcceptWebSocket (string path) + public HttpListenerWebSocketContext AcceptWebSocket () { - return new HttpListenerWebSocketContext (path, this); + return new HttpListenerWebSocketContext (this); } #endregion diff --git a/websocket-sharp/Net/HttpListenerWebSocketContext.cs b/websocket-sharp/Net/HttpListenerWebSocketContext.cs index dafd9b7a..7ed7456c 100644 --- a/websocket-sharp/Net/HttpListenerWebSocketContext.cs +++ b/websocket-sharp/Net/HttpListenerWebSocketContext.cs @@ -39,67 +39,113 @@ namespace WebSocketSharp.Net { private WebSocket _socket; private WsStream _stream; - internal HttpListenerWebSocketContext(string path, HttpListenerContext context) + internal HttpListenerWebSocketContext(HttpListenerContext context) { _context = context; _stream = WsStream.CreateServerStream(context); - _socket = new WebSocket(path.ToUri(), this); + _socket = new WebSocket(this); } internal HttpListenerContext BaseContext { - get { return _context; } + get { + return _context; + } } internal WsStream Stream { - get { return _stream; } + get { + return _stream; + } } public override CookieCollection CookieCollection { - get { return _context.Request.Cookies; } + get { + return _context.Request.Cookies; + } } public override NameValueCollection Headers { - get { return _context.Request.Headers; } + get { + return _context.Request.Headers; + } } public override bool IsAuthenticated { - get { return _context.Request.IsAuthenticated; } + get { + return _context.Request.IsAuthenticated; + } } public override bool IsSecureConnection { - get { return _context.Request.IsSecureConnection; } + get { + return _context.Request.IsSecureConnection; + } } public override bool IsLocal { - get { return _context.Request.IsLocal; } + get { + return _context.Request.IsLocal; + } } public override string Origin { - get { return Headers["Origin"]; } + get { + return Headers["Origin"]; + } + } + + public virtual string Path { + get { + return RequestUri.GetAbsolutePath(); + } } public override Uri RequestUri { - get { return _context.Request.RawUrl.ToUri(); } + get { + return _context.Request.RawUrl.ToUri(); + } } public override string SecWebSocketKey { - get { return Headers["Sec-WebSocket-Key"]; } + get { + return Headers["Sec-WebSocket-Key"]; + } } public override IEnumerable SecWebSocketProtocols { - get { return Headers.GetValues("Sec-WebSocket-Protocol"); } + get { + return Headers.GetValues("Sec-WebSocket-Protocol"); + } } public override string SecWebSocketVersion { - get { return Headers["Sec-WebSocket-Version"]; } + get { + return Headers["Sec-WebSocket-Version"]; + } + } + + public virtual System.Net.IPEndPoint ServerEndPoint { + get { + return _context.Connection.LocalEndPoint; + } } public override IPrincipal User { - get { return _context.User; } + get { + return _context.User; + } + } + + public virtual System.Net.IPEndPoint UserEndPoint { + get { + return _context.Connection.RemoteEndPoint; + } } public override WebSocket WebSocket { - get { return _socket; } + get { + return _socket; + } } } } diff --git a/websocket-sharp/Net/HttpUtility.cs b/websocket-sharp/Net/HttpUtility.cs index 7eaffa04..64f10bac 100644 --- a/websocket-sharp/Net/HttpUtility.cs +++ b/websocket-sharp/Net/HttpUtility.cs @@ -1,6 +1,6 @@ // // HttpUtility.cs -// Copied from System.Net.HttpUtility +// Copied from System.Net.HttpUtility.cs // // Authors: // Patrik Torstensson (Patrik.Torstensson@labs2.com) @@ -51,23 +51,38 @@ namespace WebSocketSharp.Net { int count = Count; if (count == 0) return ""; + StringBuilder sb = new StringBuilder (); string [] keys = AllKeys; for (int i = 0; i < count; i++) { sb.AppendFormat ("{0}={1}&", keys [i], this [keys [i]]); } + if (sb.Length > 0) sb.Length--; + return sb.ToString (); } } + #region Fields - + static Hashtable entities; - static object lock_ = new object (); - + static char [] hexChars = "0123456789abcdef".ToCharArray (); + static object lock_ = new object (); + #endregion // Fields - + + #region Constructor + + public HttpUtility () + { + } + + #endregion // Constructor + + #region Property + static Hashtable Entities { get { lock (lock_) { @@ -78,12 +93,68 @@ namespace WebSocketSharp.Net { } } } - - #region Constructors + + #endregion // Property + + #region Private Methods + + static int GetChar (byte [] bytes, int offset, int length) + { + int value = 0; + int end = length + offset; + for (int i = offset; i < end; i++) { + int current = GetInt (bytes [i]); + if (current == -1) + return -1; + + value = (value << 4) + current; + } + + return value; + } + + static int GetChar (string str, int offset, int length) + { + int val = 0; + int end = length + offset; + for (int i = offset; i < end; i++) { + char c = str [i]; + if (c > 127) + return -1; + + int current = GetInt ((byte) c); + if (current == -1) + return -1; + + val = (val << 4) + current; + } + + return val; + } + + static char [] GetChars (MemoryStream b, Encoding e) + { + return e.GetChars (b.GetBuffer (), 0, (int) b.Length); + } + + static int GetInt (byte b) + { + char c = (char) b; + if (c >= '0' && c <= '9') + return c - '0'; + + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + + return -1; + } static void InitEntities () { - // Build the hash table of HTML entity references. This list comes + // Build the hash table of HTML entity references. This list comes // from the HTML 4.01 W3C recommendation. entities = new Hashtable (); entities.Add ("nbsp", '\u00A0'); @@ -340,19 +411,151 @@ namespace WebSocketSharp.Net { entities.Add ("euro", '\u20AC'); } - public HttpUtility () + static bool NotEncoded (char c) { + return (c == '!' || c == '\'' || c == '(' || c == ')' || c == '*' || c == '-' || c == '.' || c == '_'); } - - #endregion // Constructors - - #region Methods - - public static void HtmlAttributeEncode (string s, TextWriter output) + + static void UrlEncodeChar (char c, Stream result, bool isUnicode) { - output.Write(HtmlAttributeEncode(s)); + if (c > 255) { + // FIXME: what happens when there is an internal error ? + //if (!isUnicode) + // throw new ArgumentOutOfRangeException ("c", c, "c must be less than 256"); + int idx; + int i = (int) c; + + result.WriteByte ((byte)'%'); + result.WriteByte ((byte)'u'); + idx = i >> 12; + result.WriteByte ((byte)hexChars [idx]); + idx = (i >> 8) & 0x0F; + result.WriteByte ((byte)hexChars [idx]); + idx = (i >> 4) & 0x0F; + result.WriteByte ((byte)hexChars [idx]); + idx = i & 0x0F; + result.WriteByte ((byte)hexChars [idx]); + return; + } + + if (c > ' ' && NotEncoded (c)) { + result.WriteByte ((byte)c); + return; + } + + if (c==' ') { + result.WriteByte ((byte)'+'); + return; + } + + if ((c < '0') || + (c < 'A' && c > '9') || + (c > 'Z' && c < 'a') || + (c > 'z')) { + if (isUnicode && c > 127) { + result.WriteByte ((byte)'%'); + result.WriteByte ((byte)'u'); + result.WriteByte ((byte)'0'); + result.WriteByte ((byte)'0'); + } + else + result.WriteByte ((byte)'%'); + + int idx = ((int) c) >> 4; + result.WriteByte ((byte)hexChars [idx]); + idx = ((int) c) & 0x0F; + result.WriteByte ((byte)hexChars [idx]); + } + else + result.WriteByte ((byte)c); } - + + static void UrlPathEncodeChar (char c, Stream result) + { + if (c < 33 || c > 126) { + byte [] bIn = Encoding.UTF8.GetBytes (c.ToString ()); + for (int i = 0; i < bIn.Length; i++) { + result.WriteByte ((byte) '%'); + int idx = ((int) bIn [i]) >> 4; + result.WriteByte ((byte) hexChars [idx]); + idx = ((int) bIn [i]) & 0x0F; + result.WriteByte ((byte) hexChars [idx]); + } + } + else if (c == ' ') { + result.WriteByte ((byte) '%'); + result.WriteByte ((byte) '2'); + result.WriteByte ((byte) '0'); + } + else + result.WriteByte ((byte) c); + } + + static void WriteCharBytes (IList buf, char ch, Encoding e) + { + if (ch > 255) { + foreach (byte b in e.GetBytes (new char[] { ch })) + buf.Add (b); + } else + buf.Add ((byte)ch); + } + + #endregion // Private Methods + + #region Internal Method + + internal static void ParseQueryString (string query, Encoding encoding, NameValueCollection result) + { + if (query.Length == 0) + return; + + string decoded = HtmlDecode (query); + int decodedLength = decoded.Length; + int namePos = 0; + bool first = true; + while (namePos <= decodedLength) { + int valuePos = -1, valueEnd = -1; + for (int q = namePos; q < decodedLength; q++) { + if (valuePos == -1 && decoded [q] == '=') { + valuePos = q + 1; + } else if (decoded [q] == '&') { + valueEnd = q; + break; + } + } + + if (first) { + first = false; + if (decoded [namePos] == '?') + namePos++; + } + + string name, value; + if (valuePos == -1) { + name = null; + valuePos = namePos; + } else { + name = UrlDecode (decoded.Substring (namePos, valuePos - namePos - 1), encoding); + } + + if (valueEnd < 0) { + namePos = -1; + valueEnd = decoded.Length; + } else { + namePos = valueEnd + 1; + } + + value = UrlDecode (decoded.Substring (valuePos, valueEnd - valuePos), encoding); + result.Add (name, value); + if (namePos == -1) + break; + } + } + + #endregion // Internal Methods + + #region Public Methods + public static string HtmlAttributeEncode (string s) { if (null == s) @@ -389,570 +592,436 @@ namespace WebSocketSharp.Net { return output.ToString(); } - - public static string UrlDecode (string str) - { - return UrlDecode(str, Encoding.UTF8); - } - - static char [] GetChars (MemoryStream b, Encoding e) - { - return e.GetChars (b.GetBuffer (), 0, (int) b.Length); - } - static void WriteCharBytes (IList buf, char ch, Encoding e) + public static void HtmlAttributeEncode (string s, TextWriter output) { - if (ch > 255) { - foreach (byte b in e.GetBytes (new char[] { ch })) - buf.Add (b); - } else - buf.Add ((byte)ch); + output.Write(HtmlAttributeEncode(s)); } - - public static string UrlDecode (string s, Encoding e) + + /// + /// Decodes an HTML-encoded string and returns the decoded string. + /// + /// The HTML string to decode. + /// The decoded text. + public static string HtmlDecode (string s) { - if (null == s) - return null; + if (s == null) + throw new ArgumentNullException ("s"); - if (s.IndexOf ('%') == -1 && s.IndexOf ('+') == -1) + if (s.IndexOf ('&') == -1) return s; - - if (e == null) - e = Encoding.UTF8; - long len = s.Length; - var bytes = new List (); - int xchar; - char ch; - - for (int i = 0; i < len; i++) { - ch = s [i]; - if (ch == '%' && i + 2 < len && s [i + 1] != '%') { - if (s [i + 1] == 'u' && i + 5 < len) { - // unicode hex sequence - xchar = GetChar (s, i + 2, 4); - if (xchar != -1) { - WriteCharBytes (bytes, (char)xchar, e); - i += 5; - } else - WriteCharBytes (bytes, '%', e); - } else if ((xchar = GetChar (s, i + 1, 2)) != -1) { - WriteCharBytes (bytes, (char)xchar, e); - i += 2; + StringBuilder entity = new StringBuilder (); + StringBuilder output = new StringBuilder (); + int len = s.Length; + // 0 -> nothing, + // 1 -> right after '&' + // 2 -> between '&' and ';' but no '#' + // 3 -> '#' found after '&' and getting numbers + int state = 0; + int number = 0; + bool have_trailing_digits = false; + + for (int i = 0; i < len; i++) { + char c = s [i]; + if (state == 0) { + if (c == '&') { + entity.Append (c); + state = 1; } else { - WriteCharBytes (bytes, '%', e); + output.Append (c); } continue; } - if (ch == '+') - WriteCharBytes (bytes, ' ', e); - else - WriteCharBytes (bytes, ch, e); - } - - byte[] buf = bytes.ToArray (); - bytes = null; - return e.GetString (buf); - - } - - public static string UrlDecode (byte [] bytes, Encoding e) - { - if (bytes == null) - return null; - - return UrlDecode (bytes, 0, bytes.Length, e); - } - - static int GetInt (byte b) - { - char c = (char) b; - if (c >= '0' && c <= '9') - return c - '0'; + if (c == '&') { + state = 1; + if (have_trailing_digits) { + entity.Append (number.ToString (CultureInfo.InvariantCulture)); + have_trailing_digits = false; + } - if (c >= 'a' && c <= 'f') - return c - 'a' + 10; + output.Append (entity.ToString ()); + entity.Length = 0; + entity.Append ('&'); + continue; + } - if (c >= 'A' && c <= 'F') - return c - 'A' + 10; + if (state == 1) { + if (c == ';') { + state = 0; + output.Append (entity.ToString ()); + output.Append (c); + entity.Length = 0; + } else { + number = 0; + if (c != '#') { + state = 2; + } else { + state = 3; + } + entity.Append (c); + } + } else if (state == 2) { + entity.Append (c); + if (c == ';') { + string key = entity.ToString (); + if (key.Length > 1 && Entities.ContainsKey (key.Substring (1, key.Length - 2))) + key = Entities [key.Substring (1, key.Length - 2)].ToString (); - return -1; - } + output.Append (key); + state = 0; + entity.Length = 0; + } + } else if (state == 3) { + if (c == ';') { + if (number > 65535) { + output.Append ("&#"); + output.Append (number.ToString (CultureInfo.InvariantCulture)); + output.Append (";"); + } else { + output.Append ((char) number); + } + state = 0; + entity.Length = 0; + have_trailing_digits = false; + } else if (Char.IsDigit (c)) { + number = number * 10 + ((int) c - '0'); + have_trailing_digits = true; + } else { + state = 2; + if (have_trailing_digits) { + entity.Append (number.ToString (CultureInfo.InvariantCulture)); + have_trailing_digits = false; + } + entity.Append (c); + } + } + } - static int GetChar (byte [] bytes, int offset, int length) - { - int value = 0; - int end = length + offset; - for (int i = offset; i < end; i++) { - int current = GetInt (bytes [i]); - if (current == -1) - return -1; - value = (value << 4) + current; + if (entity.Length > 0) { + output.Append (entity.ToString ()); + } else if (have_trailing_digits) { + output.Append (number.ToString (CultureInfo.InvariantCulture)); } - return value; + return output.ToString (); } - static int GetChar (string str, int offset, int length) + /// + /// Decodes an HTML-encoded string and sends the resulting output to a TextWriter output stream. + /// + /// The HTML string to decode. + /// The TextWriter output stream containing the decoded string. + public static void HtmlDecode (string s, TextWriter output) { - int val = 0; - int end = length + offset; - for (int i = offset; i < end; i++) { - char c = str [i]; - if (c > 127) - return -1; - - int current = GetInt ((byte) c); - if (current == -1) - return -1; - val = (val << 4) + current; - } - - return val; + if (s != null) + output.Write (HtmlDecode (s)); } - - public static string UrlDecode (byte [] bytes, int offset, int count, Encoding e) + + /// + /// HTML-encodes a string and returns the encoded string. + /// + /// The text string to encode. + /// The HTML-encoded text. + public static string HtmlEncode (string s) { - if (bytes == null) + if (s == null) return null; - if (count == 0) - return String.Empty; - - if (bytes == null) - throw new ArgumentNullException ("bytes"); - if (offset < 0 || offset > bytes.Length) - throw new ArgumentOutOfRangeException ("offset"); + bool needEncode = false; + for (int i = 0; i < s.Length; i++) { + char c = s [i]; + if (c == '&' || c == '"' || c == '<' || c == '>' || c > 159) { + needEncode = true; + break; + } + } - if (count < 0 || offset + count > bytes.Length) - throw new ArgumentOutOfRangeException ("count"); + if (!needEncode) + return s; StringBuilder output = new StringBuilder (); - MemoryStream acc = new MemoryStream (); - int end = count + offset; - int xchar; - for (int i = offset; i < end; i++) { - if (bytes [i] == '%' && i + 2 < count && bytes [i + 1] != '%') { - if (bytes [i + 1] == (byte) 'u' && i + 5 < end) { - if (acc.Length > 0) { - output.Append (GetChars (acc, e)); - acc.SetLength (0); - } - xchar = GetChar (bytes, i + 2, 4); - if (xchar != -1) { - output.Append ((char) xchar); - i += 5; - continue; - } - } else if ((xchar = GetChar (bytes, i + 1, 2)) != -1) { - acc.WriteByte ((byte) xchar); - i += 2; - continue; + int len = s.Length; + for (int i = 0; i < len; i++) + switch (s [i]) { + case '&' : + output.Append ("&"); + break; + case '>' : + output.Append (">"); + break; + case '<' : + output.Append ("<"); + break; + case '"' : + output.Append ("""); + break; + default: + // MS starts encoding with &# from 160 and stops at 255. + // We don't do that. One reason is the 65308/65310 unicode + // characters that look like '<' and '>'. + if (s [i] > 159) { + output.Append ("&#"); + output.Append (((int) s [i]).ToString (CultureInfo.InvariantCulture)); + output.Append (";"); + } else { + output.Append (s [i]); } + break; } - if (acc.Length > 0) { - output.Append (GetChars (acc, e)); - acc.SetLength (0); - } - - if (bytes [i] == '+') { - output.Append (' '); - } else { - output.Append ((char) bytes [i]); - } - } - - if (acc.Length > 0) { - output.Append (GetChars (acc, e)); - } - - acc = null; return output.ToString (); } - - public static byte [] UrlDecodeToBytes (byte [] bytes) - { - if (bytes == null) - return null; - return UrlDecodeToBytes (bytes, 0, bytes.Length); - } - - public static byte [] UrlDecodeToBytes (string str) + /// + /// HTML-encodes a string and sends the resulting output to a TextWriter output stream. + /// + /// The string to encode. + /// The TextWriter output stream containing the encoded string. + public static void HtmlEncode (string s, TextWriter output) { - return UrlDecodeToBytes (str, Encoding.UTF8); + if (s != null) + output.Write (HtmlEncode (s)); } - public static byte [] UrlDecodeToBytes (string str, Encoding e) + public static NameValueCollection ParseQueryString (string query) { - if (str == null) - return null; - - if (e == null) - throw new ArgumentNullException ("e"); - - return UrlDecodeToBytes (e.GetBytes (str)); + return ParseQueryString (query, Encoding.UTF8); } - public static byte [] UrlDecodeToBytes (byte [] bytes, int offset, int count) + public static NameValueCollection ParseQueryString (string query, Encoding encoding) { - if (bytes == null) - return null; - if (count == 0) - return new byte [0]; + if (query == null) + throw new ArgumentNullException ("query"); - int len = bytes.Length; - if (offset < 0 || offset >= len) - throw new ArgumentOutOfRangeException("offset"); + if (encoding == null) + throw new ArgumentNullException ("encoding"); - if (count < 0 || offset > len - count) - throw new ArgumentOutOfRangeException("count"); + if (query.Length == 0 || (query.Length == 1 && query[0] == '?')) + return new NameValueCollection (); - MemoryStream result = new MemoryStream (); - int end = offset + count; - for (int i = offset; i < end; i++){ - char c = (char) bytes [i]; - if (c == '+') { - c = ' '; - } else if (c == '%' && i < end - 2) { - int xchar = GetChar (bytes, i + 1, 2); - if (xchar != -1) { - c = (char) xchar; - i += 2; - } - } - result.WriteByte ((byte) c); - } + if (query[0] == '?') + query = query.Substring (1); - return result.ToArray (); + NameValueCollection result = new HttpQSCollection (); + ParseQueryString (query, encoding, result); + + return result; } - public static string UrlEncode(string str) + public static string UrlDecode (string str) { - return UrlEncode(str, Encoding.UTF8); + return UrlDecode(str, Encoding.UTF8); } - - public static string UrlEncode (string s, Encoding Enc) + + public static string UrlDecode (string s, Encoding e) { if (s == null) return null; - if (s == "") - return ""; - - bool needEncode = false; - int len = s.Length; - for (int i = 0; i < len; i++) { - char c = s [i]; - if ((c < '0') || (c < 'A' && c > '9') || (c > 'Z' && c < 'a') || (c > 'z')) { - if (NotEncoded (c)) - continue; - - needEncode = true; - break; - } - } - - if (!needEncode) + if (s.IndexOf ('%') == -1 && s.IndexOf ('+') == -1) return s; - // avoided GetByteCount call - byte [] bytes = new byte[Enc.GetMaxByteCount(s.Length)]; - int realLen = Enc.GetBytes (s, 0, s.Length, bytes, 0); - return Encoding.ASCII.GetString (UrlEncodeToBytes (bytes, 0, realLen)); - } - - public static string UrlEncode (byte [] bytes) - { - if (bytes == null) - return null; - - if (bytes.Length == 0) - return ""; - - return Encoding.ASCII.GetString (UrlEncodeToBytes (bytes, 0, bytes.Length)); - } + if (e == null) + e = Encoding.UTF8; - public static string UrlEncode (byte [] bytes, int offset, int count) - { - if (bytes == null) - return null; + long len = s.Length; + var bytes = new List (); + int xchar; + char ch; - if (bytes.Length == 0) - return ""; + for (int i = 0; i < len; i++) { + ch = s [i]; + if (ch == '%' && i + 2 < len && s [i + 1] != '%') { + if (s [i + 1] == 'u' && i + 5 < len) { + // unicode hex sequence + xchar = GetChar (s, i + 2, 4); + if (xchar != -1) { + WriteCharBytes (bytes, (char)xchar, e); + i += 5; + } else + WriteCharBytes (bytes, '%', e); + } else if ((xchar = GetChar (s, i + 1, 2)) != -1) { + WriteCharBytes (bytes, (char)xchar, e); + i += 2; + } else { + WriteCharBytes (bytes, '%', e); + } + continue; + } - return Encoding.ASCII.GetString (UrlEncodeToBytes (bytes, offset, count)); - } + if (ch == '+') + WriteCharBytes (bytes, ' ', e); + else + WriteCharBytes (bytes, ch, e); + } - public static byte [] UrlEncodeToBytes (string str) - { - return UrlEncodeToBytes (str, Encoding.UTF8); + byte[] buf = bytes.ToArray (); + bytes = null; + return e.GetString (buf); } - public static byte [] UrlEncodeToBytes (string str, Encoding e) + public static string UrlDecode (byte [] bytes, Encoding e) { - if (str == null) + if (bytes == null) return null; - if (str == "") - return new byte [0]; - - byte [] bytes = e.GetBytes (str); - return UrlEncodeToBytes (bytes, 0, bytes.Length); + return UrlDecode (bytes, 0, bytes.Length, e); } - public static byte [] UrlEncodeToBytes (byte [] bytes) + public static string UrlDecode (byte [] bytes, int offset, int count, Encoding e) { if (bytes == null) return null; + if (count == 0) + return String.Empty; - if (bytes.Length == 0) - return new byte [0]; + if (bytes == null) + throw new ArgumentNullException ("bytes"); - return UrlEncodeToBytes (bytes, 0, bytes.Length); - } + if (offset < 0 || offset > bytes.Length) + throw new ArgumentOutOfRangeException ("offset"); - static char [] hexChars = "0123456789abcdef".ToCharArray (); + if (count < 0 || offset + count > bytes.Length) + throw new ArgumentOutOfRangeException ("count"); - static bool NotEncoded (char c) - { - return (c == '!' || c == '\'' || c == '(' || c == ')' || c == '*' || c == '-' || c == '.' || c == '_'); - } + StringBuilder output = new StringBuilder (); + MemoryStream acc = new MemoryStream (); - static void UrlEncodeChar (char c, Stream result, bool isUnicode) { - if (c > 255) { - //FIXME: what happens when there is an internal error? - //if (!isUnicode) - // throw new ArgumentOutOfRangeException ("c", c, "c must be less than 256"); - int idx; - int i = (int) c; + int end = count + offset; + int xchar; + for (int i = offset; i < end; i++) { + if (bytes [i] == '%' && i + 2 < count && bytes [i + 1] != '%') { + if (bytes [i + 1] == (byte) 'u' && i + 5 < end) { + if (acc.Length > 0) { + output.Append (GetChars (acc, e)); + acc.SetLength (0); + } + xchar = GetChar (bytes, i + 2, 4); + if (xchar != -1) { + output.Append ((char) xchar); + i += 5; + continue; + } + } else if ((xchar = GetChar (bytes, i + 1, 2)) != -1) { + acc.WriteByte ((byte) xchar); + i += 2; + continue; + } + } - result.WriteByte ((byte)'%'); - result.WriteByte ((byte)'u'); - idx = i >> 12; - result.WriteByte ((byte)hexChars [idx]); - idx = (i >> 8) & 0x0F; - result.WriteByte ((byte)hexChars [idx]); - idx = (i >> 4) & 0x0F; - result.WriteByte ((byte)hexChars [idx]); - idx = i & 0x0F; - result.WriteByte ((byte)hexChars [idx]); - return; - } - - if (c > ' ' && NotEncoded (c)) { - result.WriteByte ((byte)c); - return; - } - if (c==' ') { - result.WriteByte ((byte)'+'); - return; - } - if ( (c < '0') || - (c < 'A' && c > '9') || - (c > 'Z' && c < 'a') || - (c > 'z')) { - if (isUnicode && c > 127) { - result.WriteByte ((byte)'%'); - result.WriteByte ((byte)'u'); - result.WriteByte ((byte)'0'); - result.WriteByte ((byte)'0'); + if (acc.Length > 0) { + output.Append (GetChars (acc, e)); + acc.SetLength (0); + } + + if (bytes [i] == '+') { + output.Append (' '); + } else { + output.Append ((char) bytes [i]); } - else - result.WriteByte ((byte)'%'); - - int idx = ((int) c) >> 4; - result.WriteByte ((byte)hexChars [idx]); - idx = ((int) c) & 0x0F; - result.WriteByte ((byte)hexChars [idx]); } - else - result.WriteByte ((byte)c); - } - public static byte [] UrlEncodeToBytes (byte [] bytes, int offset, int count) + if (acc.Length > 0) { + output.Append (GetChars (acc, e)); + } + + acc = null; + return output.ToString (); + } + + public static byte [] UrlDecodeToBytes (byte [] bytes) { if (bytes == null) return null; - int len = bytes.Length; - if (len == 0) - return new byte [0]; - - if (offset < 0 || offset >= len) - throw new ArgumentOutOfRangeException("offset"); - - if (count < 0 || count > len - offset) - throw new ArgumentOutOfRangeException("count"); - - MemoryStream result = new MemoryStream (count); - int end = offset + count; - for (int i = offset; i < end; i++) - UrlEncodeChar ((char)bytes [i], result, false); - - return result.ToArray(); + return UrlDecodeToBytes (bytes, 0, bytes.Length); } - public static string UrlEncodeUnicode (string str) + public static byte [] UrlDecodeToBytes (string str) { - if (str == null) - return null; - - return Encoding.ASCII.GetString (UrlEncodeUnicodeToBytes (str)); + return UrlDecodeToBytes (str, Encoding.UTF8); } - public static byte [] UrlEncodeUnicodeToBytes (string str) + public static byte [] UrlDecodeToBytes (string str, Encoding e) { if (str == null) return null; - if (str == "") - return new byte [0]; + if (e == null) + throw new ArgumentNullException ("e"); - MemoryStream result = new MemoryStream (str.Length); - foreach (char c in str){ - UrlEncodeChar (c, result, true); - } - return result.ToArray (); + return UrlDecodeToBytes (e.GetBytes (str)); } - /// - /// Decodes an HTML-encoded string and returns the decoded string. - /// - /// The HTML string to decode. - /// The decoded text. - public static string HtmlDecode (string s) + public static byte [] UrlDecodeToBytes (byte [] bytes, int offset, int count) { - if (s == null) - throw new ArgumentNullException ("s"); - - if (s.IndexOf ('&') == -1) - return s; - - StringBuilder entity = new StringBuilder (); - StringBuilder output = new StringBuilder (); - int len = s.Length; - // 0 -> nothing, - // 1 -> right after '&' - // 2 -> between '&' and ';' but no '#' - // 3 -> '#' found after '&' and getting numbers - int state = 0; - int number = 0; - bool have_trailing_digits = false; - - for (int i = 0; i < len; i++) { - char c = s [i]; - if (state == 0) { - if (c == '&') { - entity.Append (c); - state = 1; - } else { - output.Append (c); - } - continue; - } + if (bytes == null) + return null; - if (c == '&') { - state = 1; - if (have_trailing_digits) { - entity.Append (number.ToString (CultureInfo.InvariantCulture)); - have_trailing_digits = false; - } + if (count == 0) + return new byte [0]; - output.Append (entity.ToString ()); - entity.Length = 0; - entity.Append ('&'); - continue; - } + int len = bytes.Length; + if (offset < 0 || offset >= len) + throw new ArgumentOutOfRangeException("offset"); - if (state == 1) { - if (c == ';') { - state = 0; - output.Append (entity.ToString ()); - output.Append (c); - entity.Length = 0; - } else { - number = 0; - if (c != '#') { - state = 2; - } else { - state = 3; - } - entity.Append (c); - } - } else if (state == 2) { - entity.Append (c); - if (c == ';') { - string key = entity.ToString (); - if (key.Length > 1 && Entities.ContainsKey (key.Substring (1, key.Length - 2))) - key = Entities [key.Substring (1, key.Length - 2)].ToString (); + if (count < 0 || offset > len - count) + throw new ArgumentOutOfRangeException("count"); - output.Append (key); - state = 0; - entity.Length = 0; - } - } else if (state == 3) { - if (c == ';') { - if (number > 65535) { - output.Append ("&#"); - output.Append (number.ToString (CultureInfo.InvariantCulture)); - output.Append (";"); - } else { - output.Append ((char) number); - } - state = 0; - entity.Length = 0; - have_trailing_digits = false; - } else if (Char.IsDigit (c)) { - number = number * 10 + ((int) c - '0'); - have_trailing_digits = true; - } else { - state = 2; - if (have_trailing_digits) { - entity.Append (number.ToString (CultureInfo.InvariantCulture)); - have_trailing_digits = false; - } - entity.Append (c); + MemoryStream result = new MemoryStream (); + int end = offset + count; + for (int i = offset; i < end; i++){ + char c = (char) bytes [i]; + if (c == '+') { + c = ' '; + } else if (c == '%' && i < end - 2) { + int xchar = GetChar (bytes, i + 1, 2); + if (xchar != -1) { + c = (char) xchar; + i += 2; } } + result.WriteByte ((byte) c); } - if (entity.Length > 0) { - output.Append (entity.ToString ()); - } else if (have_trailing_digits) { - output.Append (number.ToString (CultureInfo.InvariantCulture)); - } - return output.ToString (); + return result.ToArray (); } - - /// - /// Decodes an HTML-encoded string and sends the resulting output to a TextWriter output stream. - /// - /// The HTML string to decode - /// The TextWriter output stream containing the decoded string. - public static void HtmlDecode(string s, TextWriter output) + + public static string UrlEncode (byte [] bytes) { - if (s != null) - output.Write (HtmlDecode (s)); + if (bytes == null) + return null; + + if (bytes.Length == 0) + return ""; + + return Encoding.ASCII.GetString (UrlEncodeToBytes (bytes, 0, bytes.Length)); + } + + public static string UrlEncode (string str) + { + return UrlEncode (str, Encoding.UTF8); } - /// - /// HTML-encodes a string and returns the encoded string. - /// - /// The text string to encode. - /// The HTML-encoded text. - public static string HtmlEncode (string s) + public static string UrlEncode (string s, Encoding Enc) { if (s == null) return null; + if (s == "") + return ""; + bool needEncode = false; - for (int i = 0; i < s.Length; i++) { + int len = s.Length; + for (int i = 0; i < len; i++) { char c = s [i]; - if (c == '&' || c == '"' || c == '<' || c == '>' || c > 159) { + if ((c < '0') || (c < 'A' && c > '9') || (c > 'Z' && c < 'a') || (c > 'z')) { + if (NotEncoded (c)) + continue; + needEncode = true; break; } @@ -961,156 +1030,114 @@ namespace WebSocketSharp.Net { if (!needEncode) return s; - StringBuilder output = new StringBuilder (); - - int len = s.Length; - for (int i = 0; i < len; i++) - switch (s [i]) { - case '&' : - output.Append ("&"); - break; - case '>' : - output.Append (">"); - break; - case '<' : - output.Append ("<"); - break; - case '"' : - output.Append ("""); - break; - default: - // MS starts encoding with &# from 160 and stops at 255. - // We don't do that. One reason is the 65308/65310 unicode - // characters that look like '<' and '>'. -#if TARGET_JVM - if (s [i] > 159 && s [i] < 256) { -#else - if (s [i] > 159) { -#endif - output.Append ("&#"); - output.Append (((int) s [i]).ToString (CultureInfo.InvariantCulture)); - output.Append (";"); - } else { - output.Append (s [i]); - } - break; - } - return output.ToString (); + // avoided GetByteCount call + byte [] bytes = new byte[Enc.GetMaxByteCount(s.Length)]; + int realLen = Enc.GetBytes (s, 0, s.Length, bytes, 0); + + return Encoding.ASCII.GetString (UrlEncodeToBytes (bytes, 0, realLen)); } - - /// - /// HTML-encodes a string and sends the resulting output to a TextWriter output stream. - /// - /// The string to encode. - /// The TextWriter output stream containing the encoded string. - public static void HtmlEncode(string s, TextWriter output) + + public static string UrlEncode (byte [] bytes, int offset, int count) { - if (s != null) - output.Write (HtmlEncode (s)); + if (bytes == null) + return null; + + if (bytes.Length == 0) + return ""; + + return Encoding.ASCII.GetString (UrlEncodeToBytes (bytes, offset, count)); } - public static string UrlPathEncode (string s) + public static byte [] UrlEncodeToBytes (byte [] bytes) { - if (s == null || s.Length == 0) - return s; + if (bytes == null) + return null; - MemoryStream result = new MemoryStream (); - int length = s.Length; - for (int i = 0; i < length; i++) { - UrlPathEncodeChar (s [i], result); - } - return Encoding.ASCII.GetString (result.ToArray ()); + if (bytes.Length == 0) + return new byte [0]; + + return UrlEncodeToBytes (bytes, 0, bytes.Length); } - - static void UrlPathEncodeChar (char c, Stream result) + + public static byte [] UrlEncodeToBytes (string str) { - if (c < 33 || c > 126) { - byte [] bIn = Encoding.UTF8.GetBytes (c.ToString ()); - for (int i = 0; i < bIn.Length; i++) { - result.WriteByte ((byte) '%'); - int idx = ((int) bIn [i]) >> 4; - result.WriteByte ((byte) hexChars [idx]); - idx = ((int) bIn [i]) & 0x0F; - result.WriteByte ((byte) hexChars [idx]); - } - } - else if (c == ' ') { - result.WriteByte ((byte) '%'); - result.WriteByte ((byte) '2'); - result.WriteByte ((byte) '0'); - } - else - result.WriteByte ((byte) c); + return UrlEncodeToBytes (str, Encoding.UTF8); } - public static NameValueCollection ParseQueryString (string query) + public static byte [] UrlEncodeToBytes (string str, Encoding e) { - return ParseQueryString (query, Encoding.UTF8); + if (str == null) + return null; + + if (str == "") + return new byte [0]; + + byte [] bytes = e.GetBytes (str); + + return UrlEncodeToBytes (bytes, 0, bytes.Length); } - public static NameValueCollection ParseQueryString (string query, Encoding encoding) + public static byte [] UrlEncodeToBytes (byte [] bytes, int offset, int count) { - if (query == null) - throw new ArgumentNullException ("query"); - if (encoding == null) - throw new ArgumentNullException ("encoding"); - if (query.Length == 0 || (query.Length == 1 && query[0] == '?')) - return new NameValueCollection (); - if (query[0] == '?') - query = query.Substring (1); - - NameValueCollection result = new HttpQSCollection (); - ParseQueryString (query, encoding, result); - return result; + if (bytes == null) + return null; + + int len = bytes.Length; + if (len == 0) + return new byte [0]; + + if (offset < 0 || offset >= len) + throw new ArgumentOutOfRangeException("offset"); + + if (count < 0 || count > len - offset) + throw new ArgumentOutOfRangeException("count"); + + MemoryStream result = new MemoryStream (count); + int end = offset + count; + for (int i = offset; i < end; i++) + UrlEncodeChar ((char)bytes [i], result, false); + + return result.ToArray(); } - internal static void ParseQueryString (string query, Encoding encoding, NameValueCollection result) + public static string UrlEncodeUnicode (string str) { - if (query.Length == 0) - return; + if (str == null) + return null; - string decoded = HtmlDecode (query); - int decodedLength = decoded.Length; - int namePos = 0; - bool first = true; - while (namePos <= decodedLength) { - int valuePos = -1, valueEnd = -1; - for (int q = namePos; q < decodedLength; q++) { - if (valuePos == -1 && decoded [q] == '=') { - valuePos = q + 1; - } else if (decoded [q] == '&') { - valueEnd = q; - break; - } - } + return Encoding.ASCII.GetString (UrlEncodeUnicodeToBytes (str)); + } - if (first) { - first = false; - if (decoded [namePos] == '?') - namePos++; - } - - string name, value; - if (valuePos == -1) { - name = null; - valuePos = namePos; - } else { - name = UrlDecode (decoded.Substring (namePos, valuePos - namePos - 1), encoding); - } - if (valueEnd < 0) { - namePos = -1; - valueEnd = decoded.Length; - } else { - namePos = valueEnd + 1; - } - value = UrlDecode (decoded.Substring (valuePos, valueEnd - valuePos), encoding); + public static byte [] UrlEncodeUnicodeToBytes (string str) + { + if (str == null) + return null; - result.Add (name, value); - if (namePos == -1) - break; + if (str == "") + return new byte [0]; + + MemoryStream result = new MemoryStream (str.Length); + foreach (char c in str){ + UrlEncodeChar (c, result, true); + } + + return result.ToArray (); + } + + public static string UrlPathEncode (string s) + { + if (s == null || s.Length == 0) + return s; + + MemoryStream result = new MemoryStream (); + int length = s.Length; + for (int i = 0; i < length; i++) { + UrlPathEncodeChar (s [i], result); } + + return Encoding.ASCII.GetString (result.ToArray ()); } - #endregion // Methods + + #endregion // Public Methods } } - diff --git a/websocket-sharp/Net/Sockets/TcpListenerWebSocketContext.cs b/websocket-sharp/Net/Sockets/TcpListenerWebSocketContext.cs index 8f60fb38..41dc09fc 100644 --- a/websocket-sharp/Net/Sockets/TcpListenerWebSocketContext.cs +++ b/websocket-sharp/Net/Sockets/TcpListenerWebSocketContext.cs @@ -29,6 +29,7 @@ using System; using System.Collections.Generic; using System.Collections.Specialized; +using System.Net; using System.Net.Sockets; using System.Security.Principal; @@ -44,72 +45,113 @@ namespace WebSocketSharp.Net.Sockets { internal TcpListenerWebSocketContext(TcpClient client) { - _client = client; - init(); + _client = client; + _stream = WsStream.CreateServerStream(client); + _isSecure = _stream.IsSecure; + _request = RequestHandshake.Parse(_stream.ReadHandshake()); + _socket = new WebSocket(this); } internal TcpClient Client { - get { return _client; } + get { + return _client; + } } internal WsStream Stream { - get { return _stream; } + get { + return _stream; + } } public override CookieCollection CookieCollection { - get { throw new NotImplementedException(); } + get { + throw new NotImplementedException(); + } } public override NameValueCollection Headers { - get { return _request.Headers; } + get { + return _request.Headers; + } } public override bool IsAuthenticated { - get { throw new NotImplementedException(); } + get { + throw new NotImplementedException(); + } } public override bool IsSecureConnection { - get { return _isSecure; } + get { + return _isSecure; + } } public override bool IsLocal { - get { throw new NotImplementedException(); } + get { + throw new NotImplementedException(); + } } public override string Origin { - get { return Headers["Origin"]; } + get { + return Headers["Origin"]; + } + } + + public virtual string Path { + get { + return _request.RequestUri.GetAbsolutePath(); + } } public override Uri RequestUri { - get { return _request.RequestUri; } + get { + return _request.RequestUri; + } } public override string SecWebSocketKey { - get { return Headers["Sec-WebSocket-Key"]; } + get { + return Headers["Sec-WebSocket-Key"]; + } } public override IEnumerable SecWebSocketProtocols { - get { return Headers.GetValues("Sec-WebSocket-Protocol"); } + get { + return Headers.GetValues("Sec-WebSocket-Protocol"); + } } public override string SecWebSocketVersion { - get { return Headers["Sec-WebSocket-Version"]; } + get { + return Headers["Sec-WebSocket-Version"]; + } + } + + public virtual IPEndPoint ServerEndPoint { + get { + return (IPEndPoint)_client.Client.LocalEndPoint; + } } public override IPrincipal User { - get { throw new NotImplementedException(); } + get { + throw new NotImplementedException(); + } } - public override WebSocket WebSocket { - get { return _socket; } + public virtual IPEndPoint UserEndPoint { + get { + return (IPEndPoint)_client.Client.RemoteEndPoint; + } } - private void init() - { - _stream = WsStream.CreateServerStream(_client); - _isSecure = _stream.IsSecure; - _request = RequestHandshake.Parse(_stream.ReadHandshake()); - _socket = new WebSocket(this); + public override WebSocket WebSocket { + get { + return _socket; + } } } } diff --git a/websocket-sharp/RequestHandshake.cs b/websocket-sharp/RequestHandshake.cs index 0659b832..eadb467b 100644 --- a/websocket-sharp/RequestHandshake.cs +++ b/websocket-sharp/RequestHandshake.cs @@ -35,6 +35,12 @@ namespace WebSocketSharp { public class RequestHandshake : Handshake { + #region Private Field + + private NameValueCollection _queryString; + + #endregion + #region Private Constructor private RequestHandshake() @@ -43,7 +49,6 @@ namespace WebSocketSharp { #endregion - #region Public Constructor public RequestHandshake(string uriString) @@ -59,7 +64,7 @@ namespace WebSocketSharp { #region Properties - public string HttpMethod { get; internal set; } + public string HttpMethod { get; private set; } public bool IsWebSocketRequest { @@ -89,7 +94,44 @@ namespace WebSocketSharp { } } - public Uri RequestUri { get; internal set; } + public NameValueCollection QueryString { + get { + if (_queryString == null) + { + _queryString = new NameValueCollection(); + + var i = RawUrl.IndexOf('?'); + if (i > 0) + { + var query = RawUrl.Substring(i + 1); + var components = query.Split('&'); + foreach (var c in components) + { + var nv = c.GetNameAndValue("="); + if (nv.Key != null) + { + var name = nv.Key.UrlDecode(); + var val = nv.Value.UrlDecode(); + _queryString.Add(name, val); + } + } + } + } + + return _queryString; + } + } + + public string RawUrl { + get { + if (RequestUri.IsAbsoluteUri) + return RequestUri.PathAndQuery; + + return RequestUri.OriginalString; + } + } + + public Uri RequestUri { get; private set; } #endregion @@ -109,7 +151,10 @@ namespace WebSocketSharp { { var requestLine = request[0].Split(' '); if (requestLine.Length != 3) - throw new ArgumentException("Invalid request line."); + { + var msg = "Invalid HTTP Request-Line: " + request[0]; + throw new ArgumentException(msg, "request"); + } var headers = new WebHeaderCollection(); for (int i = 1; i < request.Length; i++) @@ -130,14 +175,11 @@ namespace WebSocketSharp { public override string ToString() { var buffer = new StringBuilder(); - - buffer.AppendFormat("{0} {1} HTTP/{2}{3}", HttpMethod, RequestUri, ProtocolVersion, _crlf); - + buffer.AppendFormat("{0} {1} HTTP/{2}{3}", HttpMethod, RawUrl, ProtocolVersion, _crlf); foreach (string key in Headers.AllKeys) buffer.AppendFormat("{0}: {1}{2}", key, Headers[key], _crlf); buffer.Append(_crlf); - return buffer.ToString(); } diff --git a/websocket-sharp/Server/HttpServer.cs b/websocket-sharp/Server/HttpServer.cs index d6c5c15a..0c240eb4 100644 --- a/websocket-sharp/Server/HttpServer.cs +++ b/websocket-sharp/Server/HttpServer.cs @@ -29,6 +29,7 @@ using System; using System.Collections.Generic; using System.Configuration; +using System.Diagnostics; using System.IO; using System.Threading; using WebSocketSharp.Net; @@ -95,7 +96,7 @@ namespace WebSocketSharp.Server { try { var context = _listener.GetContext(); - respond(context); + respondAsync(context); } catch (HttpListenerException) { @@ -104,7 +105,7 @@ namespace WebSocketSharp.Server { } catch (Exception ex) { - OnError.Emit(this, new ErrorEventArgs(ex.Message)); + onError(ex.Message); break; } } @@ -143,36 +144,17 @@ namespace WebSocketSharp.Server { return true; } - private void respond(HttpListenerContext context) + private void onError(string message) { - WaitCallback respondCb = (state) => - { - var req = context.Request; - var res = context.Response; - - try - { - if (isUpgrade(req, "websocket")) - { - if (upgradeToWebSocket(context)) - return; - } - else - { - respondToClient(context); - } - - res.Close(); - } - catch (Exception ex) - { - OnError.Emit(this, new ErrorEventArgs(ex.Message)); - } - }; - ThreadPool.QueueUserWorkItem(respondCb); + #if DEBUG + var callerFrame = new StackFrame(1); + var caller = callerFrame.GetMethod(); + Console.WriteLine("HTTPSV: Error@{0}: {1}", caller.Name, message); + #endif + OnError.Emit(this, new ErrorEventArgs(message)); } - private void respondToClient(HttpListenerContext context) + private void respond(HttpListenerContext context) { var req = context.Request; var res = context.Response; @@ -235,6 +217,36 @@ namespace WebSocketSharp.Server { res.StatusCode = (int)HttpStatusCode.NotImplemented; } + private void respondAsync(HttpListenerContext context) + { + WaitCallback respondCb = (state) => + { + var req = context.Request; + var res = context.Response; + + try + { + if (isUpgrade(req, "websocket")) + { + if (upgradeToWebSocket(context)) + return; + } + else + { + respond(context); + } + + res.Close(); + } + catch (Exception ex) + { + onError(ex.Message); + } + }; + + ThreadPool.QueueUserWorkItem(respondCb); + } + private void startAcceptRequestThread() { _acceptRequestThread = new Thread(new ThreadStart(acceptRequest)); @@ -244,19 +256,17 @@ namespace WebSocketSharp.Server { private bool upgradeToWebSocket(HttpListenerContext context) { - var req = context.Request; - var res = context.Response; - - var path = req.RawUrl; + var res = context.Response; + var wsContext = context.AcceptWebSocket(); + var path = wsContext.Path.UrlDecode(); if (!_services.ContainsKey(path)) { res.StatusCode = (int)HttpStatusCode.NotImplemented; return false; } - var wsContext = context.AcceptWebSocket(path); - var socket = wsContext.WebSocket; - var service = _services[path]; + var socket = wsContext.WebSocket; + var service = _services[path]; service.BindWebSocket(socket); return true; @@ -266,11 +276,18 @@ namespace WebSocketSharp.Server { #region Public Methods - public void AddService(string path) + public void AddService(string absPath) where T : WebSocketService, new() { + string msg; + if (!absPath.IsValidAbsolutePath(out msg)) + { + onError(msg); + return; + } + var service = new WebSocketServer(); - _services.Add(path, service); + _services.Add(absPath, service); } public byte[] GetFile(string path) diff --git a/websocket-sharp/Server/WebSocketServer.cs b/websocket-sharp/Server/WebSocketServer.cs index b829ae1b..3ef15aa1 100644 --- a/websocket-sharp/Server/WebSocketServer.cs +++ b/websocket-sharp/Server/WebSocketServer.cs @@ -52,7 +52,33 @@ namespace WebSocketSharp.Server { } public WebSocketServer(int port) - : base(System.Net.IPAddress.Any, port) + : this(System.Net.IPAddress.Any, port) + { + } + + public WebSocketServer(string url) + : base(url) + { + if (BaseUri.AbsolutePath != "/") + { + var msg = "Must not contain the path component: " + url; + throw new ArgumentException(msg, "url"); + } + + init(); + } + + public WebSocketServer(System.Net.IPAddress address, int port) + : base(address, port) + { + init(); + } + + #endregion + + #region Private Method + + private void init() { _services = new Dictionary(); } @@ -61,17 +87,20 @@ namespace WebSocketSharp.Server { #region Protected Method - protected override void bindSocket(TcpClient client) + protected override void AcceptWebSocket(TcpClient client) { var context = client.AcceptWebSocket(); var socket = context.WebSocket; - var path = context.RequestUri.ToString(); + var path = context.Path.UrlDecode(); if (!_services.ContainsKey(path)) { socket.Close(HttpStatusCode.NotImplemented); return; } + if (BaseUri.IsAbsoluteUri) + socket.Url = new Uri(BaseUri, path); + var service = _services[path]; service.BindWebSocket(socket); } @@ -80,11 +109,18 @@ namespace WebSocketSharp.Server { #region Public Methods - public void AddService(string path) + public void AddService(string absPath) where T : WebSocketService, new() { + string msg; + if (!absPath.IsValidAbsolutePath(out msg)) + { + Error(msg); + return; + } + var service = new WebSocketServer(); - _services.Add(path, service); + _services.Add(absPath, service); } public override void Stop() @@ -104,7 +140,6 @@ namespace WebSocketSharp.Server { #region Fields private SessionManager _sessions; - private Uri _uri; #endregion @@ -119,29 +154,25 @@ namespace WebSocketSharp.Server { #region Public Constructors + public WebSocketServer(int port) + : this(port, "/") + { + } + public WebSocketServer(string url) : base(url) { - _uri = url.ToUri(); init(); } - public WebSocketServer(int port) - : this(port, "/") + public WebSocketServer(int port, string absPath) + : this(System.Net.IPAddress.Any, port, absPath) { } - public WebSocketServer(int port, string path) - : base(System.Net.IPAddress.Any, port) + public WebSocketServer(System.Net.IPAddress address, int port, string absPath) + : base(address, port, absPath) { - var uri = path.ToUri(); - if (uri.IsAbsoluteUri) - { - var msg = "Not absolute path: " + path; - throw new ArgumentException(msg, "path"); - } - - _uri = uri; init(); } @@ -149,9 +180,10 @@ namespace WebSocketSharp.Server { #region Property - public Uri Uri - { - get { return _uri; } + public Uri Uri { + get { + return BaseUri; + } } #endregion @@ -167,9 +199,20 @@ namespace WebSocketSharp.Server { #region Protected Method - protected override void bindSocket(TcpClient client) + protected override void AcceptWebSocket(TcpClient client) { - var socket = new WebSocket(_uri, client); + var context = client.AcceptWebSocket(); + var socket = context.WebSocket; + var path = context.Path.UrlDecode(); + if (path != Uri.GetAbsolutePath().UrlDecode()) + { + socket.Close(HttpStatusCode.NotImplemented); + return; + } + + if (Uri.IsAbsoluteUri) + socket.Url = new Uri(Uri, path); + BindWebSocket(socket); } diff --git a/websocket-sharp/Server/WebSocketServerBase.cs b/websocket-sharp/Server/WebSocketServerBase.cs index 9e1f069c..e66a7ba7 100644 --- a/websocket-sharp/Server/WebSocketServerBase.cs +++ b/websocket-sharp/Server/WebSocketServerBase.cs @@ -34,8 +34,8 @@ using System.Threading; namespace WebSocketSharp.Server { - public abstract class WebSocketServerBase - { + public abstract class WebSocketServerBase { + #region Fields private Thread _acceptClientThread; @@ -43,6 +43,7 @@ namespace WebSocketSharp.Server { private bool _isSelfHost; private int _port; private TcpListener _tcpListener; + private Uri _uri; #endregion @@ -55,31 +56,72 @@ namespace WebSocketSharp.Server { protected WebSocketServerBase(string url) { - init(url); + if (url.IsNull()) + throw new ArgumentNullException("url"); + + Uri uri; + string msg; + if (!tryCreateUri(url, out uri, out msg)) + throw new ArgumentException(msg, "url"); + + init(uri); } protected WebSocketServerBase(IPAddress address, int port) + : this(address, port, "/") + { + } + + protected WebSocketServerBase(IPAddress address, int port, string absPath) { + if (address.IsNull()) + throw new ArgumentNullException("address"); + + if (absPath.IsNull()) + throw new ArgumentNullException("absPath"); + + string msg; + if (!absPath.IsValidAbsolutePath(out msg)) + throw new ArgumentException(msg, "absPath"); + _address = address; _port = port <= 0 ? 80 : port; + _uri = absPath.ToUri(); init(); } #endregion - #region Property + #region Protected Property + + protected Uri BaseUri + { + get { + return _uri; + } + } + + #endregion + + #region Public Properties public IPAddress Address { - get { return _address; } + get { + return _address; + } } public bool IsSelfHost { - get { return _isSelfHost; } + get { + return _isSelfHost; + } } public int Port { - get { return _port; } + get { + return _port; + } } #endregion @@ -99,7 +141,7 @@ namespace WebSocketSharp.Server { try { var client = _tcpListener.AcceptTcpClient(); - acceptSocket(client); + acceptSocketAsync(client); } catch (SocketException) { @@ -108,36 +150,27 @@ namespace WebSocketSharp.Server { } catch (Exception ex) { - error(ex.Message); + onError(ex.Message); break; } } } - private void acceptSocket(TcpClient client) + private void acceptSocketAsync(TcpClient client) { WaitCallback acceptSocketCb = (state) => { try { - bindSocket(client); + AcceptWebSocket(client); } catch (Exception ex) { - error(ex.Message); + onError(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)); + ThreadPool.QueueUserWorkItem(acceptSocketCb); } private void init() @@ -146,14 +179,9 @@ namespace WebSocketSharp.Server { _isSelfHost = true; } - private void init(string url) + private void init(Uri uri) { - var uri = url.ToUri(); - - string msg; - if (!uri.IsValidWebSocketUri(out msg)) - throw new ArgumentException(msg, "url"); - + _uri = uri; var scheme = uri.Scheme; var port = uri.Port; var host = uri.DnsSafeHost; @@ -168,6 +196,16 @@ namespace WebSocketSharp.Server { init(); } + private void onError(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 startAcceptClientThread() { _acceptClientThread = new Thread(new ThreadStart(acceptClient)); @@ -175,11 +213,31 @@ namespace WebSocketSharp.Server { _acceptClientThread.Start(); } + private bool tryCreateUri(string uriString, out Uri result, out string message) + { + if (!uriString.TryCreateWebSocketUri(out result, out message)) + return false; + + if (!result.Query.IsNullOrEmpty()) + { + result = null; + message = "Must not contain the query component: " + uriString; + return false; + } + + return true; + } + #endregion #region Protected Method - protected abstract void bindSocket(TcpClient client); + protected abstract void AcceptWebSocket(TcpClient client); + + protected virtual void Error(string message) + { + onError(message); + } #endregion diff --git a/websocket-sharp/Server/WebSocketService.cs b/websocket-sharp/Server/WebSocketService.cs index df9826fe..51f4e4ad 100644 --- a/websocket-sharp/Server/WebSocketService.cs +++ b/websocket-sharp/Server/WebSocketService.cs @@ -28,6 +28,7 @@ using System; using System.Collections.Generic; +using System.Collections.Specialized; using System.Threading; using WebSocketSharp.Frame; @@ -53,9 +54,15 @@ namespace WebSocketSharp.Server #endregion - #region Protected Property + #region Protected Properties - protected SessionManager sessions { + protected NameValueCollection QueryString { + get { + return _socket.QueryString; + } + } + + protected SessionManager Sessions { get { return _sessions; } @@ -91,19 +98,19 @@ namespace WebSocketSharp.Server #region Protected Methods - protected virtual void onOpen(object sender, EventArgs e) + protected virtual void OnClose(object sender, CloseEventArgs e) { } - protected virtual void onMessage(object sender, MessageEventArgs e) + protected virtual void OnError(object sender, ErrorEventArgs e) { } - protected virtual void onError(object sender, ErrorEventArgs e) + protected virtual void OnMessage(object sender, MessageEventArgs e) { } - protected virtual void onClose(object sender, CloseEventArgs e) + protected virtual void OnOpen(object sender, EventArgs e) { } @@ -117,10 +124,10 @@ namespace WebSocketSharp.Server _sessions = sessions; defaultBind(); - _socket.OnOpen += onOpen; - _socket.OnMessage += onMessage; - _socket.OnError += onError; - _socket.OnClose += onClose; + _socket.OnOpen += OnOpen; + _socket.OnMessage += OnMessage; + _socket.OnError += OnError; + _socket.OnClose += OnClose; IsBound = true; } diff --git a/websocket-sharp/WebSocket.cs b/websocket-sharp/WebSocket.cs index 55752f87..3e013e7b 100644 --- a/websocket-sharp/WebSocket.cs +++ b/websocket-sharp/WebSocket.cs @@ -32,6 +32,7 @@ using System; using System.Collections.Generic; +using System.Collections.Specialized; using System.Diagnostics; using System.IO; using System.Linq; @@ -75,6 +76,7 @@ namespace WebSocketSharp { private bool _isSecure; private string _protocol; private string _protocols; + private NameValueCollection _queryString; private volatile WsState _readyState; private AutoResetEvent _receivePong; private TcpClient _tcpClient; @@ -100,39 +102,28 @@ namespace WebSocketSharp { #region Internal Constructor - internal WebSocket(TcpListenerWebSocketContext context) - : this() - { - _uri = context.RequestUri; - _context = context; - _tcpClient = context.Client; - _wsStream = context.Stream; - _endPoint = (System.Net.IPEndPoint)_tcpClient.Client.LocalEndPoint; - _isClient = false; - _isSecure = context.IsSecureConnection; - } - - internal WebSocket(Uri uri, HttpListenerWebSocketContext context) + internal WebSocket(HttpListenerWebSocketContext context) : this() { - _uri = uri; + _uri = context.Path.ToUri(); _context = context; _baseContext = context.BaseContext; _wsStream = context.Stream; - _endPoint = _baseContext.Connection.LocalEndPoint; + _endPoint = context.ServerEndPoint; _isClient = false; _isSecure = context.IsSecureConnection; } - internal WebSocket(Uri uri, TcpClient tcpClient) + internal WebSocket(TcpListenerWebSocketContext context) : this() { - _uri = uri; - _tcpClient = tcpClient; - _wsStream = WsStream.CreateServerStream(tcpClient); - _endPoint = (System.Net.IPEndPoint)tcpClient.Client.LocalEndPoint; + _uri = context.Path.ToUri(); + _context = context; + _tcpClient = context.Client; + _wsStream = context.Stream; + _endPoint = context.ServerEndPoint; _isClient = false; - _isSecure = _wsStream.IsSecure; + _isSecure = context.IsSecureConnection; } #endregion @@ -157,16 +148,18 @@ namespace WebSocketSharp { public WebSocket(string url, params string[] protocols) : this() { - if (url == null) + if (url.IsNull()) throw new ArgumentNullException("url"); + Uri uri; string msg; - if (!isValidUrl(url, out msg)) + if (!tryCreateUri(url, out uri, out msg)) throw new ArgumentException(msg, "url"); + _uri = uri; _protocols = protocols.ToString(", "); _isClient = true; - _isSecure = _uri.Scheme == "wss" ? true : false; + _isSecure = uri.Scheme == "wss" ? true : false; } /// @@ -215,7 +208,17 @@ namespace WebSocketSharp { #endregion - #region Properties + #region Internal Property + + internal NameValueCollection QueryString { + get { + return _queryString; + } + } + + #endregion + + #region Public Properties /// /// Gets the amount of untransmitted data. @@ -320,10 +323,8 @@ namespace WebSocketSharp { public Uri Url { get { return _uri; } set { - if (_readyState != WsState.CONNECTING || _isClient) - return; - - _uri = value; + if (_readyState == WsState.CONNECTING && !_isClient) + _uri = value; } } @@ -355,6 +356,7 @@ namespace WebSocketSharp { #region Private Methods + // As Server private void acceptHandshake() { var req = receiveOpeningHandshake(); @@ -439,7 +441,7 @@ namespace WebSocketSharp { private void close(ushort code, string reason) { var data = new List(code.ToBytes(ByteOrder.BIG)); - if (!String.IsNullOrEmpty(reason)) + if (!reason.IsNullOrEmpty()) { var buffer = Encoding.UTF8.GetBytes(reason); data.AddRange(buffer); @@ -462,20 +464,20 @@ namespace WebSocketSharp { try { - if (_baseContext != null) + if (!_baseContext.IsNull()) { _baseContext.Response.Close(); _wsStream = null; _baseContext = null; } - if (_wsStream != null) + if (!_wsStream.IsNull()) { _wsStream.Dispose(); _wsStream = null; } - if (_tcpClient != null) + if (!_tcpClient.IsNull()) { _tcpClient.Close(); _tcpClient = null; @@ -495,12 +497,13 @@ namespace WebSocketSharp { var args = new CloseEventArgs(data); var frame = createFrame(Fin.FINAL, Opcode.CLOSE, data); if (send(frame) && !Thread.CurrentThread.IsBackground) - if (_exitMessageLoop != null) + if (!_exitMessageLoop.IsNull()) _exitMessageLoop.WaitOne(5 * 1000); onClose(args); } + // As Client private void createClientStream() { var host = _uri.DnsSafeHost; @@ -529,6 +532,7 @@ namespace WebSocketSharp { : new WsFrame(fin, opcode, Mask.UNMASK, payloadData); } + // As Client private RequestHandshake createOpeningHandshake() { var path = _uri.PathAndQuery; @@ -545,13 +549,14 @@ namespace WebSocketSharp { var req = new RequestHandshake(path); req.AddHeader("Host", host); req.AddHeader("Sec-WebSocket-Key", _base64key); - if (!String.IsNullOrEmpty(_protocols)) + if (!_protocols.IsNullOrEmpty()) req.AddHeader("Sec-WebSocket-Protocol", _protocols); req.AddHeader("Sec-WebSocket-Version", _version); return req; } + // As Server private ResponseHandshake createResponseHandshake() { var res = new ResponseHandshake(); @@ -560,6 +565,7 @@ namespace WebSocketSharp { return res; } + // As Server private ResponseHandshake createResponseHandshake(HttpStatusCode code) { var res = ResponseHandshake.CreateCloseResponse(code); @@ -568,6 +574,7 @@ namespace WebSocketSharp { return res; } + // As Client private void doHandshake() { var res = sendOpeningHandshake(); @@ -601,6 +608,7 @@ namespace WebSocketSharp { return true; } + // As Server private bool isValidRequest(RequestHandshake request, out string message) { Func> func = s => @@ -617,9 +625,6 @@ namespace WebSocketSharp { return false; } - if (!isValidRequestUri(request.RequestUri, func("Request URI"), out message)) - return false; - if (_uri.IsAbsoluteUri) if (!isValidRequestHost(request.GetHeaderValues("Host")[0], func("Host"), out message)) return false; @@ -638,10 +643,13 @@ namespace WebSocketSharp { if (request.HeaderExists("Sec-WebSocket-Extensions")) _extensions = request.Headers["Sec-WebSocket-Extensions"]; + _queryString = request.QueryString; + message = String.Empty; return true; } + // As Server private bool isValidRequestHost(string value, Func func, out string message) { var host = _uri.DnsSafeHost; @@ -669,28 +677,7 @@ namespace WebSocketSharp { return true; } - private bool isValidRequestUri(Uri requestUri, Func func, out string message) - { - if (_uri.IsAbsoluteUri && requestUri.IsAbsoluteUri) - if (_uri.ToString().NotEqualsDo(requestUri.ToString(), func, out message, false)) - return false; - - if (_uri.IsAbsoluteUri && !requestUri.IsAbsoluteUri) - if (_uri.PathAndQuery.NotEqualsDo(requestUri.ToString(), func, out message, false)) - return false; - - if (!_uri.IsAbsoluteUri && requestUri.IsAbsoluteUri) - if (_uri.ToString().NotEqualsDo(requestUri.PathAndQuery, func, out message, false)) - return false; - - if (!_uri.IsAbsoluteUri && !requestUri.IsAbsoluteUri) - if (_uri.ToString().NotEqualsDo(requestUri.ToString(), func, out message, false)) - return false; - - message = String.Empty; - return true; - } - + // As Client private bool isValidResponse(ResponseHandshake response, out string message) { if (!response.IsWebSocketResponse) @@ -724,24 +711,6 @@ namespace WebSocketSharp { return true; } - private bool isValidUrl(string url, out string message) - { - if (url == String.Empty) - { - message = "'url' is empty."; - return false; - } - - var uri = url.ToUri(); - if (!uri.IsValidWebSocketUri(out message)) - return false; - - _uri = uri; - message = String.Empty; - - return true; - } - private void message() { try @@ -791,7 +760,7 @@ namespace WebSocketSharp { private void onMessage(MessageEventArgs eventArgs) { - if (eventArgs != null) + if (!eventArgs.IsNull()) OnMessage.Emit(this, eventArgs); } @@ -802,9 +771,9 @@ namespace WebSocketSharp { OnOpen.Emit(this, EventArgs.Empty); } - private bool ping(string data, int millisecondsTimeout) + private bool ping(string message, int millisecondsTimeout) { - var buffer = Encoding.UTF8.GetBytes(data); + var buffer = Encoding.UTF8.GetBytes(message); if (buffer.Length > 125) { var msg = "Ping frame must have a payload length of 125 bytes or less."; @@ -833,7 +802,7 @@ namespace WebSocketSharp { private WsFrame readFrame() { var frame = _wsStream.ReadFrame(); - if (frame == null) + if (frame.IsNull()) { var msg = "WebSocket data frame can not be read from network stream."; close(CloseStatusCode.ABNORMAL, msg); @@ -862,7 +831,7 @@ namespace WebSocketSharp { private MessageEventArgs receive() { var frame = _isClient ? readFrame() : readFrameWithTimeout(1 * 100); - if (frame == null) + if (frame.IsNull()) return null; if ((frame.Fin == Fin.FINAL && frame.Opcode == Opcode.CONT) || @@ -872,7 +841,7 @@ namespace WebSocketSharp { if (frame.Fin == Fin.MORE) {// MORE var merged = receiveFragmented(frame); - if (merged == null) + if (merged.IsNull()) return null; return new MessageEventArgs(frame.Opcode, new PayloadData(merged)); @@ -912,7 +881,7 @@ namespace WebSocketSharp { while (true) { var frame = readFrame(); - if (frame == null) + if (frame.IsNull()) return null; if (frame.Fin == Fin.MORE) @@ -972,11 +941,10 @@ namespace WebSocketSharp { return buffer.ToArray(); } + // As Server private RequestHandshake receiveOpeningHandshake() { - var req = _context != null - ? RequestHandshake.Parse(_context) - : RequestHandshake.Parse(readHandshake()); + var req = RequestHandshake.Parse(_context); #if DEBUG Console.WriteLine("WS: Info@receiveOpeningHandshake: Opening handshake from client:\n"); Console.WriteLine(req.ToString()); @@ -984,6 +952,7 @@ namespace WebSocketSharp { return req; } + // As Client private ResponseHandshake receiveResponseHandshake() { var res = ResponseHandshake.Parse(readHandshake()); @@ -1008,7 +977,7 @@ namespace WebSocketSharp { { if (_unTransmittedBuffer.Count == 0) { - if (_wsStream != null) + if (!_wsStream.IsNull()) { _wsStream.WriteFrame(frame); return true; @@ -1097,13 +1066,16 @@ namespace WebSocketSharp { return readLen; } + // As Client private ResponseHandshake sendOpeningHandshake() { var req = createOpeningHandshake(); sendOpeningHandshake(req); + return receiveResponseHandshake(); } + // As Client private void sendOpeningHandshake(RequestHandshake request) { #if DEBUG @@ -1113,18 +1085,21 @@ namespace WebSocketSharp { writeHandshake(request); } + // As Server private void sendResponseHandshake() { var res = createResponseHandshake(); sendResponseHandshake(res); } + // As Server private void sendResponseHandshake(HttpStatusCode code) { var res = createResponseHandshake(code); sendResponseHandshake(res); } + // As Server private void sendResponseHandshake(ResponseHandshake response) { #if DEBUG @@ -1147,6 +1122,11 @@ namespace WebSocketSharp { messageInvoker.BeginInvoke(messageLoopCallback, messageInvoker); } + private bool tryCreateUri(string uriString, out Uri result, out string message) + { + return uriString.TryCreateWebSocketUri(out result, out message); + } + private void writeHandshake(Handshake handshake) { _wsStream.WriteHandshake(handshake); @@ -1156,6 +1136,7 @@ namespace WebSocketSharp { #region Internal Method + // As Server internal void Close(HttpStatusCode code) { close(code); @@ -1290,15 +1271,18 @@ namespace WebSocketSharp { /// /// Sends a Ping frame with a message using the connection. /// - /// - /// A that contains the message data to be sent. + /// + /// A that contains the message to be sent. /// /// /// true if the WebSocket receives a Pong frame in a time; otherwise, false. /// - public bool Ping(string data) + public bool Ping(string message) { - return ping(data, 5 * 1000); + if (message.IsNull()) + message = String.Empty; + + return ping(message, 5 * 1000); } /// @@ -1309,6 +1293,12 @@ namespace WebSocketSharp { /// public void Send(string data) { + if (data.IsNull()) + { + onError("'data' must not be null."); + return; + } + var buffer = Encoding.UTF8.GetBytes(data); send(Opcode.TEXT, buffer); } @@ -1321,6 +1311,12 @@ namespace WebSocketSharp { /// public void Send(byte[] data) { + if (data.IsNull()) + { + onError("'data' must not be null."); + return; + } + send(Opcode.BINARY, data); } @@ -1332,6 +1328,12 @@ namespace WebSocketSharp { /// public void Send(FileInfo file) { + if (file.IsNull()) + { + onError("'file' must not be null."); + return; + } + using (FileStream fs = file.OpenRead()) { send(Opcode.BINARY, fs); diff --git a/websocket-sharp/bin/Debug/websocket-sharp.dll b/websocket-sharp/bin/Debug/websocket-sharp.dll index 59be2565..2c81ac0e 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 9c587ea2..f0adb074 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 defd4397..a7c9c8aa 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 870943a9..91b3c991 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/bin/Release/websocket-sharp.dll b/websocket-sharp/bin/Release/websocket-sharp.dll index 41b06474..e96204cf 100755 Binary files a/websocket-sharp/bin/Release/websocket-sharp.dll and b/websocket-sharp/bin/Release/websocket-sharp.dll differ diff --git a/websocket-sharp/bin/Release_Ubuntu/websocket-sharp.dll b/websocket-sharp/bin/Release_Ubuntu/websocket-sharp.dll index 975bf118..f5d4ff6c 100755 Binary files a/websocket-sharp/bin/Release_Ubuntu/websocket-sharp.dll and b/websocket-sharp/bin/Release_Ubuntu/websocket-sharp.dll differ diff --git a/websocket-sharp/websocket-sharp.pidb b/websocket-sharp/websocket-sharp.pidb index 670db912..6bb222fc 100644 Binary files a/websocket-sharp/websocket-sharp.pidb and b/websocket-sharp/websocket-sharp.pidb differ