From 4d81f2faa53ceb6b693260c8d32ff3c411edb1e5 Mon Sep 17 00:00:00 2001 From: darknessxk Date: Tue, 2 Feb 2021 09:56:04 -0300 Subject: [PATCH 1/3] Ported to NET Core --- Example/AssemblyInfo.cs | 26 ---- Example/Example.csproj | 71 --------- Example/NotificationMessage.cs | 24 ---- Example/Notifier.cs | 81 ----------- Example/Program.cs | 128 ----------------- Example1/AssemblyInfo.cs | 26 ---- Example1/AudioStreamer.cs | 192 ------------------------- Example1/BinaryMessage.cs | 74 ---------- Example1/Example1.csproj | 77 ---------- Example1/NotificationMessage.cs | 24 ---- Example1/Notifier.cs | 81 ----------- Example1/Program.cs | 37 ----- Example1/TextMessage.cs | 33 ----- Example2/App.config | 7 - Example2/AssemblyInfo.cs | 26 ---- Example2/Chat.cs | 50 ------- Example2/Echo.cs | 15 -- Example2/Example2.csproj | 70 --------- Example2/Program.cs | 135 ----------------- Example3/App.config | 8 -- Example3/AssemblyInfo.cs | 26 ---- Example3/Chat.cs | 50 ------- Example3/Echo.cs | 15 -- Example3/Example3.csproj | 76 ---------- Example3/Program.cs | 167 --------------------- Example3/Public/Js/echotest.js | 68 --------- Example3/Public/index.html | 12 -- websocket-sharp.sln | 74 +++------- websocket-sharp/AssemblyInfo.cs | 31 ++-- websocket-sharp/doc/.gitignore | 4 - websocket-sharp/doc/doc.sh | 31 ---- websocket-sharp/websocket-sharp.csproj | 153 +------------------- websocket-sharp/websocket-sharp.snk | Bin 596 -> 0 bytes 33 files changed, 38 insertions(+), 1854 deletions(-) delete mode 100644 Example/AssemblyInfo.cs delete mode 100644 Example/Example.csproj delete mode 100644 Example/NotificationMessage.cs delete mode 100644 Example/Notifier.cs delete mode 100644 Example/Program.cs delete mode 100644 Example1/AssemblyInfo.cs delete mode 100644 Example1/AudioStreamer.cs delete mode 100644 Example1/BinaryMessage.cs delete mode 100644 Example1/Example1.csproj delete mode 100644 Example1/NotificationMessage.cs delete mode 100644 Example1/Notifier.cs delete mode 100644 Example1/Program.cs delete mode 100644 Example1/TextMessage.cs delete mode 100644 Example2/App.config delete mode 100644 Example2/AssemblyInfo.cs delete mode 100644 Example2/Chat.cs delete mode 100644 Example2/Echo.cs delete mode 100644 Example2/Example2.csproj delete mode 100644 Example2/Program.cs delete mode 100644 Example3/App.config delete mode 100644 Example3/AssemblyInfo.cs delete mode 100644 Example3/Chat.cs delete mode 100644 Example3/Echo.cs delete mode 100644 Example3/Example3.csproj delete mode 100644 Example3/Program.cs delete mode 100644 Example3/Public/Js/echotest.js delete mode 100644 Example3/Public/index.html delete mode 100644 websocket-sharp/doc/.gitignore delete mode 100755 websocket-sharp/doc/doc.sh delete mode 100644 websocket-sharp/websocket-sharp.snk diff --git a/Example/AssemblyInfo.cs b/Example/AssemblyInfo.cs deleted file mode 100644 index 9a9617356..000000000 --- a/Example/AssemblyInfo.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; - -// Information about this assembly is defined by the following attributes. -// Change them to the values specific to your project. - -[assembly: AssemblyTitle("Example")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("")] -[assembly: AssemblyCopyright("sta.blockhead")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". -// The form "{Major}.{Minor}.*" will automatically update the build and revision, -// and "{Major}.{Minor}.{Build}.*" will update just the revision. - -[assembly: AssemblyVersion("1.0.*")] - -// The following attributes are used to specify the signing key for the assembly, -// if desired. See the Mono documentation for more information about signing. - -//[assembly: AssemblyDelaySign(false)] -//[assembly: AssemblyKeyFile("")] diff --git a/Example/Example.csproj b/Example/Example.csproj deleted file mode 100644 index 38c5b4200..000000000 --- a/Example/Example.csproj +++ /dev/null @@ -1,71 +0,0 @@ - - - - Debug - AnyCPU - 9.0.21022 - 2.0 - {52805AEC-EFB1-4F42-BB8E-3ED4E692C568} - Exe - Example - example - v3.5 - - - true - full - false - bin\Debug - DEBUG - prompt - 4 - true - - - none - false - bin\Release - prompt - 4 - true - - - true - full - false - bin\Debug_Ubuntu - DEBUG,UBUNTU - prompt - 4 - true - - - none - false - bin\Release_Ubuntu - UBUNTU - prompt - 4 - true - - - - - notify-sharp - - - - - - {B357BAC7-529E-4D81-A0D2-71041B19C8DE} - websocket-sharp - - - - - - - - - - \ No newline at end of file diff --git a/Example/NotificationMessage.cs b/Example/NotificationMessage.cs deleted file mode 100644 index fd1bd3071..000000000 --- a/Example/NotificationMessage.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; - -namespace Example -{ - internal class NotificationMessage - { - public string Body { - get; set; - } - - public string Icon { - get; set; - } - - public string Summary { - get; set; - } - - public override string ToString () - { - return String.Format ("{0}: {1}", Summary, Body); - } - } -} diff --git a/Example/Notifier.cs b/Example/Notifier.cs deleted file mode 100644 index 5371c37a4..000000000 --- a/Example/Notifier.cs +++ /dev/null @@ -1,81 +0,0 @@ -#if UBUNTU -using Notifications; -#endif -using System; -using System.Collections; -using System.Collections.Generic; -using System.Threading; - -namespace Example -{ - internal class Notifier : IDisposable - { - private volatile bool _enabled; - private ManualResetEvent _exited; - private Queue _queue; - private object _sync; - - public Notifier () - { - _enabled = true; - _exited = new ManualResetEvent (false); - _queue = new Queue (); - _sync = ((ICollection) _queue).SyncRoot; - - ThreadPool.QueueUserWorkItem ( - state => { - while (_enabled || Count > 0) { - var msg = dequeue (); - if (msg != null) { -#if UBUNTU - var nf = new Notification (msg.Summary, msg.Body, msg.Icon); - nf.AddHint ("append", "allowed"); - nf.Show (); -#else - Console.WriteLine (msg); -#endif - } - else { - Thread.Sleep (500); - } - } - - _exited.Set (); - } - ); - } - - public int Count { - get { - lock (_sync) - return _queue.Count; - } - } - - private NotificationMessage dequeue () - { - lock (_sync) - return _queue.Count > 0 ? _queue.Dequeue () : null; - } - - public void Close () - { - _enabled = false; - _exited.WaitOne (); - _exited.Close (); - } - - public void Notify (NotificationMessage message) - { - lock (_sync) { - if (_enabled) - _queue.Enqueue (message); - } - } - - void IDisposable.Dispose () - { - Close (); - } - } -} diff --git a/Example/Program.cs b/Example/Program.cs deleted file mode 100644 index d414bb1e5..000000000 --- a/Example/Program.cs +++ /dev/null @@ -1,128 +0,0 @@ -using System; -using System.Threading; -using WebSocketSharp; -using WebSocketSharp.Net; - -namespace Example -{ - public class Program - { - public static void Main (string[] args) - { - // Create a new instance of the WebSocket class. - // - // The WebSocket class inherits the System.IDisposable interface, so you can - // use the using statement. And the WebSocket connection will be closed with - // close status 1001 (going away) when the control leaves the using block. - // - // If you would like to connect to the server with the secure connection, - // you should create a new instance with a wss scheme WebSocket URL. - - using (var nf = new Notifier ()) - using (var ws = new WebSocket ("ws://echo.websocket.org")) - //using (var ws = new WebSocket ("wss://echo.websocket.org")) - //using (var ws = new WebSocket ("ws://localhost:4649/Echo")) - //using (var ws = new WebSocket ("wss://localhost:5963/Echo")) - //using (var ws = new WebSocket ("ws://localhost:4649/Echo?name=nobita")) - //using (var ws = new WebSocket ("wss://localhost:5963/Echo?name=nobita")) - //using (var ws = new WebSocket ("ws://localhost:4649/Chat")) - //using (var ws = new WebSocket ("wss://localhost:5963/Chat")) - //using (var ws = new WebSocket ("ws://localhost:4649/Chat?name=nobita")) - //using (var ws = new WebSocket ("wss://localhost:5963/Chat?name=nobita")) - { - // Set the WebSocket events. - - ws.OnOpen += (sender, e) => ws.Send ("Hi, there!"); - - ws.OnMessage += (sender, e) => - nf.Notify ( - new NotificationMessage { - Summary = "WebSocket Message", - Body = !e.IsPing ? e.Data : "Received a ping.", - Icon = "notification-message-im" - } - ); - - ws.OnError += (sender, e) => - nf.Notify ( - new NotificationMessage { - Summary = "WebSocket Error", - Body = e.Message, - Icon = "notification-message-im" - } - ); - - ws.OnClose += (sender, e) => - nf.Notify ( - new NotificationMessage { - Summary = String.Format ("WebSocket Close ({0})", e.Code), - Body = e.Reason, - Icon = "notification-message-im" - } - ); -#if DEBUG - // To change the logging level. - ws.Log.Level = LogLevel.Trace; - - // To change the wait time for the response to the Ping or Close. - //ws.WaitTime = TimeSpan.FromSeconds (10); - - // To emit a WebSocket.OnMessage event when receives a ping. - //ws.EmitOnPing = true; -#endif - // To enable the Per-message Compression extension. - //ws.Compression = CompressionMethod.Deflate; - - // To validate the server certificate. - /* - ws.SslConfiguration.ServerCertificateValidationCallback = - (sender, certificate, chain, sslPolicyErrors) => { - ws.Log.Debug ( - String.Format ( - "Certificate:\n- Issuer: {0}\n- Subject: {1}", - certificate.Issuer, - certificate.Subject - ) - ); - - return true; // If the server certificate is valid. - }; - */ - - // To send the credentials for the HTTP Authentication (Basic/Digest). - //ws.SetCredentials ("nobita", "password", false); - - // To send the Origin header. - //ws.Origin = "http://localhost:4649"; - - // To send the cookies. - //ws.SetCookie (new Cookie ("name", "nobita")); - //ws.SetCookie (new Cookie ("roles", "\"idiot, gunfighter\"")); - - // To connect through the HTTP Proxy server. - //ws.SetProxy ("http://localhost:3128", "nobita", "password"); - - // To enable the redirection. - //ws.EnableRedirection = true; - - // Connect to the server. - ws.Connect (); - - // Connect to the server asynchronously. - //ws.ConnectAsync (); - - Console.WriteLine ("\nType 'exit' to exit.\n"); - while (true) { - Thread.Sleep (1000); - Console.Write ("> "); - var msg = Console.ReadLine (); - if (msg == "exit") - break; - - // Send a text message. - ws.Send (msg); - } - } - } - } -} diff --git a/Example1/AssemblyInfo.cs b/Example1/AssemblyInfo.cs deleted file mode 100644 index a78e6c6de..000000000 --- a/Example1/AssemblyInfo.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; - -// Information about this assembly is defined by the following attributes. -// Change them to the values specific to your project. - -[assembly: AssemblyTitle("Example1")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("")] -[assembly: AssemblyCopyright("sta.blockhead")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". -// The form "{Major}.{Minor}.*" will automatically update the build and revision, -// and "{Major}.{Minor}.{Build}.*" will update just the revision. - -[assembly: AssemblyVersion("1.0.*")] - -// The following attributes are used to specify the signing key for the assembly, -// if desired. See the Mono documentation for more information about signing. - -//[assembly: AssemblyDelaySign(false)] -//[assembly: AssemblyKeyFile("")] diff --git a/Example1/AudioStreamer.cs b/Example1/AudioStreamer.cs deleted file mode 100644 index 711694e9a..000000000 --- a/Example1/AudioStreamer.cs +++ /dev/null @@ -1,192 +0,0 @@ -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System; -using System.Collections; -using System.Collections.Generic; -using System.Text; -using System.Threading; -using WebSocketSharp; - -namespace Example1 -{ - internal class AudioStreamer : IDisposable - { - private Dictionary _audioBox; - private uint? _id; - private string _name; - private Notifier _notifier; - private Timer _timer; - private WebSocket _websocket; - - public AudioStreamer (string url) - { - _websocket = new WebSocket (url); - - _audioBox = new Dictionary (); - _id = null; - _notifier = new Notifier (); - _timer = new Timer (sendHeartbeat, null, -1, -1); - - configure (); - } - - private void configure () - { -#if DEBUG - _websocket.Log.Level = LogLevel.Trace; -#endif - _websocket.OnOpen += (sender, e) => - _websocket.Send (createTextMessage ("connection", String.Empty)); - - _websocket.OnMessage += (sender, e) => { - if (e.IsText) { - _notifier.Notify (processTextMessage (e.Data)); - return; - } - - if (e.IsBinary) { - processBinaryMessage (e.RawData); - return; - } - }; - - _websocket.OnError += (sender, e) => - _notifier.Notify ( - new NotificationMessage { - Summary = "AudioStreamer (error)", - Body = e.Message, - Icon = "notification-message-im" - } - ); - - _websocket.OnClose += (sender, e) => - _notifier.Notify ( - new NotificationMessage { - Summary = "AudioStreamer (disconnect)", - Body = String.Format ("code: {0} reason: {1}", e.Code, e.Reason), - Icon = "notification-message-im" - } - ); - } - - private byte[] createBinaryMessage (float[,] bufferArray) - { - return new BinaryMessage { - UserID = (uint) _id, - ChannelNumber = (byte) bufferArray.GetLength (0), - BufferLength = (uint) bufferArray.GetLength (1), - BufferArray = bufferArray - } - .ToArray (); - } - - private string createTextMessage (string type, string message) - { - return new TextMessage { - UserID = _id, - Name = _name, - Type = type, - Message = message - } - .ToString (); - } - - private void processBinaryMessage (byte[] data) - { - var msg = BinaryMessage.Parse (data); - - var id = msg.UserID; - if (id == _id) - return; - - Queue queue; - if (_audioBox.TryGetValue (id, out queue)) { - queue.Enqueue (msg.BufferArray); - return; - } - - queue = Queue.Synchronized (new Queue ()); - queue.Enqueue (msg.BufferArray); - _audioBox.Add (id, queue); - } - - private NotificationMessage processTextMessage (string data) - { - var json = JObject.Parse (data); - var id = (uint) json["user_id"]; - var name = (string) json["name"]; - var type = (string) json["type"]; - - string body; - if (type == "message") { - body = String.Format ("{0}: {1}", name, (string) json["message"]); - } - else if (type == "start_music") { - body = String.Format ("{0}: Started playing music!", name); - } - else if (type == "connection") { - var users = (JArray) json["message"]; - var buff = new StringBuilder ("Now keeping connections:"); - foreach (JToken user in users) { - buff.AppendFormat ( - "\n- user_id: {0} name: {1}", (uint) user["user_id"], (string) user["name"] - ); - } - - body = buff.ToString (); - } - else if (type == "connected") { - _id = id; - _timer.Change (30000, 30000); - - body = String.Format ("user_id: {0} name: {1}", id, name); - } - else { - body = "Received unknown type message."; - } - - return new NotificationMessage { - Summary = String.Format ("AudioStreamer ({0})", type), - Body = body, - Icon = "notification-message-im" - }; - } - - private void sendHeartbeat (object state) - { - _websocket.Send (createTextMessage ("heartbeat", String.Empty)); - } - - public void Close () - { - Disconnect (); - _timer.Dispose (); - _notifier.Close (); - } - - public void Connect (string username) - { - _name = username; - _websocket.Connect (); - } - - public void Disconnect () - { - _timer.Change (-1, -1); - _websocket.Close (CloseStatusCode.Away); - _audioBox.Clear (); - _id = null; - _name = null; - } - - public void Write (string message) - { - _websocket.Send (createTextMessage ("message", message)); - } - - void IDisposable.Dispose () - { - Close (); - } - } -} diff --git a/Example1/BinaryMessage.cs b/Example1/BinaryMessage.cs deleted file mode 100644 index eafa7aa98..000000000 --- a/Example1/BinaryMessage.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System; -using System.Collections.Generic; -using WebSocketSharp; - -namespace Example1 -{ - internal class BinaryMessage - { - public uint UserID { - get; set; - } - - public byte ChannelNumber { - get; set; - } - - public uint BufferLength { - get; set; - } - - public float[,] BufferArray { - get; set; - } - - public static BinaryMessage Parse (byte[] data) - { - var id = data.SubArray (0, 4).To (ByteOrder.Big); - var num = data.SubArray (4, 1)[0]; - var len = data.SubArray (5, 4).To (ByteOrder.Big); - var arr = new float[num, len]; - - var offset = 9; - ((uint) num).Times ( - i => - len.Times ( - j => { - arr[i, j] = data.SubArray (offset, 4).To (ByteOrder.Big); - offset += 4; - } - ) - ); - - return new BinaryMessage { - UserID = id, - ChannelNumber = num, - BufferLength = len, - BufferArray = arr - }; - } - - public byte[] ToArray () - { - var buff = new List (); - - var id = UserID; - var num = ChannelNumber; - var len = BufferLength; - var arr = BufferArray; - - buff.AddRange (id.ToByteArray (ByteOrder.Big)); - buff.Add (num); - buff.AddRange (len.ToByteArray (ByteOrder.Big)); - - ((uint) num).Times ( - i => - len.Times ( - j => buff.AddRange (arr[i, j].ToByteArray (ByteOrder.Big)) - ) - ); - - return buff.ToArray (); - } - } -} diff --git a/Example1/Example1.csproj b/Example1/Example1.csproj deleted file mode 100644 index 81c52eff2..000000000 --- a/Example1/Example1.csproj +++ /dev/null @@ -1,77 +0,0 @@ - - - - Debug - AnyCPU - 9.0.21022 - 2.0 - {390E2568-57B7-4D17-91E5-C29336368CCF} - Exe - Example - example1 - v3.5 - - - true - full - false - bin\Debug - DEBUG; - prompt - 4 - true - - - none - false - bin\Release - prompt - 4 - true - - - true - full - false - bin\Debug_Ubuntu - DEBUG;UBUNTU - prompt - 4 - true - - - none - false - bin\Release_Ubuntu - prompt - 4 - true - UBUNTU - - - - - False - notify-sharp - - - False - - - - - - - - - - - - - - - {B357BAC7-529E-4D81-A0D2-71041B19C8DE} - websocket-sharp - - - \ No newline at end of file diff --git a/Example1/NotificationMessage.cs b/Example1/NotificationMessage.cs deleted file mode 100644 index 01f1692a8..000000000 --- a/Example1/NotificationMessage.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; - -namespace Example1 -{ - internal class NotificationMessage - { - public string Body { - get; set; - } - - public string Icon { - get; set; - } - - public string Summary { - get; set; - } - - public override string ToString () - { - return String.Format ("{0}: {1}", Summary, Body); - } - } -} diff --git a/Example1/Notifier.cs b/Example1/Notifier.cs deleted file mode 100644 index adf53ec9a..000000000 --- a/Example1/Notifier.cs +++ /dev/null @@ -1,81 +0,0 @@ -#if UBUNTU -using Notifications; -#endif -using System; -using System.Collections; -using System.Collections.Generic; -using System.Threading; - -namespace Example1 -{ - internal class Notifier : IDisposable - { - private volatile bool _enabled; - private ManualResetEvent _exited; - private Queue _queue; - private object _sync; - - public Notifier () - { - _enabled = true; - _exited = new ManualResetEvent (false); - _queue = new Queue (); - _sync = ((ICollection) _queue).SyncRoot; - - ThreadPool.QueueUserWorkItem ( - state => { - while (_enabled || Count > 0) { - var msg = dequeue (); - if (msg != null) { -#if UBUNTU - var nf = new Notification (msg.Summary, msg.Body, msg.Icon); - nf.AddHint ("append", "allowed"); - nf.Show (); -#else - Console.WriteLine (msg); -#endif - } - else { - Thread.Sleep (500); - } - } - - _exited.Set (); - } - ); - } - - public int Count { - get { - lock (_sync) - return _queue.Count; - } - } - - private NotificationMessage dequeue () - { - lock (_sync) - return _queue.Count > 0 ? _queue.Dequeue () : null; - } - - public void Close () - { - _enabled = false; - _exited.WaitOne (); - _exited.Close (); - } - - public void Notify (NotificationMessage message) - { - lock (_sync) { - if (_enabled) - _queue.Enqueue (message); - } - } - - void IDisposable.Dispose () - { - Close (); - } - } -} diff --git a/Example1/Program.cs b/Example1/Program.cs deleted file mode 100644 index 88c0bedfe..000000000 --- a/Example1/Program.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; -using System.Threading; - -namespace Example1 -{ - public class Program - { - public static void Main (string[] args) - { - // The AudioStreamer class provides a client (chat) for AudioStreamer - // (https://github.com/agektmr/AudioStreamer). - - using (var streamer = new AudioStreamer ("ws://localhost:3000/socket")) - { - string name; - do { - Console.Write ("Input your name> "); - name = Console.ReadLine (); - } - while (name.Length == 0); - - streamer.Connect (name); - - Console.WriteLine ("\nType 'exit' to exit.\n"); - while (true) { - Thread.Sleep (1000); - Console.Write ("> "); - var msg = Console.ReadLine (); - if (msg == "exit") - break; - - streamer.Write (msg); - } - } - } - } -} diff --git a/Example1/TextMessage.cs b/Example1/TextMessage.cs deleted file mode 100644 index 2b177d845..000000000 --- a/Example1/TextMessage.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Newtonsoft.Json; -using System; - -namespace Example1 -{ - internal class TextMessage - { - [JsonProperty ("user_id")] - public uint? UserID { - get; set; - } - - [JsonProperty ("name")] - public string Name { - get; set; - } - - [JsonProperty ("type")] - public string Type { - get; set; - } - - [JsonProperty ("message")] - public string Message { - get; set; - } - - public override string ToString () - { - return JsonConvert.SerializeObject (this); - } - } -} diff --git a/Example2/App.config b/Example2/App.config deleted file mode 100644 index 3a02690ea..000000000 --- a/Example2/App.config +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/Example2/AssemblyInfo.cs b/Example2/AssemblyInfo.cs deleted file mode 100644 index 55cd94f77..000000000 --- a/Example2/AssemblyInfo.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; - -// Information about this assembly is defined by the following attributes. -// Change them to the values specific to your project. - -[assembly: AssemblyTitle("Example2")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("")] -[assembly: AssemblyCopyright("sta.blockhead")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". -// The form "{Major}.{Minor}.*" will automatically update the build and revision, -// and "{Major}.{Minor}.{Build}.*" will update just the revision. - -[assembly: AssemblyVersion("1.0.*")] - -// The following attributes are used to specify the signing key for the assembly, -// if desired. See the Mono documentation for more information about signing. - -//[assembly: AssemblyDelaySign(false)] -//[assembly: AssemblyKeyFile("")] diff --git a/Example2/Chat.cs b/Example2/Chat.cs deleted file mode 100644 index a6b367d96..000000000 --- a/Example2/Chat.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System; -using System.Threading; -using WebSocketSharp; -using WebSocketSharp.Server; - -namespace Example2 -{ - public class Chat : WebSocketBehavior - { - private string _name; - private static int _number = 0; - private string _prefix; - - public Chat () - : this (null) - { - } - - public Chat (string prefix) - { - _prefix = !prefix.IsNullOrEmpty () ? prefix : "anon#"; - } - - private string getName () - { - var name = Context.QueryString["name"]; - return !name.IsNullOrEmpty () ? name : _prefix + getNumber (); - } - - private static int getNumber () - { - return Interlocked.Increment (ref _number); - } - - protected override void OnClose (CloseEventArgs e) - { - Sessions.Broadcast (String.Format ("{0} got logged off...", _name)); - } - - protected override void OnMessage (MessageEventArgs e) - { - Sessions.Broadcast (String.Format ("{0}: {1}", _name, e.Data)); - } - - protected override void OnOpen () - { - _name = getName (); - } - } -} diff --git a/Example2/Echo.cs b/Example2/Echo.cs deleted file mode 100644 index dd780c8d1..000000000 --- a/Example2/Echo.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using WebSocketSharp; -using WebSocketSharp.Server; - -namespace Example2 -{ - public class Echo : WebSocketBehavior - { - protected override void OnMessage (MessageEventArgs e) - { - var name = Context.QueryString["name"]; - Send (!name.IsNullOrEmpty () ? String.Format ("\"{0}\" to {1}", e.Data, name) : e.Data); - } - } -} diff --git a/Example2/Example2.csproj b/Example2/Example2.csproj deleted file mode 100644 index 685a1ef6d..000000000 --- a/Example2/Example2.csproj +++ /dev/null @@ -1,70 +0,0 @@ - - - - Debug - AnyCPU - 9.0.21022 - 2.0 - {B81A24C8-25BB-42B2-AF99-1E1EACCE74C7} - Exe - Example2 - example2 - v3.5 - - - true - full - false - bin\Debug - DEBUG; - prompt - 4 - true - - - none - false - bin\Release - prompt - 4 - true - - - true - full - false - bin\Debug_Ubuntu - DEBUG; - prompt - 4 - true - - - none - false - bin\Release_Ubuntu - prompt - 4 - true - - - - - - - - - - - - - - - {B357BAC7-529E-4D81-A0D2-71041B19C8DE} - websocket-sharp - - - - - - \ No newline at end of file diff --git a/Example2/Program.cs b/Example2/Program.cs deleted file mode 100644 index c9bd7ef3d..000000000 --- a/Example2/Program.cs +++ /dev/null @@ -1,135 +0,0 @@ -using System; -using System.Configuration; -using System.Security.Cryptography.X509Certificates; -using WebSocketSharp; -using WebSocketSharp.Net; -using WebSocketSharp.Server; - -namespace Example2 -{ - public class Program - { - public static void Main (string[] args) - { - // Create a new instance of the WebSocketServer class. - // - // If you would like to provide the secure connection, you should - // create a new instance with the 'secure' parameter set to true, - // or a wss scheme WebSocket URL. - - var wssv = new WebSocketServer (4649); - //var wssv = new WebSocketServer (5963, true); - - //var wssv = new WebSocketServer (System.Net.IPAddress.Any, 4649); - //var wssv = new WebSocketServer (System.Net.IPAddress.Any, 5963, true); - - //var wssv = new WebSocketServer (System.Net.IPAddress.IPv6Any, 4649); - //var wssv = new WebSocketServer (System.Net.IPAddress.IPv6Any, 5963, true); - - //var wssv = new WebSocketServer ("ws://0.0.0.0:4649"); - //var wssv = new WebSocketServer ("wss://0.0.0.0:5963"); - - //var wssv = new WebSocketServer ("ws://[::0]:4649"); - //var wssv = new WebSocketServer ("wss://[::0]:5963"); - - //var wssv = new WebSocketServer (System.Net.IPAddress.Loopback, 4649); - //var wssv = new WebSocketServer (System.Net.IPAddress.Loopback, 5963, true); - - //var wssv = new WebSocketServer (System.Net.IPAddress.IPv6Loopback, 4649); - //var wssv = new WebSocketServer (System.Net.IPAddress.IPv6Loopback, 5963, true); - - //var wssv = new WebSocketServer ("ws://localhost:4649"); - //var wssv = new WebSocketServer ("wss://localhost:5963"); - - //var wssv = new WebSocketServer ("ws://127.0.0.1:4649"); - //var wssv = new WebSocketServer ("wss://127.0.0.1:5963"); - - //var wssv = new WebSocketServer ("ws://[::1]:4649"); - //var wssv = new WebSocketServer ("wss://[::1]:5963"); -#if DEBUG - // To change the logging level. - wssv.Log.Level = LogLevel.Trace; - - // To change the wait time for the response to the WebSocket Ping or Close. - //wssv.WaitTime = TimeSpan.FromSeconds (2); - - // Not to remove the inactive sessions periodically. - //wssv.KeepClean = false; -#endif - // To provide the secure connection. - /* - var cert = ConfigurationManager.AppSettings["ServerCertFile"]; - var passwd = ConfigurationManager.AppSettings["CertFilePassword"]; - wssv.SslConfiguration.ServerCertificate = new X509Certificate2 (cert, passwd); - */ - - // To provide the HTTP Authentication (Basic/Digest). - /* - wssv.AuthenticationSchemes = AuthenticationSchemes.Basic; - wssv.Realm = "WebSocket Test"; - wssv.UserCredentialsFinder = id => { - var name = id.Name; - - // Return user name, password, and roles. - return name == "nobita" - ? new NetworkCredential (name, "password", "gunfighter") - : null; // If the user credentials aren't found. - }; - */ - - // To resolve to wait for socket in TIME_WAIT state. - //wssv.ReuseAddress = true; - - // Add the WebSocket services. - wssv.AddWebSocketService ("/Echo"); - wssv.AddWebSocketService ("/Chat"); - - // Add the WebSocket service with initializing. - /* - wssv.AddWebSocketService ( - "/Chat", - () => - new Chat ("Anon#") { - // To send the Sec-WebSocket-Protocol header that has a subprotocol name. - Protocol = "chat", - // To ignore the Sec-WebSocket-Extensions header. - IgnoreExtensions = true, - // To emit a WebSocket.OnMessage event when receives a ping. - EmitOnPing = true, - // To validate the Origin header. - OriginValidator = val => { - // Check the value of the Origin header, and return true if valid. - Uri origin; - return !val.IsNullOrEmpty () - && Uri.TryCreate (val, UriKind.Absolute, out origin) - && origin.Host == "localhost"; - }, - // To validate the cookies. - CookiesValidator = (req, res) => { - // Check the cookies in 'req', and set the cookies to send to - // the client with 'res' if necessary. - foreach (Cookie cookie in req) { - cookie.Expired = true; - res.Add (cookie); - } - - return true; // If valid. - } - } - ); - */ - - wssv.Start (); - if (wssv.IsListening) { - Console.WriteLine ("Listening on port {0}, and providing WebSocket services:", wssv.Port); - foreach (var path in wssv.WebSocketServices.Paths) - Console.WriteLine ("- {0}", path); - } - - Console.WriteLine ("\nPress Enter key to stop the server..."); - Console.ReadLine (); - - wssv.Stop (); - } - } -} diff --git a/Example3/App.config b/Example3/App.config deleted file mode 100644 index fa624b42b..000000000 --- a/Example3/App.config +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/Example3/AssemblyInfo.cs b/Example3/AssemblyInfo.cs deleted file mode 100644 index b9a88a257..000000000 --- a/Example3/AssemblyInfo.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; - -// Information about this assembly is defined by the following attributes. -// Change them to the values specific to your project. - -[assembly: AssemblyTitle("Example3")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("")] -[assembly: AssemblyCopyright("sta.blockhead")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". -// The form "{Major}.{Minor}.*" will automatically update the build and revision, -// and "{Major}.{Minor}.{Build}.*" will update just the revision. - -[assembly: AssemblyVersion("1.0.*")] - -// The following attributes are used to specify the signing key for the assembly, -// if desired. See the Mono documentation for more information about signing. - -//[assembly: AssemblyDelaySign(false)] -//[assembly: AssemblyKeyFile("")] diff --git a/Example3/Chat.cs b/Example3/Chat.cs deleted file mode 100644 index b1a3f4d7d..000000000 --- a/Example3/Chat.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System; -using System.Threading; -using WebSocketSharp; -using WebSocketSharp.Server; - -namespace Example3 -{ - public class Chat : WebSocketBehavior - { - private string _name; - private static int _number = 0; - private string _prefix; - - public Chat () - : this (null) - { - } - - public Chat (string prefix) - { - _prefix = !prefix.IsNullOrEmpty () ? prefix : "anon#"; - } - - private string getName () - { - var name = Context.QueryString["name"]; - return !name.IsNullOrEmpty () ? name : _prefix + getNumber (); - } - - private static int getNumber () - { - return Interlocked.Increment (ref _number); - } - - protected override void OnClose (CloseEventArgs e) - { - Sessions.Broadcast (String.Format ("{0} got logged off...", _name)); - } - - protected override void OnMessage (MessageEventArgs e) - { - Sessions.Broadcast (String.Format ("{0}: {1}", _name, e.Data)); - } - - protected override void OnOpen () - { - _name = getName (); - } - } -} diff --git a/Example3/Echo.cs b/Example3/Echo.cs deleted file mode 100644 index eb7c33410..000000000 --- a/Example3/Echo.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using WebSocketSharp; -using WebSocketSharp.Server; - -namespace Example3 -{ - public class Echo : WebSocketBehavior - { - protected override void OnMessage (MessageEventArgs e) - { - var name = Context.QueryString["name"]; - Send (!name.IsNullOrEmpty () ? String.Format ("\"{0}\" to {1}", e.Data, name) : e.Data); - } - } -} diff --git a/Example3/Example3.csproj b/Example3/Example3.csproj deleted file mode 100644 index ce4fe265c..000000000 --- a/Example3/Example3.csproj +++ /dev/null @@ -1,76 +0,0 @@ - - - - Debug - AnyCPU - 9.0.21022 - 2.0 - {C648BA25-77E5-4A40-A97F-D0AA37B9FB26} - Exe - Example3 - example3 - v3.5 - - - true - full - false - bin\Debug - DEBUG; - prompt - 4 - true - - - none - false - bin\Release - prompt - 4 - true - - - true - full - false - bin\Debug_Ubuntu - DEBUG; - prompt - 4 - true - - - none - false - bin\Release_Ubuntu - prompt - 4 - true - - - - - - - - - - - - - - - {B357BAC7-529E-4D81-A0D2-71041B19C8DE} - websocket-sharp - - - - - - - - - - - - \ No newline at end of file diff --git a/Example3/Program.cs b/Example3/Program.cs deleted file mode 100644 index b9699531f..000000000 --- a/Example3/Program.cs +++ /dev/null @@ -1,167 +0,0 @@ -using System; -using System.Configuration; -using System.Security.Cryptography.X509Certificates; -using System.Text; -using WebSocketSharp; -using WebSocketSharp.Net; -using WebSocketSharp.Server; - -namespace Example3 -{ - public class Program - { - public static void Main (string[] args) - { - // Create a new instance of the HttpServer class. - // - // If you would like to provide the secure connection, you should - // create a new instance with the 'secure' parameter set to true, - // or an https scheme HTTP URL. - - var httpsv = new HttpServer (4649); - //var httpsv = new HttpServer (5963, true); - - //var httpsv = new HttpServer (System.Net.IPAddress.Any, 4649); - //var httpsv = new HttpServer (System.Net.IPAddress.Any, 5963, true); - - //var httpsv = new HttpServer (System.Net.IPAddress.IPv6Any, 4649); - //var httpsv = new HttpServer (System.Net.IPAddress.IPv6Any, 5963, true); - - //var httpsv = new HttpServer ("http://0.0.0.0:4649"); - //var httpsv = new HttpServer ("https://0.0.0.0:5963"); - - //var httpsv = new HttpServer ("http://[::0]:4649"); - //var httpsv = new HttpServer ("https://[::0]:5963"); - - //var httpsv = new HttpServer (System.Net.IPAddress.Loopback, 4649); - //var httpsv = new HttpServer (System.Net.IPAddress.Loopback, 5963, true); - - //var httpsv = new HttpServer (System.Net.IPAddress.IPv6Loopback, 4649); - //var httpsv = new HttpServer (System.Net.IPAddress.IPv6Loopback, 5963, true); - - //var httpsv = new HttpServer ("http://localhost:4649"); - //var httpsv = new HttpServer ("https://localhost:5963"); - - //var httpsv = new HttpServer ("http://127.0.0.1:4649"); - //var httpsv = new HttpServer ("https://127.0.0.1:5963"); - - //var httpsv = new HttpServer ("http://[::1]:4649"); - //var httpsv = new HttpServer ("https://[::1]:5963"); -#if DEBUG - // To change the logging level. - httpsv.Log.Level = LogLevel.Trace; - - // To change the wait time for the response to the WebSocket Ping or Close. - //httpsv.WaitTime = TimeSpan.FromSeconds (2); - - // Not to remove the inactive WebSocket sessions periodically. - //httpsv.KeepClean = false; -#endif - // To provide the secure connection. - /* - var cert = ConfigurationManager.AppSettings["ServerCertFile"]; - var passwd = ConfigurationManager.AppSettings["CertFilePassword"]; - httpsv.SslConfiguration.ServerCertificate = new X509Certificate2 (cert, passwd); - */ - - // To provide the HTTP Authentication (Basic/Digest). - /* - httpsv.AuthenticationSchemes = AuthenticationSchemes.Basic; - httpsv.Realm = "WebSocket Test"; - httpsv.UserCredentialsFinder = id => { - var name = id.Name; - - // Return user name, password, and roles. - return name == "nobita" - ? new NetworkCredential (name, "password", "gunfighter") - : null; // If the user credentials aren't found. - }; - */ - - // To resolve to wait for socket in TIME_WAIT state. - //httpsv.ReuseAddress = true; - - // Set the document root path. - httpsv.DocumentRootPath = ConfigurationManager.AppSettings["DocumentRootPath"]; - - // Set the HTTP GET request event. - httpsv.OnGet += (sender, e) => { - var req = e.Request; - var res = e.Response; - - var path = req.RawUrl; - if (path == "/") - path += "index.html"; - - byte[] contents; - if (!e.TryReadFile (path, out contents)) { - res.StatusCode = (int) HttpStatusCode.NotFound; - return; - } - - if (path.EndsWith (".html")) { - res.ContentType = "text/html"; - res.ContentEncoding = Encoding.UTF8; - } - else if (path.EndsWith (".js")) { - res.ContentType = "application/javascript"; - res.ContentEncoding = Encoding.UTF8; - } - - res.ContentLength64 = contents.LongLength; - res.Close (contents, true); - }; - - // Add the WebSocket services. - httpsv.AddWebSocketService ("/Echo"); - httpsv.AddWebSocketService ("/Chat"); - - // Add the WebSocket service with initializing. - /* - httpsv.AddWebSocketService ( - "/Chat", - () => - new Chat ("Anon#") { - // To send the Sec-WebSocket-Protocol header that has a subprotocol name. - Protocol = "chat", - // To ignore the Sec-WebSocket-Extensions header. - IgnoreExtensions = true, - // To emit a WebSocket.OnMessage event when receives a ping. - EmitOnPing = true, - // To validate the Origin header. - OriginValidator = val => { - // Check the value of the Origin header, and return true if valid. - Uri origin; - return !val.IsNullOrEmpty () - && Uri.TryCreate (val, UriKind.Absolute, out origin) - && origin.Host == "localhost"; - }, - // To validate the cookies. - CookiesValidator = (req, res) => { - // Check the cookies in 'req', and set the cookies to send to - // the client with 'res' if necessary. - foreach (Cookie cookie in req) { - cookie.Expired = true; - res.Add (cookie); - } - - return true; // If valid. - } - } - ); - */ - - httpsv.Start (); - if (httpsv.IsListening) { - Console.WriteLine ("Listening on port {0}, and providing WebSocket services:", httpsv.Port); - foreach (var path in httpsv.WebSocketServices.Paths) - Console.WriteLine ("- {0}", path); - } - - Console.WriteLine ("\nPress Enter key to stop the server..."); - Console.ReadLine (); - - httpsv.Stop (); - } - } -} diff --git a/Example3/Public/Js/echotest.js b/Example3/Public/Js/echotest.js deleted file mode 100644 index a356f0d3e..000000000 --- a/Example3/Public/Js/echotest.js +++ /dev/null @@ -1,68 +0,0 @@ -/* - * echotest.js - * - * Derived from Echo Test of WebSocket.org (http://www.websocket.org/echo.html). - * - * Copyright (c) 2012 Kaazing Corporation. - */ - -var url = "ws://localhost:4649/Echo"; -//var url = "wss://localhost:5963/Echo"; -var output; - -function init () { - output = document.getElementById ("output"); - doWebSocket (); -} - -function doWebSocket () { - websocket = new WebSocket (url); - - websocket.onopen = function (e) { - onOpen (e); - }; - - websocket.onmessage = function (e) { - onMessage (e); - }; - - websocket.onerror = function (e) { - onError (e); - }; - - websocket.onclose = function (e) { - onClose (e); - }; -} - -function onOpen (event) { - writeToScreen ("CONNECTED"); - send ("WebSocket rocks"); -} - -function onMessage (event) { - writeToScreen ('RESPONSE: ' + event.data + ''); - websocket.close (); -} - -function onError (event) { - writeToScreen ('ERROR: ' + event.data + ''); -} - -function onClose (event) { - writeToScreen ("DISCONNECTED"); -} - -function send (message) { - writeToScreen ("SENT: " + message); - websocket.send (message); -} - -function writeToScreen (message) { - var pre = document.createElement ("p"); - pre.style.wordWrap = "break-word"; - pre.innerHTML = message; - output.appendChild (pre); -} - -window.addEventListener ("load", init, false); \ No newline at end of file diff --git a/Example3/Public/index.html b/Example3/Public/index.html deleted file mode 100644 index 8d6fe43c3..000000000 --- a/Example3/Public/index.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - WebSocket Echo Test - - - -

WebSocket Echo Test

-
- - diff --git a/websocket-sharp.sln b/websocket-sharp.sln index 3c20e06a0..406d4f516 100644 --- a/websocket-sharp.sln +++ b/websocket-sharp.sln @@ -1,64 +1,32 @@  -Microsoft Visual Studio Solution File, Format Version 10.00 -# Visual Studio 2008 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "websocket-sharp", "websocket-sharp\websocket-sharp.csproj", "{B357BAC7-529E-4D81-A0D2-71041B19C8DE}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example", "Example\Example.csproj", "{52805AEC-EFB1-4F42-BB8E-3ED4E692C568}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example1", "Example1\Example1.csproj", "{390E2568-57B7-4D17-91E5-C29336368CCF}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example2", "Example2\Example2.csproj", "{B81A24C8-25BB-42B2-AF99-1E1EACCE74C7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example3", "Example3\Example3.csproj", "{C648BA25-77E5-4A40-A97F-D0AA37B9FB26}" +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30907.101 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "websocket-sharp", "websocket-sharp\websocket-sharp.csproj", "{F6BA817C-EBA3-4765-AA28-126A09A93696}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU Debug_Ubuntu|Any CPU = Debug_Ubuntu|Any CPU + Debug|Any CPU = Debug|Any CPU Release_Ubuntu|Any CPU = Release_Ubuntu|Any CPU + Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {390E2568-57B7-4D17-91E5-C29336368CCF}.Debug_Ubuntu|Any CPU.ActiveCfg = Debug_Ubuntu|Any CPU - {390E2568-57B7-4D17-91E5-C29336368CCF}.Debug_Ubuntu|Any CPU.Build.0 = Debug_Ubuntu|Any CPU - {390E2568-57B7-4D17-91E5-C29336368CCF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {390E2568-57B7-4D17-91E5-C29336368CCF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {390E2568-57B7-4D17-91E5-C29336368CCF}.Release_Ubuntu|Any CPU.ActiveCfg = Release_Ubuntu|Any CPU - {390E2568-57B7-4D17-91E5-C29336368CCF}.Release_Ubuntu|Any CPU.Build.0 = Release_Ubuntu|Any CPU - {390E2568-57B7-4D17-91E5-C29336368CCF}.Release|Any CPU.ActiveCfg = Release|Any CPU - {390E2568-57B7-4D17-91E5-C29336368CCF}.Release|Any CPU.Build.0 = Release|Any CPU - {52805AEC-EFB1-4F42-BB8E-3ED4E692C568}.Debug_Ubuntu|Any CPU.ActiveCfg = Debug_Ubuntu|Any CPU - {52805AEC-EFB1-4F42-BB8E-3ED4E692C568}.Debug_Ubuntu|Any CPU.Build.0 = Debug_Ubuntu|Any CPU - {52805AEC-EFB1-4F42-BB8E-3ED4E692C568}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {52805AEC-EFB1-4F42-BB8E-3ED4E692C568}.Debug|Any CPU.Build.0 = Debug|Any CPU - {52805AEC-EFB1-4F42-BB8E-3ED4E692C568}.Release_Ubuntu|Any CPU.ActiveCfg = Release_Ubuntu|Any CPU - {52805AEC-EFB1-4F42-BB8E-3ED4E692C568}.Release_Ubuntu|Any CPU.Build.0 = Release_Ubuntu|Any CPU - {52805AEC-EFB1-4F42-BB8E-3ED4E692C568}.Release|Any CPU.ActiveCfg = Release|Any CPU - {52805AEC-EFB1-4F42-BB8E-3ED4E692C568}.Release|Any CPU.Build.0 = Release|Any CPU - {B357BAC7-529E-4D81-A0D2-71041B19C8DE}.Debug_Ubuntu|Any CPU.ActiveCfg = Debug_Ubuntu|Any CPU - {B357BAC7-529E-4D81-A0D2-71041B19C8DE}.Debug_Ubuntu|Any CPU.Build.0 = Debug_Ubuntu|Any CPU - {B357BAC7-529E-4D81-A0D2-71041B19C8DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B357BAC7-529E-4D81-A0D2-71041B19C8DE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B357BAC7-529E-4D81-A0D2-71041B19C8DE}.Release_Ubuntu|Any CPU.ActiveCfg = Release_Ubuntu|Any CPU - {B357BAC7-529E-4D81-A0D2-71041B19C8DE}.Release_Ubuntu|Any CPU.Build.0 = Release_Ubuntu|Any CPU - {B357BAC7-529E-4D81-A0D2-71041B19C8DE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B357BAC7-529E-4D81-A0D2-71041B19C8DE}.Release|Any CPU.Build.0 = Release|Any CPU - {B81A24C8-25BB-42B2-AF99-1E1EACCE74C7}.Debug_Ubuntu|Any CPU.ActiveCfg = Debug_Ubuntu|Any CPU - {B81A24C8-25BB-42B2-AF99-1E1EACCE74C7}.Debug_Ubuntu|Any CPU.Build.0 = Debug_Ubuntu|Any CPU - {B81A24C8-25BB-42B2-AF99-1E1EACCE74C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B81A24C8-25BB-42B2-AF99-1E1EACCE74C7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B81A24C8-25BB-42B2-AF99-1E1EACCE74C7}.Release_Ubuntu|Any CPU.ActiveCfg = Release_Ubuntu|Any CPU - {B81A24C8-25BB-42B2-AF99-1E1EACCE74C7}.Release_Ubuntu|Any CPU.Build.0 = Release_Ubuntu|Any CPU - {B81A24C8-25BB-42B2-AF99-1E1EACCE74C7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B81A24C8-25BB-42B2-AF99-1E1EACCE74C7}.Release|Any CPU.Build.0 = Release|Any CPU - {C648BA25-77E5-4A40-A97F-D0AA37B9FB26}.Debug_Ubuntu|Any CPU.ActiveCfg = Debug_Ubuntu|Any CPU - {C648BA25-77E5-4A40-A97F-D0AA37B9FB26}.Debug_Ubuntu|Any CPU.Build.0 = Debug_Ubuntu|Any CPU - {C648BA25-77E5-4A40-A97F-D0AA37B9FB26}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C648BA25-77E5-4A40-A97F-D0AA37B9FB26}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C648BA25-77E5-4A40-A97F-D0AA37B9FB26}.Release_Ubuntu|Any CPU.ActiveCfg = Release_Ubuntu|Any CPU - {C648BA25-77E5-4A40-A97F-D0AA37B9FB26}.Release_Ubuntu|Any CPU.Build.0 = Release_Ubuntu|Any CPU - {C648BA25-77E5-4A40-A97F-D0AA37B9FB26}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C648BA25-77E5-4A40-A97F-D0AA37B9FB26}.Release|Any CPU.Build.0 = Release|Any CPU + {F6BA817C-EBA3-4765-AA28-126A09A93696}.Debug_Ubuntu|Any CPU.ActiveCfg = Debug|Any CPU + {F6BA817C-EBA3-4765-AA28-126A09A93696}.Debug_Ubuntu|Any CPU.Build.0 = Debug|Any CPU + {F6BA817C-EBA3-4765-AA28-126A09A93696}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F6BA817C-EBA3-4765-AA28-126A09A93696}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F6BA817C-EBA3-4765-AA28-126A09A93696}.Release_Ubuntu|Any CPU.ActiveCfg = Release|Any CPU + {F6BA817C-EBA3-4765-AA28-126A09A93696}.Release_Ubuntu|Any CPU.Build.0 = Release|Any CPU + {F6BA817C-EBA3-4765-AA28-126A09A93696}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F6BA817C-EBA3-4765-AA28-126A09A93696}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {BBF4A59D-849A-497A-98B2-3C48213B2A45} EndGlobalSection GlobalSection(MonoDevelopProperties) = preSolution StartupItem = websocket-sharp\websocket-sharp.csproj diff --git a/websocket-sharp/AssemblyInfo.cs b/websocket-sharp/AssemblyInfo.cs index c85deaa45..aa528884e 100644 --- a/websocket-sharp/AssemblyInfo.cs +++ b/websocket-sharp/AssemblyInfo.cs @@ -1,26 +1,17 @@ -using System.Reflection; -using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; -// Information about this assembly is defined by the following attributes. -// Change them to the values specific to your project. +// In SDK-style projects such as this one, several assembly attributes that were historically +// defined in this file are now automatically added during build and populated with +// values defined in project properties. For details of which attributes are included +// and how to customise this process see: https://aka.ms/assembly-info-properties -[assembly: AssemblyTitle("websocket-sharp")] -[assembly: AssemblyDescription("A C# implementation of the WebSocket protocol client and server")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("websocket-sharp.dll")] -[assembly: AssemblyCopyright("sta.blockhead")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] -// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". -// The form "{Major}.{Minor}.*" will automatically update the build and revision, -// and "{Major}.{Minor}.{Build}.*" will update just the revision. +// Setting ComVisible to false makes the types in this assembly not visible to COM +// components. If you need to access a type in this assembly from COM, set the ComVisible +// attribute to true on that type. -[assembly: AssemblyVersion("1.0.2.*")] +[assembly: ComVisible(false)] -// The following attributes are used to specify the signing key for the assembly, -// if desired. See the Mono documentation for more information about signing. +// The following GUID is for the ID of the typelib if this project is exposed to COM. -//[assembly: AssemblyDelaySign(false)] -//[assembly: AssemblyKeyFile("")] +[assembly: Guid("9a61a627-53cb-4544-8cf4-687a8256d6be")] diff --git a/websocket-sharp/doc/.gitignore b/websocket-sharp/doc/.gitignore deleted file mode 100644 index 7b744c39d..000000000 --- a/websocket-sharp/doc/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -## Ignore MonoDevelop build results. - -html -mdoc diff --git a/websocket-sharp/doc/doc.sh b/websocket-sharp/doc/doc.sh deleted file mode 100755 index e4f3fa6b3..000000000 --- a/websocket-sharp/doc/doc.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/sh -# -# @(#) doc.sh ver.0.0.2 2013.01.24 -# -# Usage: -# doc.sh -# -# Description: -# Creating documentation for websocket-sharp. -# -########################################################################### - -SRC_DIR="../bin/Release_Ubuntu" -XML="${SRC_DIR}/websocket-sharp.xml" -DLL="${SRC_DIR}/websocket-sharp.dll" - -DOC_DIR="." -MDOC_DIR="${DOC_DIR}/mdoc" -HTML_DIR="${DOC_DIR}/html" - -createDir() { - if [ ! -d $1 ]; then - mkdir -p $1 - fi -} - -set -e -createDir ${MDOC_DIR} -createDir ${HTML_DIR} -mdoc update --delete -fno-assembly-versions -i ${XML} -o ${MDOC_DIR}/ ${DLL} -mdoc export-html -o ${HTML_DIR}/ ${MDOC_DIR}/ diff --git a/websocket-sharp/websocket-sharp.csproj b/websocket-sharp/websocket-sharp.csproj index 0860c0313..62d2803bf 100644 --- a/websocket-sharp/websocket-sharp.csproj +++ b/websocket-sharp/websocket-sharp.csproj @@ -1,149 +1,8 @@ - - + + - Debug - AnyCPU - 9.0.21022 - 2.0 - {B357BAC7-529E-4D81-A0D2-71041B19C8DE} - Library - WebSocketSharp - websocket-sharp - v3.5 - true - websocket-sharp.snk + netcoreapp3.1 + websocket_sharp - - true - full - false - bin\Debug - DEBUG - prompt - 4 - false - - - none - false - bin\Release - prompt - 4 - false - - - true - full - false - bin\Debug_Ubuntu - DEBUG - prompt - 4 - false - - - none - false - bin\Release_Ubuntu - prompt - 4 - false - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + + diff --git a/websocket-sharp/websocket-sharp.snk b/websocket-sharp/websocket-sharp.snk deleted file mode 100644 index a2546f385ff1c28221f1071dd2fbc1870d728eb2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 596 zcmV-a0;~N80ssI2Bme+XQ$aES1ONaL0000f7yF6+!;$wZ%ZuV+(mw^0ZDpvQV{)eT z)hzOzAo>fyaBB95ev6PlIrVnPNZ`ABvU}~*5T_asaF`E&^h;9-(xvYSmgrHTIc_N+d4!r0J+k@O7F^N!rZ!?-v-48IrNAb7!} z-N_==YbBhM_zb%3vcPBeECpwQOr<(i6$!ynrQ~`QXjiMFwVBazO>@JrfgI51ivGl6 zRjei2J2>~wP7}hz?AO{)>lh*();UW|5NUR0!Ji$O!OQ_E@UT|!^m$J!!@8AkltT%j z@^-wS`8NxnHzsxMW{zi$Sgj3%PBZ_5qAu2Olh=Q!lDnQk=0}kkW2jUYzmul4bgJIL zlK?W_`r?N;Z(!Kc`z;*hfOxrjI{zF803Sg&r~jaQ?Y?Fa8?Az+3a++ht8(mR9-o|j zE@`6|n4@2Af4?zHoP|-_#;rVGqar>7nH%|Nigv24jWsE z$LIC>T!MfxCrw6m*qpp=DPHF19B*63GF@WFjU6vWTmP4b`Oq(FIRY0(-TN1xl?|1D z-Sj2XD0j7$83O-B-{7UHg(P)vanCZOjten@X*^b4ZsM;$Wnsvy271`V8$NH_bgN{j zPL(SOf2I?l4(?fnuwJ_P05C5xZ~dI4vhml_=<z#%yL*k|m=6 From 7652ba73e5646f50ba9aa21f04e2e80b194feee4 Mon Sep 17 00:00:00 2001 From: darknessxk Date: Tue, 2 Feb 2021 10:01:15 -0300 Subject: [PATCH 2/3] Updated Readme --- README.md | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index e7049b224..6f0bf5ffa 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,10 @@ ## Welcome to websocket-sharp! ## +Created by [@sta](https://github.com/sta) and this fork in maintained by [@darknessxk](https://github.com/darknessxk) + +This Project is a NET Core version + websocket-sharp supports: - [RFC 6455](#supported-websocket-specifications) @@ -11,19 +15,19 @@ websocket-sharp supports: - [HTTP Authentication](#http-authentication) - [Query string, Origin header, and Cookies](#query-string-origin-header-and-cookies) - [Connecting through the HTTP proxy server](#connecting-through-the-http-proxy-server) -- .NET Framework **3.5** or later (includes compatible environment such as [Mono]) +- .NET Core **3.1** or later ## Branches ## - [master] for production releases. -- [hybi-00] for older [draft-ietf-hybi-thewebsocketprotocol-00]. No longer maintained. -- [draft75] for even more old [draft-hixie-thewebsocketprotocol-75]. No longer maintained. +- [devel] for edge features. +- [release/x.x.x.x] for releases ## Build ## websocket-sharp is built as a single assembly, **websocket-sharp.dll**. -websocket-sharp is developed with [MonoDevelop]. So a simple way to build is to open **websocket-sharp.sln** and run build for **websocket-sharp project** with any of the build configurations (e.g. `Debug`) in MonoDevelop. +websocket-sharp is developed originally with [MonoDevelop] and now ported to NET Core. So a simple way to build is to open **websocket-sharp.sln** and run build for **websocket-sharp project** with any of the build configurations (e.g. `Debug`). ## Install ## @@ -31,25 +35,12 @@ websocket-sharp is developed with [MonoDevelop]. So a simple way to build is to You should add your websocket-sharp.dll (e.g. `/path/to/websocket-sharp/bin/Debug/websocket-sharp.dll`) to the library references of your project. -If you would like to use that dll in your [Unity] project, you should add it to any folder of your project (e.g. `Assets/Plugins`) in the **Unity Editor**. - ### NuGet Gallery ### -websocket-sharp is available on the [NuGet Gallery], as still a **prerelease** version. - -- [NuGet Gallery: websocket-sharp] - -You can add websocket-sharp to your project with the NuGet Package Manager, by using the following command in the Package Manager Console. - - PM> Install-Package WebSocketSharp -Pre - -### Unity Asset Store ### - -websocket-sharp is available on the Unity Asset Store (Sorry, Not available now). +TBD -- [WebSocket-Sharp for Unity] -It works with **Unity Free**, but there are some limitations: +### Other infos - [Security Sandbox of the Webplayer] (The server is not available in Web Player) - [WebGL Networking] (Not available in WebGL) From 87ee6d0b570dffdf9f6046e9e04db8dcf7792539 Mon Sep 17 00:00:00 2001 From: darknessxk Date: Tue, 2 Feb 2021 10:36:38 -0300 Subject: [PATCH 3/3] Added proper async Tasks instead of using unsupported .NET Core BeginInvoke/EndInvoke --- websocket-sharp/Net/HttpStreamAsyncResult.cs | 301 +- websocket-sharp/WebSocket.cs | 7482 +++++++++--------- 2 files changed, 4063 insertions(+), 3720 deletions(-) diff --git a/websocket-sharp/Net/HttpStreamAsyncResult.cs b/websocket-sharp/Net/HttpStreamAsyncResult.cs index 44189303c..39fd2b495 100644 --- a/websocket-sharp/Net/HttpStreamAsyncResult.cs +++ b/websocket-sharp/Net/HttpStreamAsyncResult.cs @@ -42,143 +42,170 @@ namespace WebSocketSharp.Net { - internal class HttpStreamAsyncResult : IAsyncResult - { - #region Private Fields - - private byte[] _buffer; - private AsyncCallback _callback; - private bool _completed; - private int _count; - private Exception _exception; - private int _offset; - private object _state; - private object _sync; - private int _syncRead; - private ManualResetEvent _waitHandle; - - #endregion - - #region Internal Constructors - - internal HttpStreamAsyncResult (AsyncCallback callback, object state) + internal class HttpStreamAsyncResult : IAsyncResult { - _callback = callback; - _state = state; - _sync = new object (); + #region Private Fields + + private byte[] _buffer; + private AsyncCallback _callback; + private bool _completed; + private int _count; + private Exception _exception; + private int _offset; + private object _state; + private object _sync; + private int _syncRead; + private ManualResetEvent _waitHandle; + + #endregion + + #region Internal Constructors + + internal HttpStreamAsyncResult(AsyncCallback callback, object state) + { + _callback = callback; + _state = state; + _sync = new object(); + } + + #endregion + + #region Internal Properties + + internal byte[] Buffer + { + get + { + return _buffer; + } + + set + { + _buffer = value; + } + } + + internal int Count + { + get + { + return _count; + } + + set + { + _count = value; + } + } + + internal Exception Exception + { + get + { + return _exception; + } + } + + internal bool HasException + { + get + { + return _exception != null; + } + } + + internal int Offset + { + get + { + return _offset; + } + + set + { + _offset = value; + } + } + + internal int SyncRead + { + get + { + return _syncRead; + } + + set + { + _syncRead = value; + } + } + + #endregion + + #region Public Properties + + public object AsyncState + { + get + { + return _state; + } + } + + public WaitHandle AsyncWaitHandle + { + get + { + lock (_sync) + return _waitHandle ?? (_waitHandle = new ManualResetEvent(_completed)); + } + } + + public bool CompletedSynchronously + { + get + { + return _syncRead == _count; + } + } + + public bool IsCompleted + { + get + { + lock (_sync) + return _completed; + } + } + + #endregion + + #region Internal Methods + + internal void Complete() + { + lock (_sync) + { + if (_completed) + return; + + _completed = true; + if (_waitHandle != null) + _waitHandle.Set(); + + if (_callback != null) + { + _callback.BeginInvoke(this, ar => _callback.EndInvoke(ar), null); + } + } + } + + internal void Complete(Exception exception) + { + _exception = exception; + Complete(); + } + + #endregion } - - #endregion - - #region Internal Properties - - internal byte[] Buffer { - get { - return _buffer; - } - - set { - _buffer = value; - } - } - - internal int Count { - get { - return _count; - } - - set { - _count = value; - } - } - - internal Exception Exception { - get { - return _exception; - } - } - - internal bool HasException { - get { - return _exception != null; - } - } - - internal int Offset { - get { - return _offset; - } - - set { - _offset = value; - } - } - - internal int SyncRead { - get { - return _syncRead; - } - - set { - _syncRead = value; - } - } - - #endregion - - #region Public Properties - - public object AsyncState { - get { - return _state; - } - } - - public WaitHandle AsyncWaitHandle { - get { - lock (_sync) - return _waitHandle ?? (_waitHandle = new ManualResetEvent (_completed)); - } - } - - public bool CompletedSynchronously { - get { - return _syncRead == _count; - } - } - - public bool IsCompleted { - get { - lock (_sync) - return _completed; - } - } - - #endregion - - #region Internal Methods - - internal void Complete () - { - lock (_sync) { - if (_completed) - return; - - _completed = true; - if (_waitHandle != null) - _waitHandle.Set (); - - if (_callback != null) - _callback.BeginInvoke (this, ar => _callback.EndInvoke (ar), null); - } - } - - internal void Complete (Exception exception) - { - _exception = exception; - Complete (); - } - - #endregion - } } diff --git a/websocket-sharp/WebSocket.cs b/websocket-sharp/WebSocket.cs index 011dee00d..bae31b3c3 100644 --- a/websocket-sharp/WebSocket.cs +++ b/websocket-sharp/WebSocket.cs @@ -50,4044 +50,4360 @@ using System.Security.Cryptography; using System.Text; using System.Threading; +using System.Threading.Tasks; using WebSocketSharp.Net; using WebSocketSharp.Net.WebSockets; namespace WebSocketSharp { - /// - /// Implements the WebSocket interface. - /// - /// - /// - /// This class provides a set of methods and properties for two-way - /// communication using the WebSocket protocol. - /// - /// - /// The WebSocket protocol is defined in - /// RFC 6455. - /// - /// - public class WebSocket : IDisposable - { - #region Private Fields - - private AuthenticationChallenge _authChallenge; - private string _base64Key; - private bool _client; - private Action _closeContext; - private CompressionMethod _compression; - private WebSocketContext _context; - private CookieCollection _cookies; - private NetworkCredential _credentials; - private bool _emitOnPing; - private bool _enableRedirection; - private string _extensions; - private bool _extensionsRequested; - private object _forMessageEventQueue; - private object _forPing; - private object _forSend; - private object _forState; - private MemoryStream _fragmentsBuffer; - private bool _fragmentsCompressed; - private Opcode _fragmentsOpcode; - private const string _guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; - private Func _handshakeRequestChecker; - private bool _ignoreExtensions; - private bool _inContinuation; - private volatile bool _inMessage; - private volatile Logger _logger; - private static readonly int _maxRetryCountForConnect; - private Action _message; - private Queue _messageEventQueue; - private uint _nonceCount; - private string _origin; - private ManualResetEvent _pongReceived; - private bool _preAuth; - private string _protocol; - private string[] _protocols; - private bool _protocolsRequested; - private NetworkCredential _proxyCredentials; - private Uri _proxyUri; - private volatile WebSocketState _readyState; - private ManualResetEvent _receivingExited; - private int _retryCountForConnect; - private bool _secure; - private ClientSslConfiguration _sslConfig; - private Stream _stream; - private TcpClient _tcpClient; - private Uri _uri; - private const string _version = "13"; - private TimeSpan _waitTime; - - #endregion - - #region Internal Fields - - /// - /// Represents the empty array of used internally. - /// - internal static readonly byte[] EmptyBytes; - /// - /// Represents the length used to determine whether the data should be fragmented in sending. + /// Implements the WebSocket interface. /// /// /// - /// The data will be fragmented if that length is greater than the value of this field. + /// This class provides a set of methods and properties for two-way + /// communication using the WebSocket protocol. /// /// - /// If you would like to change the value, you must set it to a value between 125 and - /// Int32.MaxValue - 14 inclusive. + /// The WebSocket protocol is defined in + /// RFC 6455. /// /// - internal static readonly int FragmentLength; - - /// - /// Represents the random number generator used internally. - /// - internal static readonly RandomNumberGenerator RandomNumber; - - #endregion - - #region Static Constructor - - static WebSocket () + public class WebSocket : IDisposable { - _maxRetryCountForConnect = 10; - EmptyBytes = new byte[0]; - FragmentLength = 1016; - RandomNumber = new RNGCryptoServiceProvider (); - } - - #endregion + #region Private Fields + + private AuthenticationChallenge _authChallenge; + private string _base64Key; + private bool _client; + private Action _closeContext; + private CompressionMethod _compression; + private WebSocketContext _context; + private CookieCollection _cookies; + private NetworkCredential _credentials; + private bool _emitOnPing; + private bool _enableRedirection; + private string _extensions; + private bool _extensionsRequested; + private object _forMessageEventQueue; + private object _forPing; + private object _forSend; + private object _forState; + private MemoryStream _fragmentsBuffer; + private bool _fragmentsCompressed; + private Opcode _fragmentsOpcode; + private const string _guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + private Func _handshakeRequestChecker; + private bool _ignoreExtensions; + private bool _inContinuation; + private volatile bool _inMessage; + private volatile Logger _logger; + private static readonly int _maxRetryCountForConnect; + private Action _message; + private Queue _messageEventQueue; + private uint _nonceCount; + private string _origin; + private ManualResetEvent _pongReceived; + private bool _preAuth; + private string _protocol; + private string[] _protocols; + private bool _protocolsRequested; + private NetworkCredential _proxyCredentials; + private Uri _proxyUri; + private volatile WebSocketState _readyState; + private ManualResetEvent _receivingExited; + private int _retryCountForConnect; + private bool _secure; + private ClientSslConfiguration _sslConfig; + private Stream _stream; + private TcpClient _tcpClient; + private Uri _uri; + private const string _version = "13"; + private TimeSpan _waitTime; + + #endregion + + #region Internal Fields + + /// + /// Represents the empty array of used internally. + /// + internal static readonly byte[] EmptyBytes; + + /// + /// Represents the length used to determine whether the data should be fragmented in sending. + /// + /// + /// + /// The data will be fragmented if that length is greater than the value of this field. + /// + /// + /// If you would like to change the value, you must set it to a value between 125 and + /// Int32.MaxValue - 14 inclusive. + /// + /// + internal static readonly int FragmentLength; + + /// + /// Represents the random number generator used internally. + /// + internal static readonly RandomNumberGenerator RandomNumber; + + #endregion + + #region Static Constructor + + static WebSocket() + { + _maxRetryCountForConnect = 10; + EmptyBytes = new byte[0]; + FragmentLength = 1016; + RandomNumber = new RNGCryptoServiceProvider(); + } - #region Internal Constructors + #endregion - // As server - internal WebSocket (HttpListenerWebSocketContext context, string protocol) - { - _context = context; - _protocol = protocol; + #region Internal Constructors - _closeContext = context.Close; - _logger = context.Log; - _message = messages; - _secure = context.IsSecureConnection; - _stream = context.Stream; - _waitTime = TimeSpan.FromSeconds (1); + // As server + internal WebSocket(HttpListenerWebSocketContext context, string protocol) + { + _context = context; + _protocol = protocol; - init (); - } + _closeContext = context.Close; + _logger = context.Log; + _message = messages; + _secure = context.IsSecureConnection; + _stream = context.Stream; + _waitTime = TimeSpan.FromSeconds(1); - // As server - internal WebSocket (TcpListenerWebSocketContext context, string protocol) - { - _context = context; - _protocol = protocol; + init(); + } - _closeContext = context.Close; - _logger = context.Log; - _message = messages; - _secure = context.IsSecureConnection; - _stream = context.Stream; - _waitTime = TimeSpan.FromSeconds (1); + // As server + internal WebSocket(TcpListenerWebSocketContext context, string protocol) + { + _context = context; + _protocol = protocol; - init (); - } + _closeContext = context.Close; + _logger = context.Log; + _message = messages; + _secure = context.IsSecureConnection; + _stream = context.Stream; + _waitTime = TimeSpan.FromSeconds(1); - #endregion + init(); + } - #region Public Constructors + #endregion + + #region Public Constructors + + /// + /// Initializes a new instance of the class with + /// and optionally . + /// + /// + /// + /// A that specifies the URL to which to connect. + /// + /// + /// The scheme of the URL must be ws or wss. + /// + /// + /// The new instance uses a secure connection if the scheme is wss. + /// + /// + /// + /// + /// An array of that specifies the names of + /// the subprotocols if necessary. + /// + /// + /// Each value of the array must be a token defined in + /// + /// RFC 2616. + /// + /// + /// + /// is . + /// + /// + /// + /// is an empty string. + /// + /// + /// -or- + /// + /// + /// is an invalid WebSocket URL string. + /// + /// + /// -or- + /// + /// + /// contains a value that is not a token. + /// + /// + /// -or- + /// + /// + /// contains a value twice. + /// + /// + public WebSocket(string url, params string[] protocols) + { + if (url == null) + throw new ArgumentNullException("url"); + + if (url.Length == 0) + throw new ArgumentException("An empty string.", "url"); + + string msg; + if (!url.TryCreateWebSocketUri(out _uri, out msg)) + throw new ArgumentException(msg, "url"); + + if (protocols != null && protocols.Length > 0) + { + if (!checkProtocols(protocols, out msg)) + throw new ArgumentException(msg, "protocols"); + + _protocols = protocols; + } - /// - /// Initializes a new instance of the class with - /// and optionally . - /// - /// - /// - /// A that specifies the URL to which to connect. - /// - /// - /// The scheme of the URL must be ws or wss. - /// - /// - /// The new instance uses a secure connection if the scheme is wss. - /// - /// - /// - /// - /// An array of that specifies the names of - /// the subprotocols if necessary. - /// - /// - /// Each value of the array must be a token defined in - /// - /// RFC 2616. - /// - /// - /// - /// is . - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// is an invalid WebSocket URL string. - /// - /// - /// -or- - /// - /// - /// contains a value that is not a token. - /// - /// - /// -or- - /// - /// - /// contains a value twice. - /// - /// - public WebSocket (string url, params string[] protocols) - { - if (url == null) - throw new ArgumentNullException ("url"); + _base64Key = CreateBase64Key(); + _client = true; + _logger = new Logger(); + _message = messagec; + _secure = _uri.Scheme == "wss"; + _waitTime = TimeSpan.FromSeconds(5); - if (url.Length == 0) - throw new ArgumentException ("An empty string.", "url"); + init(); + } - string msg; - if (!url.TryCreateWebSocketUri (out _uri, out msg)) - throw new ArgumentException (msg, "url"); + #endregion - if (protocols != null && protocols.Length > 0) { - if (!checkProtocols (protocols, out msg)) - throw new ArgumentException (msg, "protocols"); + #region Internal Properties - _protocols = protocols; - } + internal CookieCollection CookieCollection + { + get + { + return _cookies; + } + } - _base64Key = CreateBase64Key (); - _client = true; - _logger = new Logger (); - _message = messagec; - _secure = _uri.Scheme == "wss"; - _waitTime = TimeSpan.FromSeconds (5); + // As server + internal Func CustomHandshakeRequestChecker + { + get + { + return _handshakeRequestChecker; + } - init (); - } + set + { + _handshakeRequestChecker = value; + } + } - #endregion + internal bool HasMessage + { + get + { + lock (_forMessageEventQueue) + return _messageEventQueue.Count > 0; + } + } - #region Internal Properties + // As server + internal bool IgnoreExtensions + { + get + { + return _ignoreExtensions; + } - internal CookieCollection CookieCollection { - get { - return _cookies; - } - } + set + { + _ignoreExtensions = value; + } + } - // As server - internal Func CustomHandshakeRequestChecker { - get { - return _handshakeRequestChecker; - } + internal bool IsConnected + { + get + { + return _readyState == WebSocketState.Open || _readyState == WebSocketState.Closing; + } + } - set { - _handshakeRequestChecker = value; - } - } + #endregion + + #region Public Properties + + /// + /// Gets or sets the compression method used to compress a message. + /// + /// + /// The set operation does nothing if the connection has already been + /// established or it is closing. + /// + /// + /// + /// One of the enum values. + /// + /// + /// It specifies the compression method used to compress a message. + /// + /// + /// The default value is . + /// + /// + /// + /// The set operation is not available if this instance is not a client. + /// + public CompressionMethod Compression + { + get + { + return _compression; + } - internal bool HasMessage { - get { - lock (_forMessageEventQueue) - return _messageEventQueue.Count > 0; - } - } + set + { + string msg = null; + + if (!_client) + { + msg = "This instance is not a client."; + throw new InvalidOperationException(msg); + } + + if (!canSet(out msg)) + { + _logger.Warn(msg); + return; + } + + lock (_forState) + { + if (!canSet(out msg)) + { + _logger.Warn(msg); + return; + } + + _compression = value; + } + } + } - // As server - internal bool IgnoreExtensions { - get { - return _ignoreExtensions; - } + /// + /// Gets the HTTP cookies included in the handshake request/response. + /// + /// + /// + /// An + /// instance. + /// + /// + /// It provides an enumerator which supports the iteration over + /// the collection of the cookies. + /// + /// + public IEnumerable Cookies + { + get + { + lock (_cookies.SyncRoot) + { + foreach (Cookie cookie in _cookies) + yield return cookie; + } + } + } - set { - _ignoreExtensions = value; - } - } + /// + /// Gets the credentials for the HTTP authentication (Basic/Digest). + /// + /// + /// + /// A that represents the credentials + /// used to authenticate the client. + /// + /// + /// The default value is . + /// + /// + public NetworkCredential Credentials + { + get + { + return _credentials; + } + } - internal bool IsConnected { - get { - return _readyState == WebSocketState.Open || _readyState == WebSocketState.Closing; - } - } + /// + /// Gets or sets a value indicating whether a event + /// is emitted when a ping is received. + /// + /// + /// + /// true if this instance emits a event + /// when receives a ping; otherwise, false. + /// + /// + /// The default value is false. + /// + /// + public bool EmitOnPing + { + get + { + return _emitOnPing; + } - #endregion + set + { + _emitOnPing = value; + } + } - #region Public Properties + /// + /// Gets or sets a value indicating whether the URL redirection for + /// the handshake request is allowed. + /// + /// + /// The set operation does nothing if the connection has already been + /// established or it is closing. + /// + /// + /// + /// true if this instance allows the URL redirection for + /// the handshake request; otherwise, false. + /// + /// + /// The default value is false. + /// + /// + /// + /// The set operation is not available if this instance is not a client. + /// + public bool EnableRedirection + { + get + { + return _enableRedirection; + } - /// - /// Gets or sets the compression method used to compress a message. - /// - /// - /// The set operation does nothing if the connection has already been - /// established or it is closing. - /// - /// - /// - /// One of the enum values. - /// - /// - /// It specifies the compression method used to compress a message. - /// - /// - /// The default value is . - /// - /// - /// - /// The set operation is not available if this instance is not a client. - /// - public CompressionMethod Compression { - get { - return _compression; - } + set + { + string msg = null; + + if (!_client) + { + msg = "This instance is not a client."; + throw new InvalidOperationException(msg); + } + + if (!canSet(out msg)) + { + _logger.Warn(msg); + return; + } + + lock (_forState) + { + if (!canSet(out msg)) + { + _logger.Warn(msg); + return; + } + + _enableRedirection = value; + } + } + } - set { - string msg = null; + /// + /// Gets the extensions selected by server. + /// + /// + /// A that will be a list of the extensions + /// negotiated between client and server, or an empty string if + /// not specified or selected. + /// + public string Extensions + { + get + { + return _extensions ?? String.Empty; + } + } - if (!_client) { - msg = "This instance is not a client."; - throw new InvalidOperationException (msg); + /// + /// Gets a value indicating whether the connection is alive. + /// + /// + /// The get operation returns the value by using a ping/pong + /// if the current state of the connection is Open. + /// + /// + /// true if the connection is alive; otherwise, false. + /// + public bool IsAlive + { + get + { + return ping(EmptyBytes); + } } - if (!canSet (out msg)) { - _logger.Warn (msg); - return; + /// + /// Gets a value indicating whether a secure connection is used. + /// + /// + /// true if this instance uses a secure connection; otherwise, + /// false. + /// + public bool IsSecure + { + get + { + return _secure; + } } - lock (_forState) { - if (!canSet (out msg)) { - _logger.Warn (msg); - return; - } + /// + /// Gets the logging function. + /// + /// + /// The default logging level is . + /// + /// + /// A that provides the logging function. + /// + public Logger Log + { + get + { + return _logger; + } - _compression = value; + internal set + { + _logger = value; + } } - } - } - /// - /// Gets the HTTP cookies included in the handshake request/response. - /// - /// - /// - /// An - /// instance. - /// - /// - /// It provides an enumerator which supports the iteration over - /// the collection of the cookies. - /// - /// - public IEnumerable Cookies { - get { - lock (_cookies.SyncRoot) { - foreach (Cookie cookie in _cookies) - yield return cookie; - } - } - } + /// + /// Gets or sets the value of the HTTP Origin header to send with + /// the handshake request. + /// + /// + /// + /// The HTTP Origin header is defined in + /// + /// Section 7 of RFC 6454. + /// + /// + /// This instance sends the Origin header if this property has any. + /// + /// + /// The set operation does nothing if the connection has already been + /// established or it is closing. + /// + /// + /// + /// + /// A that represents the value of the Origin + /// header to send. + /// + /// + /// The syntax is <scheme>://<host>[:<port>]. + /// + /// + /// The default value is . + /// + /// + /// + /// The set operation is not available if this instance is not a client. + /// + /// + /// + /// The value specified for a set operation is not an absolute URI string. + /// + /// + /// -or- + /// + /// + /// The value specified for a set operation includes the path segments. + /// + /// + public string Origin + { + get + { + return _origin; + } - /// - /// Gets the credentials for the HTTP authentication (Basic/Digest). - /// - /// - /// - /// A that represents the credentials - /// used to authenticate the client. - /// - /// - /// The default value is . - /// - /// - public NetworkCredential Credentials { - get { - return _credentials; - } - } + set + { + string msg = null; + + if (!_client) + { + msg = "This instance is not a client."; + throw new InvalidOperationException(msg); + } + + if (!value.IsNullOrEmpty()) + { + Uri uri; + if (!Uri.TryCreate(value, UriKind.Absolute, out uri)) + { + msg = "Not an absolute URI string."; + throw new ArgumentException(msg, "value"); + } + + if (uri.Segments.Length > 1) + { + msg = "It includes the path segments."; + throw new ArgumentException(msg, "value"); + } + } + + if (!canSet(out msg)) + { + _logger.Warn(msg); + return; + } + + lock (_forState) + { + if (!canSet(out msg)) + { + _logger.Warn(msg); + return; + } + + _origin = !value.IsNullOrEmpty() ? value.TrimEnd('/') : value; + } + } + } - /// - /// Gets or sets a value indicating whether a event - /// is emitted when a ping is received. - /// - /// - /// - /// true if this instance emits a event - /// when receives a ping; otherwise, false. - /// - /// - /// The default value is false. - /// - /// - public bool EmitOnPing { - get { - return _emitOnPing; - } - - set { - _emitOnPing = value; - } - } + /// + /// Gets the name of subprotocol selected by the server. + /// + /// + /// + /// A that will be one of the names of + /// subprotocols specified by client. + /// + /// + /// An empty string if not specified or selected. + /// + /// + public string Protocol + { + get + { + return _protocol ?? String.Empty; + } - /// - /// Gets or sets a value indicating whether the URL redirection for - /// the handshake request is allowed. - /// - /// - /// The set operation does nothing if the connection has already been - /// established or it is closing. - /// - /// - /// - /// true if this instance allows the URL redirection for - /// the handshake request; otherwise, false. - /// - /// - /// The default value is false. - /// - /// - /// - /// The set operation is not available if this instance is not a client. - /// - public bool EnableRedirection { - get { - return _enableRedirection; - } + internal set + { + _protocol = value; + } + } - set { - string msg = null; + /// + /// Gets the current state of the connection. + /// + /// + /// + /// One of the enum values. + /// + /// + /// It indicates the current state of the connection. + /// + /// + /// The default value is . + /// + /// + public WebSocketState ReadyState + { + get + { + return _readyState; + } + } - if (!_client) { - msg = "This instance is not a client."; - throw new InvalidOperationException (msg); + /// + /// Gets the configuration for secure connection. + /// + /// + /// This configuration will be referenced when attempts to connect, + /// so it must be configured before any connect method is called. + /// + /// + /// A that represents + /// the configuration used to establish a secure connection. + /// + /// + /// + /// This instance is not a client. + /// + /// + /// This instance does not use a secure connection. + /// + /// + public ClientSslConfiguration SslConfiguration + { + get + { + if (!_client) + { + var msg = "This instance is not a client."; + throw new InvalidOperationException(msg); + } + + if (!_secure) + { + var msg = "This instance does not use a secure connection."; + throw new InvalidOperationException(msg); + } + + return getSslConfiguration(); + } } - if (!canSet (out msg)) { - _logger.Warn (msg); - return; + /// + /// Gets the URL to which to connect. + /// + /// + /// A that represents the URL to which to connect. + /// + public Uri Url + { + get + { + return _client ? _uri : _context.RequestUri; + } } - lock (_forState) { - if (!canSet (out msg)) { - _logger.Warn (msg); - return; - } + /// + /// Gets or sets the time to wait for the response to the ping or close. + /// + /// + /// The set operation does nothing if the connection has already been + /// established or it is closing. + /// + /// + /// + /// A to wait for the response. + /// + /// + /// The default value is the same as 5 seconds if this instance is + /// a client. + /// + /// + /// + /// The value specified for a set operation is zero or less. + /// + public TimeSpan WaitTime + { + get + { + return _waitTime; + } - _enableRedirection = value; + set + { + if (value <= TimeSpan.Zero) + throw new ArgumentOutOfRangeException("value", "Zero or less."); + + string msg; + if (!canSet(out msg)) + { + _logger.Warn(msg); + return; + } + + lock (_forState) + { + if (!canSet(out msg)) + { + _logger.Warn(msg); + return; + } + + _waitTime = value; + } + } } - } - } - - /// - /// Gets the extensions selected by server. - /// - /// - /// A that will be a list of the extensions - /// negotiated between client and server, or an empty string if - /// not specified or selected. - /// - public string Extensions { - get { - return _extensions ?? String.Empty; - } - } - /// - /// Gets a value indicating whether the connection is alive. - /// - /// - /// The get operation returns the value by using a ping/pong - /// if the current state of the connection is Open. - /// - /// - /// true if the connection is alive; otherwise, false. - /// - public bool IsAlive { - get { - return ping (EmptyBytes); - } - } + #endregion - /// - /// Gets a value indicating whether a secure connection is used. - /// - /// - /// true if this instance uses a secure connection; otherwise, - /// false. - /// - public bool IsSecure { - get { - return _secure; - } - } + #region Public Events - /// - /// Gets the logging function. - /// - /// - /// The default logging level is . - /// - /// - /// A that provides the logging function. - /// - public Logger Log { - get { - return _logger; - } - - internal set { - _logger = value; - } - } + /// + /// Occurs when the WebSocket connection has been closed. + /// + public event EventHandler OnClose; - /// - /// Gets or sets the value of the HTTP Origin header to send with - /// the handshake request. - /// - /// - /// - /// The HTTP Origin header is defined in - /// - /// Section 7 of RFC 6454. - /// - /// - /// This instance sends the Origin header if this property has any. - /// - /// - /// The set operation does nothing if the connection has already been - /// established or it is closing. - /// - /// - /// - /// - /// A that represents the value of the Origin - /// header to send. - /// - /// - /// The syntax is <scheme>://<host>[:<port>]. - /// - /// - /// The default value is . - /// - /// - /// - /// The set operation is not available if this instance is not a client. - /// - /// - /// - /// The value specified for a set operation is not an absolute URI string. - /// - /// - /// -or- - /// - /// - /// The value specified for a set operation includes the path segments. - /// - /// - public string Origin { - get { - return _origin; - } + /// + /// Occurs when the gets an error. + /// + public event EventHandler OnError; - set { - string msg = null; + /// + /// Occurs when the receives a message. + /// + public event EventHandler OnMessage; - if (!_client) { - msg = "This instance is not a client."; - throw new InvalidOperationException (msg); - } + /// + /// Occurs when the WebSocket connection has been established. + /// + public event EventHandler OnOpen; - if (!value.IsNullOrEmpty ()) { - Uri uri; - if (!Uri.TryCreate (value, UriKind.Absolute, out uri)) { - msg = "Not an absolute URI string."; - throw new ArgumentException (msg, "value"); - } + #endregion - if (uri.Segments.Length > 1) { - msg = "It includes the path segments."; - throw new ArgumentException (msg, "value"); - } - } + #region Private Methods - if (!canSet (out msg)) { - _logger.Warn (msg); - return; - } + // As server + private bool accept() + { + if (_readyState == WebSocketState.Open) + { + var msg = "The handshake request has already been accepted."; + _logger.Warn(msg); - lock (_forState) { - if (!canSet (out msg)) { - _logger.Warn (msg); - return; - } + return false; + } - _origin = !value.IsNullOrEmpty () ? value.TrimEnd ('/') : value; - } - } - } + lock (_forState) + { + if (_readyState == WebSocketState.Open) + { + var msg = "The handshake request has already been accepted."; + _logger.Warn(msg); - /// - /// Gets the name of subprotocol selected by the server. - /// - /// - /// - /// A that will be one of the names of - /// subprotocols specified by client. - /// - /// - /// An empty string if not specified or selected. - /// - /// - public string Protocol { - get { - return _protocol ?? String.Empty; - } - - internal set { - _protocol = value; - } - } + return false; + } - /// - /// Gets the current state of the connection. - /// - /// - /// - /// One of the enum values. - /// - /// - /// It indicates the current state of the connection. - /// - /// - /// The default value is . - /// - /// - public WebSocketState ReadyState { - get { - return _readyState; - } - } + if (_readyState == WebSocketState.Closing) + { + var msg = "The close process has set in."; + _logger.Error(msg); - /// - /// Gets the configuration for secure connection. - /// - /// - /// This configuration will be referenced when attempts to connect, - /// so it must be configured before any connect method is called. - /// - /// - /// A that represents - /// the configuration used to establish a secure connection. - /// - /// - /// - /// This instance is not a client. - /// - /// - /// This instance does not use a secure connection. - /// - /// - public ClientSslConfiguration SslConfiguration { - get { - if (!_client) { - var msg = "This instance is not a client."; - throw new InvalidOperationException (msg); - } + msg = "An interruption has occurred while attempting to accept."; + error(msg, null); - if (!_secure) { - var msg = "This instance does not use a secure connection."; - throw new InvalidOperationException (msg); - } + return false; + } - return getSslConfiguration (); - } - } + if (_readyState == WebSocketState.Closed) + { + var msg = "The connection has been closed."; + _logger.Error(msg); - /// - /// Gets the URL to which to connect. - /// - /// - /// A that represents the URL to which to connect. - /// - public Uri Url { - get { - return _client ? _uri : _context.RequestUri; - } - } + msg = "An interruption has occurred while attempting to accept."; + error(msg, null); - /// - /// Gets or sets the time to wait for the response to the ping or close. - /// - /// - /// The set operation does nothing if the connection has already been - /// established or it is closing. - /// - /// - /// - /// A to wait for the response. - /// - /// - /// The default value is the same as 5 seconds if this instance is - /// a client. - /// - /// - /// - /// The value specified for a set operation is zero or less. - /// - public TimeSpan WaitTime { - get { - return _waitTime; - } - - set { - if (value <= TimeSpan.Zero) - throw new ArgumentOutOfRangeException ("value", "Zero or less."); - - string msg; - if (!canSet (out msg)) { - _logger.Warn (msg); - return; - } - - lock (_forState) { - if (!canSet (out msg)) { - _logger.Warn (msg); - return; - } - - _waitTime = value; - } - } - } + return false; + } - #endregion + try + { + if (!acceptHandshake()) + return false; + } + catch (Exception ex) + { + _logger.Fatal(ex.Message); + _logger.Debug(ex.ToString()); - #region Public Events + var msg = "An exception has occurred while attempting to accept."; + fatal(msg, ex); - /// - /// Occurs when the WebSocket connection has been closed. - /// - public event EventHandler OnClose; + return false; + } - /// - /// Occurs when the gets an error. - /// - public event EventHandler OnError; + _readyState = WebSocketState.Open; + return true; + } + } - /// - /// Occurs when the receives a message. - /// - public event EventHandler OnMessage; + // As server + private bool acceptHandshake() + { + _logger.Debug( + String.Format( + "A handshake request from {0}:\n{1}", _context.UserEndPoint, _context + ) + ); - /// - /// Occurs when the WebSocket connection has been established. - /// - public event EventHandler OnOpen; + string msg; + if (!checkHandshakeRequest(_context, out msg)) + { + _logger.Error(msg); - #endregion + refuseHandshake( + CloseStatusCode.ProtocolError, + "A handshake error has occurred while attempting to accept." + ); - #region Private Methods + return false; + } - // As server - private bool accept () - { - if (_readyState == WebSocketState.Open) { - var msg = "The handshake request has already been accepted."; - _logger.Warn (msg); + if (!customCheckHandshakeRequest(_context, out msg)) + { + _logger.Error(msg); - return false; - } + refuseHandshake( + CloseStatusCode.PolicyViolation, + "A handshake error has occurred while attempting to accept." + ); - lock (_forState) { - if (_readyState == WebSocketState.Open) { - var msg = "The handshake request has already been accepted."; - _logger.Warn (msg); + return false; + } - return false; - } + _base64Key = _context.Headers["Sec-WebSocket-Key"]; - if (_readyState == WebSocketState.Closing) { - var msg = "The close process has set in."; - _logger.Error (msg); + if (_protocol != null) + { + var vals = _context.SecWebSocketProtocols; + processSecWebSocketProtocolClientHeader(vals); + } - msg = "An interruption has occurred while attempting to accept."; - error (msg, null); + if (!_ignoreExtensions) + { + var val = _context.Headers["Sec-WebSocket-Extensions"]; + processSecWebSocketExtensionsClientHeader(val); + } - return false; + return sendHttpResponse(createHandshakeResponse()); } - if (_readyState == WebSocketState.Closed) { - var msg = "The connection has been closed."; - _logger.Error (msg); + private bool canSet(out string message) + { + message = null; - msg = "An interruption has occurred while attempting to accept."; - error (msg, null); - - return false; - } - - try { - if (!acceptHandshake ()) - return false; - } - catch (Exception ex) { - _logger.Fatal (ex.Message); - _logger.Debug (ex.ToString ()); + if (_readyState == WebSocketState.Open) + { + message = "The connection has already been established."; + return false; + } - var msg = "An exception has occurred while attempting to accept."; - fatal (msg, ex); + if (_readyState == WebSocketState.Closing) + { + message = "The connection is closing."; + return false; + } - return false; + return true; } - _readyState = WebSocketState.Open; - return true; - } - } - - // As server - private bool acceptHandshake () - { - _logger.Debug ( - String.Format ( - "A handshake request from {0}:\n{1}", _context.UserEndPoint, _context + // As server + private bool checkHandshakeRequest( + WebSocketContext context, out string message ) - ); + { + message = null; - string msg; - if (!checkHandshakeRequest (_context, out msg)) { - _logger.Error (msg); + if (!context.IsWebSocketRequest) + { + message = "Not a handshake request."; + return false; + } - refuseHandshake ( - CloseStatusCode.ProtocolError, - "A handshake error has occurred while attempting to accept." - ); + if (context.RequestUri == null) + { + message = "It specifies an invalid Request-URI."; + return false; + } - return false; - } + var headers = context.Headers; - if (!customCheckHandshakeRequest (_context, out msg)) { - _logger.Error (msg); + var key = headers["Sec-WebSocket-Key"]; + if (key == null) + { + message = "It includes no Sec-WebSocket-Key header."; + return false; + } - refuseHandshake ( - CloseStatusCode.PolicyViolation, - "A handshake error has occurred while attempting to accept." - ); + if (key.Length == 0) + { + message = "It includes an invalid Sec-WebSocket-Key header."; + return false; + } - return false; - } + var version = headers["Sec-WebSocket-Version"]; + if (version == null) + { + message = "It includes no Sec-WebSocket-Version header."; + return false; + } - _base64Key = _context.Headers["Sec-WebSocket-Key"]; + if (version != _version) + { + message = "It includes an invalid Sec-WebSocket-Version header."; + return false; + } - if (_protocol != null) { - var vals = _context.SecWebSocketProtocols; - processSecWebSocketProtocolClientHeader (vals); - } - - if (!_ignoreExtensions) { - var val = _context.Headers["Sec-WebSocket-Extensions"]; - processSecWebSocketExtensionsClientHeader (val); - } + var protocol = headers["Sec-WebSocket-Protocol"]; + if (protocol != null && protocol.Length == 0) + { + message = "It includes an invalid Sec-WebSocket-Protocol header."; + return false; + } - return sendHttpResponse (createHandshakeResponse ()); - } + if (!_ignoreExtensions) + { + var extensions = headers["Sec-WebSocket-Extensions"]; + if (extensions != null && extensions.Length == 0) + { + message = "It includes an invalid Sec-WebSocket-Extensions header."; + return false; + } + } - private bool canSet (out string message) - { - message = null; + return true; + } - if (_readyState == WebSocketState.Open) { - message = "The connection has already been established."; - return false; - } + // As client + private bool checkHandshakeResponse(HttpResponse response, out string message) + { + message = null; - if (_readyState == WebSocketState.Closing) { - message = "The connection is closing."; - return false; - } + if (response.IsRedirect) + { + message = "Indicates the redirection."; + return false; + } - return true; - } + if (response.IsUnauthorized) + { + message = "Requires the authentication."; + return false; + } - // As server - private bool checkHandshakeRequest ( - WebSocketContext context, out string message - ) - { - message = null; - - if (!context.IsWebSocketRequest) { - message = "Not a handshake request."; - return false; - } - - if (context.RequestUri == null) { - message = "It specifies an invalid Request-URI."; - return false; - } - - var headers = context.Headers; - - var key = headers["Sec-WebSocket-Key"]; - if (key == null) { - message = "It includes no Sec-WebSocket-Key header."; - return false; - } - - if (key.Length == 0) { - message = "It includes an invalid Sec-WebSocket-Key header."; - return false; - } - - var version = headers["Sec-WebSocket-Version"]; - if (version == null) { - message = "It includes no Sec-WebSocket-Version header."; - return false; - } - - if (version != _version) { - message = "It includes an invalid Sec-WebSocket-Version header."; - return false; - } - - var protocol = headers["Sec-WebSocket-Protocol"]; - if (protocol != null && protocol.Length == 0) { - message = "It includes an invalid Sec-WebSocket-Protocol header."; - return false; - } - - if (!_ignoreExtensions) { - var extensions = headers["Sec-WebSocket-Extensions"]; - if (extensions != null && extensions.Length == 0) { - message = "It includes an invalid Sec-WebSocket-Extensions header."; - return false; - } - } - - return true; - } + if (!response.IsWebSocketResponse) + { + message = "Not a WebSocket handshake response."; + return false; + } - // As client - private bool checkHandshakeResponse (HttpResponse response, out string message) - { - message = null; - - if (response.IsRedirect) { - message = "Indicates the redirection."; - return false; - } - - if (response.IsUnauthorized) { - message = "Requires the authentication."; - return false; - } - - if (!response.IsWebSocketResponse) { - message = "Not a WebSocket handshake response."; - return false; - } - - var headers = response.Headers; - if (!validateSecWebSocketAcceptHeader (headers["Sec-WebSocket-Accept"])) { - message = "Includes no Sec-WebSocket-Accept header, or it has an invalid value."; - return false; - } - - if (!validateSecWebSocketProtocolServerHeader (headers["Sec-WebSocket-Protocol"])) { - message = "Includes no Sec-WebSocket-Protocol header, or it has an invalid value."; - return false; - } - - if (!validateSecWebSocketExtensionsServerHeader (headers["Sec-WebSocket-Extensions"])) { - message = "Includes an invalid Sec-WebSocket-Extensions header."; - return false; - } - - if (!validateSecWebSocketVersionServerHeader (headers["Sec-WebSocket-Version"])) { - message = "Includes an invalid Sec-WebSocket-Version header."; - return false; - } - - return true; - } + var headers = response.Headers; + if (!validateSecWebSocketAcceptHeader(headers["Sec-WebSocket-Accept"])) + { + message = "Includes no Sec-WebSocket-Accept header, or it has an invalid value."; + return false; + } - private static bool checkProtocols (string[] protocols, out string message) - { - message = null; + if (!validateSecWebSocketProtocolServerHeader(headers["Sec-WebSocket-Protocol"])) + { + message = "Includes no Sec-WebSocket-Protocol header, or it has an invalid value."; + return false; + } - Func cond = protocol => protocol.IsNullOrEmpty () - || !protocol.IsToken (); + if (!validateSecWebSocketExtensionsServerHeader(headers["Sec-WebSocket-Extensions"])) + { + message = "Includes an invalid Sec-WebSocket-Extensions header."; + return false; + } - if (protocols.Contains (cond)) { - message = "It contains a value that is not a token."; - return false; - } + if (!validateSecWebSocketVersionServerHeader(headers["Sec-WebSocket-Version"])) + { + message = "Includes an invalid Sec-WebSocket-Version header."; + return false; + } - if (protocols.ContainsTwice ()) { - message = "It contains a value twice."; - return false; - } + return true; + } - return true; - } + private static bool checkProtocols(string[] protocols, out string message) + { + message = null; - private bool checkReceivedFrame (WebSocketFrame frame, out string message) - { - message = null; - - var masked = frame.IsMasked; - if (_client && masked) { - message = "A frame from the server is masked."; - return false; - } - - if (!_client && !masked) { - message = "A frame from a client is not masked."; - return false; - } - - if (_inContinuation && frame.IsData) { - message = "A data frame has been received while receiving continuation frames."; - return false; - } - - if (frame.IsCompressed && _compression == CompressionMethod.None) { - message = "A compressed frame has been received without any agreement for it."; - return false; - } - - if (frame.Rsv2 == Rsv.On) { - message = "The RSV2 of a frame is non-zero without any negotiation for it."; - return false; - } - - if (frame.Rsv3 == Rsv.On) { - message = "The RSV3 of a frame is non-zero without any negotiation for it."; - return false; - } - - return true; - } + Func cond = protocol => protocol.IsNullOrEmpty() + || !protocol.IsToken(); - private void close (ushort code, string reason) - { - if (_readyState == WebSocketState.Closing) { - _logger.Info ("The closing is already in progress."); - return; - } - - if (_readyState == WebSocketState.Closed) { - _logger.Info ("The connection has already been closed."); - return; - } - - if (code == 1005) { // == no status - close (PayloadData.Empty, true, true, false); - return; - } - - var send = !code.IsReserved (); - close (new PayloadData (code, reason), send, send, false); - } + if (protocols.Contains(cond)) + { + message = "It contains a value that is not a token."; + return false; + } - private void close ( - PayloadData payloadData, bool send, bool receive, bool received - ) - { - lock (_forState) { - if (_readyState == WebSocketState.Closing) { - _logger.Info ("The closing is already in progress."); - return; - } + if (protocols.ContainsTwice()) + { + message = "It contains a value twice."; + return false; + } - if (_readyState == WebSocketState.Closed) { - _logger.Info ("The connection has already been closed."); - return; + return true; } - send = send && _readyState == WebSocketState.Open; - receive = send && receive; - - _readyState = WebSocketState.Closing; - } + private bool checkReceivedFrame(WebSocketFrame frame, out string message) + { + message = null; - _logger.Trace ("Begin closing the connection."); + var masked = frame.IsMasked; + if (_client && masked) + { + message = "A frame from the server is masked."; + return false; + } - var res = closeHandshake (payloadData, send, receive, received); - releaseResources (); + if (!_client && !masked) + { + message = "A frame from a client is not masked."; + return false; + } - _logger.Trace ("End closing the connection."); + if (_inContinuation && frame.IsData) + { + message = "A data frame has been received while receiving continuation frames."; + return false; + } - _readyState = WebSocketState.Closed; + if (frame.IsCompressed && _compression == CompressionMethod.None) + { + message = "A compressed frame has been received without any agreement for it."; + return false; + } - var e = new CloseEventArgs (payloadData, res); + if (frame.Rsv2 == Rsv.On) + { + message = "The RSV2 of a frame is non-zero without any negotiation for it."; + return false; + } - try { - OnClose.Emit (this, e); - } - catch (Exception ex) { - _logger.Error (ex.Message); - _logger.Debug (ex.ToString ()); - } - } + if (frame.Rsv3 == Rsv.On) + { + message = "The RSV3 of a frame is non-zero without any negotiation for it."; + return false; + } - private void closeAsync (ushort code, string reason) - { - if (_readyState == WebSocketState.Closing) { - _logger.Info ("The closing is already in progress."); - return; - } - - if (_readyState == WebSocketState.Closed) { - _logger.Info ("The connection has already been closed."); - return; - } - - if (code == 1005) { // == no status - closeAsync (PayloadData.Empty, true, true, false); - return; - } - - var send = !code.IsReserved (); - closeAsync (new PayloadData (code, reason), send, send, false); - } + return true; + } - private void closeAsync ( - PayloadData payloadData, bool send, bool receive, bool received - ) - { - Action closer = close; - closer.BeginInvoke ( - payloadData, send, receive, received, ar => closer.EndInvoke (ar), null - ); - } + private void close(ushort code, string reason) + { + if (_readyState == WebSocketState.Closing) + { + _logger.Info("The closing is already in progress."); + return; + } - private bool closeHandshake (byte[] frameAsBytes, bool receive, bool received) - { - var sent = frameAsBytes != null && sendBytes (frameAsBytes); + if (_readyState == WebSocketState.Closed) + { + _logger.Info("The connection has already been closed."); + return; + } - var wait = !received && sent && receive && _receivingExited != null; - if (wait) - received = _receivingExited.WaitOne (_waitTime); + if (code == 1005) + { // == no status + close(PayloadData.Empty, true, true, false); + return; + } - var ret = sent && received; + var send = !code.IsReserved(); + close(new PayloadData(code, reason), send, send, false); + } - _logger.Debug ( - String.Format ( - "Was clean?: {0}\n sent: {1}\n received: {2}", ret, sent, received + private void close( + PayloadData payloadData, bool send, bool receive, bool received ) - ); + { + lock (_forState) + { + if (_readyState == WebSocketState.Closing) + { + _logger.Info("The closing is already in progress."); + return; + } + + if (_readyState == WebSocketState.Closed) + { + _logger.Info("The connection has already been closed."); + return; + } + + send = send && _readyState == WebSocketState.Open; + receive = send && receive; + + _readyState = WebSocketState.Closing; + } - return ret; - } + _logger.Trace("Begin closing the connection."); - private bool closeHandshake ( - PayloadData payloadData, bool send, bool receive, bool received - ) - { - var sent = false; - if (send) { - var frame = WebSocketFrame.CreateCloseFrame (payloadData, _client); - sent = sendBytes (frame.ToArray ()); + var res = closeHandshake(payloadData, send, receive, received); + releaseResources(); - if (_client) - frame.Unmask (); - } + _logger.Trace("End closing the connection."); - var wait = !received && sent && receive && _receivingExited != null; - if (wait) - received = _receivingExited.WaitOne (_waitTime); + _readyState = WebSocketState.Closed; - var ret = sent && received; + var e = new CloseEventArgs(payloadData, res); - _logger.Debug ( - String.Format ( - "Was clean?: {0}\n sent: {1}\n received: {2}", ret, sent, received - ) - ); + try + { + OnClose.Emit(this, e); + } + catch (Exception ex) + { + _logger.Error(ex.Message); + _logger.Debug(ex.ToString()); + } + } - return ret; - } + private void closeAsync(ushort code, string reason) + { + if (_readyState == WebSocketState.Closing) + { + _logger.Info("The closing is already in progress."); + return; + } - // As client - private bool connect () - { - if (_readyState == WebSocketState.Open) { - var msg = "The connection has already been established."; - _logger.Warn (msg); + if (_readyState == WebSocketState.Closed) + { + _logger.Info("The connection has already been closed."); + return; + } + + if (code == 1005) + { // == no status + closeAsync(PayloadData.Empty, true, true, false); + return; + } - return false; - } + var send = !code.IsReserved(); + closeAsync(new PayloadData(code, reason), send, send, false); + } - lock (_forState) { - if (_readyState == WebSocketState.Open) { - var msg = "The connection has already been established."; - _logger.Warn (msg); + private void closeAsync( + PayloadData payloadData, bool send, bool receive, bool received + ) + { + Action closer = close; - return false; + Task.Run(() => closer(payloadData, send, receive, received)); } - if (_readyState == WebSocketState.Closing) { - var msg = "The close process has set in."; - _logger.Error (msg); + private bool closeHandshake(byte[] frameAsBytes, bool receive, bool received) + { + var sent = frameAsBytes != null && sendBytes(frameAsBytes); - msg = "An interruption has occurred while attempting to connect."; - error (msg, null); + var wait = !received && sent && receive && _receivingExited != null; + if (wait) + received = _receivingExited.WaitOne(_waitTime); + + var ret = sent && received; + + _logger.Debug( + String.Format( + "Was clean?: {0}\n sent: {1}\n received: {2}", ret, sent, received + ) + ); - return false; + return ret; } - if (_retryCountForConnect > _maxRetryCountForConnect) { - var msg = "An opportunity for reconnecting has been lost."; - _logger.Error (msg); + private bool closeHandshake( + PayloadData payloadData, bool send, bool receive, bool received + ) + { + var sent = false; + if (send) + { + var frame = WebSocketFrame.CreateCloseFrame(payloadData, _client); + sent = sendBytes(frame.ToArray()); + + if (_client) + frame.Unmask(); + } - msg = "An interruption has occurred while attempting to connect."; - error (msg, null); + var wait = !received && sent && receive && _receivingExited != null; + if (wait) + received = _receivingExited.WaitOne(_waitTime); - return false; - } + var ret = sent && received; - _readyState = WebSocketState.Connecting; + _logger.Debug( + String.Format( + "Was clean?: {0}\n sent: {1}\n received: {2}", ret, sent, received + ) + ); - try { - doHandshake (); + return ret; } - catch (Exception ex) { - _retryCountForConnect++; - _logger.Fatal (ex.Message); - _logger.Debug (ex.ToString ()); + // As client + private bool connect() + { + if (_readyState == WebSocketState.Open) + { + var msg = "The connection has already been established."; + _logger.Warn(msg); - var msg = "An exception has occurred while attempting to connect."; - fatal (msg, ex); + return false; + } - return false; - } + lock (_forState) + { + if (_readyState == WebSocketState.Open) + { + var msg = "The connection has already been established."; + _logger.Warn(msg); - _retryCountForConnect = 1; - _readyState = WebSocketState.Open; + return false; + } - return true; - } - } + if (_readyState == WebSocketState.Closing) + { + var msg = "The close process has set in."; + _logger.Error(msg); - // As client - private string createExtensions () - { - var buff = new StringBuilder (80); + msg = "An interruption has occurred while attempting to connect."; + error(msg, null); - if (_compression != CompressionMethod.None) { - var str = _compression.ToExtensionString ( - "server_no_context_takeover", "client_no_context_takeover"); + return false; + } - buff.AppendFormat ("{0}, ", str); - } + if (_retryCountForConnect > _maxRetryCountForConnect) + { + var msg = "An opportunity for reconnecting has been lost."; + _logger.Error(msg); - var len = buff.Length; - if (len > 2) { - buff.Length = len - 2; - return buff.ToString (); - } + msg = "An interruption has occurred while attempting to connect."; + error(msg, null); - return null; - } + return false; + } - // As server - private HttpResponse createHandshakeFailureResponse (HttpStatusCode code) - { - var ret = HttpResponse.CreateCloseResponse (code); - ret.Headers["Sec-WebSocket-Version"] = _version; + _readyState = WebSocketState.Connecting; - return ret; - } + try + { + doHandshake(); + } + catch (Exception ex) + { + _retryCountForConnect++; - // As client - private HttpRequest createHandshakeRequest () - { - var ret = HttpRequest.CreateWebSocketRequest (_uri); + _logger.Fatal(ex.Message); + _logger.Debug(ex.ToString()); - var headers = ret.Headers; - if (!_origin.IsNullOrEmpty ()) - headers["Origin"] = _origin; + var msg = "An exception has occurred while attempting to connect."; + fatal(msg, ex); - headers["Sec-WebSocket-Key"] = _base64Key; + return false; + } - _protocolsRequested = _protocols != null; - if (_protocolsRequested) - headers["Sec-WebSocket-Protocol"] = _protocols.ToString (", "); + _retryCountForConnect = 1; + _readyState = WebSocketState.Open; - _extensionsRequested = _compression != CompressionMethod.None; - if (_extensionsRequested) - headers["Sec-WebSocket-Extensions"] = createExtensions (); + return true; + } + } - headers["Sec-WebSocket-Version"] = _version; + // As client + private string createExtensions() + { + var buff = new StringBuilder(80); - AuthenticationResponse authRes = null; - if (_authChallenge != null && _credentials != null) { - authRes = new AuthenticationResponse (_authChallenge, _credentials, _nonceCount); - _nonceCount = authRes.NonceCount; - } - else if (_preAuth) { - authRes = new AuthenticationResponse (_credentials); - } + if (_compression != CompressionMethod.None) + { + var str = _compression.ToExtensionString( + "server_no_context_takeover", "client_no_context_takeover"); - if (authRes != null) - headers["Authorization"] = authRes.ToString (); + buff.AppendFormat("{0}, ", str); + } - if (_cookies.Count > 0) - ret.SetCookies (_cookies); + var len = buff.Length; + if (len > 2) + { + buff.Length = len - 2; + return buff.ToString(); + } - return ret; - } + return null; + } - // As server - private HttpResponse createHandshakeResponse () - { - var ret = HttpResponse.CreateWebSocketResponse (); + // As server + private HttpResponse createHandshakeFailureResponse(HttpStatusCode code) + { + var ret = HttpResponse.CreateCloseResponse(code); + ret.Headers["Sec-WebSocket-Version"] = _version; - var headers = ret.Headers; - headers["Sec-WebSocket-Accept"] = CreateResponseKey (_base64Key); + return ret; + } - if (_protocol != null) - headers["Sec-WebSocket-Protocol"] = _protocol; + // As client + private HttpRequest createHandshakeRequest() + { + var ret = HttpRequest.CreateWebSocketRequest(_uri); - if (_extensions != null) - headers["Sec-WebSocket-Extensions"] = _extensions; + var headers = ret.Headers; + if (!_origin.IsNullOrEmpty()) + headers["Origin"] = _origin; - if (_cookies.Count > 0) - ret.SetCookies (_cookies); + headers["Sec-WebSocket-Key"] = _base64Key; - return ret; - } + _protocolsRequested = _protocols != null; + if (_protocolsRequested) + headers["Sec-WebSocket-Protocol"] = _protocols.ToString(", "); - // As server - private bool customCheckHandshakeRequest ( - WebSocketContext context, out string message - ) - { - message = null; + _extensionsRequested = _compression != CompressionMethod.None; + if (_extensionsRequested) + headers["Sec-WebSocket-Extensions"] = createExtensions(); - if (_handshakeRequestChecker == null) - return true; + headers["Sec-WebSocket-Version"] = _version; - message = _handshakeRequestChecker (context); - return message == null; - } + AuthenticationResponse authRes = null; + if (_authChallenge != null && _credentials != null) + { + authRes = new AuthenticationResponse(_authChallenge, _credentials, _nonceCount); + _nonceCount = authRes.NonceCount; + } + else if (_preAuth) + { + authRes = new AuthenticationResponse(_credentials); + } - private MessageEventArgs dequeueFromMessageEventQueue () - { - lock (_forMessageEventQueue) - return _messageEventQueue.Count > 0 ? _messageEventQueue.Dequeue () : null; - } + if (authRes != null) + headers["Authorization"] = authRes.ToString(); - // As client - private void doHandshake () - { - setClientStream (); - var res = sendHandshakeRequest (); + if (_cookies.Count > 0) + ret.SetCookies(_cookies); - string msg; - if (!checkHandshakeResponse (res, out msg)) - throw new WebSocketException (CloseStatusCode.ProtocolError, msg); + return ret; + } - if (_protocolsRequested) - _protocol = res.Headers["Sec-WebSocket-Protocol"]; + // As server + private HttpResponse createHandshakeResponse() + { + var ret = HttpResponse.CreateWebSocketResponse(); - if (_extensionsRequested) - processSecWebSocketExtensionsServerHeader (res.Headers["Sec-WebSocket-Extensions"]); + var headers = ret.Headers; + headers["Sec-WebSocket-Accept"] = CreateResponseKey(_base64Key); - processCookies (res.Cookies); - } + if (_protocol != null) + headers["Sec-WebSocket-Protocol"] = _protocol; - private void enqueueToMessageEventQueue (MessageEventArgs e) - { - lock (_forMessageEventQueue) - _messageEventQueue.Enqueue (e); - } + if (_extensions != null) + headers["Sec-WebSocket-Extensions"] = _extensions; - private void error (string message, Exception exception) - { - try { - OnError.Emit (this, new ErrorEventArgs (message, exception)); - } - catch (Exception ex) { - _logger.Error (ex.Message); - _logger.Debug (ex.ToString ()); - } - } + if (_cookies.Count > 0) + ret.SetCookies(_cookies); - private void fatal (string message, Exception exception) - { - var code = exception is WebSocketException - ? ((WebSocketException) exception).Code - : CloseStatusCode.Abnormal; + return ret; + } - fatal (message, (ushort) code); - } + // As server + private bool customCheckHandshakeRequest( + WebSocketContext context, out string message + ) + { + message = null; - private void fatal (string message, ushort code) - { - var payload = new PayloadData (code, message); - close (payload, !code.IsReserved (), false, false); - } + if (_handshakeRequestChecker == null) + return true; - private void fatal (string message, CloseStatusCode code) - { - fatal (message, (ushort) code); - } + message = _handshakeRequestChecker(context); + return message == null; + } - private ClientSslConfiguration getSslConfiguration () - { - if (_sslConfig == null) - _sslConfig = new ClientSslConfiguration (_uri.DnsSafeHost); + private MessageEventArgs dequeueFromMessageEventQueue() + { + lock (_forMessageEventQueue) + return _messageEventQueue.Count > 0 ? _messageEventQueue.Dequeue() : null; + } - return _sslConfig; - } + // As client + private void doHandshake() + { + setClientStream(); + var res = sendHandshakeRequest(); - private void init () - { - _compression = CompressionMethod.None; - _cookies = new CookieCollection (); - _forPing = new object (); - _forSend = new object (); - _forState = new object (); - _messageEventQueue = new Queue (); - _forMessageEventQueue = ((ICollection) _messageEventQueue).SyncRoot; - _readyState = WebSocketState.Connecting; - } + string msg; + if (!checkHandshakeResponse(res, out msg)) + throw new WebSocketException(CloseStatusCode.ProtocolError, msg); - private void message () - { - MessageEventArgs e = null; - lock (_forMessageEventQueue) { - if (_inMessage || _messageEventQueue.Count == 0 || _readyState != WebSocketState.Open) - return; + if (_protocolsRequested) + _protocol = res.Headers["Sec-WebSocket-Protocol"]; - _inMessage = true; - e = _messageEventQueue.Dequeue (); - } + if (_extensionsRequested) + processSecWebSocketExtensionsServerHeader(res.Headers["Sec-WebSocket-Extensions"]); - _message (e); - } + processCookies(res.Cookies); + } - private void messagec (MessageEventArgs e) - { - do { - try { - OnMessage.Emit (this, e); + private void enqueueToMessageEventQueue(MessageEventArgs e) + { + lock (_forMessageEventQueue) + _messageEventQueue.Enqueue(e); } - catch (Exception ex) { - _logger.Error (ex.ToString ()); - error ("An error has occurred during an OnMessage event.", ex); + + private void error(string message, Exception exception) + { + try + { + OnError.Emit(this, new ErrorEventArgs(message, exception)); + } + catch (Exception ex) + { + _logger.Error(ex.Message); + _logger.Debug(ex.ToString()); + } } - lock (_forMessageEventQueue) { - if (_messageEventQueue.Count == 0 || _readyState != WebSocketState.Open) { - _inMessage = false; - break; - } + private void fatal(string message, Exception exception) + { + var code = exception is WebSocketException + ? ((WebSocketException)exception).Code + : CloseStatusCode.Abnormal; - e = _messageEventQueue.Dequeue (); + fatal(message, (ushort)code); } - } - while (true); - } - - private void messages (MessageEventArgs e) - { - try { - OnMessage.Emit (this, e); - } - catch (Exception ex) { - _logger.Error (ex.ToString ()); - error ("An error has occurred during an OnMessage event.", ex); - } - lock (_forMessageEventQueue) { - if (_messageEventQueue.Count == 0 || _readyState != WebSocketState.Open) { - _inMessage = false; - return; + private void fatal(string message, ushort code) + { + var payload = new PayloadData(code, message); + close(payload, !code.IsReserved(), false, false); } - e = _messageEventQueue.Dequeue (); - } + private void fatal(string message, CloseStatusCode code) + { + fatal(message, (ushort)code); + } - ThreadPool.QueueUserWorkItem (state => messages (e)); - } + private ClientSslConfiguration getSslConfiguration() + { + if (_sslConfig == null) + _sslConfig = new ClientSslConfiguration(_uri.DnsSafeHost); - private void open () - { - _inMessage = true; - startReceiving (); - try { - OnOpen.Emit (this, EventArgs.Empty); - } - catch (Exception ex) { - _logger.Error (ex.ToString ()); - error ("An error has occurred during the OnOpen event.", ex); - } - - MessageEventArgs e = null; - lock (_forMessageEventQueue) { - if (_messageEventQueue.Count == 0 || _readyState != WebSocketState.Open) { - _inMessage = false; - return; - } - - e = _messageEventQueue.Dequeue (); - } - - _message.BeginInvoke (e, ar => _message.EndInvoke (ar), null); - } + return _sslConfig; + } - private bool ping (byte[] data) - { - if (_readyState != WebSocketState.Open) - return false; + private void init() + { + _compression = CompressionMethod.None; + _cookies = new CookieCollection(); + _forPing = new object(); + _forSend = new object(); + _forState = new object(); + _messageEventQueue = new Queue(); + _forMessageEventQueue = ((ICollection)_messageEventQueue).SyncRoot; + _readyState = WebSocketState.Connecting; + } - var pongReceived = _pongReceived; - if (pongReceived == null) - return false; + private void message() + { + MessageEventArgs e = null; + lock (_forMessageEventQueue) + { + if (_inMessage || _messageEventQueue.Count == 0 || _readyState != WebSocketState.Open) + return; - lock (_forPing) { - try { - pongReceived.Reset (); - if (!send (Fin.Final, Opcode.Ping, data, false)) - return false; + _inMessage = true; + e = _messageEventQueue.Dequeue(); + } - return pongReceived.WaitOne (_waitTime); + _message(e); } - catch (ObjectDisposedException) { - return false; - } - } - } - private bool processCloseFrame (WebSocketFrame frame) - { - var payload = frame.PayloadData; - close (payload, !payload.HasReservedCode, false, true); + private void messagec(MessageEventArgs e) + { + do + { + try + { + OnMessage.Emit(this, e); + } + catch (Exception ex) + { + _logger.Error(ex.ToString()); + error("An error has occurred during an OnMessage event.", ex); + } + + lock (_forMessageEventQueue) + { + if (_messageEventQueue.Count == 0 || _readyState != WebSocketState.Open) + { + _inMessage = false; + break; + } + + e = _messageEventQueue.Dequeue(); + } + } + while (true); + } - return false; - } + private void messages(MessageEventArgs e) + { + try + { + OnMessage.Emit(this, e); + } + catch (Exception ex) + { + _logger.Error(ex.ToString()); + error("An error has occurred during an OnMessage event.", ex); + } - // As client - private void processCookies (CookieCollection cookies) - { - if (cookies.Count == 0) - return; + lock (_forMessageEventQueue) + { + if (_messageEventQueue.Count == 0 || _readyState != WebSocketState.Open) + { + _inMessage = false; + return; + } - _cookies.SetOrRemove (cookies); - } + e = _messageEventQueue.Dequeue(); + } - private bool processDataFrame (WebSocketFrame frame) - { - enqueueToMessageEventQueue ( - frame.IsCompressed - ? new MessageEventArgs ( - frame.Opcode, frame.PayloadData.ApplicationData.Decompress (_compression)) - : new MessageEventArgs (frame)); + ThreadPool.QueueUserWorkItem(state => messages(e)); + } - return true; - } + private void open() + { + _inMessage = true; + startReceiving(); + try + { + OnOpen.Emit(this, EventArgs.Empty); + } + catch (Exception ex) + { + _logger.Error(ex.ToString()); + error("An error has occurred during the OnOpen event.", ex); + } - private bool processFragmentFrame (WebSocketFrame frame) - { - if (!_inContinuation) { - // Must process first fragment. - if (frame.IsContinuation) - return true; + MessageEventArgs e = null; + lock (_forMessageEventQueue) + { + if (_messageEventQueue.Count == 0 || _readyState != WebSocketState.Open) + { + _inMessage = false; + return; + } - _fragmentsOpcode = frame.Opcode; - _fragmentsCompressed = frame.IsCompressed; - _fragmentsBuffer = new MemoryStream (); - _inContinuation = true; - } + e = _messageEventQueue.Dequeue(); + } - _fragmentsBuffer.WriteBytes (frame.PayloadData.ApplicationData, 1024); - if (frame.IsFinal) { - using (_fragmentsBuffer) { - var data = _fragmentsCompressed - ? _fragmentsBuffer.DecompressToArray (_compression) - : _fragmentsBuffer.ToArray (); + Task.Run(() => _message.Invoke(e)); + } - enqueueToMessageEventQueue (new MessageEventArgs (_fragmentsOpcode, data)); + private bool ping(byte[] data) + { + if (_readyState != WebSocketState.Open) + return false; + + var pongReceived = _pongReceived; + if (pongReceived == null) + return false; + + lock (_forPing) + { + try + { + pongReceived.Reset(); + if (!send(Fin.Final, Opcode.Ping, data, false)) + return false; + + return pongReceived.WaitOne(_waitTime); + } + catch (ObjectDisposedException) + { + return false; + } + } } - _fragmentsBuffer = null; - _inContinuation = false; - } + private bool processCloseFrame(WebSocketFrame frame) + { + var payload = frame.PayloadData; + close(payload, !payload.HasReservedCode, false, true); - return true; - } + return false; + } - private bool processPingFrame (WebSocketFrame frame) - { - _logger.Trace ("A ping was received."); + // As client + private void processCookies(CookieCollection cookies) + { + if (cookies.Count == 0) + return; + + _cookies.SetOrRemove(cookies); + } - var pong = WebSocketFrame.CreatePongFrame (frame.PayloadData, _client); + private bool processDataFrame(WebSocketFrame frame) + { + enqueueToMessageEventQueue( + frame.IsCompressed + ? new MessageEventArgs( + frame.Opcode, frame.PayloadData.ApplicationData.Decompress(_compression)) + : new MessageEventArgs(frame)); - lock (_forState) { - if (_readyState != WebSocketState.Open) { - _logger.Error ("The connection is closing."); - return true; + return true; } - if (!sendBytes (pong.ToArray ())) - return false; - } + private bool processFragmentFrame(WebSocketFrame frame) + { + if (!_inContinuation) + { + // Must process first fragment. + if (frame.IsContinuation) + return true; + + _fragmentsOpcode = frame.Opcode; + _fragmentsCompressed = frame.IsCompressed; + _fragmentsBuffer = new MemoryStream(); + _inContinuation = true; + } - _logger.Trace ("A pong to this ping has been sent."); + _fragmentsBuffer.WriteBytes(frame.PayloadData.ApplicationData, 1024); + if (frame.IsFinal) + { + using (_fragmentsBuffer) + { + var data = _fragmentsCompressed + ? _fragmentsBuffer.DecompressToArray(_compression) + : _fragmentsBuffer.ToArray(); - if (_emitOnPing) { - if (_client) - pong.Unmask (); + enqueueToMessageEventQueue(new MessageEventArgs(_fragmentsOpcode, data)); + } - enqueueToMessageEventQueue (new MessageEventArgs (frame)); - } + _fragmentsBuffer = null; + _inContinuation = false; + } - return true; - } + return true; + } - private bool processPongFrame (WebSocketFrame frame) - { - _logger.Trace ("A pong was received."); + private bool processPingFrame(WebSocketFrame frame) + { + _logger.Trace("A ping was received."); - try { - _pongReceived.Set (); - } - catch (NullReferenceException ex) { - _logger.Error (ex.Message); - _logger.Debug (ex.ToString ()); + var pong = WebSocketFrame.CreatePongFrame(frame.PayloadData, _client); - return false; - } - catch (ObjectDisposedException ex) { - _logger.Error (ex.Message); - _logger.Debug (ex.ToString ()); + lock (_forState) + { + if (_readyState != WebSocketState.Open) + { + _logger.Error("The connection is closing."); + return true; + } - return false; - } + if (!sendBytes(pong.ToArray())) + return false; + } - _logger.Trace ("It has been signaled."); + _logger.Trace("A pong to this ping has been sent."); - return true; - } + if (_emitOnPing) + { + if (_client) + pong.Unmask(); - private bool processReceivedFrame (WebSocketFrame frame) - { - string msg; - if (!checkReceivedFrame (frame, out msg)) - throw new WebSocketException (CloseStatusCode.ProtocolError, msg); - - frame.Unmask (); - return frame.IsFragment - ? processFragmentFrame (frame) - : frame.IsData - ? processDataFrame (frame) - : frame.IsPing - ? processPingFrame (frame) - : frame.IsPong - ? processPongFrame (frame) - : frame.IsClose - ? processCloseFrame (frame) - : processUnsupportedFrame (frame); - } + enqueueToMessageEventQueue(new MessageEventArgs(frame)); + } - // As server - private void processSecWebSocketExtensionsClientHeader (string value) - { - if (value == null) - return; + return true; + } - var buff = new StringBuilder (80); - var comp = false; + private bool processPongFrame(WebSocketFrame frame) + { + _logger.Trace("A pong was received."); - foreach (var elm in value.SplitHeaderValue (',')) { - var extension = elm.Trim (); - if (extension.Length == 0) - continue; + try + { + _pongReceived.Set(); + } + catch (NullReferenceException ex) + { + _logger.Error(ex.Message); + _logger.Debug(ex.ToString()); - if (!comp) { - if (extension.IsCompressionExtension (CompressionMethod.Deflate)) { - _compression = CompressionMethod.Deflate; + return false; + } + catch (ObjectDisposedException ex) + { + _logger.Error(ex.Message); + _logger.Debug(ex.ToString()); - buff.AppendFormat ( - "{0}, ", - _compression.ToExtensionString ( - "client_no_context_takeover", "server_no_context_takeover" - ) - ); + return false; + } + + _logger.Trace("It has been signaled."); - comp = true; - } + return true; } - } - var len = buff.Length; - if (len <= 2) - return; + private bool processReceivedFrame(WebSocketFrame frame) + { + string msg; + if (!checkReceivedFrame(frame, out msg)) + throw new WebSocketException(CloseStatusCode.ProtocolError, msg); + + frame.Unmask(); + return frame.IsFragment + ? processFragmentFrame(frame) + : frame.IsData + ? processDataFrame(frame) + : frame.IsPing + ? processPingFrame(frame) + : frame.IsPong + ? processPongFrame(frame) + : frame.IsClose + ? processCloseFrame(frame) + : processUnsupportedFrame(frame); + } - buff.Length = len - 2; - _extensions = buff.ToString (); - } + // As server + private void processSecWebSocketExtensionsClientHeader(string value) + { + if (value == null) + return; - // As client - private void processSecWebSocketExtensionsServerHeader (string value) - { - if (value == null) { - _compression = CompressionMethod.None; - return; - } + var buff = new StringBuilder(80); + var comp = false; + + foreach (var elm in value.SplitHeaderValue(',')) + { + var extension = elm.Trim(); + if (extension.Length == 0) + continue; + + if (!comp) + { + if (extension.IsCompressionExtension(CompressionMethod.Deflate)) + { + _compression = CompressionMethod.Deflate; + + buff.AppendFormat( + "{0}, ", + _compression.ToExtensionString( + "client_no_context_takeover", "server_no_context_takeover" + ) + ); + + comp = true; + } + } + } - _extensions = value; - } + var len = buff.Length; + if (len <= 2) + return; - // As server - private void processSecWebSocketProtocolClientHeader ( - IEnumerable values - ) - { - if (values.Contains (val => val == _protocol)) - return; + buff.Length = len - 2; + _extensions = buff.ToString(); + } - _protocol = null; - } + // As client + private void processSecWebSocketExtensionsServerHeader(string value) + { + if (value == null) + { + _compression = CompressionMethod.None; + return; + } - private bool processUnsupportedFrame (WebSocketFrame frame) - { - _logger.Fatal ("An unsupported frame:" + frame.PrintToString (false)); - fatal ("There is no way to handle it.", CloseStatusCode.PolicyViolation); + _extensions = value; + } - return false; - } + // As server + private void processSecWebSocketProtocolClientHeader( + IEnumerable values + ) + { + if (values.Contains(val => val == _protocol)) + return; - // As server - private void refuseHandshake (CloseStatusCode code, string reason) - { - _readyState = WebSocketState.Closing; + _protocol = null; + } - var res = createHandshakeFailureResponse (HttpStatusCode.BadRequest); - sendHttpResponse (res); + private bool processUnsupportedFrame(WebSocketFrame frame) + { + _logger.Fatal("An unsupported frame:" + frame.PrintToString(false)); + fatal("There is no way to handle it.", CloseStatusCode.PolicyViolation); - releaseServerResources (); + return false; + } - _readyState = WebSocketState.Closed; + // As server + private void refuseHandshake(CloseStatusCode code, string reason) + { + _readyState = WebSocketState.Closing; - var e = new CloseEventArgs ((ushort) code, reason, false); + var res = createHandshakeFailureResponse(HttpStatusCode.BadRequest); + sendHttpResponse(res); - try { - OnClose.Emit (this, e); - } - catch (Exception ex) { - _logger.Error (ex.Message); - _logger.Debug (ex.ToString ()); - } - } + releaseServerResources(); - // As client - private void releaseClientResources () - { - if (_stream != null) { - _stream.Dispose (); - _stream = null; - } - - if (_tcpClient != null) { - _tcpClient.Close (); - _tcpClient = null; - } - } + _readyState = WebSocketState.Closed; - private void releaseCommonResources () - { - if (_fragmentsBuffer != null) { - _fragmentsBuffer.Dispose (); - _fragmentsBuffer = null; - _inContinuation = false; - } - - if (_pongReceived != null) { - _pongReceived.Close (); - _pongReceived = null; - } - - if (_receivingExited != null) { - _receivingExited.Close (); - _receivingExited = null; - } - } + var e = new CloseEventArgs((ushort)code, reason, false); - private void releaseResources () - { - if (_client) - releaseClientResources (); - else - releaseServerResources (); + try + { + OnClose.Emit(this, e); + } + catch (Exception ex) + { + _logger.Error(ex.Message); + _logger.Debug(ex.ToString()); + } + } - releaseCommonResources (); - } + // As client + private void releaseClientResources() + { + if (_stream != null) + { + _stream.Dispose(); + _stream = null; + } - // As server - private void releaseServerResources () - { - if (_closeContext == null) - return; + if (_tcpClient != null) + { + _tcpClient.Close(); + _tcpClient = null; + } + } - _closeContext (); - _closeContext = null; - _stream = null; - _context = null; - } + private void releaseCommonResources() + { + if (_fragmentsBuffer != null) + { + _fragmentsBuffer.Dispose(); + _fragmentsBuffer = null; + _inContinuation = false; + } - private bool send (Opcode opcode, Stream stream) - { - lock (_forSend) { - var src = stream; - var compressed = false; - var sent = false; - try { - if (_compression != CompressionMethod.None) { - stream = stream.Compress (_compression); - compressed = true; - } + if (_pongReceived != null) + { + _pongReceived.Close(); + _pongReceived = null; + } - sent = send (opcode, stream, compressed); - if (!sent) - error ("A send has been interrupted.", null); - } - catch (Exception ex) { - _logger.Error (ex.ToString ()); - error ("An error has occurred during a send.", ex); + if (_receivingExited != null) + { + _receivingExited.Close(); + _receivingExited = null; + } } - finally { - if (compressed) - stream.Dispose (); - src.Dispose (); + private void releaseResources() + { + if (_client) + releaseClientResources(); + else + releaseServerResources(); + + releaseCommonResources(); } - return sent; - } - } + // As server + private void releaseServerResources() + { + if (_closeContext == null) + return; - private bool send (Opcode opcode, Stream stream, bool compressed) - { - var len = stream.Length; - if (len == 0) - return send (Fin.Final, opcode, EmptyBytes, false); - - var quo = len / FragmentLength; - var rem = (int) (len % FragmentLength); - - byte[] buff = null; - if (quo == 0) { - buff = new byte[rem]; - return stream.Read (buff, 0, rem) == rem - && send (Fin.Final, opcode, buff, compressed); - } - - if (quo == 1 && rem == 0) { - buff = new byte[FragmentLength]; - return stream.Read (buff, 0, FragmentLength) == FragmentLength - && send (Fin.Final, opcode, buff, compressed); - } - - /* Send fragments */ - - // Begin - buff = new byte[FragmentLength]; - var sent = stream.Read (buff, 0, FragmentLength) == FragmentLength - && send (Fin.More, opcode, buff, compressed); - - if (!sent) - return false; - - var n = rem == 0 ? quo - 2 : quo - 1; - for (long i = 0; i < n; i++) { - sent = stream.Read (buff, 0, FragmentLength) == FragmentLength - && send (Fin.More, Opcode.Cont, buff, false); - - if (!sent) - return false; - } - - // End - if (rem == 0) - rem = FragmentLength; - else - buff = new byte[rem]; - - return stream.Read (buff, 0, rem) == rem - && send (Fin.Final, Opcode.Cont, buff, false); - } + _closeContext(); + _closeContext = null; + _stream = null; + _context = null; + } - private bool send (Fin fin, Opcode opcode, byte[] data, bool compressed) - { - lock (_forState) { - if (_readyState != WebSocketState.Open) { - _logger.Error ("The connection is closing."); - return false; + private bool send(Opcode opcode, Stream stream) + { + lock (_forSend) + { + var src = stream; + var compressed = false; + var sent = false; + try + { + if (_compression != CompressionMethod.None) + { + stream = stream.Compress(_compression); + compressed = true; + } + + sent = send(opcode, stream, compressed); + if (!sent) + error("A send has been interrupted.", null); + } + catch (Exception ex) + { + _logger.Error(ex.ToString()); + error("An error has occurred during a send.", ex); + } + finally + { + if (compressed) + stream.Dispose(); + + src.Dispose(); + } + + return sent; + } } - var frame = new WebSocketFrame (fin, opcode, data, compressed, _client); - return sendBytes (frame.ToArray ()); - } - } + private bool send(Opcode opcode, Stream stream, bool compressed) + { + var len = stream.Length; + if (len == 0) + return send(Fin.Final, opcode, EmptyBytes, false); + + var quo = len / FragmentLength; + var rem = (int)(len % FragmentLength); + + byte[] buff = null; + if (quo == 0) + { + buff = new byte[rem]; + return stream.Read(buff, 0, rem) == rem + && send(Fin.Final, opcode, buff, compressed); + } - private void sendAsync (Opcode opcode, Stream stream, Action completed) - { - Func sender = send; - sender.BeginInvoke ( - opcode, - stream, - ar => { - try { - var sent = sender.EndInvoke (ar); - if (completed != null) - completed (sent); - } - catch (Exception ex) { - _logger.Error (ex.ToString ()); - error ( - "An error has occurred during the callback for an async send.", - ex - ); - } - }, - null - ); - } + if (quo == 1 && rem == 0) + { + buff = new byte[FragmentLength]; + return stream.Read(buff, 0, FragmentLength) == FragmentLength + && send(Fin.Final, opcode, buff, compressed); + } - private bool sendBytes (byte[] bytes) - { - try { - _stream.Write (bytes, 0, bytes.Length); - } - catch (Exception ex) { - _logger.Error (ex.Message); - _logger.Debug (ex.ToString ()); + /* Send fragments */ - return false; - } + // Begin + buff = new byte[FragmentLength]; + var sent = stream.Read(buff, 0, FragmentLength) == FragmentLength + && send(Fin.More, opcode, buff, compressed); - return true; - } + if (!sent) + return false; - // As client - private HttpResponse sendHandshakeRequest () - { - var req = createHandshakeRequest (); - var res = sendHttpRequest (req, 90000); - if (res.IsUnauthorized) { - var chal = res.Headers["WWW-Authenticate"]; - _logger.Warn (String.Format ("Received an authentication requirement for '{0}'.", chal)); - if (chal.IsNullOrEmpty ()) { - _logger.Error ("No authentication challenge is specified."); - return res; - } - - _authChallenge = AuthenticationChallenge.Parse (chal); - if (_authChallenge == null) { - _logger.Error ("An invalid authentication challenge is specified."); - return res; - } - - if (_credentials != null && - (!_preAuth || _authChallenge.Scheme == AuthenticationSchemes.Digest)) { - if (res.HasConnectionClose) { - releaseClientResources (); - setClientStream (); - } - - var authRes = new AuthenticationResponse (_authChallenge, _credentials, _nonceCount); - _nonceCount = authRes.NonceCount; - req.Headers["Authorization"] = authRes.ToString (); - res = sendHttpRequest (req, 15000); - } - } - - if (res.IsRedirect) { - var url = res.Headers["Location"]; - _logger.Warn (String.Format ("Received a redirection to '{0}'.", url)); - if (_enableRedirection) { - if (url.IsNullOrEmpty ()) { - _logger.Error ("No url to redirect is located."); - return res; - } + var n = rem == 0 ? quo - 2 : quo - 1; + for (long i = 0; i < n; i++) + { + sent = stream.Read(buff, 0, FragmentLength) == FragmentLength + && send(Fin.More, Opcode.Cont, buff, false); - Uri uri; - string msg; - if (!url.TryCreateWebSocketUri (out uri, out msg)) { - _logger.Error ("An invalid url to redirect is located: " + msg); - return res; - } + if (!sent) + return false; + } - releaseClientResources (); + // End + if (rem == 0) + rem = FragmentLength; + else + buff = new byte[rem]; - _uri = uri; - _secure = uri.Scheme == "wss"; + return stream.Read(buff, 0, rem) == rem + && send(Fin.Final, Opcode.Cont, buff, false); + } - setClientStream (); - return sendHandshakeRequest (); + private bool send(Fin fin, Opcode opcode, byte[] data, bool compressed) + { + lock (_forState) + { + if (_readyState != WebSocketState.Open) + { + _logger.Error("The connection is closing."); + return false; + } + + var frame = new WebSocketFrame(fin, opcode, data, compressed, _client); + return sendBytes(frame.ToArray()); + } } - } - return res; - } + private void sendAsync(Opcode opcode, Stream stream, Action completed) + { + Func sender = send; + + Task.Run(() => sender.Invoke(opcode, stream)) + .ContinueWith(sent => + { + try + { + if (completed != null) + { + completed(sent.Result); + } + } + catch (Exception ex) + { + _logger.Error(ex.ToString()); + error("An error has occurred during the callback for an async send.", ex); + } + }); + } - // As client - private HttpResponse sendHttpRequest (HttpRequest request, int millisecondsTimeout) - { - _logger.Debug ("A request to the server:\n" + request.ToString ()); - var res = request.GetResponse (_stream, millisecondsTimeout); - _logger.Debug ("A response to this request:\n" + res.ToString ()); + private bool sendBytes(byte[] bytes) + { + try + { + _stream.Write(bytes, 0, bytes.Length); + } + catch (Exception ex) + { + _logger.Error(ex.Message); + _logger.Debug(ex.ToString()); - return res; - } + return false; + } - // As server - private bool sendHttpResponse (HttpResponse response) - { - _logger.Debug ( - String.Format ( - "A response to {0}:\n{1}", _context.UserEndPoint, response - ) - ); + return true; + } - return sendBytes (response.ToByteArray ()); - } + // As client + private HttpResponse sendHandshakeRequest() + { + var req = createHandshakeRequest(); + var res = sendHttpRequest(req, 90000); + if (res.IsUnauthorized) + { + var chal = res.Headers["WWW-Authenticate"]; + _logger.Warn(String.Format("Received an authentication requirement for '{0}'.", chal)); + if (chal.IsNullOrEmpty()) + { + _logger.Error("No authentication challenge is specified."); + return res; + } + + _authChallenge = AuthenticationChallenge.Parse(chal); + if (_authChallenge == null) + { + _logger.Error("An invalid authentication challenge is specified."); + return res; + } + + if (_credentials != null && + (!_preAuth || _authChallenge.Scheme == AuthenticationSchemes.Digest)) + { + if (res.HasConnectionClose) + { + releaseClientResources(); + setClientStream(); + } + + var authRes = new AuthenticationResponse(_authChallenge, _credentials, _nonceCount); + _nonceCount = authRes.NonceCount; + req.Headers["Authorization"] = authRes.ToString(); + res = sendHttpRequest(req, 15000); + } + } - // As client - private void sendProxyConnectRequest () - { - var req = HttpRequest.CreateConnectRequest (_uri); - var res = sendHttpRequest (req, 90000); - if (res.IsProxyAuthenticationRequired) { - var chal = res.Headers["Proxy-Authenticate"]; - _logger.Warn ( - String.Format ("Received a proxy authentication requirement for '{0}'.", chal)); - - if (chal.IsNullOrEmpty ()) - throw new WebSocketException ("No proxy authentication challenge is specified."); - - var authChal = AuthenticationChallenge.Parse (chal); - if (authChal == null) - throw new WebSocketException ("An invalid proxy authentication challenge is specified."); - - if (_proxyCredentials != null) { - if (res.HasConnectionClose) { - releaseClientResources (); - _tcpClient = new TcpClient (_proxyUri.DnsSafeHost, _proxyUri.Port); - _stream = _tcpClient.GetStream (); - } - - var authRes = new AuthenticationResponse (authChal, _proxyCredentials, 0); - req.Headers["Proxy-Authorization"] = authRes.ToString (); - res = sendHttpRequest (req, 15000); - } - - if (res.IsProxyAuthenticationRequired) - throw new WebSocketException ("A proxy authentication is required."); - } - - if (res.StatusCode[0] != '2') - throw new WebSocketException ( - "The proxy has failed a connection to the requested host and port."); - } + if (res.IsRedirect) + { + var url = res.Headers["Location"]; + _logger.Warn(String.Format("Received a redirection to '{0}'.", url)); + if (_enableRedirection) + { + if (url.IsNullOrEmpty()) + { + _logger.Error("No url to redirect is located."); + return res; + } + + Uri uri; + string msg; + if (!url.TryCreateWebSocketUri(out uri, out msg)) + { + _logger.Error("An invalid url to redirect is located: " + msg); + return res; + } + + releaseClientResources(); + + _uri = uri; + _secure = uri.Scheme == "wss"; + + setClientStream(); + return sendHandshakeRequest(); + } + } - // As client - private void setClientStream () - { - if (_proxyUri != null) { - _tcpClient = new TcpClient (_proxyUri.DnsSafeHost, _proxyUri.Port); - _stream = _tcpClient.GetStream (); - sendProxyConnectRequest (); - } - else { - _tcpClient = new TcpClient (_uri.DnsSafeHost, _uri.Port); - _stream = _tcpClient.GetStream (); - } - - if (_secure) { - var conf = getSslConfiguration (); - var host = conf.TargetHost; - if (host != _uri.DnsSafeHost) - throw new WebSocketException ( - CloseStatusCode.TlsHandshakeFailure, "An invalid host name is specified."); - - try { - var sslStream = new SslStream ( - _stream, - false, - conf.ServerCertificateValidationCallback, - conf.ClientCertificateSelectionCallback); - - sslStream.AuthenticateAsClient ( - host, - conf.ClientCertificates, - conf.EnabledSslProtocols, - conf.CheckCertificateRevocation); - - _stream = sslStream; - } - catch (Exception ex) { - throw new WebSocketException (CloseStatusCode.TlsHandshakeFailure, ex); - } - } - } + return res; + } - private void startReceiving () - { - if (_messageEventQueue.Count > 0) - _messageEventQueue.Clear (); - - _pongReceived = new ManualResetEvent (false); - _receivingExited = new ManualResetEvent (false); - - Action receive = null; - receive = - () => - WebSocketFrame.ReadFrameAsync ( - _stream, - false, - frame => { - if (!processReceivedFrame (frame) || _readyState == WebSocketState.Closed) { - var exited = _receivingExited; - if (exited != null) - exited.Set (); + // As client + private HttpResponse sendHttpRequest(HttpRequest request, int millisecondsTimeout) + { + _logger.Debug("A request to the server:\n" + request.ToString()); + var res = request.GetResponse(_stream, millisecondsTimeout); + _logger.Debug("A response to this request:\n" + res.ToString()); - return; - } + return res; + } - // Receive next asap because the Ping or Close needs a response to it. - receive (); + // As server + private bool sendHttpResponse(HttpResponse response) + { + _logger.Debug( + String.Format( + "A response to {0}:\n{1}", _context.UserEndPoint, response + ) + ); - if (_inMessage || !HasMessage || _readyState != WebSocketState.Open) - return; + return sendBytes(response.ToByteArray()); + } - message (); - }, - ex => { - _logger.Fatal (ex.ToString ()); - fatal ("An exception has occurred while receiving.", ex); + // As client + private void sendProxyConnectRequest() + { + var req = HttpRequest.CreateConnectRequest(_uri); + var res = sendHttpRequest(req, 90000); + if (res.IsProxyAuthenticationRequired) + { + var chal = res.Headers["Proxy-Authenticate"]; + _logger.Warn( + String.Format("Received a proxy authentication requirement for '{0}'.", chal)); + + if (chal.IsNullOrEmpty()) + throw new WebSocketException("No proxy authentication challenge is specified."); + + var authChal = AuthenticationChallenge.Parse(chal); + if (authChal == null) + throw new WebSocketException("An invalid proxy authentication challenge is specified."); + + if (_proxyCredentials != null) + { + if (res.HasConnectionClose) + { + releaseClientResources(); + _tcpClient = new TcpClient(_proxyUri.DnsSafeHost, _proxyUri.Port); + _stream = _tcpClient.GetStream(); + } + + var authRes = new AuthenticationResponse(authChal, _proxyCredentials, 0); + req.Headers["Proxy-Authorization"] = authRes.ToString(); + res = sendHttpRequest(req, 15000); + } + + if (res.IsProxyAuthenticationRequired) + throw new WebSocketException("A proxy authentication is required."); } - ); - receive (); - } + if (res.StatusCode[0] != '2') + throw new WebSocketException( + "The proxy has failed a connection to the requested host and port."); + } - // As client - private bool validateSecWebSocketAcceptHeader (string value) - { - return value != null && value == CreateResponseKey (_base64Key); - } + // As client + private void setClientStream() + { + if (_proxyUri != null) + { + _tcpClient = new TcpClient(_proxyUri.DnsSafeHost, _proxyUri.Port); + _stream = _tcpClient.GetStream(); + sendProxyConnectRequest(); + } + else + { + _tcpClient = new TcpClient(_uri.DnsSafeHost, _uri.Port); + _stream = _tcpClient.GetStream(); + } - // As client - private bool validateSecWebSocketExtensionsServerHeader (string value) - { - if (value == null) - return true; + if (_secure) + { + var conf = getSslConfiguration(); + var host = conf.TargetHost; + if (host != _uri.DnsSafeHost) + throw new WebSocketException( + CloseStatusCode.TlsHandshakeFailure, "An invalid host name is specified."); + + try + { + var sslStream = new SslStream( + _stream, + false, + conf.ServerCertificateValidationCallback, + conf.ClientCertificateSelectionCallback); + + sslStream.AuthenticateAsClient( + host, + conf.ClientCertificates, + conf.EnabledSslProtocols, + conf.CheckCertificateRevocation); + + _stream = sslStream; + } + catch (Exception ex) + { + throw new WebSocketException(CloseStatusCode.TlsHandshakeFailure, ex); + } + } + } - if (value.Length == 0) - return false; + private void startReceiving() + { + if (_messageEventQueue.Count > 0) + _messageEventQueue.Clear(); + + _pongReceived = new ManualResetEvent(false); + _receivingExited = new ManualResetEvent(false); + + Action receive = null; + receive = + () => + WebSocketFrame.ReadFrameAsync( + _stream, + false, + frame => { + if (!processReceivedFrame(frame) || _readyState == WebSocketState.Closed) + { + var exited = _receivingExited; + if (exited != null) + exited.Set(); + + return; + } + + // Receive next asap because the Ping or Close needs a response to it. + receive(); + + if (_inMessage || !HasMessage || _readyState != WebSocketState.Open) + return; + + message(); + }, + ex => { + _logger.Fatal(ex.ToString()); + fatal("An exception has occurred while receiving.", ex); + } + ); + + receive(); + } - if (!_extensionsRequested) - return false; + // As client + private bool validateSecWebSocketAcceptHeader(string value) + { + return value != null && value == CreateResponseKey(_base64Key); + } - var comp = _compression != CompressionMethod.None; - foreach (var e in value.SplitHeaderValue (',')) { - var ext = e.Trim (); - if (comp && ext.IsCompressionExtension (_compression)) { - if (!ext.Contains ("server_no_context_takeover")) { - _logger.Error ("The server hasn't sent back 'server_no_context_takeover'."); - return false; - } - - if (!ext.Contains ("client_no_context_takeover")) - _logger.Warn ("The server hasn't sent back 'client_no_context_takeover'."); - - var method = _compression.ToExtensionString (); - var invalid = - ext.SplitHeaderValue (';').Contains ( - t => { - t = t.Trim (); - return t != method - && t != "server_no_context_takeover" - && t != "client_no_context_takeover"; - } - ); + // As client + private bool validateSecWebSocketExtensionsServerHeader(string value) + { + if (value == null) + return true; + + if (value.Length == 0) + return false; + + if (!_extensionsRequested) + return false; + + var comp = _compression != CompressionMethod.None; + foreach (var e in value.SplitHeaderValue(',')) + { + var ext = e.Trim(); + if (comp && ext.IsCompressionExtension(_compression)) + { + if (!ext.Contains("server_no_context_takeover")) + { + _logger.Error("The server hasn't sent back 'server_no_context_takeover'."); + return false; + } + + if (!ext.Contains("client_no_context_takeover")) + _logger.Warn("The server hasn't sent back 'client_no_context_takeover'."); + + var method = _compression.ToExtensionString(); + var invalid = + ext.SplitHeaderValue(';').Contains( + t => { + t = t.Trim(); + return t != method + && t != "server_no_context_takeover" + && t != "client_no_context_takeover"; + } + ); + + if (invalid) + return false; + } + else + { + return false; + } + } - if (invalid) - return false; + return true; } - else { - return false; + + // As client + private bool validateSecWebSocketProtocolServerHeader(string value) + { + if (value == null) + return !_protocolsRequested; + + if (value.Length == 0) + return false; + + return _protocolsRequested && _protocols.Contains(p => p == value); } - } - return true; - } + // As client + private bool validateSecWebSocketVersionServerHeader(string value) + { + return value == null || value == _version; + } - // As client - private bool validateSecWebSocketProtocolServerHeader (string value) - { - if (value == null) - return !_protocolsRequested; + #endregion - if (value.Length == 0) - return false; + #region Internal Methods - return _protocolsRequested && _protocols.Contains (p => p == value); - } + // As server + internal void Close(HttpResponse response) + { + _readyState = WebSocketState.Closing; - // As client - private bool validateSecWebSocketVersionServerHeader (string value) - { - return value == null || value == _version; - } + sendHttpResponse(response); + releaseServerResources(); - #endregion + _readyState = WebSocketState.Closed; + } - #region Internal Methods + // As server + internal void Close(HttpStatusCode code) + { + Close(createHandshakeFailureResponse(code)); + } - // As server - internal void Close (HttpResponse response) - { - _readyState = WebSocketState.Closing; + // As server + internal void Close(PayloadData payloadData, byte[] frameAsBytes) + { + lock (_forState) + { + if (_readyState == WebSocketState.Closing) + { + _logger.Info("The closing is already in progress."); + return; + } + + if (_readyState == WebSocketState.Closed) + { + _logger.Info("The connection has already been closed."); + return; + } + + _readyState = WebSocketState.Closing; + } - sendHttpResponse (response); - releaseServerResources (); + _logger.Trace("Begin closing the connection."); - _readyState = WebSocketState.Closed; - } + var sent = frameAsBytes != null && sendBytes(frameAsBytes); + var received = sent && _receivingExited != null + ? _receivingExited.WaitOne(_waitTime) + : false; - // As server - internal void Close (HttpStatusCode code) - { - Close (createHandshakeFailureResponse (code)); - } + var res = sent && received; - // As server - internal void Close (PayloadData payloadData, byte[] frameAsBytes) - { - lock (_forState) { - if (_readyState == WebSocketState.Closing) { - _logger.Info ("The closing is already in progress."); - return; + _logger.Debug( + String.Format( + "Was clean?: {0}\n sent: {1}\n received: {2}", res, sent, received + ) + ); + + releaseServerResources(); + releaseCommonResources(); + + _logger.Trace("End closing the connection."); + + _readyState = WebSocketState.Closed; + + var e = new CloseEventArgs(payloadData, res); + + try + { + OnClose.Emit(this, e); + } + catch (Exception ex) + { + _logger.Error(ex.Message); + _logger.Debug(ex.ToString()); + } } - if (_readyState == WebSocketState.Closed) { - _logger.Info ("The connection has already been closed."); - return; + // As client + internal static string CreateBase64Key() + { + var src = new byte[16]; + RandomNumber.GetBytes(src); + + return Convert.ToBase64String(src); } - _readyState = WebSocketState.Closing; - } + internal static string CreateResponseKey(string base64Key) + { + var buff = new StringBuilder(base64Key, 64); + buff.Append(_guid); + SHA1 sha1 = new SHA1CryptoServiceProvider(); + var src = sha1.ComputeHash(buff.ToString().GetUTF8EncodedBytes()); - _logger.Trace ("Begin closing the connection."); + return Convert.ToBase64String(src); + } - var sent = frameAsBytes != null && sendBytes (frameAsBytes); - var received = sent && _receivingExited != null - ? _receivingExited.WaitOne (_waitTime) - : false; + // As server + internal void InternalAccept() + { + try + { + if (!acceptHandshake()) + return; + } + catch (Exception ex) + { + _logger.Fatal(ex.Message); + _logger.Debug(ex.ToString()); - var res = sent && received; + var msg = "An exception has occurred while attempting to accept."; + fatal(msg, ex); - _logger.Debug ( - String.Format ( - "Was clean?: {0}\n sent: {1}\n received: {2}", res, sent, received - ) - ); + return; + } - releaseServerResources (); - releaseCommonResources (); + _readyState = WebSocketState.Open; - _logger.Trace ("End closing the connection."); + open(); + } - _readyState = WebSocketState.Closed; + // As server + internal bool Ping(byte[] frameAsBytes, TimeSpan timeout) + { + if (_readyState != WebSocketState.Open) + return false; + + var pongReceived = _pongReceived; + if (pongReceived == null) + return false; + + lock (_forPing) + { + try + { + pongReceived.Reset(); + + lock (_forState) + { + if (_readyState != WebSocketState.Open) + return false; + + if (!sendBytes(frameAsBytes)) + return false; + } + + return pongReceived.WaitOne(timeout); + } + catch (ObjectDisposedException) + { + return false; + } + } + } - var e = new CloseEventArgs (payloadData, res); + // As server + internal void Send( + Opcode opcode, byte[] data, Dictionary cache + ) + { + lock (_forSend) + { + lock (_forState) + { + if (_readyState != WebSocketState.Open) + { + _logger.Error("The connection is closing."); + return; + } + + byte[] found; + if (!cache.TryGetValue(_compression, out found)) + { + found = new WebSocketFrame( + Fin.Final, + opcode, + data.Compress(_compression), + _compression != CompressionMethod.None, + false + ) + .ToArray(); - try { - OnClose.Emit (this, e); - } - catch (Exception ex) { - _logger.Error (ex.Message); - _logger.Debug (ex.ToString ()); - } - } + cache.Add(_compression, found); + } - // As client - internal static string CreateBase64Key () - { - var src = new byte[16]; - RandomNumber.GetBytes (src); + sendBytes(found); + } + } + } - return Convert.ToBase64String (src); - } + // As server + internal void Send( + Opcode opcode, Stream stream, Dictionary cache + ) + { + lock (_forSend) + { + Stream found; + if (!cache.TryGetValue(_compression, out found)) + { + found = stream.Compress(_compression); + cache.Add(_compression, found); + } + else + { + found.Position = 0; + } + + send(opcode, found, _compression != CompressionMethod.None); + } + } - internal static string CreateResponseKey (string base64Key) - { - var buff = new StringBuilder (base64Key, 64); - buff.Append (_guid); - SHA1 sha1 = new SHA1CryptoServiceProvider (); - var src = sha1.ComputeHash (buff.ToString ().GetUTF8EncodedBytes ()); + #endregion + + #region Public Methods + + /// + /// Accepts the handshake request. + /// + /// + /// This method does nothing if the handshake request has already been + /// accepted. + /// + /// + /// + /// This instance is a client. + /// + /// + /// -or- + /// + /// + /// The close process is in progress. + /// + /// + /// -or- + /// + /// + /// The connection has already been closed. + /// + /// + public void Accept() + { + if (_client) + { + var msg = "This instance is a client."; + throw new InvalidOperationException(msg); + } - return Convert.ToBase64String (src); - } + if (_readyState == WebSocketState.Closing) + { + var msg = "The close process is in progress."; + throw new InvalidOperationException(msg); + } - // As server - internal void InternalAccept () - { - try { - if (!acceptHandshake ()) - return; - } - catch (Exception ex) { - _logger.Fatal (ex.Message); - _logger.Debug (ex.ToString ()); + if (_readyState == WebSocketState.Closed) + { + var msg = "The connection has already been closed."; + throw new InvalidOperationException(msg); + } - var msg = "An exception has occurred while attempting to accept."; - fatal (msg, ex); + if (accept()) + open(); + } - return; - } + /// + /// Accepts the handshake request asynchronously. + /// + /// + /// + /// This method does not wait for the accept process to be complete. + /// + /// + /// This method does nothing if the handshake request has already been + /// accepted. + /// + /// + /// + /// + /// This instance is a client. + /// + /// + /// -or- + /// + /// + /// The close process is in progress. + /// + /// + /// -or- + /// + /// + /// The connection has already been closed. + /// + /// + public void AcceptAsync() + { + if (_client) + { + var msg = "This instance is a client."; + throw new InvalidOperationException(msg); + } - _readyState = WebSocketState.Open; + if (_readyState == WebSocketState.Closing) + { + var msg = "The close process is in progress."; + throw new InvalidOperationException(msg); + } - open (); - } + if (_readyState == WebSocketState.Closed) + { + var msg = "The connection has already been closed."; + throw new InvalidOperationException(msg); + } - // As server - internal bool Ping (byte[] frameAsBytes, TimeSpan timeout) - { - if (_readyState != WebSocketState.Open) - return false; + Func acceptor = accept; - var pongReceived = _pongReceived; - if (pongReceived == null) - return false; + Task.Run(acceptor) + .ContinueWith(ar => + { + if (ar.Result) open(); + }); + } - lock (_forPing) { - try { - pongReceived.Reset (); + /// + /// Closes the connection. + /// + /// + /// This method does nothing if the current state of the connection is + /// Closing or Closed. + /// + public void Close() + { + close(1005, String.Empty); + } - lock (_forState) { - if (_readyState != WebSocketState.Open) - return false; + /// + /// Closes the connection with the specified code. + /// + /// + /// This method does nothing if the current state of the connection is + /// Closing or Closed. + /// + /// + /// + /// A that represents the status code indicating + /// the reason for the close. + /// + /// + /// The status codes are defined in + /// + /// Section 7.4 of RFC 6455. + /// + /// + /// + /// is less than 1000 or greater than 4999. + /// + /// + /// + /// is 1011 (server error). + /// It cannot be used by clients. + /// + /// + /// -or- + /// + /// + /// is 1010 (mandatory extension). + /// It cannot be used by servers. + /// + /// + public void Close(ushort code) + { + if (!code.IsCloseStatusCode()) + { + var msg = "Less than 1000 or greater than 4999."; + throw new ArgumentOutOfRangeException("code", msg); + } - if (!sendBytes (frameAsBytes)) - return false; - } + if (_client && code == 1011) + { + var msg = "1011 cannot be used."; + throw new ArgumentException(msg, "code"); + } + + if (!_client && code == 1010) + { + var msg = "1010 cannot be used."; + throw new ArgumentException(msg, "code"); + } - return pongReceived.WaitOne (timeout); + close(code, String.Empty); } - catch (ObjectDisposedException) { - return false; + + /// + /// Closes the connection with the specified code. + /// + /// + /// This method does nothing if the current state of the connection is + /// Closing or Closed. + /// + /// + /// + /// One of the enum values. + /// + /// + /// It represents the status code indicating the reason for the close. + /// + /// + /// + /// + /// is + /// . + /// It cannot be used by clients. + /// + /// + /// -or- + /// + /// + /// is + /// . + /// It cannot be used by servers. + /// + /// + public void Close(CloseStatusCode code) + { + if (_client && code == CloseStatusCode.ServerError) + { + var msg = "ServerError cannot be used."; + throw new ArgumentException(msg, "code"); + } + + if (!_client && code == CloseStatusCode.MandatoryExtension) + { + var msg = "MandatoryExtension cannot be used."; + throw new ArgumentException(msg, "code"); + } + + close((ushort)code, String.Empty); } - } - } - // As server - internal void Send ( - Opcode opcode, byte[] data, Dictionary cache - ) - { - lock (_forSend) { - lock (_forState) { - if (_readyState != WebSocketState.Open) { - _logger.Error ("The connection is closing."); - return; - } - - byte[] found; - if (!cache.TryGetValue (_compression, out found)) { - found = new WebSocketFrame ( - Fin.Final, - opcode, - data.Compress (_compression), - _compression != CompressionMethod.None, - false - ) - .ToArray (); - - cache.Add (_compression, found); - } - - sendBytes (found); - } - } - } + /// + /// Closes the connection with the specified code and reason. + /// + /// + /// This method does nothing if the current state of the connection is + /// Closing or Closed. + /// + /// + /// + /// A that represents the status code indicating + /// the reason for the close. + /// + /// + /// The status codes are defined in + /// + /// Section 7.4 of RFC 6455. + /// + /// + /// + /// + /// A that represents the reason for the close. + /// + /// + /// The size must be 123 bytes or less in UTF-8. + /// + /// + /// + /// + /// is less than 1000 or greater than 4999. + /// + /// + /// -or- + /// + /// + /// The size of is greater than 123 bytes. + /// + /// + /// + /// + /// is 1011 (server error). + /// It cannot be used by clients. + /// + /// + /// -or- + /// + /// + /// is 1010 (mandatory extension). + /// It cannot be used by servers. + /// + /// + /// -or- + /// + /// + /// is 1005 (no status) and there is reason. + /// + /// + /// -or- + /// + /// + /// could not be UTF-8-encoded. + /// + /// + public void Close(ushort code, string reason) + { + if (!code.IsCloseStatusCode()) + { + var msg = "Less than 1000 or greater than 4999."; + throw new ArgumentOutOfRangeException("code", msg); + } - // As server - internal void Send ( - Opcode opcode, Stream stream, Dictionary cache - ) - { - lock (_forSend) { - Stream found; - if (!cache.TryGetValue (_compression, out found)) { - found = stream.Compress (_compression); - cache.Add (_compression, found); + if (_client && code == 1011) + { + var msg = "1011 cannot be used."; + throw new ArgumentException(msg, "code"); + } + + if (!_client && code == 1010) + { + var msg = "1010 cannot be used."; + throw new ArgumentException(msg, "code"); + } + + if (reason.IsNullOrEmpty()) + { + close(code, String.Empty); + return; + } + + if (code == 1005) + { + var msg = "1005 cannot be used."; + throw new ArgumentException(msg, "code"); + } + + byte[] bytes; + if (!reason.TryGetUTF8EncodedBytes(out bytes)) + { + var msg = "It could not be UTF-8-encoded."; + throw new ArgumentException(msg, "reason"); + } + + if (bytes.Length > 123) + { + var msg = "Its size is greater than 123 bytes."; + throw new ArgumentOutOfRangeException("reason", msg); + } + + close(code, reason); } - else { - found.Position = 0; + + /// + /// Closes the connection with the specified code and reason. + /// + /// + /// This method does nothing if the current state of the connection is + /// Closing or Closed. + /// + /// + /// + /// One of the enum values. + /// + /// + /// It represents the status code indicating the reason for the close. + /// + /// + /// + /// + /// A that represents the reason for the close. + /// + /// + /// The size must be 123 bytes or less in UTF-8. + /// + /// + /// + /// + /// is + /// . + /// It cannot be used by clients. + /// + /// + /// -or- + /// + /// + /// is + /// . + /// It cannot be used by servers. + /// + /// + /// -or- + /// + /// + /// is + /// and there is reason. + /// + /// + /// -or- + /// + /// + /// could not be UTF-8-encoded. + /// + /// + /// + /// The size of is greater than 123 bytes. + /// + public void Close(CloseStatusCode code, string reason) + { + if (_client && code == CloseStatusCode.ServerError) + { + var msg = "ServerError cannot be used."; + throw new ArgumentException(msg, "code"); + } + + if (!_client && code == CloseStatusCode.MandatoryExtension) + { + var msg = "MandatoryExtension cannot be used."; + throw new ArgumentException(msg, "code"); + } + + if (reason.IsNullOrEmpty()) + { + close((ushort)code, String.Empty); + return; + } + + if (code == CloseStatusCode.NoStatus) + { + var msg = "NoStatus cannot be used."; + throw new ArgumentException(msg, "code"); + } + + byte[] bytes; + if (!reason.TryGetUTF8EncodedBytes(out bytes)) + { + var msg = "It could not be UTF-8-encoded."; + throw new ArgumentException(msg, "reason"); + } + + if (bytes.Length > 123) + { + var msg = "Its size is greater than 123 bytes."; + throw new ArgumentOutOfRangeException("reason", msg); + } + + close((ushort)code, reason); } - send (opcode, found, _compression != CompressionMethod.None); - } - } + /// + /// Closes the connection asynchronously. + /// + /// + /// + /// This method does not wait for the close to be complete. + /// + /// + /// This method does nothing if the current state of the connection is + /// Closing or Closed. + /// + /// + public void CloseAsync() + { + closeAsync(1005, String.Empty); + } - #endregion + /// + /// Closes the connection asynchronously with the specified code. + /// + /// + /// + /// This method does not wait for the close to be complete. + /// + /// + /// This method does nothing if the current state of the connection is + /// Closing or Closed. + /// + /// + /// + /// + /// A that represents the status code indicating + /// the reason for the close. + /// + /// + /// The status codes are defined in + /// + /// Section 7.4 of RFC 6455. + /// + /// + /// + /// is less than 1000 or greater than 4999. + /// + /// + /// + /// is 1011 (server error). + /// It cannot be used by clients. + /// + /// + /// -or- + /// + /// + /// is 1010 (mandatory extension). + /// It cannot be used by servers. + /// + /// + public void CloseAsync(ushort code) + { + if (!code.IsCloseStatusCode()) + { + var msg = "Less than 1000 or greater than 4999."; + throw new ArgumentOutOfRangeException("code", msg); + } - #region Public Methods + if (_client && code == 1011) + { + var msg = "1011 cannot be used."; + throw new ArgumentException(msg, "code"); + } - /// - /// Accepts the handshake request. - /// - /// - /// This method does nothing if the handshake request has already been - /// accepted. - /// - /// - /// - /// This instance is a client. - /// - /// - /// -or- - /// - /// - /// The close process is in progress. - /// - /// - /// -or- - /// - /// - /// The connection has already been closed. - /// - /// - public void Accept () - { - if (_client) { - var msg = "This instance is a client."; - throw new InvalidOperationException (msg); - } - - if (_readyState == WebSocketState.Closing) { - var msg = "The close process is in progress."; - throw new InvalidOperationException (msg); - } - - if (_readyState == WebSocketState.Closed) { - var msg = "The connection has already been closed."; - throw new InvalidOperationException (msg); - } - - if (accept ()) - open (); - } + if (!_client && code == 1010) + { + var msg = "1010 cannot be used."; + throw new ArgumentException(msg, "code"); + } - /// - /// Accepts the handshake request asynchronously. - /// - /// - /// - /// This method does not wait for the accept process to be complete. - /// - /// - /// This method does nothing if the handshake request has already been - /// accepted. - /// - /// - /// - /// - /// This instance is a client. - /// - /// - /// -or- - /// - /// - /// The close process is in progress. - /// - /// - /// -or- - /// - /// - /// The connection has already been closed. - /// - /// - public void AcceptAsync () - { - if (_client) { - var msg = "This instance is a client."; - throw new InvalidOperationException (msg); - } - - if (_readyState == WebSocketState.Closing) { - var msg = "The close process is in progress."; - throw new InvalidOperationException (msg); - } - - if (_readyState == WebSocketState.Closed) { - var msg = "The connection has already been closed."; - throw new InvalidOperationException (msg); - } - - Func acceptor = accept; - acceptor.BeginInvoke ( - ar => { - if (acceptor.EndInvoke (ar)) - open (); - }, - null - ); - } + closeAsync(code, String.Empty); + } - /// - /// Closes the connection. - /// - /// - /// This method does nothing if the current state of the connection is - /// Closing or Closed. - /// - public void Close () - { - close (1005, String.Empty); - } + /// + /// Closes the connection asynchronously with the specified code. + /// + /// + /// + /// This method does not wait for the close to be complete. + /// + /// + /// This method does nothing if the current state of the connection is + /// Closing or Closed. + /// + /// + /// + /// + /// One of the enum values. + /// + /// + /// It represents the status code indicating the reason for the close. + /// + /// + /// + /// + /// is + /// . + /// It cannot be used by clients. + /// + /// + /// -or- + /// + /// + /// is + /// . + /// It cannot be used by servers. + /// + /// + public void CloseAsync(CloseStatusCode code) + { + if (_client && code == CloseStatusCode.ServerError) + { + var msg = "ServerError cannot be used."; + throw new ArgumentException(msg, "code"); + } - /// - /// Closes the connection with the specified code. - /// - /// - /// This method does nothing if the current state of the connection is - /// Closing or Closed. - /// - /// - /// - /// A that represents the status code indicating - /// the reason for the close. - /// - /// - /// The status codes are defined in - /// - /// Section 7.4 of RFC 6455. - /// - /// - /// - /// is less than 1000 or greater than 4999. - /// - /// - /// - /// is 1011 (server error). - /// It cannot be used by clients. - /// - /// - /// -or- - /// - /// - /// is 1010 (mandatory extension). - /// It cannot be used by servers. - /// - /// - public void Close (ushort code) - { - if (!code.IsCloseStatusCode ()) { - var msg = "Less than 1000 or greater than 4999."; - throw new ArgumentOutOfRangeException ("code", msg); - } - - if (_client && code == 1011) { - var msg = "1011 cannot be used."; - throw new ArgumentException (msg, "code"); - } - - if (!_client && code == 1010) { - var msg = "1010 cannot be used."; - throw new ArgumentException (msg, "code"); - } - - close (code, String.Empty); - } + if (!_client && code == CloseStatusCode.MandatoryExtension) + { + var msg = "MandatoryExtension cannot be used."; + throw new ArgumentException(msg, "code"); + } - /// - /// Closes the connection with the specified code. - /// - /// - /// This method does nothing if the current state of the connection is - /// Closing or Closed. - /// - /// - /// - /// One of the enum values. - /// - /// - /// It represents the status code indicating the reason for the close. - /// - /// - /// - /// - /// is - /// . - /// It cannot be used by clients. - /// - /// - /// -or- - /// - /// - /// is - /// . - /// It cannot be used by servers. - /// - /// - public void Close (CloseStatusCode code) - { - if (_client && code == CloseStatusCode.ServerError) { - var msg = "ServerError cannot be used."; - throw new ArgumentException (msg, "code"); - } + closeAsync((ushort)code, String.Empty); + } - if (!_client && code == CloseStatusCode.MandatoryExtension) { - var msg = "MandatoryExtension cannot be used."; - throw new ArgumentException (msg, "code"); - } + /// + /// Closes the connection asynchronously with the specified code and reason. + /// + /// + /// + /// This method does not wait for the close to be complete. + /// + /// + /// This method does nothing if the current state of the connection is + /// Closing or Closed. + /// + /// + /// + /// + /// A that represents the status code indicating + /// the reason for the close. + /// + /// + /// The status codes are defined in + /// + /// Section 7.4 of RFC 6455. + /// + /// + /// + /// + /// A that represents the reason for the close. + /// + /// + /// The size must be 123 bytes or less in UTF-8. + /// + /// + /// + /// + /// is less than 1000 or greater than 4999. + /// + /// + /// -or- + /// + /// + /// The size of is greater than 123 bytes. + /// + /// + /// + /// + /// is 1011 (server error). + /// It cannot be used by clients. + /// + /// + /// -or- + /// + /// + /// is 1010 (mandatory extension). + /// It cannot be used by servers. + /// + /// + /// -or- + /// + /// + /// is 1005 (no status) and there is reason. + /// + /// + /// -or- + /// + /// + /// could not be UTF-8-encoded. + /// + /// + public void CloseAsync(ushort code, string reason) + { + if (!code.IsCloseStatusCode()) + { + var msg = "Less than 1000 or greater than 4999."; + throw new ArgumentOutOfRangeException("code", msg); + } - close ((ushort) code, String.Empty); - } + if (_client && code == 1011) + { + var msg = "1011 cannot be used."; + throw new ArgumentException(msg, "code"); + } - /// - /// Closes the connection with the specified code and reason. - /// - /// - /// This method does nothing if the current state of the connection is - /// Closing or Closed. - /// - /// - /// - /// A that represents the status code indicating - /// the reason for the close. - /// - /// - /// The status codes are defined in - /// - /// Section 7.4 of RFC 6455. - /// - /// - /// - /// - /// A that represents the reason for the close. - /// - /// - /// The size must be 123 bytes or less in UTF-8. - /// - /// - /// - /// - /// is less than 1000 or greater than 4999. - /// - /// - /// -or- - /// - /// - /// The size of is greater than 123 bytes. - /// - /// - /// - /// - /// is 1011 (server error). - /// It cannot be used by clients. - /// - /// - /// -or- - /// - /// - /// is 1010 (mandatory extension). - /// It cannot be used by servers. - /// - /// - /// -or- - /// - /// - /// is 1005 (no status) and there is reason. - /// - /// - /// -or- - /// - /// - /// could not be UTF-8-encoded. - /// - /// - public void Close (ushort code, string reason) - { - if (!code.IsCloseStatusCode ()) { - var msg = "Less than 1000 or greater than 4999."; - throw new ArgumentOutOfRangeException ("code", msg); - } - - if (_client && code == 1011) { - var msg = "1011 cannot be used."; - throw new ArgumentException (msg, "code"); - } - - if (!_client && code == 1010) { - var msg = "1010 cannot be used."; - throw new ArgumentException (msg, "code"); - } - - if (reason.IsNullOrEmpty ()) { - close (code, String.Empty); - return; - } - - if (code == 1005) { - var msg = "1005 cannot be used."; - throw new ArgumentException (msg, "code"); - } - - byte[] bytes; - if (!reason.TryGetUTF8EncodedBytes (out bytes)) { - var msg = "It could not be UTF-8-encoded."; - throw new ArgumentException (msg, "reason"); - } - - if (bytes.Length > 123) { - var msg = "Its size is greater than 123 bytes."; - throw new ArgumentOutOfRangeException ("reason", msg); - } - - close (code, reason); - } + if (!_client && code == 1010) + { + var msg = "1010 cannot be used."; + throw new ArgumentException(msg, "code"); + } - /// - /// Closes the connection with the specified code and reason. - /// - /// - /// This method does nothing if the current state of the connection is - /// Closing or Closed. - /// - /// - /// - /// One of the enum values. - /// - /// - /// It represents the status code indicating the reason for the close. - /// - /// - /// - /// - /// A that represents the reason for the close. - /// - /// - /// The size must be 123 bytes or less in UTF-8. - /// - /// - /// - /// - /// is - /// . - /// It cannot be used by clients. - /// - /// - /// -or- - /// - /// - /// is - /// . - /// It cannot be used by servers. - /// - /// - /// -or- - /// - /// - /// is - /// and there is reason. - /// - /// - /// -or- - /// - /// - /// could not be UTF-8-encoded. - /// - /// - /// - /// The size of is greater than 123 bytes. - /// - public void Close (CloseStatusCode code, string reason) - { - if (_client && code == CloseStatusCode.ServerError) { - var msg = "ServerError cannot be used."; - throw new ArgumentException (msg, "code"); - } - - if (!_client && code == CloseStatusCode.MandatoryExtension) { - var msg = "MandatoryExtension cannot be used."; - throw new ArgumentException (msg, "code"); - } - - if (reason.IsNullOrEmpty ()) { - close ((ushort) code, String.Empty); - return; - } - - if (code == CloseStatusCode.NoStatus) { - var msg = "NoStatus cannot be used."; - throw new ArgumentException (msg, "code"); - } - - byte[] bytes; - if (!reason.TryGetUTF8EncodedBytes (out bytes)) { - var msg = "It could not be UTF-8-encoded."; - throw new ArgumentException (msg, "reason"); - } - - if (bytes.Length > 123) { - var msg = "Its size is greater than 123 bytes."; - throw new ArgumentOutOfRangeException ("reason", msg); - } - - close ((ushort) code, reason); - } - - /// - /// Closes the connection asynchronously. - /// - /// - /// - /// This method does not wait for the close to be complete. - /// - /// - /// This method does nothing if the current state of the connection is - /// Closing or Closed. - /// - /// - public void CloseAsync () - { - closeAsync (1005, String.Empty); - } + if (reason.IsNullOrEmpty()) + { + closeAsync(code, String.Empty); + return; + } - /// - /// Closes the connection asynchronously with the specified code. - /// - /// - /// - /// This method does not wait for the close to be complete. - /// - /// - /// This method does nothing if the current state of the connection is - /// Closing or Closed. - /// - /// - /// - /// - /// A that represents the status code indicating - /// the reason for the close. - /// - /// - /// The status codes are defined in - /// - /// Section 7.4 of RFC 6455. - /// - /// - /// - /// is less than 1000 or greater than 4999. - /// - /// - /// - /// is 1011 (server error). - /// It cannot be used by clients. - /// - /// - /// -or- - /// - /// - /// is 1010 (mandatory extension). - /// It cannot be used by servers. - /// - /// - public void CloseAsync (ushort code) - { - if (!code.IsCloseStatusCode ()) { - var msg = "Less than 1000 or greater than 4999."; - throw new ArgumentOutOfRangeException ("code", msg); - } - - if (_client && code == 1011) { - var msg = "1011 cannot be used."; - throw new ArgumentException (msg, "code"); - } - - if (!_client && code == 1010) { - var msg = "1010 cannot be used."; - throw new ArgumentException (msg, "code"); - } - - closeAsync (code, String.Empty); - } + if (code == 1005) + { + var msg = "1005 cannot be used."; + throw new ArgumentException(msg, "code"); + } - /// - /// Closes the connection asynchronously with the specified code. - /// - /// - /// - /// This method does not wait for the close to be complete. - /// - /// - /// This method does nothing if the current state of the connection is - /// Closing or Closed. - /// - /// - /// - /// - /// One of the enum values. - /// - /// - /// It represents the status code indicating the reason for the close. - /// - /// - /// - /// - /// is - /// . - /// It cannot be used by clients. - /// - /// - /// -or- - /// - /// - /// is - /// . - /// It cannot be used by servers. - /// - /// - public void CloseAsync (CloseStatusCode code) - { - if (_client && code == CloseStatusCode.ServerError) { - var msg = "ServerError cannot be used."; - throw new ArgumentException (msg, "code"); - } + byte[] bytes; + if (!reason.TryGetUTF8EncodedBytes(out bytes)) + { + var msg = "It could not be UTF-8-encoded."; + throw new ArgumentException(msg, "reason"); + } - if (!_client && code == CloseStatusCode.MandatoryExtension) { - var msg = "MandatoryExtension cannot be used."; - throw new ArgumentException (msg, "code"); - } + if (bytes.Length > 123) + { + var msg = "Its size is greater than 123 bytes."; + throw new ArgumentOutOfRangeException("reason", msg); + } - closeAsync ((ushort) code, String.Empty); - } + closeAsync(code, reason); + } - /// - /// Closes the connection asynchronously with the specified code and reason. - /// - /// - /// - /// This method does not wait for the close to be complete. - /// - /// - /// This method does nothing if the current state of the connection is - /// Closing or Closed. - /// - /// - /// - /// - /// A that represents the status code indicating - /// the reason for the close. - /// - /// - /// The status codes are defined in - /// - /// Section 7.4 of RFC 6455. - /// - /// - /// - /// - /// A that represents the reason for the close. - /// - /// - /// The size must be 123 bytes or less in UTF-8. - /// - /// - /// - /// - /// is less than 1000 or greater than 4999. - /// - /// - /// -or- - /// - /// - /// The size of is greater than 123 bytes. - /// - /// - /// - /// - /// is 1011 (server error). - /// It cannot be used by clients. - /// - /// - /// -or- - /// - /// - /// is 1010 (mandatory extension). - /// It cannot be used by servers. - /// - /// - /// -or- - /// - /// - /// is 1005 (no status) and there is reason. - /// - /// - /// -or- - /// - /// - /// could not be UTF-8-encoded. - /// - /// - public void CloseAsync (ushort code, string reason) - { - if (!code.IsCloseStatusCode ()) { - var msg = "Less than 1000 or greater than 4999."; - throw new ArgumentOutOfRangeException ("code", msg); - } - - if (_client && code == 1011) { - var msg = "1011 cannot be used."; - throw new ArgumentException (msg, "code"); - } - - if (!_client && code == 1010) { - var msg = "1010 cannot be used."; - throw new ArgumentException (msg, "code"); - } - - if (reason.IsNullOrEmpty ()) { - closeAsync (code, String.Empty); - return; - } - - if (code == 1005) { - var msg = "1005 cannot be used."; - throw new ArgumentException (msg, "code"); - } - - byte[] bytes; - if (!reason.TryGetUTF8EncodedBytes (out bytes)) { - var msg = "It could not be UTF-8-encoded."; - throw new ArgumentException (msg, "reason"); - } - - if (bytes.Length > 123) { - var msg = "Its size is greater than 123 bytes."; - throw new ArgumentOutOfRangeException ("reason", msg); - } - - closeAsync (code, reason); - } + /// + /// Closes the connection asynchronously with the specified code and reason. + /// + /// + /// + /// This method does not wait for the close to be complete. + /// + /// + /// This method does nothing if the current state of the connection is + /// Closing or Closed. + /// + /// + /// + /// + /// One of the enum values. + /// + /// + /// It represents the status code indicating the reason for the close. + /// + /// + /// + /// + /// A that represents the reason for the close. + /// + /// + /// The size must be 123 bytes or less in UTF-8. + /// + /// + /// + /// + /// is + /// . + /// It cannot be used by clients. + /// + /// + /// -or- + /// + /// + /// is + /// . + /// It cannot be used by servers. + /// + /// + /// -or- + /// + /// + /// is + /// and there is reason. + /// + /// + /// -or- + /// + /// + /// could not be UTF-8-encoded. + /// + /// + /// + /// The size of is greater than 123 bytes. + /// + public void CloseAsync(CloseStatusCode code, string reason) + { + if (_client && code == CloseStatusCode.ServerError) + { + var msg = "ServerError cannot be used."; + throw new ArgumentException(msg, "code"); + } - /// - /// Closes the connection asynchronously with the specified code and reason. - /// - /// - /// - /// This method does not wait for the close to be complete. - /// - /// - /// This method does nothing if the current state of the connection is - /// Closing or Closed. - /// - /// - /// - /// - /// One of the enum values. - /// - /// - /// It represents the status code indicating the reason for the close. - /// - /// - /// - /// - /// A that represents the reason for the close. - /// - /// - /// The size must be 123 bytes or less in UTF-8. - /// - /// - /// - /// - /// is - /// . - /// It cannot be used by clients. - /// - /// - /// -or- - /// - /// - /// is - /// . - /// It cannot be used by servers. - /// - /// - /// -or- - /// - /// - /// is - /// and there is reason. - /// - /// - /// -or- - /// - /// - /// could not be UTF-8-encoded. - /// - /// - /// - /// The size of is greater than 123 bytes. - /// - public void CloseAsync (CloseStatusCode code, string reason) - { - if (_client && code == CloseStatusCode.ServerError) { - var msg = "ServerError cannot be used."; - throw new ArgumentException (msg, "code"); - } - - if (!_client && code == CloseStatusCode.MandatoryExtension) { - var msg = "MandatoryExtension cannot be used."; - throw new ArgumentException (msg, "code"); - } - - if (reason.IsNullOrEmpty ()) { - closeAsync ((ushort) code, String.Empty); - return; - } - - if (code == CloseStatusCode.NoStatus) { - var msg = "NoStatus cannot be used."; - throw new ArgumentException (msg, "code"); - } - - byte[] bytes; - if (!reason.TryGetUTF8EncodedBytes (out bytes)) { - var msg = "It could not be UTF-8-encoded."; - throw new ArgumentException (msg, "reason"); - } - - if (bytes.Length > 123) { - var msg = "Its size is greater than 123 bytes."; - throw new ArgumentOutOfRangeException ("reason", msg); - } - - closeAsync ((ushort) code, reason); - } + if (!_client && code == CloseStatusCode.MandatoryExtension) + { + var msg = "MandatoryExtension cannot be used."; + throw new ArgumentException(msg, "code"); + } - /// - /// Establishes a connection. - /// - /// - /// This method does nothing if the connection has already been established. - /// - /// - /// - /// This instance is not a client. - /// - /// - /// -or- - /// - /// - /// The close process is in progress. - /// - /// - /// -or- - /// - /// - /// A series of reconnecting has failed. - /// - /// - public void Connect () - { - if (!_client) { - var msg = "This instance is not a client."; - throw new InvalidOperationException (msg); - } - - if (_readyState == WebSocketState.Closing) { - var msg = "The close process is in progress."; - throw new InvalidOperationException (msg); - } - - if (_retryCountForConnect > _maxRetryCountForConnect) { - var msg = "A series of reconnecting has failed."; - throw new InvalidOperationException (msg); - } - - if (connect ()) - open (); - } + if (reason.IsNullOrEmpty()) + { + closeAsync((ushort)code, String.Empty); + return; + } - /// - /// Establishes a connection asynchronously. - /// - /// - /// - /// This method does not wait for the connect process to be complete. - /// - /// - /// This method does nothing if the connection has already been - /// established. - /// - /// - /// - /// - /// This instance is not a client. - /// - /// - /// -or- - /// - /// - /// The close process is in progress. - /// - /// - /// -or- - /// - /// - /// A series of reconnecting has failed. - /// - /// - public void ConnectAsync () - { - if (!_client) { - var msg = "This instance is not a client."; - throw new InvalidOperationException (msg); - } - - if (_readyState == WebSocketState.Closing) { - var msg = "The close process is in progress."; - throw new InvalidOperationException (msg); - } - - if (_retryCountForConnect > _maxRetryCountForConnect) { - var msg = "A series of reconnecting has failed."; - throw new InvalidOperationException (msg); - } - - Func connector = connect; - connector.BeginInvoke ( - ar => { - if (connector.EndInvoke (ar)) - open (); - }, - null - ); - } + if (code == CloseStatusCode.NoStatus) + { + var msg = "NoStatus cannot be used."; + throw new ArgumentException(msg, "code"); + } - /// - /// Sends a ping using the WebSocket connection. - /// - /// - /// true if the send has done with no error and a pong has been - /// received within a time; otherwise, false. - /// - public bool Ping () - { - return ping (EmptyBytes); - } + byte[] bytes; + if (!reason.TryGetUTF8EncodedBytes(out bytes)) + { + var msg = "It could not be UTF-8-encoded."; + throw new ArgumentException(msg, "reason"); + } - /// - /// Sends a ping with using the WebSocket - /// connection. - /// - /// - /// true if the send has done with no error and a pong has been - /// received within a time; otherwise, false. - /// - /// - /// - /// A that represents the message to send. - /// - /// - /// The size must be 125 bytes or less in UTF-8. - /// - /// - /// - /// could not be UTF-8-encoded. - /// - /// - /// The size of is greater than 125 bytes. - /// - public bool Ping (string message) - { - if (message.IsNullOrEmpty ()) - return ping (EmptyBytes); + if (bytes.Length > 123) + { + var msg = "Its size is greater than 123 bytes."; + throw new ArgumentOutOfRangeException("reason", msg); + } - byte[] bytes; - if (!message.TryGetUTF8EncodedBytes (out bytes)) { - var msg = "It could not be UTF-8-encoded."; - throw new ArgumentException (msg, "message"); - } + closeAsync((ushort)code, reason); + } - if (bytes.Length > 125) { - var msg = "Its size is greater than 125 bytes."; - throw new ArgumentOutOfRangeException ("message", msg); - } + /// + /// Establishes a connection. + /// + /// + /// This method does nothing if the connection has already been established. + /// + /// + /// + /// This instance is not a client. + /// + /// + /// -or- + /// + /// + /// The close process is in progress. + /// + /// + /// -or- + /// + /// + /// A series of reconnecting has failed. + /// + /// + public void Connect() + { + if (!_client) + { + var msg = "This instance is not a client."; + throw new InvalidOperationException(msg); + } - return ping (bytes); - } + if (_readyState == WebSocketState.Closing) + { + var msg = "The close process is in progress."; + throw new InvalidOperationException(msg); + } - /// - /// Sends the specified data using the WebSocket connection. - /// - /// - /// An array of that represents the binary data to send. - /// - /// - /// The current state of the connection is not Open. - /// - /// - /// is . - /// - public void Send (byte[] data) - { - if (_readyState != WebSocketState.Open) { - var msg = "The current state of the connection is not Open."; - throw new InvalidOperationException (msg); - } + if (_retryCountForConnect > _maxRetryCountForConnect) + { + var msg = "A series of reconnecting has failed."; + throw new InvalidOperationException(msg); + } - if (data == null) - throw new ArgumentNullException ("data"); + if (connect()) + open(); + } - send (Opcode.Binary, new MemoryStream (data)); - } + /// + /// Establishes a connection asynchronously. + /// + /// + /// + /// This method does not wait for the connect process to be complete. + /// + /// + /// This method does nothing if the connection has already been + /// established. + /// + /// + /// + /// + /// This instance is not a client. + /// + /// + /// -or- + /// + /// + /// The close process is in progress. + /// + /// + /// -or- + /// + /// + /// A series of reconnecting has failed. + /// + /// + public void ConnectAsync() + { + if (!_client) + { + var msg = "This instance is not a client."; + throw new InvalidOperationException(msg); + } - /// - /// Sends the specified file using the WebSocket connection. - /// - /// - /// - /// A that specifies the file to send. - /// - /// - /// The file is sent as the binary data. - /// - /// - /// - /// The current state of the connection is not Open. - /// - /// - /// is . - /// - /// - /// - /// The file does not exist. - /// - /// - /// -or- - /// - /// - /// The file could not be opened. - /// - /// - public void Send (FileInfo fileInfo) - { - if (_readyState != WebSocketState.Open) { - var msg = "The current state of the connection is not Open."; - throw new InvalidOperationException (msg); - } - - if (fileInfo == null) - throw new ArgumentNullException ("fileInfo"); - - if (!fileInfo.Exists) { - var msg = "The file does not exist."; - throw new ArgumentException (msg, "fileInfo"); - } - - FileStream stream; - if (!fileInfo.TryOpenRead (out stream)) { - var msg = "The file could not be opened."; - throw new ArgumentException (msg, "fileInfo"); - } - - send (Opcode.Binary, stream); - } + if (_readyState == WebSocketState.Closing) + { + var msg = "The close process is in progress."; + throw new InvalidOperationException(msg); + } - /// - /// Sends the specified data using the WebSocket connection. - /// - /// - /// A that represents the text data to send. - /// - /// - /// The current state of the connection is not Open. - /// - /// - /// is . - /// - /// - /// could not be UTF-8-encoded. - /// - public void Send (string data) - { - if (_readyState != WebSocketState.Open) { - var msg = "The current state of the connection is not Open."; - throw new InvalidOperationException (msg); - } + if (_retryCountForConnect > _maxRetryCountForConnect) + { + var msg = "A series of reconnecting has failed."; + throw new InvalidOperationException(msg); + } - if (data == null) - throw new ArgumentNullException ("data"); + Func connector = connect; - byte[] bytes; - if (!data.TryGetUTF8EncodedBytes (out bytes)) { - var msg = "It could not be UTF-8-encoded."; - throw new ArgumentException (msg, "data"); - } + Task.Run(connector) + .ContinueWith(r => + { + if (r.Result) open(); + }); + } - send (Opcode.Text, new MemoryStream (bytes)); - } + /// + /// Sends a ping using the WebSocket connection. + /// + /// + /// true if the send has done with no error and a pong has been + /// received within a time; otherwise, false. + /// + public bool Ping() + { + return ping(EmptyBytes); + } - /// - /// Sends the data from the specified stream using the WebSocket connection. - /// - /// - /// - /// A instance from which to read the data to send. - /// - /// - /// The data is sent as the binary data. - /// - /// - /// - /// An that specifies the number of bytes to send. - /// - /// - /// The current state of the connection is not Open. - /// - /// - /// is . - /// - /// - /// - /// cannot be read. - /// - /// - /// -or- - /// - /// - /// is less than 1. - /// - /// - /// -or- - /// - /// - /// No data could be read from . - /// - /// - public void Send (Stream stream, int length) - { - if (_readyState != WebSocketState.Open) { - var msg = "The current state of the connection is not Open."; - throw new InvalidOperationException (msg); - } - - if (stream == null) - throw new ArgumentNullException ("stream"); - - if (!stream.CanRead) { - var msg = "It cannot be read."; - throw new ArgumentException (msg, "stream"); - } - - if (length < 1) { - var msg = "Less than 1."; - throw new ArgumentException (msg, "length"); - } - - var bytes = stream.ReadBytes (length); - - var len = bytes.Length; - if (len == 0) { - var msg = "No data could be read from it."; - throw new ArgumentException (msg, "stream"); - } - - if (len < length) { - _logger.Warn ( - String.Format ( - "Only {0} byte(s) of data could be read from the stream.", - len - ) - ); - } - - send (Opcode.Binary, new MemoryStream (bytes)); - } + /// + /// Sends a ping with using the WebSocket + /// connection. + /// + /// + /// true if the send has done with no error and a pong has been + /// received within a time; otherwise, false. + /// + /// + /// + /// A that represents the message to send. + /// + /// + /// The size must be 125 bytes or less in UTF-8. + /// + /// + /// + /// could not be UTF-8-encoded. + /// + /// + /// The size of is greater than 125 bytes. + /// + public bool Ping(string message) + { + if (message.IsNullOrEmpty()) + return ping(EmptyBytes); + + byte[] bytes; + if (!message.TryGetUTF8EncodedBytes(out bytes)) + { + var msg = "It could not be UTF-8-encoded."; + throw new ArgumentException(msg, "message"); + } - /// - /// Sends the specified data asynchronously using the WebSocket connection. - /// - /// - /// This method does not wait for the send to be complete. - /// - /// - /// An array of that represents the binary data to send. - /// - /// - /// - /// An Action<bool> delegate or - /// if not needed. - /// - /// - /// The delegate invokes the method called when the send is complete. - /// - /// - /// true is passed to the method if the send has done with - /// no error; otherwise, false. - /// - /// - /// - /// The current state of the connection is not Open. - /// - /// - /// is . - /// - public void SendAsync (byte[] data, Action completed) - { - if (_readyState != WebSocketState.Open) { - var msg = "The current state of the connection is not Open."; - throw new InvalidOperationException (msg); - } + if (bytes.Length > 125) + { + var msg = "Its size is greater than 125 bytes."; + throw new ArgumentOutOfRangeException("message", msg); + } - if (data == null) - throw new ArgumentNullException ("data"); + return ping(bytes); + } - sendAsync (Opcode.Binary, new MemoryStream (data), completed); - } + /// + /// Sends the specified data using the WebSocket connection. + /// + /// + /// An array of that represents the binary data to send. + /// + /// + /// The current state of the connection is not Open. + /// + /// + /// is . + /// + public void Send(byte[] data) + { + if (_readyState != WebSocketState.Open) + { + var msg = "The current state of the connection is not Open."; + throw new InvalidOperationException(msg); + } - /// - /// Sends the specified file asynchronously using the WebSocket connection. - /// - /// - /// This method does not wait for the send to be complete. - /// - /// - /// - /// A that specifies the file to send. - /// - /// - /// The file is sent as the binary data. - /// - /// - /// - /// - /// An Action<bool> delegate or - /// if not needed. - /// - /// - /// The delegate invokes the method called when the send is complete. - /// - /// - /// true is passed to the method if the send has done with - /// no error; otherwise, false. - /// - /// - /// - /// The current state of the connection is not Open. - /// - /// - /// is . - /// - /// - /// - /// The file does not exist. - /// - /// - /// -or- - /// - /// - /// The file could not be opened. - /// - /// - public void SendAsync (FileInfo fileInfo, Action completed) - { - if (_readyState != WebSocketState.Open) { - var msg = "The current state of the connection is not Open."; - throw new InvalidOperationException (msg); - } - - if (fileInfo == null) - throw new ArgumentNullException ("fileInfo"); - - if (!fileInfo.Exists) { - var msg = "The file does not exist."; - throw new ArgumentException (msg, "fileInfo"); - } - - FileStream stream; - if (!fileInfo.TryOpenRead (out stream)) { - var msg = "The file could not be opened."; - throw new ArgumentException (msg, "fileInfo"); - } - - sendAsync (Opcode.Binary, stream, completed); - } + if (data == null) + throw new ArgumentNullException("data"); - /// - /// Sends the specified data asynchronously using the WebSocket connection. - /// - /// - /// This method does not wait for the send to be complete. - /// - /// - /// A that represents the text data to send. - /// - /// - /// - /// An Action<bool> delegate or - /// if not needed. - /// - /// - /// The delegate invokes the method called when the send is complete. - /// - /// - /// true is passed to the method if the send has done with - /// no error; otherwise, false. - /// - /// - /// - /// The current state of the connection is not Open. - /// - /// - /// is . - /// - /// - /// could not be UTF-8-encoded. - /// - public void SendAsync (string data, Action completed) - { - if (_readyState != WebSocketState.Open) { - var msg = "The current state of the connection is not Open."; - throw new InvalidOperationException (msg); - } + send(Opcode.Binary, new MemoryStream(data)); + } - if (data == null) - throw new ArgumentNullException ("data"); + /// + /// Sends the specified file using the WebSocket connection. + /// + /// + /// + /// A that specifies the file to send. + /// + /// + /// The file is sent as the binary data. + /// + /// + /// + /// The current state of the connection is not Open. + /// + /// + /// is . + /// + /// + /// + /// The file does not exist. + /// + /// + /// -or- + /// + /// + /// The file could not be opened. + /// + /// + public void Send(FileInfo fileInfo) + { + if (_readyState != WebSocketState.Open) + { + var msg = "The current state of the connection is not Open."; + throw new InvalidOperationException(msg); + } - byte[] bytes; - if (!data.TryGetUTF8EncodedBytes (out bytes)) { - var msg = "It could not be UTF-8-encoded."; - throw new ArgumentException (msg, "data"); - } + if (fileInfo == null) + throw new ArgumentNullException("fileInfo"); - sendAsync (Opcode.Text, new MemoryStream (bytes), completed); - } + if (!fileInfo.Exists) + { + var msg = "The file does not exist."; + throw new ArgumentException(msg, "fileInfo"); + } - /// - /// Sends the data from the specified stream asynchronously using - /// the WebSocket connection. - /// - /// - /// This method does not wait for the send to be complete. - /// - /// - /// - /// A instance from which to read the data to send. - /// - /// - /// The data is sent as the binary data. - /// - /// - /// - /// An that specifies the number of bytes to send. - /// - /// - /// - /// An Action<bool> delegate or - /// if not needed. - /// - /// - /// The delegate invokes the method called when the send is complete. - /// - /// - /// true is passed to the method if the send has done with - /// no error; otherwise, false. - /// - /// - /// - /// The current state of the connection is not Open. - /// - /// - /// is . - /// - /// - /// - /// cannot be read. - /// - /// - /// -or- - /// - /// - /// is less than 1. - /// - /// - /// -or- - /// - /// - /// No data could be read from . - /// - /// - public void SendAsync (Stream stream, int length, Action completed) - { - if (_readyState != WebSocketState.Open) { - var msg = "The current state of the connection is not Open."; - throw new InvalidOperationException (msg); - } - - if (stream == null) - throw new ArgumentNullException ("stream"); - - if (!stream.CanRead) { - var msg = "It cannot be read."; - throw new ArgumentException (msg, "stream"); - } - - if (length < 1) { - var msg = "Less than 1."; - throw new ArgumentException (msg, "length"); - } - - var bytes = stream.ReadBytes (length); - - var len = bytes.Length; - if (len == 0) { - var msg = "No data could be read from it."; - throw new ArgumentException (msg, "stream"); - } - - if (len < length) { - _logger.Warn ( - String.Format ( - "Only {0} byte(s) of data could be read from the stream.", - len - ) - ); - } - - sendAsync (Opcode.Binary, new MemoryStream (bytes), completed); - } + FileStream stream; + if (!fileInfo.TryOpenRead(out stream)) + { + var msg = "The file could not be opened."; + throw new ArgumentException(msg, "fileInfo"); + } - /// - /// Sets an HTTP cookie to send with the handshake request. - /// - /// - /// This method does nothing if the connection has already been - /// established or it is closing. - /// - /// - /// A that represents the cookie to send. - /// - /// - /// This instance is not a client. - /// - /// - /// is . - /// - public void SetCookie (Cookie cookie) - { - string msg = null; + send(Opcode.Binary, stream); + } - if (!_client) { - msg = "This instance is not a client."; - throw new InvalidOperationException (msg); - } + /// + /// Sends the specified data using the WebSocket connection. + /// + /// + /// A that represents the text data to send. + /// + /// + /// The current state of the connection is not Open. + /// + /// + /// is . + /// + /// + /// could not be UTF-8-encoded. + /// + public void Send(string data) + { + if (_readyState != WebSocketState.Open) + { + var msg = "The current state of the connection is not Open."; + throw new InvalidOperationException(msg); + } - if (cookie == null) - throw new ArgumentNullException ("cookie"); + if (data == null) + throw new ArgumentNullException("data"); - if (!canSet (out msg)) { - _logger.Warn (msg); - return; - } + byte[] bytes; + if (!data.TryGetUTF8EncodedBytes(out bytes)) + { + var msg = "It could not be UTF-8-encoded."; + throw new ArgumentException(msg, "data"); + } - lock (_forState) { - if (!canSet (out msg)) { - _logger.Warn (msg); - return; + send(Opcode.Text, new MemoryStream(bytes)); } - lock (_cookies.SyncRoot) - _cookies.SetOrRemove (cookie); - } - } + /// + /// Sends the data from the specified stream using the WebSocket connection. + /// + /// + /// + /// A instance from which to read the data to send. + /// + /// + /// The data is sent as the binary data. + /// + /// + /// + /// An that specifies the number of bytes to send. + /// + /// + /// The current state of the connection is not Open. + /// + /// + /// is . + /// + /// + /// + /// cannot be read. + /// + /// + /// -or- + /// + /// + /// is less than 1. + /// + /// + /// -or- + /// + /// + /// No data could be read from . + /// + /// + public void Send(Stream stream, int length) + { + if (_readyState != WebSocketState.Open) + { + var msg = "The current state of the connection is not Open."; + throw new InvalidOperationException(msg); + } - /// - /// Sets the credentials for the HTTP authentication (Basic/Digest). - /// - /// - /// This method does nothing if the connection has already been - /// established or it is closing. - /// - /// - /// - /// A that represents the username associated with - /// the credentials. - /// - /// - /// or an empty string if initializes - /// the credentials. - /// - /// - /// - /// - /// A that represents the password for the username - /// associated with the credentials. - /// - /// - /// or an empty string if not necessary. - /// - /// - /// - /// true if sends the credentials for the Basic authentication in - /// advance with the first handshake request; otherwise, false. - /// - /// - /// This instance is not a client. - /// - /// - /// - /// contains an invalid character. - /// - /// - /// -or- - /// - /// - /// contains an invalid character. - /// - /// - public void SetCredentials (string username, string password, bool preAuth) - { - string msg = null; + if (stream == null) + throw new ArgumentNullException("stream"); + + if (!stream.CanRead) + { + var msg = "It cannot be read."; + throw new ArgumentException(msg, "stream"); + } + + if (length < 1) + { + var msg = "Less than 1."; + throw new ArgumentException(msg, "length"); + } + + var bytes = stream.ReadBytes(length); - if (!_client) { - msg = "This instance is not a client."; - throw new InvalidOperationException (msg); - } + var len = bytes.Length; + if (len == 0) + { + var msg = "No data could be read from it."; + throw new ArgumentException(msg, "stream"); + } + + if (len < length) + { + _logger.Warn( + String.Format( + "Only {0} byte(s) of data could be read from the stream.", + len + ) + ); + } - if (!username.IsNullOrEmpty ()) { - if (username.Contains (':') || !username.IsText ()) { - msg = "It contains an invalid character."; - throw new ArgumentException (msg, "username"); + send(Opcode.Binary, new MemoryStream(bytes)); } - } - if (!password.IsNullOrEmpty ()) { - if (!password.IsText ()) { - msg = "It contains an invalid character."; - throw new ArgumentException (msg, "password"); + /// + /// Sends the specified data asynchronously using the WebSocket connection. + /// + /// + /// This method does not wait for the send to be complete. + /// + /// + /// An array of that represents the binary data to send. + /// + /// + /// + /// An Action<bool> delegate or + /// if not needed. + /// + /// + /// The delegate invokes the method called when the send is complete. + /// + /// + /// true is passed to the method if the send has done with + /// no error; otherwise, false. + /// + /// + /// + /// The current state of the connection is not Open. + /// + /// + /// is . + /// + public void SendAsync(byte[] data, Action completed) + { + if (_readyState != WebSocketState.Open) + { + var msg = "The current state of the connection is not Open."; + throw new InvalidOperationException(msg); + } + + if (data == null) + throw new ArgumentNullException("data"); + + sendAsync(Opcode.Binary, new MemoryStream(data), completed); } - } - if (!canSet (out msg)) { - _logger.Warn (msg); - return; - } + /// + /// Sends the specified file asynchronously using the WebSocket connection. + /// + /// + /// This method does not wait for the send to be complete. + /// + /// + /// + /// A that specifies the file to send. + /// + /// + /// The file is sent as the binary data. + /// + /// + /// + /// + /// An Action<bool> delegate or + /// if not needed. + /// + /// + /// The delegate invokes the method called when the send is complete. + /// + /// + /// true is passed to the method if the send has done with + /// no error; otherwise, false. + /// + /// + /// + /// The current state of the connection is not Open. + /// + /// + /// is . + /// + /// + /// + /// The file does not exist. + /// + /// + /// -or- + /// + /// + /// The file could not be opened. + /// + /// + public void SendAsync(FileInfo fileInfo, Action completed) + { + if (_readyState != WebSocketState.Open) + { + var msg = "The current state of the connection is not Open."; + throw new InvalidOperationException(msg); + } + + if (fileInfo == null) + throw new ArgumentNullException("fileInfo"); + + if (!fileInfo.Exists) + { + var msg = "The file does not exist."; + throw new ArgumentException(msg, "fileInfo"); + } - lock (_forState) { - if (!canSet (out msg)) { - _logger.Warn (msg); - return; + FileStream stream; + if (!fileInfo.TryOpenRead(out stream)) + { + var msg = "The file could not be opened."; + throw new ArgumentException(msg, "fileInfo"); + } + + sendAsync(Opcode.Binary, stream, completed); } - if (username.IsNullOrEmpty ()) { - _credentials = null; - _preAuth = false; + /// + /// Sends the specified data asynchronously using the WebSocket connection. + /// + /// + /// This method does not wait for the send to be complete. + /// + /// + /// A that represents the text data to send. + /// + /// + /// + /// An Action<bool> delegate or + /// if not needed. + /// + /// + /// The delegate invokes the method called when the send is complete. + /// + /// + /// true is passed to the method if the send has done with + /// no error; otherwise, false. + /// + /// + /// + /// The current state of the connection is not Open. + /// + /// + /// is . + /// + /// + /// could not be UTF-8-encoded. + /// + public void SendAsync(string data, Action completed) + { + if (_readyState != WebSocketState.Open) + { + var msg = "The current state of the connection is not Open."; + throw new InvalidOperationException(msg); + } + + if (data == null) + throw new ArgumentNullException("data"); + + byte[] bytes; + if (!data.TryGetUTF8EncodedBytes(out bytes)) + { + var msg = "It could not be UTF-8-encoded."; + throw new ArgumentException(msg, "data"); + } - return; + sendAsync(Opcode.Text, new MemoryStream(bytes), completed); } - _credentials = new NetworkCredential ( - username, password, _uri.PathAndQuery - ); + /// + /// Sends the data from the specified stream asynchronously using + /// the WebSocket connection. + /// + /// + /// This method does not wait for the send to be complete. + /// + /// + /// + /// A instance from which to read the data to send. + /// + /// + /// The data is sent as the binary data. + /// + /// + /// + /// An that specifies the number of bytes to send. + /// + /// + /// + /// An Action<bool> delegate or + /// if not needed. + /// + /// + /// The delegate invokes the method called when the send is complete. + /// + /// + /// true is passed to the method if the send has done with + /// no error; otherwise, false. + /// + /// + /// + /// The current state of the connection is not Open. + /// + /// + /// is . + /// + /// + /// + /// cannot be read. + /// + /// + /// -or- + /// + /// + /// is less than 1. + /// + /// + /// -or- + /// + /// + /// No data could be read from . + /// + /// + public void SendAsync(Stream stream, int length, Action completed) + { + if (_readyState != WebSocketState.Open) + { + var msg = "The current state of the connection is not Open."; + throw new InvalidOperationException(msg); + } - _preAuth = preAuth; - } - } + if (stream == null) + throw new ArgumentNullException("stream"); - /// - /// Sets the URL of the HTTP proxy server through which to connect and - /// the credentials for the HTTP proxy authentication (Basic/Digest). - /// - /// - /// This method does nothing if the connection has already been - /// established or it is closing. - /// - /// - /// - /// A that represents the URL of the proxy server - /// through which to connect. - /// - /// - /// The syntax is http://<host>[:<port>]. - /// - /// - /// or an empty string if initializes the URL and - /// the credentials. - /// - /// - /// - /// - /// A that represents the username associated with - /// the credentials. - /// - /// - /// or an empty string if the credentials are not - /// necessary. - /// - /// - /// - /// - /// A that represents the password for the username - /// associated with the credentials. - /// - /// - /// or an empty string if not necessary. - /// - /// - /// - /// This instance is not a client. - /// - /// - /// - /// is not an absolute URI string. - /// - /// - /// -or- - /// - /// - /// The scheme of is not http. - /// - /// - /// -or- - /// - /// - /// includes the path segments. - /// - /// - /// -or- - /// - /// - /// contains an invalid character. - /// - /// - /// -or- - /// - /// - /// contains an invalid character. - /// - /// - public void SetProxy (string url, string username, string password) - { - string msg = null; + if (!stream.CanRead) + { + var msg = "It cannot be read."; + throw new ArgumentException(msg, "stream"); + } - if (!_client) { - msg = "This instance is not a client."; - throw new InvalidOperationException (msg); - } + if (length < 1) + { + var msg = "Less than 1."; + throw new ArgumentException(msg, "length"); + } - Uri uri = null; + var bytes = stream.ReadBytes(length); - if (!url.IsNullOrEmpty ()) { - if (!Uri.TryCreate (url, UriKind.Absolute, out uri)) { - msg = "Not an absolute URI string."; - throw new ArgumentException (msg, "url"); - } + var len = bytes.Length; + if (len == 0) + { + var msg = "No data could be read from it."; + throw new ArgumentException(msg, "stream"); + } - if (uri.Scheme != "http") { - msg = "The scheme part is not http."; - throw new ArgumentException (msg, "url"); - } + if (len < length) + { + _logger.Warn( + String.Format( + "Only {0} byte(s) of data could be read from the stream.", + len + ) + ); + } - if (uri.Segments.Length > 1) { - msg = "It includes the path segments."; - throw new ArgumentException (msg, "url"); + sendAsync(Opcode.Binary, new MemoryStream(bytes), completed); } - } - if (!username.IsNullOrEmpty ()) { - if (username.Contains (':') || !username.IsText ()) { - msg = "It contains an invalid character."; - throw new ArgumentException (msg, "username"); - } - } + /// + /// Sets an HTTP cookie to send with the handshake request. + /// + /// + /// This method does nothing if the connection has already been + /// established or it is closing. + /// + /// + /// A that represents the cookie to send. + /// + /// + /// This instance is not a client. + /// + /// + /// is . + /// + public void SetCookie(Cookie cookie) + { + string msg = null; + + if (!_client) + { + msg = "This instance is not a client."; + throw new InvalidOperationException(msg); + } - if (!password.IsNullOrEmpty ()) { - if (!password.IsText ()) { - msg = "It contains an invalid character."; - throw new ArgumentException (msg, "password"); - } - } + if (cookie == null) + throw new ArgumentNullException("cookie"); + + if (!canSet(out msg)) + { + _logger.Warn(msg); + return; + } - if (!canSet (out msg)) { - _logger.Warn (msg); - return; - } + lock (_forState) + { + if (!canSet(out msg)) + { + _logger.Warn(msg); + return; + } - lock (_forState) { - if (!canSet (out msg)) { - _logger.Warn (msg); - return; + lock (_cookies.SyncRoot) + _cookies.SetOrRemove(cookie); + } } - if (url.IsNullOrEmpty ()) { - _proxyUri = null; - _proxyCredentials = null; + /// + /// Sets the credentials for the HTTP authentication (Basic/Digest). + /// + /// + /// This method does nothing if the connection has already been + /// established or it is closing. + /// + /// + /// + /// A that represents the username associated with + /// the credentials. + /// + /// + /// or an empty string if initializes + /// the credentials. + /// + /// + /// + /// + /// A that represents the password for the username + /// associated with the credentials. + /// + /// + /// or an empty string if not necessary. + /// + /// + /// + /// true if sends the credentials for the Basic authentication in + /// advance with the first handshake request; otherwise, false. + /// + /// + /// This instance is not a client. + /// + /// + /// + /// contains an invalid character. + /// + /// + /// -or- + /// + /// + /// contains an invalid character. + /// + /// + public void SetCredentials(string username, string password, bool preAuth) + { + string msg = null; + + if (!_client) + { + msg = "This instance is not a client."; + throw new InvalidOperationException(msg); + } + + if (!username.IsNullOrEmpty()) + { + if (username.Contains(':') || !username.IsText()) + { + msg = "It contains an invalid character."; + throw new ArgumentException(msg, "username"); + } + } + + if (!password.IsNullOrEmpty()) + { + if (!password.IsText()) + { + msg = "It contains an invalid character."; + throw new ArgumentException(msg, "password"); + } + } + + if (!canSet(out msg)) + { + _logger.Warn(msg); + return; + } - return; + lock (_forState) + { + if (!canSet(out msg)) + { + _logger.Warn(msg); + return; + } + + if (username.IsNullOrEmpty()) + { + _credentials = null; + _preAuth = false; + + return; + } + + _credentials = new NetworkCredential( + username, password, _uri.PathAndQuery + ); + + _preAuth = preAuth; + } } - _proxyUri = uri; - _proxyCredentials = !username.IsNullOrEmpty () - ? new NetworkCredential ( - username, - password, - String.Format ( - "{0}:{1}", _uri.DnsSafeHost, _uri.Port - ) - ) - : null; - } - } + /// + /// Sets the URL of the HTTP proxy server through which to connect and + /// the credentials for the HTTP proxy authentication (Basic/Digest). + /// + /// + /// This method does nothing if the connection has already been + /// established or it is closing. + /// + /// + /// + /// A that represents the URL of the proxy server + /// through which to connect. + /// + /// + /// The syntax is http://<host>[:<port>]. + /// + /// + /// or an empty string if initializes the URL and + /// the credentials. + /// + /// + /// + /// + /// A that represents the username associated with + /// the credentials. + /// + /// + /// or an empty string if the credentials are not + /// necessary. + /// + /// + /// + /// + /// A that represents the password for the username + /// associated with the credentials. + /// + /// + /// or an empty string if not necessary. + /// + /// + /// + /// This instance is not a client. + /// + /// + /// + /// is not an absolute URI string. + /// + /// + /// -or- + /// + /// + /// The scheme of is not http. + /// + /// + /// -or- + /// + /// + /// includes the path segments. + /// + /// + /// -or- + /// + /// + /// contains an invalid character. + /// + /// + /// -or- + /// + /// + /// contains an invalid character. + /// + /// + public void SetProxy(string url, string username, string password) + { + string msg = null; + + if (!_client) + { + msg = "This instance is not a client."; + throw new InvalidOperationException(msg); + } - #endregion + Uri uri = null; + + if (!url.IsNullOrEmpty()) + { + if (!Uri.TryCreate(url, UriKind.Absolute, out uri)) + { + msg = "Not an absolute URI string."; + throw new ArgumentException(msg, "url"); + } + + if (uri.Scheme != "http") + { + msg = "The scheme part is not http."; + throw new ArgumentException(msg, "url"); + } + + if (uri.Segments.Length > 1) + { + msg = "It includes the path segments."; + throw new ArgumentException(msg, "url"); + } + } - #region Explicit Interface Implementations + if (!username.IsNullOrEmpty()) + { + if (username.Contains(':') || !username.IsText()) + { + msg = "It contains an invalid character."; + throw new ArgumentException(msg, "username"); + } + } - /// - /// Closes the connection and releases all associated resources. - /// - /// - /// - /// This method closes the connection with close status 1001 (going away). - /// - /// - /// And this method does nothing if the current state of the connection is - /// Closing or Closed. - /// - /// - void IDisposable.Dispose () - { - close (1001, String.Empty); - } + if (!password.IsNullOrEmpty()) + { + if (!password.IsText()) + { + msg = "It contains an invalid character."; + throw new ArgumentException(msg, "password"); + } + } - #endregion - } + if (!canSet(out msg)) + { + _logger.Warn(msg); + return; + } + + lock (_forState) + { + if (!canSet(out msg)) + { + _logger.Warn(msg); + return; + } + + if (url.IsNullOrEmpty()) + { + _proxyUri = null; + _proxyCredentials = null; + + return; + } + + _proxyUri = uri; + _proxyCredentials = !username.IsNullOrEmpty() + ? new NetworkCredential( + username, + password, + String.Format( + "{0}:{1}", _uri.DnsSafeHost, _uri.Port + ) + ) + : null; + } + } + + #endregion + + #region Explicit Interface Implementations + + /// + /// Closes the connection and releases all associated resources. + /// + /// + /// + /// This method closes the connection with close status 1001 (going away). + /// + /// + /// And this method does nothing if the current state of the connection is + /// Closing or Closed. + /// + /// + void IDisposable.Dispose() + { + close(1001, String.Empty); + } + + #endregion + } }