diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 376bdfd86..000000000 --- a/.gitignore +++ /dev/null @@ -1,14 +0,0 @@ -## Ignore build results and temporary files. - -Backup* -_UpgradeReport_Files -bin -obj - -*.mdb -*.pdb -*.pidb -*.suo -*.user -*.userprefs -UpgradeLog*.* 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 ee89d9f6e..000000000 --- a/Example/Example.csproj +++ /dev/null @@ -1,65 +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 - 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/Example/Program.cs b/Example/Program.cs deleted file mode 100644 index 8d16c0794..000000000 --- a/Example/Program.cs +++ /dev/null @@ -1,149 +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 ws = new WebSocket ("ws://localhost:4649/Echo")) - //using (var ws = new WebSocket ("wss://localhost:5963/Echo")) - 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")) - { -#if DEBUG - // To change the logging level. - ws.Log.Level = LogLevel.Trace; - - // To enable the Per-message Compression extension. - //ws.Compression = CompressionMethod.Deflate; - - // To emit a WebSocket.OnMessage event when receives a ping. - //ws.EmitOnPing = true; - - // To enable the redirection. - //ws.EnableRedirection = true; - - // To disable a delay when send or receive buffer of the underlying - // TCP socket is not full. - ws.NoDelay = true; - - // 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 send the credentials for the HTTP Authentication (Basic/Digest). - //ws.SetCredentials ("nobita", "password", false); - - // To connect through the HTTP Proxy server. - //ws.SetProxy ("http://localhost:3128", "nobita", "password"); - - // To send a user header. - - var reqHeader = "RequestForID"; - var resHeader = "ID"; - - ws.SetUserHeader (reqHeader, resHeader); - - - // To validate the server certificate. - /* - ws.SslConfiguration.ServerCertificateValidationCallback = - (sender, certificate, chain, sslPolicyErrors) => { - var fmt = "Certificate:\n- Issuer: {0}\n- Subject: {1}"; - var msg = String.Format ( - fmt, - certificate.Issuer, - certificate.Subject - ); - - ws.Log.Debug (msg); - - return true; // If the server certificate is valid. - }; - */ - - // To change the wait time for the response to the Ping or Close. - //ws.WaitTime = TimeSpan.FromSeconds (10); -#endif - // Set the WebSocket events. - - ws.OnClose += - (sender, e) => { - var fmt = "[WebSocket Close ({0})] {1}"; - - Console.WriteLine (fmt, e.Code, e.Reason); - }; - - ws.OnError += - (sender, e) => { - var fmt = "[WebSocket Error] {0}"; - - Console.WriteLine (fmt, e.Message); - }; - - ws.OnMessage += - (sender, e) => { - var fmt = e.IsPing - ? "[WebSocket Ping] {0}" - : "[WebSocket Message] {0}"; - - Console.WriteLine (fmt, e.Data); - }; - - ws.OnOpen += - (sender, e) => { -#if DEBUG - var val = ws.HandshakeResponseHeaders[resHeader]; - - if (!val.IsNullOrEmpty ()) { - var fmt = "[WebSocket Open] {0}: {1}"; - - Console.WriteLine (fmt, resHeader, val); - } -#endif - ws.Send ("Hi, there!"); - }; - - // 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/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 2391bf313..000000000 --- a/Example2/Chat.cs +++ /dev/null @@ -1,70 +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 () - { - _prefix = "anon#"; - } - - public string Prefix { - get { - return _prefix; - } - - set { - _prefix = !value.IsNullOrEmpty () ? value : "anon#"; - } - } - - private string getName () - { - var name = QueryString["name"]; - - return !name.IsNullOrEmpty () ? name : _prefix + getNumber (); - } - - private static int getNumber () - { - return Interlocked.Increment (ref _number); - } - - protected override void OnClose (CloseEventArgs e) - { - if (_name == null) - return; - - var fmt = "{0} got logged off..."; - var msg = String.Format (fmt, _name); - - Sessions.Broadcast (msg); - } - - protected override void OnMessage (MessageEventArgs e) - { - var fmt = "{0}: {1}"; - var msg = String.Format (fmt, _name, e.Data); - - Sessions.Broadcast (msg); - } - - protected override void OnOpen () - { - _name = getName (); - - var fmt = "{0} has logged in!"; - var msg = String.Format (fmt, _name); - - Sessions.Broadcast (msg); - } - } -} diff --git a/Example2/Echo.cs b/Example2/Echo.cs deleted file mode 100644 index edc8872f9..000000000 --- a/Example2/Echo.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using WebSocketSharp; -using WebSocketSharp.Server; - -namespace Example2 -{ - public class Echo : WebSocketBehavior - { - protected override void OnMessage (MessageEventArgs e) - { - Send (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 4a2e8c0bf..000000000 --- a/Example2/Program.cs +++ /dev/null @@ -1,172 +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 - // with 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 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 are not found. - }; - */ - - // To remove the inactive sessions periodically. - //wssv.KeepClean = true; - - // To resolve to wait for socket in TIME_WAIT state. - //wssv.ReuseAddress = true; - - // To provide the secure connection. - /* - var cert = ConfigurationManager.AppSettings["ServerCertFile"]; - var passwd = ConfigurationManager.AppSettings["CertFilePassword"]; - - wssv.SslConfiguration.ServerCertificate = new X509Certificate2 ( - cert, - passwd - ); - */ - - // To change the wait time for the response to the WebSocket Ping or Close. - //wssv.WaitTime = TimeSpan.FromSeconds (2); -#endif - // Add the WebSocket services. - - wssv.AddWebSocketService ("/Echo"); - - // With initializing. - wssv.AddWebSocketService ( - "/Chat", - s => { - s.Prefix = "Anon#"; -#if DEBUG - // To respond to the cookies. - /* - s.CookiesResponder = - (reqCookies, resCookies) => { - foreach (var cookie in reqCookies) { - cookie.Expired = true; - - resCookies.Add (cookie); - } - }; - */ - - // To emit a WebSocket.OnMessage event when receives a ping. - //s.EmitOnPing = true; - - // To ignore the Sec-WebSocket-Extensions header. - //s.IgnoreExtensions = true; - - // To disable a delay when send or receive buffer of the underlying - // TCP socket is not full. - s.NoDelay = true; - - // To validate the Origin header. - /* - s.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 send the Sec-WebSocket-Protocol header that has a subprotocol - // name. - //s.Protocol = "chat"; - - // To respond to the user headers. - - s.UserHeadersResponder = - (reqHeaders, userHeaders) => { - var val = reqHeaders["RequestForID"]; - - if (!val.IsNullOrEmpty ()) - userHeaders[val] = s.ID; - }; - -#endif - } - ); - - // Start the server. - wssv.Start (); - - if (wssv.IsListening) { - var fmt = "Listening on port {0}, and providing WebSocket services:"; - - Console.WriteLine (fmt, wssv.Port); - - foreach (var path in wssv.WebSocketServices.Paths) - Console.WriteLine ("- {0}", path); - } - - Console.WriteLine ("\nPress Enter key to stop the server..."); - - Console.ReadLine (); - - // Stop the server. - 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 0e3f38214..000000000 --- a/Example3/Chat.cs +++ /dev/null @@ -1,70 +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 () - { - _prefix = "anon#"; - } - - public string Prefix { - get { - return _prefix; - } - - set { - _prefix = !value.IsNullOrEmpty () ? value : "anon#"; - } - } - - private string getName () - { - var name = QueryString["name"]; - - return !name.IsNullOrEmpty () ? name : _prefix + getNumber (); - } - - private static int getNumber () - { - return Interlocked.Increment (ref _number); - } - - protected override void OnClose (CloseEventArgs e) - { - if (_name == null) - return; - - var fmt = "{0} got logged off..."; - var msg = String.Format (fmt, _name); - - Sessions.Broadcast (msg); - } - - protected override void OnMessage (MessageEventArgs e) - { - var fmt = "{0}: {1}"; - var msg = String.Format (fmt, _name, e.Data); - - Sessions.Broadcast (msg); - } - - protected override void OnOpen () - { - _name = getName (); - - var fmt = "{0} has logged in!"; - var msg = String.Format (fmt, _name); - - Sessions.Broadcast (msg); - } - } -} diff --git a/Example3/Echo.cs b/Example3/Echo.cs deleted file mode 100644 index 01e2f16af..000000000 --- a/Example3/Echo.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using WebSocketSharp; -using WebSocketSharp.Server; - -namespace Example3 -{ - public class Echo : WebSocketBehavior - { - protected override void OnMessage (MessageEventArgs e) - { - Send (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 259b5164b..000000000 --- a/Example3/Program.cs +++ /dev/null @@ -1,210 +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 - // with 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 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 are not found. - }; - */ - - // To remove the inactive WebSocket sessions periodically. - //httpsv.KeepClean = true; - - // To resolve to wait for socket in TIME_WAIT state. - //httpsv.ReuseAddress = true; - - // To provide the secure connection. - /* - var cert = ConfigurationManager.AppSettings["ServerCertFile"]; - var passwd = ConfigurationManager.AppSettings["CertFilePassword"]; - - httpsv.SslConfiguration.ServerCertificate = new X509Certificate2 ( - cert, - passwd - ); - */ - - // To change the wait time for the response to the WebSocket Ping or Close. - //httpsv.WaitTime = TimeSpan.FromSeconds (2); -#endif - // 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"); - - // With initializing. - httpsv.AddWebSocketService ( - "/Chat", - s => { - s.Prefix = "Anon#"; -#if DEBUG - // To respond to the cookies. - /* - s.CookiesResponder = - (reqCookies, resCookies) => { - foreach (var cookie in reqCookies) { - cookie.Expired = true; - - resCookies.Add (cookie); - } - }; - */ - - // To emit a WebSocket.OnMessage event when receives a ping. - //s.EmitOnPing = true; - - // To ignore the Sec-WebSocket-Extensions header. - //s.IgnoreExtensions = true; - - // To disable a delay when send or receive buffer of the underlying - // TCP socket is not full. - s.NoDelay = true; - - // To validate the Origin header. - /* - s.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 send the Sec-WebSocket-Protocol header that has a subprotocol - // name. - //s.Protocol = "chat"; - - // To respond to the user headers. - - s.UserHeadersResponder = - (reqHeaders, userHeaders) => { - var val = reqHeaders["RequestForID"]; - - if (!val.IsNullOrEmpty ()) - userHeaders[val] = s.ID; - }; - -#endif - } - ); - - // Start the server. - httpsv.Start (); - - if (httpsv.IsListening) { - var fmt = "Listening on port {0}, and providing WebSocket services:"; - - Console.WriteLine (fmt, httpsv.Port); - - foreach (var path in httpsv.WebSocketServices.Paths) - Console.WriteLine ("- {0}", path); - } - - Console.WriteLine ("\nPress Enter key to stop the server..."); - - Console.ReadLine (); - - // Stop the server. - 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/LICENSE.txt b/LICENSE.txt deleted file mode 100644 index 5f791375f..000000000 --- a/LICENSE.txt +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2010-2026 sta.blockhead - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/README.md b/README.md deleted file mode 100644 index 4d9a5fd7b..000000000 --- a/README.md +++ /dev/null @@ -1,721 +0,0 @@ -![Logo](websocket-sharp_logo.png) - -## Welcome to websocket-sharp! ## - -websocket-sharp supports: - -- [RFC 6455](#supported-websocket-specifications) -- [WebSocket Client](#websocket-client) and [Server](#websocket-server) -- [Per-message Compression](#per-message-compression) extension -- [Secure Connection](#secure-connection) -- [HTTP Authentication](#http-authentication) -- [Query string, Origin header, Cookies, and User headers](#query-string-origin-header-cookies-and-user-headers) -- [Connecting through the HTTP proxy server](#connecting-through-the-http-proxy-server) -- .NET Framework **3.5** or later versions of .NET Framework (includes compatible environment such as [Mono]) - -## 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. - -## 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. - -## Install ## - -### Self Build ### - -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 - -## Usage ## - -### WebSocket Client ### - -```csharp -using System; -using WebSocketSharp; - -namespace Example -{ - public class Program - { - public static void Main (string[] args) - { - using (var ws = new WebSocket ("ws://dragonsnest.far/Laputa")) { - ws.OnMessage += (sender, e) => - Console.WriteLine ("Laputa says: " + e.Data); - - ws.Connect (); - ws.Send ("BALUS"); - Console.ReadKey (true); - } - } - } -} -``` - -#### Step 1 #### - -Required namespace. - -```csharp -using WebSocketSharp; -``` - -The `WebSocket` class exists in the `WebSocketSharp` namespace. - -#### Step 2 #### - -Creating a new instance of the `WebSocket` class with the WebSocket URL to connect. - -```csharp -var ws = new WebSocket ("ws://example.com"); -``` - -The `WebSocket` class inherits the `System.IDisposable` interface, so you can create it with the `using` statement. - -```csharp -using (var ws = new WebSocket ("ws://example.com")) { - ... -} -``` - -This will **close** the WebSocket connection with status code `1001` (going away) when the control leaves the `using` block. - -#### Step 3 #### - -Setting the `WebSocket` events. - -##### WebSocket.OnOpen Event ##### - -This event occurs when the WebSocket connection has been established. - -```csharp -ws.OnOpen += (sender, e) => { - ... - }; -``` - -`System.EventArgs.Empty` is passed as `e`, so you do not need to use it. - -##### WebSocket.OnMessage Event ##### - -This event occurs when the `WebSocket` instance receives a message. - -```csharp -ws.OnMessage += (sender, e) => { - ... - }; -``` - -A `WebSocketSharp.MessageEventArgs` instance is passed as `e`. - -If you would like to get the message data, you should access `e.Data` or `e.RawData` property. - -`e.Data` property returns a `string`, so it is mainly used to get the **text** message data. - -`e.RawData` property returns a `byte[]`, so it is mainly used to get the **binary** message data. - -```csharp -if (e.IsText) { - // Do something with e.Data. - ... - - return; -} - -if (e.IsBinary) { - // Do something with e.RawData. - ... - - return; -} -``` - -And if you would like to notify that a **ping** has been received, via this event, you should set the `WebSocket.EmitOnPing` property to `true`. - -```csharp -ws.EmitOnPing = true; -ws.OnMessage += (sender, e) => { - if (e.IsPing) { - // Do something to notify that a ping has been received. - ... - - return; - } - }; -``` - -##### WebSocket.OnError Event ##### - -This event occurs when the `WebSocket` instance gets an error. - -```csharp -ws.OnError += (sender, e) => { - ... - }; -``` - -A `WebSocketSharp.ErrorEventArgs` instance is passed as `e`. - -If you would like to get the error message, you should access `e.Message` property. - -`e.Message` property returns a `string` that represents the error message. - -And `e.Exception` property returns a `System.Exception` instance that represents the cause of the error if it is due to an exception. - -##### WebSocket.OnClose Event ##### - -This event occurs when the WebSocket connection has been closed. - -```csharp -ws.OnClose += (sender, e) => { - ... - }; -``` - -A `WebSocketSharp.CloseEventArgs` instance is passed as `e`. - -If you would like to get the reason for the close, you should access `e.Code` or `e.Reason` property. - -`e.Code` property returns a `ushort` that represents the status code for the close. - -`e.Reason` property returns a `string` that represents the reason for the close. - -#### Step 4 #### - -Connecting to the WebSocket server. - -```csharp -ws.Connect (); -``` - -If you would like to connect to the server asynchronously, you should use the `WebSocket.ConnectAsync ()` method. - -#### Step 5 #### - -Sending data to the WebSocket server. - -```csharp -ws.Send (data); -``` - -The `WebSocket.Send` method is overloaded. - -You can use the `WebSocket.Send (string)`, `WebSocket.Send (byte[])`, `WebSocket.Send (System.IO.FileInfo)`, or `WebSocket.Send (System.IO.Stream, int)` method to send the data. - -If you would like to send the data asynchronously, you should use the `WebSocket.SendAsync` method. - -```csharp -ws.SendAsync (data, completed); -``` - -And also if you would like to do something when the send is complete, you should set `completed` to any `Action` delegate. - -#### Step 6 #### - -Closing the WebSocket connection. - -```csharp -ws.Close (code, reason); -``` - -If you would like to close the connection explicitly, you should use the `WebSocket.Close` method. - -The `WebSocket.Close` method is overloaded. - -You can use the `WebSocket.Close ()`, `WebSocket.Close (ushort)`, `WebSocket.Close (WebSocketSharp.CloseStatusCode)`, `WebSocket.Close (ushort, string)`, or `WebSocket.Close (WebSocketSharp.CloseStatusCode, string)` method to close the connection. - -If you would like to close the connection asynchronously, you should use the `WebSocket.CloseAsync` method. - -### WebSocket Server ### - -```csharp -using System; -using WebSocketSharp; -using WebSocketSharp.Server; - -namespace Example -{ - public class Laputa : WebSocketBehavior - { - protected override void OnMessage (MessageEventArgs e) - { - var msg = e.Data == "BALUS" - ? "Are you kidding?" - : "I'm not available now."; - - Send (msg); - } - } - - public class Program - { - public static void Main (string[] args) - { - var wssv = new WebSocketServer ("ws://dragonsnest.far"); - - wssv.AddWebSocketService ("/Laputa"); - wssv.Start (); - Console.ReadKey (true); - wssv.Stop (); - } - } -} -``` - -#### Step 1 #### - -Required namespace. - -```csharp -using WebSocketSharp.Server; -``` - -The `WebSocketBehavior` and `WebSocketServer` classes exist in the `WebSocketSharp.Server` namespace. - -#### Step 2 #### - -Creating the class that inherits the `WebSocketBehavior` class. - -For example, if you would like to provide an echo service, - -```csharp -using System; -using WebSocketSharp; -using WebSocketSharp.Server; - -public class Echo : WebSocketBehavior -{ - protected override void OnMessage (MessageEventArgs e) - { - Send (e.Data); - } -} -``` - -And if you would like to provide a chat service, - -```csharp -using System; -using WebSocketSharp; -using WebSocketSharp.Server; - -public class Chat : WebSocketBehavior -{ - private string _suffix; - - public Chat () - { - _suffix = String.Empty; - } - - public string Suffix { - get { - return _suffix; - } - - set { - _suffix = value ?? String.Empty; - } - } - - protected override void OnMessage (MessageEventArgs e) - { - Sessions.Broadcast (e.Data + _suffix); - } -} -``` - -You can define the behavior of any WebSocket service by creating the class that inherits the `WebSocketBehavior` class. - -If you override the `WebSocketBehavior.OnMessage (MessageEventArgs)` method, it will be called when the `WebSocket` used in a session in the service receives a message. - -And if you override the `WebSocketBehavior.OnOpen ()`, `WebSocketBehavior.OnError (ErrorEventArgs)`, and `WebSocketBehavior.OnClose (CloseEventArgs)` methods, each of them will be called when each of the `WebSocket` events (`OnOpen`, `OnError`, and `OnClose`) occurs. - -The `WebSocketBehavior.Send` method can send data to the client on a session in the service. - -If you would like to get the sessions in the service, you should access the `WebSocketBehavior.Sessions` property (returns a `WebSocketSharp.Server.WebSocketSessionManager`). - -The `WebSocketBehavior.Sessions.Broadcast` method can send data to every client in the service. - -#### Step 3 #### - -Creating a new instance of the `WebSocketServer` class. - -```csharp -var wssv = new WebSocketServer (4649); - -wssv.AddWebSocketService ("/Echo"); -wssv.AddWebSocketService ("/Chat"); -wssv.AddWebSocketService ("/ChatWithNyan", s => s.Suffix = " Nyan!"); -``` - -You can add any WebSocket service to your `WebSocketServer` with the specified behavior and absolute path to the service, by using the `WebSocketServer.AddWebSocketService (string)` or `WebSocketServer.AddWebSocketService (string, Action)` method. - -The type of `TBehavior` must inherit the `WebSocketBehavior` class, and must have a public parameterless constructor. - -So you can use a class in the above Step 2 to add the service. - -If you create a new instance of the `WebSocketServer` class without a port number, it sets the port number to **80**. So it is necessary to run with root permission. - - $ sudo mono example2.exe - -#### Step 4 #### - -Starting the WebSocket server. - -```csharp -wssv.Start (); -``` - -#### Step 5 #### - -Stopping the WebSocket server. - -```csharp -wssv.Stop (); -``` - -### HTTP Server with the WebSocket ### - -I have modified the `System.Net.HttpListener`, `System.Net.HttpListenerContext`, and some other classes from **[Mono]** to create an HTTP server that allows to accept the WebSocket handshake requests. - -So websocket-sharp provides the `WebSocketSharp.Server.HttpServer` class. - -You can add any WebSocket service to your `HttpServer` with the specified behavior and path to the service, by using the `HttpServer.AddWebSocketService (string)` or `HttpServer.AddWebSocketService (string, Action)` method. - -```csharp -var httpsv = new HttpServer (4649); - -httpsv.AddWebSocketService ("/Echo"); -httpsv.AddWebSocketService ("/Chat"); -httpsv.AddWebSocketService ("/ChatWithNyan", s => s.Suffix = " Nyan!"); -``` - -For more information, would you see **[Example3]**? - -### WebSocket Extensions ### - -#### Per-message Compression #### - -websocket-sharp supports the [Per-message Compression][rfc7692] extension (but does not support it with the [context take over]). - -As a WebSocket client, if you would like to enable this extension, you should set the `WebSocket.Compression` property to a compression method before calling the connect method. - -```csharp -ws.Compression = CompressionMethod.Deflate; -``` - -And then the client will send the following header in the handshake request to the server. - - Sec-WebSocket-Extensions: permessage-deflate; server_no_context_takeover; client_no_context_takeover - -If the server supports this extension, it will return the same header which has the corresponding value. - -So eventually this extension will be available when the client receives the header in the handshake response. - -#### Ignoring the extensions #### - -As a WebSocket server, if you would like to ignore the extensions requested from a client, you should set the `WebSocketBehavior.IgnoreExtensions` property to `true` in your `WebSocketBehavior` constructor or initializing it, such as the following. - -```csharp -wssv.AddWebSocketService ( - "/Chat", - s => s.IgnoreExtensions = true // To ignore the extensions requested from a client. -); -``` - -If it is set to `true`, the service will not return the Sec-WebSocket-Extensions header in its handshake response. - -I think this is useful when you get something error in connecting the server and exclude the extensions as a cause of the error. - -### Secure Connection ### - -websocket-sharp supports the secure connection with **SSL/TLS**. - -As a WebSocket client, you should create a new instance of the `WebSocket` class with a **wss** scheme WebSocket URL. - -```csharp -var ws = new WebSocket ("wss://example.com"); -``` - -If you would like to set a custom validation for the server certificate, you should set the `WebSocket.SslConfiguration.ServerCertificateValidationCallback` property to a callback for it. - -```csharp -ws.SslConfiguration.ServerCertificateValidationCallback = - (sender, certificate, chain, sslPolicyErrors) => { - // Do something to validate the server certificate. - ... - - return true; // If the server certificate is valid. - }; -``` - -The default callback always returns `true`. - -As a WebSocket server, you should create a new instance of the `WebSocketServer` or `HttpServer` class with some settings for the secure connection, such as the following. - -```csharp -var wssv = new WebSocketServer (5963, true); -wssv.SslConfiguration.ServerCertificate = new X509Certificate2 ( - "/path/to/cert.pfx", "password for cert.pfx" - ); -``` - -### HTTP Authentication ### - -websocket-sharp supports the [HTTP Authentication (Basic/Digest)][rfc2617]. - -As a WebSocket client, you should set a pair of user name and password for the HTTP authentication, by using the `WebSocket.SetCredentials (string, string, bool)` method before calling the connect method. - -```csharp -ws.SetCredentials ("nobita", "password", preAuth); -``` - -If `preAuth` is `true`, the client will send the credentials for the Basic authentication in the first handshake request to the server. - -Otherwise, it will send the credentials for either the Basic or Digest (determined by the unauthorized response to the first handshake request) authentication in the second handshake request to the server. - -As a WebSocket server, you should set an HTTP authentication scheme, a realm, and any function to find the user credentials before calling the start method, such as the following. - -```csharp -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 are not found. - }; -``` - -If you would like to provide the Digest authentication, you should set such as the following. - -```csharp -wssv.AuthenticationSchemes = AuthenticationSchemes.Digest; -``` - -### Query string, Origin header, Cookies, and User headers ### - -#### Query string #### - -As a WebSocket client, if you would like to send the query string in the handshake request, you should create a new instance of the `WebSocket` class with a WebSocket URL that includes the [Query] string parameters. - -```csharp -var ws = new WebSocket ("ws://example.com/?name=nobita"); -``` - -As a WebSocket server, if you would like to get the query string included in a handshake request, you should access the `WebSocketBehavior.QueryString` property, such as the following. - -```csharp -public class Chat : WebSocketBehavior -{ - private string _name; - ... - - protected override void OnOpen () - { - _name = QueryString["name"]; - } - - ... -} -``` - -#### Origin header #### - -As a WebSocket client, if you would like to send the Origin header in the handshake request, you should set the `WebSocket.Origin` property to an allowable value as the [Origin] header before calling the connect method. - -```csharp -ws.Origin = "http://example.com"; -``` - -As a WebSocket server, if you would like to validate the Origin header, you should set a validation for it with your `WebSocketBehavior`, for example, by using the `WebSocketServer.AddWebSocketService (string, Action)` method with initializing, such as the following. - -```csharp -wssv.AddWebSocketService ( - "/Chat", - s => { - s.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 == "example.com"; - }; - } -); -``` - -#### Cookies #### - -As a WebSocket client, if you would like to send the cookies in the handshake request, you should set any cookie by using the `WebSocket.SetCookie (WebSocketSharp.Net.Cookie)` method before calling the connect method. - -```csharp -ws.SetCookie (new Cookie ("name", "nobita")); -``` - -As a WebSocket server, if you would like to respond to the cookies, you should set a response action for it with your `WebSocketBehavior`, for example, by using the `WebSocketServer.AddWebSocketService (string, Action)` method with initializing, such as the following. - -```csharp -wssv.AddWebSocketService ( - "/Chat", - s => { - s.CookiesResponder = - (reqCookies, resCookies) => { - foreach (var cookie in reqCookies) { - cookie.Expired = true; - - resCookies.Add (cookie); - } - }; - } -); -``` - -#### User headers #### - -As a WebSocket client, if you would like to send the user headers in the handshake request, you should set any user defined header by using the `WebSocket.SetUserHeader (string, string)` method before calling the connect method. - -```csharp -ws.SetUserHeader ("RequestForID", "ID"); -``` - -And if you would like to get the user headers included in the handshake response, you should access the `WebSocket.HandshakeResponseHeaders` property after the handshake is done. - -```csharp -var id = ws.HandshakeResponseHeaders["ID"]; -``` - -As a WebSocket server, if you would like to respond to the user headers, you should set a response action for it with your `WebSocketBehavior`, for example, by using the `WebSocketServer.AddWebSocketService (string, Action)` method with initializing, such as the following. - -```csharp -wssv.AddWebSocketService ( - "/Chat", - s => { - s.UserHeadersResponder = - (reqHeaders, userHeaders) => { - var val = reqHeaders["RequestForID"]; - - if (!val.IsNullOrEmpty ()) - userHeaders[val] = s.ID; - }; - } -); -``` - -### Connecting through the HTTP proxy server ### - -websocket-sharp supports to connect through the HTTP proxy server. - -If you would like to connect to a WebSocket server through the HTTP proxy server, you should set the proxy server URL, and if necessary, a pair of user name and password for the proxy server authentication (Basic/Digest), by using the `WebSocket.SetProxy (string, string, string)` method before calling the connect method. - -```csharp -var ws = new WebSocket ("ws://example.com"); -ws.SetProxy ("http://localhost:3128", "nobita", "password"); -``` - -I have tested this with **[Squid]**. It is necessary to disable the following option in **squid.conf** (e.g. `/etc/squid/squid.conf`). - -``` -# Deny CONNECT to other than SSL ports -#http_access deny CONNECT !SSL_ports -``` - -### Logging ### - -The `WebSocket` class has the own logging function. - -You can use it with the `WebSocket.Log` property (returns a `WebSocketSharp.Logger`). - -So if you would like to change the current logging level (`WebSocketSharp.LogLevel.Error` as the default), you should set the `WebSocket.Log.Level` property to any of the `LogLevel` enum values. - -```csharp -ws.Log.Level = LogLevel.Debug; -``` - -The above means a log with lower than `LogLevel.Debug` cannot be outputted. - -And if you would like to output a log, you should use any of the output methods. The following outputs a log with `LogLevel.Debug`. - -```csharp -ws.Log.Debug ("This is a debug message."); -``` - -The `WebSocketServer` and `HttpServer` classes have the same logging function. - -## Examples ## - -Examples using websocket-sharp. - -### Example ### - -[Example] connects to the server executed by [Example2] or [Example3]. - -### Example2 ### - -[Example2] starts a WebSocket server. - -### Example3 ### - -[Example3] starts an HTTP server that allows to accept the WebSocket handshake requests. - -Would you access to [http://localhost:4649](http://localhost:4649) to do **WebSocket Echo Test** with your web browser while Example3 is running? - -## Supported WebSocket Specifications ## - -websocket-sharp supports **RFC 6455**, and it is based on the following references: - -- [The WebSocket Protocol][rfc6455] -- [The WebSocket API][api] -- [Compression Extensions for WebSocket][rfc7692] - -## License ## - -websocket-sharp is provided under [The MIT License]. - - -[Example]: https://github.com/sta/websocket-sharp/tree/master/Example -[Example2]: https://github.com/sta/websocket-sharp/tree/master/Example2 -[Example3]: https://github.com/sta/websocket-sharp/tree/master/Example3 -[Mono]: http://www.mono-project.com -[MonoDevelop]: http://monodevelop.com -[NuGet Gallery]: http://www.nuget.org -[NuGet Gallery: websocket-sharp]: http://www.nuget.org/packages/WebSocketSharp -[Origin]: http://tools.ietf.org/html/rfc6454#section-7 -[Query]: http://tools.ietf.org/html/rfc3986#section-3.4 -[Squid]: http://www.squid-cache.org -[The MIT License]: https://raw.github.com/sta/websocket-sharp/master/LICENSE.txt -[Unity]: http://unity3d.com -[api]: http://www.w3.org/TR/websockets -[context take over]: https://datatracker.ietf.org/doc/html/rfc7692#section-7.1.1 -[draft-hixie-thewebsocketprotocol-75]: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75 -[draft-ietf-hybi-thewebsocketprotocol-00]: http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-00 -[draft75]: https://github.com/sta/websocket-sharp/tree/draft75 -[hybi-00]: https://github.com/sta/websocket-sharp/tree/hybi-00 -[master]: https://github.com/sta/websocket-sharp/tree/master -[rfc2617]: http://tools.ietf.org/html/rfc2617 -[rfc6455]: http://tools.ietf.org/html/rfc6455 -[rfc7692]: https://datatracker.ietf.org/doc/html/rfc7692 diff --git a/_config.yml b/_config.yml new file mode 100644 index 000000000..2dad95fc8 --- /dev/null +++ b/_config.yml @@ -0,0 +1,8 @@ +title: websocket-sharp +description: A C# implementation of the WebSocket protocol client and server +google_analytics: UA-9752433-2 +show_downloads: true +theme: jekyll-theme-cayman + +gems: + - jekyll-mentions diff --git a/fonts/OpenSans-Bold-webfont.eot b/fonts/OpenSans-Bold-webfont.eot new file mode 100644 index 000000000..e1c767443 Binary files /dev/null and b/fonts/OpenSans-Bold-webfont.eot differ diff --git a/fonts/OpenSans-Bold-webfont.svg b/fonts/OpenSans-Bold-webfont.svg new file mode 100644 index 000000000..364b36867 --- /dev/null +++ b/fonts/OpenSans-Bold-webfont.svg @@ -0,0 +1,146 @@ + + + + +This is a custom SVG webfont generated by Font Squirrel. +Copyright : Digitized data copyright 20102011 Google Corporation +Foundry : Ascender Corporation +Foundry URL : httpwwwascendercorpcom + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/fonts/OpenSans-Bold-webfont.ttf b/fonts/OpenSans-Bold-webfont.ttf new file mode 100644 index 000000000..2d94f0629 Binary files /dev/null and b/fonts/OpenSans-Bold-webfont.ttf differ diff --git a/fonts/OpenSans-Bold-webfont.woff b/fonts/OpenSans-Bold-webfont.woff new file mode 100644 index 000000000..cd86852d0 Binary files /dev/null and b/fonts/OpenSans-Bold-webfont.woff differ diff --git a/fonts/OpenSans-BoldItalic-webfont.eot b/fonts/OpenSans-BoldItalic-webfont.eot new file mode 100644 index 000000000..f44ac9a33 Binary files /dev/null and b/fonts/OpenSans-BoldItalic-webfont.eot differ diff --git a/fonts/OpenSans-BoldItalic-webfont.svg b/fonts/OpenSans-BoldItalic-webfont.svg new file mode 100644 index 000000000..8392240a1 --- /dev/null +++ b/fonts/OpenSans-BoldItalic-webfont.svg @@ -0,0 +1,146 @@ + + + + +This is a custom SVG webfont generated by Font Squirrel. +Copyright : Digitized data copyright 20102011 Google Corporation +Foundry : Ascender Corporation +Foundry URL : httpwwwascendercorpcom + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/fonts/OpenSans-BoldItalic-webfont.ttf b/fonts/OpenSans-BoldItalic-webfont.ttf new file mode 100644 index 000000000..f74e0e3ca Binary files /dev/null and b/fonts/OpenSans-BoldItalic-webfont.ttf differ diff --git a/fonts/OpenSans-BoldItalic-webfont.woff b/fonts/OpenSans-BoldItalic-webfont.woff new file mode 100644 index 000000000..f3248c114 Binary files /dev/null and b/fonts/OpenSans-BoldItalic-webfont.woff differ diff --git a/fonts/OpenSans-Italic-webfont.eot b/fonts/OpenSans-Italic-webfont.eot new file mode 100644 index 000000000..277c1899c Binary files /dev/null and b/fonts/OpenSans-Italic-webfont.eot differ diff --git a/fonts/OpenSans-Italic-webfont.svg b/fonts/OpenSans-Italic-webfont.svg new file mode 100644 index 000000000..29c7497fe --- /dev/null +++ b/fonts/OpenSans-Italic-webfont.svg @@ -0,0 +1,146 @@ + + + + +This is a custom SVG webfont generated by Font Squirrel. +Copyright : Digitized data copyright 20102011 Google Corporation +Foundry : Ascender Corporation +Foundry URL : httpwwwascendercorpcom + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/fonts/OpenSans-Italic-webfont.ttf b/fonts/OpenSans-Italic-webfont.ttf new file mode 100644 index 000000000..63f187e98 Binary files /dev/null and b/fonts/OpenSans-Italic-webfont.ttf differ diff --git a/fonts/OpenSans-Italic-webfont.woff b/fonts/OpenSans-Italic-webfont.woff new file mode 100644 index 000000000..469a29bbf Binary files /dev/null and b/fonts/OpenSans-Italic-webfont.woff differ diff --git a/fonts/OpenSans-Light-webfont.eot b/fonts/OpenSans-Light-webfont.eot new file mode 100644 index 000000000..837daab8d Binary files /dev/null and b/fonts/OpenSans-Light-webfont.eot differ diff --git a/fonts/OpenSans-Light-webfont.svg b/fonts/OpenSans-Light-webfont.svg new file mode 100644 index 000000000..bdb672653 --- /dev/null +++ b/fonts/OpenSans-Light-webfont.svg @@ -0,0 +1,146 @@ + + + + +This is a custom SVG webfont generated by Font Squirrel. +Copyright : Digitized data copyright 20102011 Google Corporation +Foundry : Ascender Corporation +Foundry URL : httpwwwascendercorpcom + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/fonts/OpenSans-Light-webfont.ttf b/fonts/OpenSans-Light-webfont.ttf new file mode 100644 index 000000000..b50ef9dcb Binary files /dev/null and b/fonts/OpenSans-Light-webfont.ttf differ diff --git a/fonts/OpenSans-Light-webfont.woff b/fonts/OpenSans-Light-webfont.woff new file mode 100644 index 000000000..99514d1a8 Binary files /dev/null and b/fonts/OpenSans-Light-webfont.woff differ diff --git a/fonts/OpenSans-LightItalic-webfont.eot b/fonts/OpenSans-LightItalic-webfont.eot new file mode 100644 index 000000000..f0ebf2c0e Binary files /dev/null and b/fonts/OpenSans-LightItalic-webfont.eot differ diff --git a/fonts/OpenSans-LightItalic-webfont.svg b/fonts/OpenSans-LightItalic-webfont.svg new file mode 100644 index 000000000..60765da83 --- /dev/null +++ b/fonts/OpenSans-LightItalic-webfont.svg @@ -0,0 +1,146 @@ + + + + +This is a custom SVG webfont generated by Font Squirrel. +Copyright : Digitized data copyright 20102011 Google Corporation +Foundry : Ascender Corporation +Foundry URL : httpwwwascendercorpcom + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/fonts/OpenSans-LightItalic-webfont.ttf b/fonts/OpenSans-LightItalic-webfont.ttf new file mode 100644 index 000000000..5898c8c78 Binary files /dev/null and b/fonts/OpenSans-LightItalic-webfont.ttf differ diff --git a/fonts/OpenSans-LightItalic-webfont.woff b/fonts/OpenSans-LightItalic-webfont.woff new file mode 100644 index 000000000..9c978dc30 Binary files /dev/null and b/fonts/OpenSans-LightItalic-webfont.woff differ diff --git a/fonts/OpenSans-Regular-webfont.eot b/fonts/OpenSans-Regular-webfont.eot new file mode 100644 index 000000000..dd6fd2cb3 Binary files /dev/null and b/fonts/OpenSans-Regular-webfont.eot differ diff --git a/fonts/OpenSans-Regular-webfont.svg b/fonts/OpenSans-Regular-webfont.svg new file mode 100644 index 000000000..01038bb1c --- /dev/null +++ b/fonts/OpenSans-Regular-webfont.svg @@ -0,0 +1,146 @@ + + + + +This is a custom SVG webfont generated by Font Squirrel. +Copyright : Digitized data copyright 20102011 Google Corporation +Foundry : Ascender Corporation +Foundry URL : httpwwwascendercorpcom + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/fonts/OpenSans-Regular-webfont.ttf b/fonts/OpenSans-Regular-webfont.ttf new file mode 100644 index 000000000..05951e7b3 Binary files /dev/null and b/fonts/OpenSans-Regular-webfont.ttf differ diff --git a/fonts/OpenSans-Regular-webfont.woff b/fonts/OpenSans-Regular-webfont.woff new file mode 100644 index 000000000..274664b28 Binary files /dev/null and b/fonts/OpenSans-Regular-webfont.woff differ diff --git a/fonts/OpenSans-Semibold-webfont.eot b/fonts/OpenSans-Semibold-webfont.eot new file mode 100644 index 000000000..289aade3e Binary files /dev/null and b/fonts/OpenSans-Semibold-webfont.eot differ diff --git a/fonts/OpenSans-Semibold-webfont.svg b/fonts/OpenSans-Semibold-webfont.svg new file mode 100644 index 000000000..cc2ca4275 --- /dev/null +++ b/fonts/OpenSans-Semibold-webfont.svg @@ -0,0 +1,146 @@ + + + + +This is a custom SVG webfont generated by Font Squirrel. +Copyright : Digitized data copyright 2011 Google Corporation +Foundry : Ascender Corporation +Foundry URL : httpwwwascendercorpcom + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/fonts/OpenSans-Semibold-webfont.ttf b/fonts/OpenSans-Semibold-webfont.ttf new file mode 100644 index 000000000..6f1507312 Binary files /dev/null and b/fonts/OpenSans-Semibold-webfont.ttf differ diff --git a/fonts/OpenSans-Semibold-webfont.woff b/fonts/OpenSans-Semibold-webfont.woff new file mode 100644 index 000000000..4e47cb1a6 Binary files /dev/null and b/fonts/OpenSans-Semibold-webfont.woff differ diff --git a/fonts/OpenSans-SemiboldItalic-webfont.eot b/fonts/OpenSans-SemiboldItalic-webfont.eot new file mode 100644 index 000000000..50a8a6f75 Binary files /dev/null and b/fonts/OpenSans-SemiboldItalic-webfont.eot differ diff --git a/fonts/OpenSans-SemiboldItalic-webfont.svg b/fonts/OpenSans-SemiboldItalic-webfont.svg new file mode 100644 index 000000000..65b50e2a6 --- /dev/null +++ b/fonts/OpenSans-SemiboldItalic-webfont.svg @@ -0,0 +1,146 @@ + + + + +This is a custom SVG webfont generated by Font Squirrel. +Copyright : Digitized data copyright 20102011 Google Corporation +Foundry : Ascender Corporation +Foundry URL : httpwwwascendercorpcom + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/fonts/OpenSans-SemiboldItalic-webfont.ttf b/fonts/OpenSans-SemiboldItalic-webfont.ttf new file mode 100644 index 000000000..55ba3120f Binary files /dev/null and b/fonts/OpenSans-SemiboldItalic-webfont.ttf differ diff --git a/fonts/OpenSans-SemiboldItalic-webfont.woff b/fonts/OpenSans-SemiboldItalic-webfont.woff new file mode 100644 index 000000000..0adc6df16 Binary files /dev/null and b/fonts/OpenSans-SemiboldItalic-webfont.woff differ diff --git a/images/body-bg.jpg b/images/body-bg.jpg new file mode 100644 index 000000000..0e0f861ba Binary files /dev/null and b/images/body-bg.jpg differ diff --git a/images/bullet.png b/images/bullet.png new file mode 100644 index 000000000..0614eb65f Binary files /dev/null and b/images/bullet.png differ diff --git a/images/checker.png b/images/checker.png new file mode 100644 index 000000000..ab1454032 Binary files /dev/null and b/images/checker.png differ diff --git a/images/download-button.png b/images/download-button.png new file mode 100644 index 000000000..df3f09a63 Binary files /dev/null and b/images/download-button.png differ diff --git a/images/github-button.png b/images/github-button.png new file mode 100644 index 000000000..efe07f9ad Binary files /dev/null and b/images/github-button.png differ diff --git a/images/header-bg.jpg b/images/header-bg.jpg new file mode 100644 index 000000000..960bff75f Binary files /dev/null and b/images/header-bg.jpg differ diff --git a/images/highlight-bg.jpg b/images/highlight-bg.jpg new file mode 100644 index 000000000..4c4a78eff Binary files /dev/null and b/images/highlight-bg.jpg differ diff --git a/images/hr.gif b/images/hr.gif new file mode 100644 index 000000000..bdb4168de Binary files /dev/null and b/images/hr.gif differ diff --git a/images/nav-bg.gif b/images/nav-bg.gif new file mode 100644 index 000000000..474396561 Binary files /dev/null and b/images/nav-bg.gif differ diff --git a/images/sidebar-bg.jpg b/images/sidebar-bg.jpg new file mode 100644 index 000000000..42890fe73 Binary files /dev/null and b/images/sidebar-bg.jpg differ diff --git a/index.md b/index.md new file mode 100644 index 000000000..ac0ef186b --- /dev/null +++ b/index.md @@ -0,0 +1,686 @@ +## Welcome to websocket-sharp! ## + +**websocket-sharp** supports: + +- **[RFC 6455](#supported-websocket-specifications)** +- **[WebSocket Client](#websocket-client)** and **[Server](#websocket-server)** +- **[Per-message Compression](#per-message-compression)** extension +- **[Secure Connection](#secure-connection)** +- **[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]**) + +## 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. + +## Install ## + +### Self Build ### + +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 **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**. + +- **[WebSocket-Sharp for Unity]** + +It works with **Unity Free**, but there are some limitations: + +- **[Security Sandbox of the Webplayer]** (The server isn't available in Web Player) +- **[WebGL Networking]** (Not available in WebGL) +- **Incompatible platform** (Not available for such UWP) +- **Limited support for the System.IO.Compression** (The compression extension isn't available on Windows) +- **.NET Socket Support for iOS/Android** (It requires iOS/Android Pro if your Unity is earlier than Unity 5) +- **.NET API 2.0 compatibility level for iOS/Android** + +**.NET API 2.0 compatibility level for iOS/Android** may require to fix lack of some features for later than .NET 2.0, such as the `System.Func<...>` delegates (so i have added them in that asset package). + +And it's priced at **US$15**. I think your $15 makes this project more better and accelerated, **Thank you!** + +## Usage ## + +### WebSocket Client ### + +```csharp +using System; +using WebSocketSharp; + +namespace Example +{ + public class Program + { + public static void Main (string[] args) + { + using (var ws = new WebSocket ("ws://dragonsnest.far/Laputa")) { + ws.OnMessage += (sender, e) => + Console.WriteLine ("Laputa says: " + e.Data); + + ws.Connect (); + ws.Send ("BALUS"); + Console.ReadKey (true); + } + } + } +} +``` + +#### Step 1 #### + +Required namespace. + +```csharp +using WebSocketSharp; +``` + +The `WebSocket` class exists in the `WebSocketSharp` namespace. + +#### Step 2 #### + +Creating a new instance of the `WebSocket` class with the WebSocket URL to connect. + +```csharp +using (var ws = new WebSocket ("ws://example.com")) { + ... +} +``` + +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. + +#### Step 3 #### + +Setting the `WebSocket` events. + +##### WebSocket.OnOpen Event ##### + +A `WebSocket.OnOpen` event occurs when the WebSocket connection has been established. + +```csharp +ws.OnOpen += (sender, e) => { + ... + }; +``` + +`e` has passed as the `System.EventArgs.Empty`, so you don't need to use it. + +##### WebSocket.OnMessage Event ##### + +A `WebSocket.OnMessage` event occurs when the `WebSocket` receives a message. + +```csharp +ws.OnMessage += (sender, e) => { + ... + }; +``` + +`e` has passed as a `WebSocketSharp.MessageEventArgs`. + +If you would like to get the message data, you should access `e.Data` or `e.RawData` property. + +And you can determine which property you should access by checking `e.IsText` or `e.IsBinary` property. + +If `e.IsText` is `true`, you should access `e.Data` that returns a `string` (represents a **text** message). + +Or if `e.IsBinary` is `true`, you should access `e.RawData` that returns a `byte[]` (represents a **binary** message). + +```csharp +if (e.IsText) { + // Do something with e.Data. + ... + + return; +} + +if (e.IsBinary) { + // Do something with e.RawData. + ... + + return; +} +``` + +And if you would like to notify that a **ping** has been received, via this event, you should set the `WebSocket.EmitOnPing` property to `true`, such as the following. + +```csharp +ws.EmitOnPing = true; +ws.OnMessage += (sender, e) => { + if (e.IsPing) { + // Do something to notify that a ping has been received. + ... + + return; + } + }; +``` + +##### WebSocket.OnError Event ##### + +A `WebSocket.OnError` event occurs when the `WebSocket` gets an error. + +```csharp +ws.OnError += (sender, e) => { + ... + }; +``` + +`e` has passed as a `WebSocketSharp.ErrorEventArgs`. + +`e.Message` property returns a `string` that represents the error message. + +If the error is due to an exception, `e.Exception` property returns a `System.Exception` instance that caused the error. + +##### WebSocket.OnClose Event ##### + +A `WebSocket.OnClose` event occurs when the WebSocket connection has been closed. + +```csharp +ws.OnClose += (sender, e) => { + ... + }; +``` + +`e` has passed as a `WebSocketSharp.CloseEventArgs`. + +`e.Code` property returns a `ushort` that represents the status code for the close, and `e.Reason` property returns a `string` that represents the reason for the close. + +#### Step 4 #### + +Connecting to the WebSocket server. + +```csharp +ws.Connect (); +``` + +If you would like to connect to the server asynchronously, you should use the `WebSocket.ConnectAsync ()` method. + +#### Step 5 #### + +Sending data to the WebSocket server. + +```csharp +ws.Send (data); +``` + +The `WebSocket.Send` method is overloaded. + +You can use the `WebSocket.Send (string)`, `WebSocket.Send (byte[])`, or `WebSocket.Send (System.IO.FileInfo)` method to send the data. + +If you would like to send the data asynchronously, you should use the `WebSocket.SendAsync` method. + +```csharp +ws.SendAsync (data, completed); +``` + +And also if you would like to do something when the send is complete, you should set `completed` to any `Action` delegate. + +#### Step 6 #### + +Closing the WebSocket connection. + +```csharp +ws.Close (code, reason); +``` + +If you would like to close the connection explicitly, you should use the `WebSocket.Close` method. + +The `WebSocket.Close` method is overloaded. + +You can use the `WebSocket.Close ()`, `WebSocket.Close (ushort)`, `WebSocket.Close (WebSocketSharp.CloseStatusCode)`, `WebSocket.Close (ushort, string)`, or `WebSocket.Close (WebSocketSharp.CloseStatusCode, string)` method to close the connection. + +If you would like to close the connection asynchronously, you should use the `WebSocket.CloseAsync` method. + +### WebSocket Server ### + +```csharp +using System; +using WebSocketSharp; +using WebSocketSharp.Server; + +namespace Example +{ + public class Laputa : WebSocketBehavior + { + protected override void OnMessage (MessageEventArgs e) + { + var msg = e.Data == "BALUS" + ? "I've been balused already..." + : "I'm not available now."; + + Send (msg); + } + } + + public class Program + { + public static void Main (string[] args) + { + var wssv = new WebSocketServer ("ws://dragonsnest.far"); + wssv.AddWebSocketService ("/Laputa"); + wssv.Start (); + Console.ReadKey (true); + wssv.Stop (); + } + } +} +``` + +#### Step 1 #### + +Required namespace. + +```csharp +using WebSocketSharp.Server; +``` + +The `WebSocketBehavior` and `WebSocketServer` classes exist in the `WebSocketSharp.Server` namespace. + +#### Step 2 #### + +Creating the class that inherits the `WebSocketBehavior` class. + +For example, if you would like to provide an echo service, + +```csharp +using System; +using WebSocketSharp; +using WebSocketSharp.Server; + +public class Echo : WebSocketBehavior +{ + protected override void OnMessage (MessageEventArgs e) + { + Send (e.Data); + } +} +``` + +And if you would like to provide a chat service, + +```csharp +using System; +using WebSocketSharp; +using WebSocketSharp.Server; + +public class Chat : WebSocketBehavior +{ + private string _suffix; + + public Chat () + : this (null) + { + } + + public Chat (string suffix) + { + _suffix = suffix ?? String.Empty; + } + + protected override void OnMessage (MessageEventArgs e) + { + Sessions.Broadcast (e.Data + _suffix); + } +} +``` + +You can define the behavior of any WebSocket service by creating the class that inherits the `WebSocketBehavior` class. + +If you override the `WebSocketBehavior.OnMessage (MessageEventArgs)` method, it will be called when the `WebSocket` used in a session in the service receives a message. + +And if you override the `WebSocketBehavior.OnOpen ()`, `WebSocketBehavior.OnError (ErrorEventArgs)`, and `WebSocketBehavior.OnClose (CloseEventArgs)` methods, each of them will be called when each of the `WebSocket` events (`OnOpen`, `OnError`, and `OnClose`) occurs. + +The `WebSocketBehavior.Send` method can send data to the client on a session in the service. + +If you would like to get the sessions in the service, you should access the `WebSocketBehavior.Sessions` property (returns a `WebSocketSharp.Server.WebSocketSessionManager`). + +The `WebSocketBehavior.Sessions.Broadcast` method can send data to every client in the service. + +#### Step 3 #### + +Creating a new instance of the `WebSocketServer` class. + +```csharp +var wssv = new WebSocketServer (4649); +wssv.AddWebSocketService ("/Echo"); +wssv.AddWebSocketService ("/Chat"); +wssv.AddWebSocketService ("/ChatWithNyan", () => new Chat (" Nyan!")); +``` + +You can add any WebSocket service to your `WebSocketServer` with the specified behavior and path to the service, by using the `WebSocketServer.AddWebSocketService (string)` or `WebSocketServer.AddWebSocketService (string, Func)` method. + +The type of `TBehaviorWithNew` must inherit the `WebSocketBehavior` class, and must have a public parameterless constructor. + +And also the type of `TBehavior` must inherit the `WebSocketBehavior` class. + +So you can use the classes created in **Step 2** to add the service. + +If you create a instance of the `WebSocketServer` class without a port number, the `WebSocketServer` class set the port number to **80** automatically. So it's necessary to run with root permission. + + $ sudo mono example2.exe + +#### Step 4 #### + +Starting the WebSocket server. + +```csharp +wssv.Start (); +``` + +#### Step 5 #### + +Stopping the WebSocket server. + +```csharp +wssv.Stop (code, reason); +``` + +The `WebSocketServer.Stop` method is overloaded. + +You can use the `WebSocketServer.Stop ()`, `WebSocketServer.Stop (ushort, string)`, or `WebSocketServer.Stop (WebSocketSharp.CloseStatusCode, string)` method to stop the server. + +### HTTP Server with the WebSocket ### + +I have modified the `System.Net.HttpListener`, `System.Net.HttpListenerContext`, and some other classes from **[Mono]** to create an HTTP server that allows to accept the WebSocket handshake requests. + +So websocket-sharp provides the `WebSocketSharp.Server.HttpServer` class. + +You can add any WebSocket service to your `HttpServer` with the specified behavior and path to the service, by using the `HttpServer.AddWebSocketService (string)` or `HttpServer.AddWebSocketService (string, Func)` method. + +```csharp +var httpsv = new HttpServer (4649); +httpsv.AddWebSocketService ("/Echo"); +httpsv.AddWebSocketService ("/Chat"); +httpsv.AddWebSocketService ("/ChatWithNyan", () => new Chat (" Nyan!")); +``` + +For more information, would you see **[Example3]**? + +### WebSocket Extensions ### + +#### Per-message Compression #### + +websocket-sharp supports the **[Per-message Compression][compression]** extension (but doesn't support this extension with the [context take over]). + +As a WebSocket client, if you would like to enable this extension, you should set such as the following. + +```csharp +ws.Compression = CompressionMethod.Deflate; +``` + +And then your client will send the following header in the handshake request to the server. + + Sec-WebSocket-Extensions: permessage-deflate; server_no_context_takeover; client_no_context_takeover + +If the server accepts this extension, it will return the same header which has the corresponding value. And when your client receives it, this extension will be available. + +#### Ignoring the extensions #### + +As a WebSocket server, if you would like to ignore the extensions requested from a client, you should set the `WebSocketBehavior.IgnoreExtensions` property to `true` in your `WebSocketBehavior` constructor or initializing it, such as the following. + +```csharp +wssv.AddWebSocketService ( + "/Chat", + () => + new Chat () { + // To ignore the extensions requested from a client. + IgnoreExtensions = true + } +); +``` + +If it's set to `true`, the service will not return the **Sec-WebSocket-Extensions** header in its handshake response. + +I think this is useful when you get something error in connecting the server and exclude the extensions as a cause of the error. + +### Secure Connection ### + +websocket-sharp supports the **Secure Connection** with **SSL/TLS**. + +As a **WebSocket Client**, you should create a new instance of the `WebSocket` class with the **wss** scheme WebSocket URL. + +```csharp +using (var ws = new WebSocket ("wss://example.com")) { + ... +} +``` + +And if you would like to use the custom validation for the server certificate, you should set the `WebSocket.SslConfiguration.ServerCertificateValidationCallback` property. + +```csharp +ws.SslConfiguration.ServerCertificateValidationCallback = + (sender, certificate, chain, sslPolicyErrors) => { + // Do something to validate the server certificate. + ... + + return true; // If the server certificate is valid. + }; +``` + +If you set this property to nothing, the validation does nothing with the server certificate, and returns `true`. + +As a **WebSocket Server**, you should create a new instance of the `WebSocketServer` or `HttpServer` class with some settings for secure connection, such as the following. + +```csharp +var wssv = new WebSocketServer (5963, true); +wssv.SslConfiguration.ServerCertificate = + new X509Certificate2 ("/path/to/cert.pfx", "password for cert.pfx"); +``` + +### HTTP Authentication ### + +websocket-sharp supports the **[HTTP Authentication (Basic/Digest)][rfc2617]**. + +As a **WebSocket Client**, you should set a pair of user name and password for the HTTP authentication, by using the `WebSocket.SetCredentials (string, string, bool)` method before connecting. + +```csharp +ws.SetCredentials ("nobita", "password", preAuth); +``` + +If `preAuth` is `true`, the `WebSocket` sends the Basic authentication credentials with the first handshake request to the server. + +Or if `preAuth` is `false`, the `WebSocket` sends either the Basic or Digest (determined by the unauthorized response to the first handshake request) authentication credentials with the second handshake request to the server. + +As a **WebSocket Server**, you should set an HTTP authentication scheme, a realm, and any function to find the user credentials before starting, such as the following. + +```csharp +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. + }; +``` + +If you would like to provide the Digest authentication, you should set such as the following. + +```csharp +wssv.AuthenticationSchemes = AuthenticationSchemes.Digest; +``` + +### Query String, Origin header and Cookies ### + +As a **WebSocket Client**, if you would like to send the **Query String** with the handshake request to the server, you should create a new instance of the `WebSocket` class with the WebSocket URL that includes the [Query] string parameters. + +```csharp +using (var ws = new WebSocket ("ws://example.com/?name=nobita")) { + ... +} +``` + +And if you would like to send the **Origin** header with the handshake request to the server, you should set the `WebSocket.Origin` property to an allowable value as the [Origin] header before connecting, such as the following. + +```csharp +ws.Origin = "http://example.com"; +``` + +And also if you would like to send the **Cookies** with the handshake request to the server, you should set any cookie by using the `WebSocket.SetCookie (WebSocketSharp.Net.Cookie)` method before connecting, such as the following. + +```csharp +ws.SetCookie (new Cookie ("name", "nobita")); +``` + +As a **WebSocket Server**, if you would like to get the **Query String** included in a handshake request, you should access the `WebSocketBehavior.Context.QueryString` property, such as the following. + +```csharp +public class Chat : WebSocketBehavior +{ + private string _name; + ... + + protected override void OnOpen () + { + _name = Context.QueryString["name"]; + } + + ... +} +``` + +And if you would like to validate the **Origin** header, **Cookies**, or both included in a handshake request, you should set each validation with your `WebSocketBehavior`, for example, by using the `AddWebSocketService (string, Func)` method with initializing, such as the following. + +```csharp +wssv.AddWebSocketService ( + "/Chat", + () => + new Chat () { + 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 == "example.com"; + }, + 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. + } + } +); +``` + +And also if you would like to get each value of the Origin header and cookies, you should access each of the `WebSocketBehavior.Context.Origin` and `WebSocketBehavior.Context.CookieCollection` properties. + +### Connecting through the HTTP Proxy server ### + +websocket-sharp supports to connect through the **HTTP Proxy** server. + +If you would like to connect to a WebSocket server through the HTTP proxy server, you should set the proxy server URL, and if necessary, a pair of user name and password for the proxy server authentication (Basic/Digest), by using the `WebSocket.SetProxy (string, string, string)` method before connecting. + +```csharp +var ws = new WebSocket ("ws://example.com"); +ws.SetProxy ("http://localhost:3128", "nobita", "password"); +``` + +I have tested this with **[Squid]**. It's necessary to disable the following configuration option in **squid.conf** (e.g. `/etc/squid/squid.conf`). + +``` +# Deny CONNECT to other than SSL ports +#http_access deny CONNECT !SSL_ports +``` + +### Logging ### + +The `WebSocket` class has the own logging function. + +You can use it with the `WebSocket.Log` property (returns a `WebSocketSharp.Logger`). + +So if you would like to change the current logging level (`WebSocketSharp.LogLevel.Error` as the default), you should set the `WebSocket.Log.Level` property to any of the `LogLevel` enum values. + +```csharp +ws.Log.Level = LogLevel.Debug; +``` + +The above means a log with lower than `LogLevel.Debug` cannot be outputted. + +And if you would like to output a log, you should use any of the output methods. The following outputs a log with `LogLevel.Debug`. + +```csharp +ws.Log.Debug ("This is a debug message."); +``` + +The `WebSocketServer` and `HttpServer` classes have the same logging function. + +## Examples ## + +Examples using websocket-sharp. + +### Example ### + +**[Example]** connects to the **[Echo server]** with the WebSocket. + +### Example2 ### + +**[Example2]** starts a WebSocket server. + +### Example3 ### + +**[Example3]** starts an HTTP server that allows to accept the WebSocket handshake requests. + +Would you access to [http://localhost:4649](http://localhost:4649) to do **WebSocket Echo Test** with your web browser while Example3 is running? + +## Supported WebSocket Specifications ## + +websocket-sharp supports **[RFC 6455][rfc6455]**, and it's based on the following WebSocket references: + +- **[The WebSocket Protocol][rfc6455]** +- **[The WebSocket API][api]** +- **[Compression Extensions for WebSocket][compression]** + +Thanks for translating to japanese. + +- **[The WebSocket Protocol 日本語訳][rfc6455_ja]** +- **[The WebSocket API 日本語訳][api_ja]** + +## License ## + +websocket-sharp is provided under **[The MIT License]**. + + +[Echo server]: http://www.websocket.org/echo.html +[Example]: https://github.com/sta/websocket-sharp/tree/master/Example +[Example2]: https://github.com/sta/websocket-sharp/tree/master/Example2 +[Example3]: https://github.com/sta/websocket-sharp/tree/master/Example3 +[Mono]: http://www.mono-project.com +[MonoDevelop]: http://monodevelop.com +[NuGet Gallery]: http://www.nuget.org +[NuGet Gallery: websocket-sharp]: http://www.nuget.org/packages/WebSocketSharp +[Origin]: http://tools.ietf.org/html/rfc6454#section-7 +[Query]: http://tools.ietf.org/html/rfc3986#section-3.4 +[Security Sandbox of the Webplayer]: http://docs.unity3d.com/Manual/SecuritySandbox.html +[Squid]: http://www.squid-cache.org +[The MIT License]: https://raw.github.com/sta/websocket-sharp/master/LICENSE.txt +[Unity]: http://unity3d.com +[WebGL Networking]: http://docs.unity3d.com/Manual/webgl-networking.html +[WebSocket-Sharp for Unity]: http://u3d.as/content/sta-blockhead/websocket-sharp-for-unity +[api]: http://www.w3.org/TR/websockets +[api_ja]: http://www.hcn.zaq.ne.jp/___/WEB/WebSocket-ja.html +[compression]: http://tools.ietf.org/html/draft-ietf-hybi-permessage-compression-19 +[context take over]: http://tools.ietf.org/html/draft-ietf-hybi-permessage-compression-19#section-8.1.1 +[rfc2617]: http://tools.ietf.org/html/rfc2617 +[rfc6455]: http://tools.ietf.org/html/rfc6455 +[rfc6455_ja]: http://www.hcn.zaq.ne.jp/___/WEB/RFC6455-ja.html diff --git a/javascripts/main.js b/javascripts/main.js new file mode 100644 index 000000000..d8135d37b --- /dev/null +++ b/javascripts/main.js @@ -0,0 +1 @@ +console.log('This would be the main JS file.'); diff --git a/javascripts/respond.js b/javascripts/respond.js new file mode 100644 index 000000000..76bc26048 --- /dev/null +++ b/javascripts/respond.js @@ -0,0 +1,779 @@ +if(typeof Object.create!=="function"){ +Object.create=function(o){ +function F(){ +}; +F.prototype=o; +return new F(); +}; +} +var ua={toString:function(){ +return navigator.userAgent; +},test:function(s){ +return this.toString().toLowerCase().indexOf(s.toLowerCase())>-1; +}}; +ua.version=(ua.toString().toLowerCase().match(/[\s\S]+(?:rv|it|ra|ie)[\/: ]([\d.]+)/)||[])[1]; +ua.webkit=ua.test("webkit"); +ua.gecko=ua.test("gecko")&&!ua.webkit; +ua.opera=ua.test("opera"); +ua.ie=ua.test("msie")&&!ua.opera; +ua.ie6=ua.ie&&document.compatMode&&typeof document.documentElement.style.maxHeight==="undefined"; +ua.ie7=ua.ie&&document.documentElement&&typeof document.documentElement.style.maxHeight!=="undefined"&&typeof XDomainRequest==="undefined"; +ua.ie8=ua.ie&&typeof XDomainRequest!=="undefined"; +var domReady=function(){ +var _1=[]; +var _2=function(){ +if(!arguments.callee.done){ +arguments.callee.done=true; +for(var i=0;i<_1.length;i++){ +_1[i](); +} +} +}; +if(document.addEventListener){ +document.addEventListener("DOMContentLoaded",_2,false); +} +if(ua.ie){ +(function(){ +try{ +document.documentElement.doScroll("left"); +} +catch(e){ +setTimeout(arguments.callee,50); +return; +} +_2(); +})(); +document.onreadystatechange=function(){ +if(document.readyState==="complete"){ +document.onreadystatechange=null; +_2(); +} +}; +} +if(ua.webkit&&document.readyState){ +(function(){ +if(document.readyState!=="loading"){ +_2(); +}else{ +setTimeout(arguments.callee,10); +} +})(); +} +window.onload=_2; +return function(fn){ +if(typeof fn==="function"){ +_1[_1.length]=fn; +} +return fn; +}; +}(); +var cssHelper=function(){ +var _3={BLOCKS:/[^\s{][^{]*\{(?:[^{}]*\{[^{}]*\}[^{}]*|[^{}]*)*\}/g,BLOCKS_INSIDE:/[^\s{][^{]*\{[^{}]*\}/g,DECLARATIONS:/[a-zA-Z\-]+[^;]*:[^;]+;/g,RELATIVE_URLS:/url\(['"]?([^\/\)'"][^:\)'"]+)['"]?\)/g,REDUNDANT_COMPONENTS:/(?:\/\*([^*\\\\]|\*(?!\/))+\*\/|@import[^;]+;)/g,REDUNDANT_WHITESPACE:/\s*(,|:|;|\{|\})\s*/g,MORE_WHITESPACE:/\s{2,}/g,FINAL_SEMICOLONS:/;\}/g,NOT_WHITESPACE:/\S+/g}; +var _4,_5=false; +var _6=[]; +var _7=function(fn){ +if(typeof fn==="function"){ +_6[_6.length]=fn; +} +}; +var _8=function(){ +for(var i=0;i<_6.length;i++){ +_6[i](_4); +} +}; +var _9={}; +var _a=function(n,v){ +if(_9[n]){ +var _b=_9[n].listeners; +if(_b){ +for(var i=0;i<_b.length;i++){ +_b[i](v); +} +} +} +}; +var _c=function(_d,_e,_f){ +if(ua.ie&&!window.XMLHttpRequest){ +window.XMLHttpRequest=function(){ +return new ActiveXObject("Microsoft.XMLHTTP"); +}; +} +if(!XMLHttpRequest){ +return ""; +} +var r=new XMLHttpRequest(); +try{ +r.open("get",_d,true); +r.setRequestHeader("X_REQUESTED_WITH","XMLHttpRequest"); +} +catch(e){ +_f(); +return; +} +var _10=false; +setTimeout(function(){ +_10=true; +},5000); +document.documentElement.style.cursor="progress"; +r.onreadystatechange=function(){ +if(r.readyState===4&&!_10){ +if(!r.status&&location.protocol==="file:"||(r.status>=200&&r.status<300)||r.status===304||navigator.userAgent.indexOf("Safari")>-1&&typeof r.status==="undefined"){ +_e(r.responseText); +}else{ +_f(); +} +document.documentElement.style.cursor=""; +r=null; +} +}; +r.send(""); +}; +var _11=function(_12){ +_12=_12.replace(_3.REDUNDANT_COMPONENTS,""); +_12=_12.replace(_3.REDUNDANT_WHITESPACE,"$1"); +_12=_12.replace(_3.MORE_WHITESPACE," "); +_12=_12.replace(_3.FINAL_SEMICOLONS,"}"); +return _12; +}; +var _13={mediaQueryList:function(s){ +var o={}; +var idx=s.indexOf("{"); +var lt=s.substring(0,idx); +s=s.substring(idx+1,s.length-1); +var mqs=[],rs=[]; +var qts=lt.toLowerCase().substring(7).split(","); +for(var i=0;i-1&&_23.href&&_23.href.length!==0&&!_23.disabled){ +_1f[_1f.length]=_23; +} +} +if(_1f.length>0){ +var c=0; +var _24=function(){ +c++; +if(c===_1f.length){ +_20(); +} +}; +var _25=function(_26){ +var _27=_26.href; +_c(_27,function(_28){ +_28=_11(_28).replace(_3.RELATIVE_URLS,"url("+_27.substring(0,_27.lastIndexOf("/"))+"/$1)"); +_26.cssHelperText=_28; +_24(); +},_24); +}; +for(i=0;i<_1f.length;i++){ +_25(_1f[i]); +} +}else{ +_20(); +} +}; +var _29={mediaQueryLists:"array",rules:"array",selectors:"object",declarations:"array",properties:"object"}; +var _2a={mediaQueryLists:null,rules:null,selectors:null,declarations:null,properties:null}; +var _2b=function(_2c,v){ +if(_2a[_2c]!==null){ +if(_29[_2c]==="array"){ +return (_2a[_2c]=_2a[_2c].concat(v)); +}else{ +var c=_2a[_2c]; +for(var n in v){ +if(v.hasOwnProperty(n)){ +if(!c[n]){ +c[n]=v[n]; +}else{ +c[n]=c[n].concat(v[n]); +} +} +} +return c; +} +} +}; +var _2d=function(_2e){ +_2a[_2e]=(_29[_2e]==="array")?[]:{}; +for(var i=0;i<_4.length;i++){ +_2b(_2e,_4[i].cssHelperParsed[_2e]); +} +return _2a[_2e]; +}; +domReady(function(){ +var els=document.body.getElementsByTagName("*"); +for(var i=0;i=_44)||(max&&_46<_44)||(!min&&!max&&_46===_44)); +}else{ +return false; +} +}else{ +return _46>0; +} +}else{ +if("device-height"===_41.substring(l-13,l)){ +_47=screen.height; +if(_42!==null){ +if(_43==="length"){ +return ((min&&_47>=_44)||(max&&_47<_44)||(!min&&!max&&_47===_44)); +}else{ +return false; +} +}else{ +return _47>0; +} +}else{ +if("width"===_41.substring(l-5,l)){ +_46=document.documentElement.clientWidth||document.body.clientWidth; +if(_42!==null){ +if(_43==="length"){ +return ((min&&_46>=_44)||(max&&_46<_44)||(!min&&!max&&_46===_44)); +}else{ +return false; +} +}else{ +return _46>0; +} +}else{ +if("height"===_41.substring(l-6,l)){ +_47=document.documentElement.clientHeight||document.body.clientHeight; +if(_42!==null){ +if(_43==="length"){ +return ((min&&_47>=_44)||(max&&_47<_44)||(!min&&!max&&_47===_44)); +}else{ +return false; +} +}else{ +return _47>0; +} +}else{ +if("device-aspect-ratio"===_41.substring(l-19,l)){ +return _43==="aspect-ratio"&&screen.width*_44[1]===screen.height*_44[0]; +}else{ +if("color-index"===_41.substring(l-11,l)){ +var _48=Math.pow(2,screen.colorDepth); +if(_42!==null){ +if(_43==="absolute"){ +return ((min&&_48>=_44)||(max&&_48<_44)||(!min&&!max&&_48===_44)); +}else{ +return false; +} +}else{ +return _48>0; +} +}else{ +if("color"===_41.substring(l-5,l)){ +var _49=screen.colorDepth; +if(_42!==null){ +if(_43==="absolute"){ +return ((min&&_49>=_44)||(max&&_49<_44)||(!min&&!max&&_49===_44)); +}else{ +return false; +} +}else{ +return _49>0; +} +}else{ +if("resolution"===_41.substring(l-10,l)){ +var res; +if(_45==="dpcm"){ +res=_3d("1cm"); +}else{ +res=_3d("1in"); +} +if(_42!==null){ +if(_43==="resolution"){ +return ((min&&res>=_44)||(max&&res<_44)||(!min&&!max&&res===_44)); +}else{ +return false; +} +}else{ +return res>0; +} +}else{ +return false; +} +} +} +} +} +} +} +} +}; +var _4a=function(mq){ +var _4b=mq.getValid(); +var _4c=mq.getExpressions(); +var l=_4c.length; +if(l>0){ +for(var i=0;i0){ +s[c++]=","; +} +s[c++]=n; +} +} +if(s.length>0){ +_39[_39.length]=cssHelper.addStyle("@media "+s.join("")+"{"+mql.getCssText()+"}",false); +} +}; +var _4e=function(_4f){ +for(var i=0;i<_4f.length;i++){ +_4d(_4f[i]); +} +if(ua.ie){ +document.documentElement.style.display="block"; +setTimeout(function(){ +document.documentElement.style.display=""; +},0); +setTimeout(function(){ +cssHelper.broadcast("cssMediaQueriesTested"); +},100); +}else{ +cssHelper.broadcast("cssMediaQueriesTested"); +} +}; +var _50=function(){ +for(var i=0;i<_39.length;i++){ +cssHelper.removeStyle(_39[i]); +} +_39=[]; +cssHelper.mediaQueryLists(_4e); +}; +var _51=0; +var _52=function(){ +var _53=cssHelper.getViewportWidth(); +var _54=cssHelper.getViewportHeight(); +if(ua.ie){ +var el=document.createElement("div"); +el.style.position="absolute"; +el.style.top="-9999em"; +el.style.overflow="scroll"; +document.body.appendChild(el); +_51=el.offsetWidth-el.clientWidth; +document.body.removeChild(el); +} +var _55; +var _56=function(){ +var vpw=cssHelper.getViewportWidth(); +var vph=cssHelper.getViewportHeight(); +if(Math.abs(vpw-_53)>_51||Math.abs(vph-_54)>_51){ +_53=vpw; +_54=vph; +clearTimeout(_55); +_55=setTimeout(function(){ +if(!_3a()){ +_50(); +}else{ +cssHelper.broadcast("cssMediaQueriesTested"); +} +},500); +} +}; +window.onresize=function(){ +var x=window.onresize||function(){ +}; +return function(){ +x(); +_56(); +}; +}(); +}; +var _57=document.documentElement; +_57.style.marginLeft="-32767px"; +setTimeout(function(){ +_57.style.marginTop=""; +},20000); +return function(){ +if(!_3a()){ +cssHelper.addListener("newStyleParsed",function(el){ +_4e(el.cssHelperParsed.mediaQueryLists); +}); +cssHelper.addListener("cssMediaQueriesTested",function(){ +if(ua.ie){ +_57.style.width="1px"; +} +setTimeout(function(){ +_57.style.width=""; +_57.style.marginLeft=""; +},0); +cssHelper.removeListener("cssMediaQueriesTested",arguments.callee); +}); +_3c(); +_50(); +}else{ +_57.style.marginLeft=""; +} +_52(); +}; +}()); +try{ +document.execCommand("BackgroundImageCache",false,true); +} +catch(e){ +} + diff --git a/javascripts/scale.fix.js b/javascripts/scale.fix.js new file mode 100644 index 000000000..08716c006 --- /dev/null +++ b/javascripts/scale.fix.js @@ -0,0 +1,20 @@ +fixScale = function(doc) { + + var addEvent = 'addEventListener', + type = 'gesturestart', + qsa = 'querySelectorAll', + scales = [1, 1], + meta = qsa in doc ? doc[qsa]('meta[name=viewport]') : []; + + function fix() { + meta.content = 'width=device-width,minimum-scale=' + scales[0] + ',maximum-scale=' + scales[1]; + doc.removeEventListener(type, fix, true); + } + + if ((meta = meta[meta.length - 1]) && addEvent in doc) { + fix(); + scales = [.25, 1.6]; + doc[addEvent](type, fix, true); + } + +}; \ No newline at end of file diff --git a/stylesheets/github-light.css b/stylesheets/github-light.css new file mode 100644 index 000000000..0c6b24d82 --- /dev/null +++ b/stylesheets/github-light.css @@ -0,0 +1,124 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016 GitHub, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +.pl-c /* comment */ { + color: #969896; +} + +.pl-c1 /* constant, variable.other.constant, support, meta.property-name, support.constant, support.variable, meta.module-reference, markup.raw, meta.diff.header */, +.pl-s .pl-v /* string variable */ { + color: #0086b3; +} + +.pl-e /* entity */, +.pl-en /* entity.name */ { + color: #795da3; +} + +.pl-smi /* variable.parameter.function, storage.modifier.package, storage.modifier.import, storage.type.java, variable.other */, +.pl-s .pl-s1 /* string source */ { + color: #333; +} + +.pl-ent /* entity.name.tag */ { + color: #63a35c; +} + +.pl-k /* keyword, storage, storage.type */ { + color: #a71d5d; +} + +.pl-s /* string */, +.pl-pds /* punctuation.definition.string, string.regexp.character-class */, +.pl-s .pl-pse .pl-s1 /* string punctuation.section.embedded source */, +.pl-sr /* string.regexp */, +.pl-sr .pl-cce /* string.regexp constant.character.escape */, +.pl-sr .pl-sre /* string.regexp source.ruby.embedded */, +.pl-sr .pl-sra /* string.regexp string.regexp.arbitrary-repitition */ { + color: #183691; +} + +.pl-v /* variable */ { + color: #ed6a43; +} + +.pl-id /* invalid.deprecated */ { + color: #b52a1d; +} + +.pl-ii /* invalid.illegal */ { + color: #f8f8f8; + background-color: #b52a1d; +} + +.pl-sr .pl-cce /* string.regexp constant.character.escape */ { + font-weight: bold; + color: #63a35c; +} + +.pl-ml /* markup.list */ { + color: #693a17; +} + +.pl-mh /* markup.heading */, +.pl-mh .pl-en /* markup.heading entity.name */, +.pl-ms /* meta.separator */ { + font-weight: bold; + color: #1d3e81; +} + +.pl-mq /* markup.quote */ { + color: #008080; +} + +.pl-mi /* markup.italic */ { + font-style: italic; + color: #333; +} + +.pl-mb /* markup.bold */ { + font-weight: bold; + color: #333; +} + +.pl-md /* markup.deleted, meta.diff.header.from-file */ { + color: #bd2c00; + background-color: #ffecec; +} + +.pl-mi1 /* markup.inserted, meta.diff.header.to-file */ { + color: #55a532; + background-color: #eaffea; +} + +.pl-mdr /* meta.diff.range */ { + font-weight: bold; + color: #795da3; +} + +.pl-mo /* meta.output */ { + color: #1d3e81; +} + diff --git a/stylesheets/ie.css b/stylesheets/ie.css new file mode 100644 index 000000000..43882f2ee --- /dev/null +++ b/stylesheets/ie.css @@ -0,0 +1,3 @@ +nav { + display: none; +} diff --git a/stylesheets/normalize.css b/stylesheets/normalize.css new file mode 100644 index 000000000..30366a6e9 --- /dev/null +++ b/stylesheets/normalize.css @@ -0,0 +1,424 @@ +/*! normalize.css v3.0.2 | MIT License | git.io/normalize */ + +/** + * 1. Set default font family to sans-serif. + * 2. Prevent iOS text size adjust after orientation change, without disabling + * user zoom. + */ + +html { + font-family: sans-serif; /* 1 */ + -ms-text-size-adjust: 100%; /* 2 */ + -webkit-text-size-adjust: 100%; /* 2 */ +} + +/** + * Remove default margin. + */ + +body { + margin: 0; +} + +/* HTML5 display definitions + ========================================================================== */ + +/** + * Correct `block` display not defined for any HTML5 element in IE 8/9. + * Correct `block` display not defined for `details` or `summary` in IE 10/11 + * and Firefox. + * Correct `block` display not defined for `main` in IE 11. + */ + +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +main, +menu, +nav, +section, +summary { + display: block; +} + +/** + * 1. Correct `inline-block` display not defined in IE 8/9. + * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera. + */ + +audio, +canvas, +progress, +video { + display: inline-block; /* 1 */ + vertical-align: baseline; /* 2 */ +} + +/** + * Prevent modern browsers from displaying `audio` without controls. + * Remove excess height in iOS 5 devices. + */ + +audio:not([controls]) { + display: none; + height: 0; +} + +/** + * Address `[hidden]` styling not present in IE 8/9/10. + * Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22. + */ + +[hidden], +template { + display: none; +} + +/* Links + ========================================================================== */ + +/** + * Remove the gray background color from active links in IE 10. + */ + +a { + background-color: transparent; +} + +/** + * Improve readability when focused and also mouse hovered in all browsers. + */ + +a:active, +a:hover { + outline: 0; +} + +/* Text-level semantics + ========================================================================== */ + +/** + * Address styling not present in IE 8/9/10/11, Safari, and Chrome. + */ + +abbr[title] { + border-bottom: 1px dotted; +} + +/** + * Address style set to `bolder` in Firefox 4+, Safari, and Chrome. + */ + +b, +strong { + font-weight: bold; +} + +/** + * Address styling not present in Safari and Chrome. + */ + +dfn { + font-style: italic; +} + +/** + * Address variable `h1` font-size and margin within `section` and `article` + * contexts in Firefox 4+, Safari, and Chrome. + */ + +h1 { + font-size: 2em; + margin: 0.67em 0; +} + +/** + * Address styling not present in IE 8/9. + */ + +mark { + background: #ff0; + color: #000; +} + +/** + * Address inconsistent and variable font size in all browsers. + */ + +small { + font-size: 80%; +} + +/** + * Prevent `sub` and `sup` affecting `line-height` in all browsers. + */ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sup { + top: -0.5em; +} + +sub { + bottom: -0.25em; +} + +/* Embedded content + ========================================================================== */ + +/** + * Remove border when inside `a` element in IE 8/9/10. + */ + +img { + border: 0; +} + +/** + * Correct overflow not hidden in IE 9/10/11. + */ + +svg:not(:root) { + overflow: hidden; +} + +/* Grouping content + ========================================================================== */ + +/** + * Address margin not present in IE 8/9 and Safari. + */ + +figure { + margin: 1em 40px; +} + +/** + * Address differences between Firefox and other browsers. + */ + +hr { + box-sizing: content-box; + height: 0; +} + +/** + * Contain overflow in all browsers. + */ + +pre { + overflow: auto; +} + +/** + * Address odd `em`-unit font size rendering in all browsers. + */ + +code, +kbd, +pre, +samp { + font-family: monospace, monospace; + font-size: 1em; +} + +/* Forms + ========================================================================== */ + +/** + * Known limitation: by default, Chrome and Safari on OS X allow very limited + * styling of `select`, unless a `border` property is set. + */ + +/** + * 1. Correct color not being inherited. + * Known issue: affects color of disabled elements. + * 2. Correct font properties not being inherited. + * 3. Address margins set differently in Firefox 4+, Safari, and Chrome. + */ + +button, +input, +optgroup, +select, +textarea { + color: inherit; /* 1 */ + font: inherit; /* 2 */ + margin: 0; /* 3 */ +} + +/** + * Address `overflow` set to `hidden` in IE 8/9/10/11. + */ + +button { + overflow: visible; +} + +/** + * Address inconsistent `text-transform` inheritance for `button` and `select`. + * All other form control elements do not inherit `text-transform` values. + * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera. + * Correct `select` style inheritance in Firefox. + */ + +button, +select { + text-transform: none; +} + +/** + * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` + * and `video` controls. + * 2. Correct inability to style clickable `input` types in iOS. + * 3. Improve usability and consistency of cursor style between image-type + * `input` and others. + */ + +button, +html input[type="button"], /* 1 */ +input[type="reset"], +input[type="submit"] { + -webkit-appearance: button; /* 2 */ + cursor: pointer; /* 3 */ +} + +/** + * Re-set default cursor for disabled elements. + */ + +button[disabled], +html input[disabled] { + cursor: default; +} + +/** + * Remove inner padding and border in Firefox 4+. + */ + +button::-moz-focus-inner, +input::-moz-focus-inner { + border: 0; + padding: 0; +} + +/** + * Address Firefox 4+ setting `line-height` on `input` using `!important` in + * the UA stylesheet. + */ + +input { + line-height: normal; +} + +/** + * It's recommended that you don't attempt to style these elements. + * Firefox's implementation doesn't respect box-sizing, padding, or width. + * + * 1. Address box sizing set to `content-box` in IE 8/9/10. + * 2. Remove excess padding in IE 8/9/10. + */ + +input[type="checkbox"], +input[type="radio"] { + box-sizing: border-box; /* 1 */ + padding: 0; /* 2 */ +} + +/** + * Fix the cursor style for Chrome's increment/decrement buttons. For certain + * `font-size` values of the `input`, it causes the cursor style of the + * decrement button to change from `default` to `text`. + */ + +input[type="number"]::-webkit-inner-spin-button, +input[type="number"]::-webkit-outer-spin-button { + height: auto; +} + +/** + * 1. Address `appearance` set to `searchfield` in Safari and Chrome. + * 2. Address `box-sizing` set to `border-box` in Safari and Chrome + * (include `-moz` to future-proof). + */ + +input[type="search"] { + -webkit-appearance: textfield; /* 1 */ /* 2 */ + box-sizing: content-box; +} + +/** + * Remove inner padding and search cancel button in Safari and Chrome on OS X. + * Safari (but not Chrome) clips the cancel button when the search input has + * padding (and `textfield` appearance). + */ + +input[type="search"]::-webkit-search-cancel-button, +input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +/** + * Define consistent border, margin, and padding. + */ + +fieldset { + border: 1px solid #c0c0c0; + margin: 0 2px; + padding: 0.35em 0.625em 0.75em; +} + +/** + * 1. Correct `color` not being inherited in IE 8/9/10/11. + * 2. Remove padding so people aren't caught out if they zero out fieldsets. + */ + +legend { + border: 0; /* 1 */ + padding: 0; /* 2 */ +} + +/** + * Remove default vertical scrollbar in IE 8/9/10/11. + */ + +textarea { + overflow: auto; +} + +/** + * Don't inherit the `font-weight` (applied by a rule above). + * NOTE: the default cannot safely be changed in Chrome and Safari on OS X. + */ + +optgroup { + font-weight: bold; +} + +/* Tables + ========================================================================== */ + +/** + * Remove most spacing between table cells. + */ + +table { + border-collapse: collapse; + border-spacing: 0; +} + +td, +th { + padding: 0; +} diff --git a/stylesheets/print.css b/stylesheets/print.css new file mode 100644 index 000000000..541695bfd --- /dev/null +++ b/stylesheets/print.css @@ -0,0 +1,226 @@ +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, canvas, details, embed, +figure, figcaption, footer, header, hgroup, +menu, nav, output, ruby, section, summary, +time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; +} +/* HTML5 display-role reset for older browsers */ +article, aside, details, figcaption, figure, +footer, header, hgroup, menu, nav, section { + display: block; +} +body { + line-height: 1; +} +ol, ul { + list-style: none; +} +blockquote, q { + quotes: none; +} +blockquote:before, blockquote:after, +q:before, q:after { + content: ''; + content: none; +} +table { + border-collapse: collapse; + border-spacing: 0; +} +body { + font-size: 13px; + line-height: 1.5; + font-family: 'Helvetica Neue', Helvetica, Arial, serif; + color: #000; +} + +a { + color: #d5000d; + font-weight: bold; +} + +header { + padding-top: 35px; + padding-bottom: 10px; +} + +header h1 { + font-weight: bold; + letter-spacing: -1px; + font-size: 48px; + color: #303030; + line-height: 1.2; +} + +header h2 { + letter-spacing: -1px; + font-size: 24px; + color: #aaa; + font-weight: normal; + line-height: 1.3; +} +#downloads { + display: none; +} +#main_content { + padding-top: 20px; +} + +code, pre { + font-family: Monaco, "Bitstream Vera Sans Mono", "Lucida Console", Terminal; + color: #222; + margin-bottom: 30px; + font-size: 12px; +} + +code { + padding: 0 3px; +} + +pre { + border: solid 1px #ddd; + padding: 20px; + overflow: auto; +} +pre code { + padding: 0; +} + +ul, ol, dl { + margin-bottom: 20px; +} + + +/* COMMON STYLES */ + +table { + width: 100%; + border: 1px solid #ebebeb; +} + +th { + font-weight: 500; +} + +td { + border: 1px solid #ebebeb; + text-align: center; + font-weight: 300; +} + +form { + background: #f2f2f2; + padding: 20px; + +} + + +/* GENERAL ELEMENT TYPE STYLES */ + +h1 { + font-size: 2.8em; +} + +h2 { + font-size: 22px; + font-weight: bold; + color: #303030; + margin-bottom: 8px; +} + +h3 { + color: #d5000d; + font-size: 18px; + font-weight: bold; + margin-bottom: 8px; +} + +h4 { + font-size: 16px; + color: #303030; + font-weight: bold; +} + +h5 { + font-size: 1em; + color: #303030; +} + +h6 { + font-size: .8em; + color: #303030; +} + +p { + font-weight: 300; + margin-bottom: 20px; +} + +a { + text-decoration: none; +} + +p a { + font-weight: 400; +} + +blockquote { + font-size: 1.6em; + border-left: 10px solid #e9e9e9; + margin-bottom: 20px; + padding: 0 0 0 30px; +} + +ul li { + list-style: disc inside; + padding-left: 20px; +} + +ol li { + list-style: decimal inside; + padding-left: 3px; +} + +dl dd { + font-style: italic; + font-weight: 100; +} + +footer { + margin-top: 40px; + padding-top: 20px; + padding-bottom: 30px; + font-size: 13px; + color: #aaa; +} + +footer a { + color: #666; +} + +/* MISC */ +.clearfix:after { + clear: both; + content: '.'; + display: block; + visibility: hidden; + height: 0; +} + +.clearfix {display: inline-block;} +* html .clearfix {height: 1%;} +.clearfix {display: block;} \ No newline at end of file diff --git a/stylesheets/pygment_trac.css b/stylesheets/pygment_trac.css new file mode 100644 index 000000000..c79bef456 --- /dev/null +++ b/stylesheets/pygment_trac.css @@ -0,0 +1,70 @@ +.highlight .hll { background-color: #404040 } +.highlight { color: #d0d0d0 } +.highlight .c { color: #999999; font-style: italic } /* Comment */ +.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ +.highlight .g { color: #d0d0d0 } /* Generic */ +.highlight .k { color: #6ab825; font-weight: normal } /* Keyword */ +.highlight .l { color: #d0d0d0 } /* Literal */ +.highlight .n { color: #d0d0d0 } /* Name */ +.highlight .o { color: #d0d0d0 } /* Operator */ +.highlight .x { color: #d0d0d0 } /* Other */ +.highlight .p { color: #d0d0d0 } /* Punctuation */ +.highlight .cm { color: #999999; font-style: italic } /* Comment.Multiline */ +.highlight .cp { color: #cd2828; font-weight: normal } /* Comment.Preproc */ +.highlight .c1 { color: #999999; font-style: italic } /* Comment.Single */ +.highlight .cs { color: #e50808; font-weight: normal; background-color: #520000 } /* Comment.Special */ +.highlight .gd { color: #d22323 } /* Generic.Deleted */ +.highlight .ge { color: #d0d0d0; font-style: italic } /* Generic.Emph */ +.highlight .gr { color: #d22323 } /* Generic.Error */ +.highlight .gh { color: #ffffff; font-weight: normal } /* Generic.Heading */ +.highlight .gi { color: #589819 } /* Generic.Inserted */ +.highlight .go { color: #cccccc } /* Generic.Output */ +.highlight .gp { color: #aaaaaa } /* Generic.Prompt */ +.highlight .gs { color: #d0d0d0; font-weight: normal } /* Generic.Strong */ +.highlight .gu { color: #ffffff; text-decoration: underline } /* Generic.Subheading */ +.highlight .gt { color: #d22323 } /* Generic.Traceback */ +.highlight .kc { color: #6ab825; font-weight: normal } /* Keyword.Constant */ +.highlight .kd { color: #6ab825; font-weight: normal } /* Keyword.Declaration */ +.highlight .kn { color: #6ab825; font-weight: normal } /* Keyword.Namespace */ +.highlight .kp { color: #6ab825 } /* Keyword.Pseudo */ +.highlight .kr { color: #6ab825; font-weight: normal } /* Keyword.Reserved */ +.highlight .kt { color: #6ab825; font-weight: normal } /* Keyword.Type */ +.highlight .ld { color: #d0d0d0 } /* Literal.Date */ +.highlight .m { color: #3677a9 } /* Literal.Number */ +.highlight .s { color: #9dd5f1 } /* Literal.String */ +.highlight .na { color: #bbbbbb } /* Name.Attribute */ +.highlight .nb { color: #24909d } /* Name.Builtin */ +.highlight .nc { color: #447fcf; text-decoration: underline } /* Name.Class */ +.highlight .no { color: #40ffff } /* Name.Constant */ +.highlight .nd { color: #ffa500 } /* Name.Decorator */ +.highlight .ni { color: #d0d0d0 } /* Name.Entity */ +.highlight .ne { color: #bbbbbb } /* Name.Exception */ +.highlight .nf { color: #447fcf } /* Name.Function */ +.highlight .nl { color: #d0d0d0 } /* Name.Label */ +.highlight .nn { color: #447fcf; text-decoration: underline } /* Name.Namespace */ +.highlight .nx { color: #d0d0d0 } /* Name.Other */ +.highlight .py { color: #d0d0d0 } /* Name.Property */ +.highlight .nt { color: #6ab825;} /* Name.Tag */ +.highlight .nv { color: #40ffff } /* Name.Variable */ +.highlight .ow { color: #6ab825; font-weight: normal } /* Operator.Word */ +.highlight .w { color: #666666 } /* Text.Whitespace */ +.highlight .mf { color: #3677a9 } /* Literal.Number.Float */ +.highlight .mh { color: #3677a9 } /* Literal.Number.Hex */ +.highlight .mi { color: #3677a9 } /* Literal.Number.Integer */ +.highlight .mo { color: #3677a9 } /* Literal.Number.Oct */ +.highlight .sb { color: #9dd5f1 } /* Literal.String.Backtick */ +.highlight .sc { color: #9dd5f1 } /* Literal.String.Char */ +.highlight .sd { color: #9dd5f1 } /* Literal.String.Doc */ +.highlight .s2 { color: #9dd5f1 } /* Literal.String.Double */ +.highlight .se { color: #9dd5f1 } /* Literal.String.Escape */ +.highlight .sh { color: #9dd5f1 } /* Literal.String.Heredoc */ +.highlight .si { color: #9dd5f1 } /* Literal.String.Interpol */ +.highlight .sx { color: #ffa500 } /* Literal.String.Other */ +.highlight .sr { color: #9dd5f1 } /* Literal.String.Regex */ +.highlight .s1 { color: #9dd5f1 } /* Literal.String.Single */ +.highlight .ss { color: #9dd5f1 } /* Literal.String.Symbol */ +.highlight .bp { color: #24909d } /* Name.Builtin.Pseudo */ +.highlight .vc { color: #40ffff } /* Name.Variable.Class */ +.highlight .vg { color: #40ffff } /* Name.Variable.Global */ +.highlight .vi { color: #40ffff } /* Name.Variable.Instance */ +.highlight .il { color: #3677a9 } /* Literal.Number.Integer.Long */ \ No newline at end of file diff --git a/stylesheets/styles.css b/stylesheets/styles.css new file mode 100644 index 000000000..e7b4ffccd --- /dev/null +++ b/stylesheets/styles.css @@ -0,0 +1,851 @@ +@font-face { + font-family: 'OpenSansLight'; + src: url("../fonts/OpenSans-Light-webfont.eot"); + src: url("../fonts/OpenSans-Light-webfont.eot?#iefix") format("embedded-opentype"), url("../fonts/OpenSans-Light-webfont.woff") format("woff"), url("../fonts/OpenSans-Light-webfont.ttf") format("truetype"), url("../fonts/OpenSans-Light-webfont.svg#OpenSansLight") format("svg"); + font-weight: normal; + font-style: normal; +} + +@font-face { + font-family: 'OpenSansLightItalic'; + src: url("../fonts/OpenSans-LightItalic-webfont.eot"); + src: url("../fonts/OpenSans-LightItalic-webfont.eot?#iefix") format("embedded-opentype"), url("../fonts/OpenSans-LightItalic-webfont.woff") format("woff"), url("../fonts/OpenSans-LightItalic-webfont.ttf") format("truetype"), url("../fonts/OpenSans-LightItalic-webfont.svg#OpenSansLightItalic") format("svg"); + font-weight: normal; + font-style: normal; +} + +@font-face { + font-family: 'OpenSansRegular'; + src: url("../fonts/OpenSans-Regular-webfont.eot"); + src: url("../fonts/OpenSans-Regular-webfont.eot?#iefix") format("embedded-opentype"), url("../fonts/OpenSans-Regular-webfont.woff") format("woff"), url("../fonts/OpenSans-Regular-webfont.ttf") format("truetype"), url("../fonts/OpenSans-Regular-webfont.svg#OpenSansRegular") format("svg"); + font-weight: normal; + font-style: normal; + -webkit-font-smoothing: antialiased; +} + +@font-face { + font-family: 'OpenSansItalic'; + src: url("../fonts/OpenSans-Italic-webfont.eot"); + src: url("../fonts/OpenSans-Italic-webfont.eot?#iefix") format("embedded-opentype"), url("../fonts/OpenSans-Italic-webfont.woff") format("woff"), url("../fonts/OpenSans-Italic-webfont.ttf") format("truetype"), url("../fonts/OpenSans-Italic-webfont.svg#OpenSansItalic") format("svg"); + font-weight: normal; + font-style: normal; + -webkit-font-smoothing: antialiased; +} + +@font-face { + font-family: 'OpenSansSemibold'; + src: url("../fonts/OpenSans-Semibold-webfont.eot"); + src: url("../fonts/OpenSans-Semibold-webfont.eot?#iefix") format("embedded-opentype"), url("../fonts/OpenSans-Semibold-webfont.woff") format("woff"), url("../fonts/OpenSans-Semibold-webfont.ttf") format("truetype"), url("../fonts/OpenSans-Semibold-webfont.svg#OpenSansSemibold") format("svg"); + font-weight: normal; + font-style: normal; + -webkit-font-smoothing: antialiased; +} + +@font-face { + font-family: 'OpenSansSemiboldItalic'; + src: url("../fonts/OpenSans-SemiboldItalic-webfont.eot"); + src: url("../fonts/OpenSans-SemiboldItalic-webfont.eot?#iefix") format("embedded-opentype"), url("../fonts/OpenSans-SemiboldItalic-webfont.woff") format("woff"), url("../fonts/OpenSans-SemiboldItalic-webfont.ttf") format("truetype"), url("../fonts/OpenSans-SemiboldItalic-webfont.svg#OpenSansSemiboldItalic") format("svg"); + font-weight: normal; + font-style: normal; + -webkit-font-smoothing: antialiased; +} + +@font-face { + font-family: 'OpenSansBold'; + src: url("../fonts/OpenSans-Bold-webfont.eot"); + src: url("../fonts/OpenSans-Bold-webfont.eot?#iefix") format("embedded-opentype"), url("../fonts/OpenSans-Bold-webfont.woff") format("woff"), url("../fonts/OpenSans-Bold-webfont.ttf") format("truetype"), url("../fonts/OpenSans-Bold-webfont.svg#OpenSansBold") format("svg"); + font-weight: normal; + font-style: normal; + -webkit-font-smoothing: antialiased; +} + +@font-face { + font-family: 'OpenSansBoldItalic'; + src: url("../fonts/OpenSans-BoldItalic-webfont.eot"); + src: url("../fonts/OpenSans-BoldItalic-webfont.eot?#iefix") format("embedded-opentype"), url("../fonts/OpenSans-BoldItalic-webfont.woff") format("woff"), url("../fonts/OpenSans-BoldItalic-webfont.ttf") format("truetype"), url("../fonts/OpenSans-BoldItalic-webfont.svg#OpenSansBoldItalic") format("svg"); + font-weight: normal; + font-style: normal; + -webkit-font-smoothing: antialiased; +} + +/* normalize.css 2012-02-07T12:37 UTC - http://github.com/necolas/normalize.css */ +/* ============================================================================= + HTML5 display definitions + ========================================================================== */ +/* + * Corrects block display not defined in IE6/7/8/9 & FF3 + */ +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +nav, +section, +summary { + display: block; +} + +/* + * Corrects inline-block display not defined in IE6/7/8/9 & FF3 + */ +audio, +canvas, +video { + display: inline-block; + *display: inline; + *zoom: 1; +} + +/* + * Prevents modern browsers from displaying 'audio' without controls + */ +audio:not([controls]) { + display: none; +} + +/* + * Addresses styling for 'hidden' attribute not present in IE7/8/9, FF3, S4 + * Known issue: no IE6 support + */ +[hidden] { + display: none; +} + +/* ============================================================================= + Base + ========================================================================== */ +/* + * 1. Corrects text resizing oddly in IE6/7 when body font-size is set using em units + * http://clagnut.com/blog/348/#c790 + * 2. Prevents iOS text size adjust after orientation change, without disabling user zoom + * www.456bereastreet.com/archive/201012/controlling_text_size_in_safari_for_ios_without_disabling_user_zoom/ + */ +html { + font-size: 100%; + /* 1 */ + -webkit-text-size-adjust: 100%; + /* 2 */ + -ms-text-size-adjust: 100%; + /* 2 */ +} + +/* + * Addresses font-family inconsistency between 'textarea' and other form elements. + */ +html, +button, +input, +select, +textarea { + font-family: sans-serif; +} + +/* + * Addresses margins handled incorrectly in IE6/7 + */ +body { + margin: 0; +} + +/* ============================================================================= + Links + ========================================================================== */ +/* + * Addresses outline displayed oddly in Chrome + */ +a:focus { + outline: thin dotted; +} + +/* + * Improves readability when focused and also mouse hovered in all browsers + * people.opera.com/patrickl/experiments/keyboard/test + */ +a:hover, +a:active { + outline: 0; +} + +/* ============================================================================= + Typography + ========================================================================== */ +/* + * Addresses font sizes and margins set differently in IE6/7 + * Addresses font sizes within 'section' and 'article' in FF4+, Chrome, S5 + */ +h1 { + font-size: 2em; + margin: 0.67em 0; +} + +h2 { + font-size: 1.5em; + margin: 0.83em 0; +} + +h3 { + font-size: 1.17em; + margin: 1em 0; +} + +h4 { + font-size: 1em; + margin: 1.33em 0; +} + +h5 { + font-size: 0.83em; + margin: 1.67em 0; +} + +h6 { + font-size: 0.75em; + margin: 2.33em 0; +} + +/* + * Addresses styling not present in IE7/8/9, S5, Chrome + */ +abbr[title] { + border-bottom: 1px dotted; +} + +/* + * Addresses style set to 'bolder' in FF3+, S4/5, Chrome +*/ +b, +strong { + font-weight: bold; +} + +blockquote { + margin: 1em 40px; +} + +/* + * Addresses styling not present in S5, Chrome + */ +dfn { + font-style: italic; +} + +/* + * Addresses styling not present in IE6/7/8/9 + */ +mark { + background: #ff0; + color: #000; +} + +/* + * Addresses margins set differently in IE6/7 + */ +p, +pre { + margin: 1em 0; +} + +/* + * Corrects font family set oddly in IE6, S4/5, Chrome + * en.wikipedia.org/wiki/User:Davidgothberg/Test59 + */ +pre, +code, +kbd, +samp { + font-family: monospace, serif; + _font-family: 'courier new', monospace; + font-size: 1em; +} + +/* + * 1. Addresses CSS quotes not supported in IE6/7 + * 2. Addresses quote property not supported in S4 + */ +/* 1 */ +q { + quotes: none; +} + +/* 2 */ +q:before, +q:after { + content: ''; + content: none; +} + +small { + font-size: 75%; +} + +/* + * Prevents sub and sup affecting line-height in all browsers + * gist.github.com/413930 + */ +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sup { + top: -0.5em; +} + +sub { + bottom: -0.25em; +} + +/* ============================================================================= + Lists + ========================================================================== */ +/* + * Addresses margins set differently in IE6/7 + */ +dl, +menu, +ol, +ul { + margin: 1em 0; +} + +dd { + margin: 0 0 0 40px; +} + +/* + * Addresses paddings set differently in IE6/7 + */ +menu, +ol, +ul { + padding: 0 0 0 40px; +} + +/* + * Corrects list images handled incorrectly in IE7 + */ +nav ul, +nav ol { + list-style: none; + list-style-image: none; +} + +/* ============================================================================= + Embedded content + ========================================================================== */ +/* + * 1. Removes border when inside 'a' element in IE6/7/8/9, FF3 + * 2. Improves image quality when scaled in IE7 + * code.flickr.com/blog/2008/11/12/on-ui-quality-the-little-things-client-side-image-resizing/ + */ +img { + border: 0; + /* 1 */ + -ms-interpolation-mode: bicubic; + /* 2 */ +} + +/* + * Corrects overflow displayed oddly in IE9 + */ +svg:not(:root) { + overflow: hidden; +} + +/* ============================================================================= + Figures + ========================================================================== */ +/* + * Addresses margin not present in IE6/7/8/9, S5, O11 + */ +figure { + margin: 0; +} + +/* ============================================================================= + Forms + ========================================================================== */ +/* + * Corrects margin displayed oddly in IE6/7 + */ +form { + margin: 0; +} + +/* + * Define consistent border, margin, and padding + */ +fieldset { + border: 1px solid #c0c0c0; + margin: 0 2px; + padding: 0.35em 0.625em 0.75em; +} + +/* + * 1. Corrects color not being inherited in IE6/7/8/9 + * 2. Corrects text not wrapping in FF3 + * 3. Corrects alignment displayed oddly in IE6/7 + */ +legend { + border: 0; + /* 1 */ + padding: 0; + white-space: normal; + /* 2 */ + *margin-left: -7px; + /* 3 */ +} + +/* + * 1. Corrects font size not being inherited in all browsers + * 2. Addresses margins set differently in IE6/7, FF3+, S5, Chrome + * 3. Improves appearance and consistency in all browsers + */ +button, +input, +select, +textarea { + font-size: 100%; + /* 1 */ + margin: 0; + /* 2 */ + vertical-align: baseline; + /* 3 */ + *vertical-align: middle; + /* 3 */ +} + +/* + * Addresses FF3/4 setting line-height on 'input' using !important in the UA stylesheet + */ +button, +input { + line-height: normal; + /* 1 */ +} + +/* + * 1. Improves usability and consistency of cursor style between image-type 'input' and others + * 2. Corrects inability to style clickable 'input' types in iOS + * 3. Removes inner spacing in IE7 without affecting normal text inputs + * Known issue: inner spacing remains in IE6 + */ +button, +input[type="button"], +input[type="reset"], +input[type="submit"] { + cursor: pointer; + /* 1 */ + -webkit-appearance: button; + /* 2 */ + *overflow: visible; + /* 3 */ +} + +/* + * Re-set default cursor for disabled elements + */ +button[disabled], +input[disabled] { + cursor: default; +} + +/* + * 1. Addresses box sizing set to content-box in IE8/9 + * 2. Removes excess padding in IE8/9 + * 3. Removes excess padding in IE7 + Known issue: excess padding remains in IE6 + */ +input[type="checkbox"], +input[type="radio"] { + box-sizing: border-box; + /* 1 */ + padding: 0; + /* 2 */ + *height: 13px; + /* 3 */ + *width: 13px; + /* 3 */ +} + +/* + * 1. Addresses appearance set to searchfield in S5, Chrome + * 2. Addresses box-sizing set to border-box in S5, Chrome (include -moz to future-proof) + */ +input[type="search"] { + -webkit-appearance: textfield; + /* 1 */ + -moz-box-sizing: content-box; + -webkit-box-sizing: content-box; + /* 2 */ + box-sizing: content-box; +} + +/* + * Removes inner padding and search cancel button in S5, Chrome on OS X + */ +input[type="search"]::-webkit-search-decoration, +input[type="search"]::-webkit-search-cancel-button { + -webkit-appearance: none; +} + +/* + * Removes inner padding and border in FF3+ + * www.sitepen.com/blog/2008/05/14/the-devils-in-the-details-fixing-dojos-toolbar-buttons/ + */ +button::-moz-focus-inner, +input::-moz-focus-inner { + border: 0; + padding: 0; +} + +/* + * 1. Removes default vertical scrollbar in IE6/7/8/9 + * 2. Improves readability and alignment in all browsers + */ +textarea { + overflow: auto; + /* 1 */ + vertical-align: top; + /* 2 */ +} + +/* ============================================================================= + Tables + ========================================================================== */ +/* + * Remove most spacing between table cells + */ +table { + border-collapse: collapse; + border-spacing: 0; +} + +body { + padding: 0px 0 20px 0px; + margin: 0px; + font: 14px/1.5 "OpenSansRegular", "Helvetica Neue", Helvetica, Arial, sans-serif; + color: #f0e7d5; + font-weight: normal; + background: #252525; + background-attachment: fixed !important; + background: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #2a2a29), color-stop(100%, #1c1c1c)); + background: -webkit-linear-gradient(#2a2a29, #1c1c1c); + background: -moz-linear-gradient(#2a2a29, #1c1c1c); + background: -o-linear-gradient(#2a2a29, #1c1c1c); + background: -ms-linear-gradient(#2a2a29, #1c1c1c); + background: linear-gradient(#2a2a29, #1c1c1c); +} + +h1, h2, h3, h4, h5, h6 { + color: #e8e8e8; + margin: 0 0 10px; + font-family: 'OpenSansRegular', "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: normal; +} + +p, ul, ol, table, pre, dl { + margin: 0 0 20px; +} + +h1, h2, h3 { + line-height: 1.1; +} + +h1 { + font-size: 28px; +} + +h2 { + font-size: 24px; +} + +h4, h5, h6 { + color: #e8e8e8; +} + +h3 { + font-size: 18px; + line-height: 24px; + font-family: 'OpenSansRegular', "Helvetica Neue", Helvetica, Arial, sans-serif !important; + font-weight: normal; + color: #b6b6b6; +} + +a { + color: #ffcc00; + font-weight: 400; + text-decoration: none; +} +a:hover { + color: #ffeb9b; +} + +a small { + font-size: 11px; + color: #666; + margin-top: -0.6em; + display: block; +} + +ul { + list-style-image: url("../images/bullet.png"); +} + +strong { + font-family: 'OpenSansBold', "Helvetica Neue", Helvetica, Arial, sans-serif !important; + font-weight: normal; +} + +.wrapper { + max-width: 650px; + margin: 0 auto; + position: relative; + padding: 0 20px; +} + +section img { + max-width: 100%; +} + +blockquote { + border-left: 3px solid #ffcc00; + margin: 0; + padding: 0 0 0 20px; + font-style: italic; +} + +code { + font-family: "Lucida Sans", Monaco, Bitstream Vera Sans Mono, Lucida Console, Terminal; + color: #efefef; + font-size: 13px; + margin: 0 4px; + padding: 4px 6px; + -moz-border-radius: 2px; + -webkit-border-radius: 2px; + -o-border-radius: 2px; + -ms-border-radius: 2px; + -khtml-border-radius: 2px; + border-radius: 2px; +} + +pre { + padding: 8px 15px; + background: #191919; + -moz-border-radius: 2px; + -webkit-border-radius: 2px; + -o-border-radius: 2px; + -ms-border-radius: 2px; + -khtml-border-radius: 2px; + border-radius: 2px; + border: 1px solid #121212; + -moz-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.3); + -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.3); + -o-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.3); + box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.3); + overflow: auto; + overflow-y: hidden; +} +pre code { + color: #efefef; + text-shadow: 0px 1px 0px #000; + margin: 0; + padding: 0; +} + +table { + width: 100%; + border-collapse: collapse; +} + +th { + text-align: left; + padding: 5px 10px; + border-bottom: 1px solid #434343; + color: #b6b6b6; + font-family: 'OpenSansSemibold', "Helvetica Neue", Helvetica, Arial, sans-serif !important; + font-weight: normal; +} + +td { + text-align: left; + padding: 5px 10px; + border-bottom: 1px solid #434343; +} + +hr { + border: 0; + outline: none; + height: 3px; + background: transparent url("../images/hr.gif") center center repeat-x; + margin: 0 0 20px; +} + +dt { + color: #F0E7D5; + font-family: 'OpenSansSemibold', "Helvetica Neue", Helvetica, Arial, sans-serif !important; + font-weight: normal; +} + +#header { + z-index: 100; + left: 0; + top: 0px; + height: 60px; + width: 100%; + position: fixed; + background: url(../images/nav-bg.gif) #353535; + border-bottom: 4px solid #434343; + -moz-box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.25); + -webkit-box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.25); + -o-box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.25); + box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.25); +} +#header nav { + max-width: 650px; + margin: 0 auto; + padding: 0 10px; + background: blue; + margin: 6px auto; +} +#header nav li { + font-family: 'OpenSansLight', "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: normal; + list-style: none; + display: inline; + color: white; + line-height: 50px; + text-shadow: 0px 1px 0px rgba(0, 0, 0, 0.2); + font-size: 14px; +} +#header nav li a { + color: white; + border: 1px solid #5d910b; + background: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #93bd20), color-stop(100%, #659e10)); + background: -webkit-linear-gradient(#93bd20, #659e10); + background: -moz-linear-gradient(#93bd20, #659e10); + background: -o-linear-gradient(#93bd20, #659e10); + background: -ms-linear-gradient(#93bd20, #659e10); + background: linear-gradient(#93bd20, #659e10); + -moz-border-radius: 2px; + -webkit-border-radius: 2px; + -o-border-radius: 2px; + -ms-border-radius: 2px; + -khtml-border-radius: 2px; + border-radius: 2px; + -moz-box-shadow: inset 0px 1px 0px rgba(255, 255, 255, 0.3), 0px 3px 7px rgba(0, 0, 0, 0.7); + -webkit-box-shadow: inset 0px 1px 0px rgba(255, 255, 255, 0.3), 0px 3px 7px rgba(0, 0, 0, 0.7); + -o-box-shadow: inset 0px 1px 0px rgba(255, 255, 255, 0.3), 0px 3px 7px rgba(0, 0, 0, 0.7); + box-shadow: inset 0px 1px 0px rgba(255, 255, 255, 0.3), 0px 3px 7px rgba(0, 0, 0, 0.7); + background-color: #93bd20; + padding: 10px 12px; + margin-top: 6px; + line-height: 14px; + font-size: 14px; + display: inline-block; + text-align: center; +} +#header nav li a:hover { + background: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #749619), color-stop(100%, #527f0e)); + background: -webkit-linear-gradient(#749619, #527f0e); + background: -moz-linear-gradient(#749619, #527f0e); + background: -o-linear-gradient(#749619, #527f0e); + background: -ms-linear-gradient(#749619, #527f0e); + background: linear-gradient(#749619, #527f0e); + background-color: #659e10; + border: 1px solid #527f0e; + -moz-box-shadow: inset 0px 1px 1px rgba(0, 0, 0, 0.2), 0px 1px 0px rgba(0, 0, 0, 0); + -webkit-box-shadow: inset 0px 1px 1px rgba(0, 0, 0, 0.2), 0px 1px 0px rgba(0, 0, 0, 0); + -o-box-shadow: inset 0px 1px 1px rgba(0, 0, 0, 0.2), 0px 1px 0px rgba(0, 0, 0, 0); + box-shadow: inset 0px 1px 1px rgba(0, 0, 0, 0.2), 0px 1px 0px rgba(0, 0, 0, 0); +} +#header nav li.fork { + float: left; + margin-left: 0px; +} +#header nav li.downloads { + float: right; + margin-left: 6px; +} +#header nav li.title { + float: right; + margin-right: 10px; + font-size: 11px; +} + +section { + max-width: 650px; + padding: 30px 0px 50px 0px; + margin: 20px 0; + margin-top: 70px; +} +section #title { + border: 0; + outline: none; + margin: 0 0 50px 0; + padding: 0 0 5px 0; +} +section #title h1 { + font-family: 'OpenSansLight', "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: normal; + font-size: 40px; + text-align: center; + line-height: 36px; +} +section #title p { + color: #d7cfbe; + font-family: 'OpenSansLight', "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: normal; + font-size: 18px; + text-align: center; +} +section #title .credits { + font-size: 11px; + font-family: 'OpenSansRegular', "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: normal; + color: #696969; + margin-top: -10px; +} +section #title .credits.left { + float: left; +} +section #title .credits.right { + float: right; +} + +@media print, screen and (max-width: 720px) { + #title .credits { + display: block; + width: 100%; + line-height: 30px; + text-align: center; + } + #title .credits .left { + float: none; + display: block; + } + #title .credits .right { + float: none; + display: block; + } +} +@media print, screen and (max-width: 480px) { + #header { + margin-top: -20px; + } + + section { + margin-top: 40px; + } + + nav { + display: none; + } +} diff --git a/stylesheets/stylesheet.css b/stylesheets/stylesheet.css new file mode 100644 index 000000000..b5f20c235 --- /dev/null +++ b/stylesheets/stylesheet.css @@ -0,0 +1,245 @@ +* { + box-sizing: border-box; } + +body { + padding: 0; + margin: 0; + font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 16px; + line-height: 1.5; + color: #606c71; } + +a { + color: #1e6bb8; + text-decoration: none; } + a:hover { + text-decoration: underline; } + +.btn { + display: inline-block; + margin-bottom: 1rem; + color: rgba(255, 255, 255, 0.7); + background-color: rgba(255, 255, 255, 0.08); + border-color: rgba(255, 255, 255, 0.2); + border-style: solid; + border-width: 1px; + border-radius: 0.3rem; + transition: color 0.2s, background-color 0.2s, border-color 0.2s; } + .btn + .btn { + margin-left: 1rem; } + +.btn:hover { + color: rgba(255, 255, 255, 0.8); + text-decoration: none; + background-color: rgba(255, 255, 255, 0.2); + border-color: rgba(255, 255, 255, 0.3); } + +@media screen and (min-width: 64em) { + .btn { + padding: 0.75rem 1rem; } } + +@media screen and (min-width: 42em) and (max-width: 64em) { + .btn { + padding: 0.6rem 0.9rem; + font-size: 0.9rem; } } + +@media screen and (max-width: 42em) { + .btn { + display: block; + width: 100%; + padding: 0.75rem; + font-size: 0.9rem; } + .btn + .btn { + margin-top: 1rem; + margin-left: 0; } } + +.page-header { + color: #fff; + text-align: center; + background-color: #159957; + background-image: linear-gradient(120deg, #155799, #159957); } + +@media screen and (min-width: 64em) { + .page-header { + padding: 5rem 6rem; } } + +@media screen and (min-width: 42em) and (max-width: 64em) { + .page-header { + padding: 3rem 4rem; } } + +@media screen and (max-width: 42em) { + .page-header { + padding: 2rem 1rem; } } + +.project-name { + margin-top: 0; + margin-bottom: 0.1rem; } + +@media screen and (min-width: 64em) { + .project-name { + font-size: 3.25rem; } } + +@media screen and (min-width: 42em) and (max-width: 64em) { + .project-name { + font-size: 2.25rem; } } + +@media screen and (max-width: 42em) { + .project-name { + font-size: 1.75rem; } } + +.project-tagline { + margin-bottom: 2rem; + font-weight: normal; + opacity: 0.7; } + +@media screen and (min-width: 64em) { + .project-tagline { + font-size: 1.25rem; } } + +@media screen and (min-width: 42em) and (max-width: 64em) { + .project-tagline { + font-size: 1.15rem; } } + +@media screen and (max-width: 42em) { + .project-tagline { + font-size: 1rem; } } + +.main-content :first-child { + margin-top: 0; } +.main-content img { + max-width: 100%; } +.main-content h1, .main-content h2, .main-content h3, .main-content h4, .main-content h5, .main-content h6 { + margin-top: 2rem; + margin-bottom: 1rem; + font-weight: normal; + color: #159957; } +.main-content p { + margin-bottom: 1em; } +.main-content code { + padding: 2px 4px; + font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; + font-size: 0.9rem; + color: #383e41; + background-color: #f3f6fa; + border-radius: 0.3rem; } +.main-content pre { + padding: 0.8rem; + margin-top: 0; + margin-bottom: 1rem; + font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace; + color: #567482; + word-wrap: normal; + background-color: #f3f6fa; + border: solid 1px #dce6f0; + border-radius: 0.3rem; } + .main-content pre > code { + padding: 0; + margin: 0; + font-size: 0.9rem; + color: #567482; + word-break: normal; + white-space: pre; + background: transparent; + border: 0; } +.main-content .highlight { + margin-bottom: 1rem; } + .main-content .highlight pre { + margin-bottom: 0; + word-break: normal; } +.main-content .highlight pre, .main-content pre { + padding: 0.8rem; + overflow: auto; + font-size: 0.9rem; + line-height: 1.45; + border-radius: 0.3rem; } +.main-content pre code, .main-content pre tt { + display: inline; + max-width: initial; + padding: 0; + margin: 0; + overflow: initial; + line-height: inherit; + word-wrap: normal; + background-color: transparent; + border: 0; } + .main-content pre code:before, .main-content pre code:after, .main-content pre tt:before, .main-content pre tt:after { + content: normal; } +.main-content ul, .main-content ol { + margin-top: 0; } +.main-content blockquote { + padding: 0 1rem; + margin-left: 0; + color: #819198; + border-left: 0.3rem solid #dce6f0; } + .main-content blockquote > :first-child { + margin-top: 0; } + .main-content blockquote > :last-child { + margin-bottom: 0; } +.main-content table { + display: block; + width: 100%; + overflow: auto; + word-break: normal; + word-break: keep-all; } + .main-content table th { + font-weight: bold; } + .main-content table th, .main-content table td { + padding: 0.5rem 1rem; + border: 1px solid #e9ebec; } +.main-content dl { + padding: 0; } + .main-content dl dt { + padding: 0; + margin-top: 1rem; + font-size: 1rem; + font-weight: bold; } + .main-content dl dd { + padding: 0; + margin-bottom: 1rem; } +.main-content hr { + height: 2px; + padding: 0; + margin: 1rem 0; + background-color: #eff0f1; + border: 0; } + +@media screen and (min-width: 64em) { + .main-content { + max-width: 64rem; + padding: 2rem 6rem; + margin: 0 auto; + font-size: 1.1rem; } } + +@media screen and (min-width: 42em) and (max-width: 64em) { + .main-content { + padding: 2rem 4rem; + font-size: 1.1rem; } } + +@media screen and (max-width: 42em) { + .main-content { + padding: 2rem 1rem; + font-size: 1rem; } } + +.site-footer { + padding-top: 2rem; + margin-top: 2rem; + border-top: solid 1px #eff0f1; } + +.site-footer-owner { + display: block; + font-weight: bold; } + +.site-footer-credits { + color: #819198; } + +@media screen and (min-width: 64em) { + .site-footer { + font-size: 1rem; } } + +@media screen and (min-width: 42em) and (max-width: 64em) { + .site-footer { + font-size: 1rem; } } + +@media screen and (max-width: 42em) { + .site-footer { + font-size: 0.9rem; } } diff --git a/websocket-sharp.sln b/websocket-sharp.sln deleted file mode 100644 index 6ff1c0362..000000000 --- a/websocket-sharp.sln +++ /dev/null @@ -1,64 +0,0 @@ - -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}") = "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}" -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 - Release_Ubuntu|Any CPU = Release_Ubuntu|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {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 - EndGlobalSection - GlobalSection(MonoDevelopProperties) = preSolution - StartupItem = websocket-sharp\websocket-sharp.csproj - Policies = $0 - $0.TextStylePolicy = $1 - $1.inheritsSet = null - $1.scope = text/x-csharp - $0.CSharpFormattingPolicy = $2 - $2.inheritsSet = Mono - $2.inheritsScope = text/x-csharp - $2.scope = text/x-csharp - EndGlobalSection -EndGlobal diff --git a/websocket-sharp/AssemblyInfo.cs b/websocket-sharp/AssemblyInfo.cs deleted file mode 100644 index c85deaa45..000000000 --- a/websocket-sharp/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("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. - -[assembly: AssemblyVersion("1.0.2.*")] - -// 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/websocket-sharp/ByteOrder.cs b/websocket-sharp/ByteOrder.cs deleted file mode 100644 index 317f462ea..000000000 --- a/websocket-sharp/ByteOrder.cs +++ /dev/null @@ -1,47 +0,0 @@ -#region License -/* - * ByteOrder.cs - * - * The MIT License - * - * Copyright (c) 2012-2015 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -using System; - -namespace WebSocketSharp -{ - /// - /// Specifies the byte order. - /// - public enum ByteOrder - { - /// - /// Specifies Little-endian. - /// - Little, - /// - /// Specifies Big-endian. - /// - Big - } -} diff --git a/websocket-sharp/CloseEventArgs.cs b/websocket-sharp/CloseEventArgs.cs deleted file mode 100644 index 8aa46e2d2..000000000 --- a/websocket-sharp/CloseEventArgs.cs +++ /dev/null @@ -1,118 +0,0 @@ -#region License -/* - * CloseEventArgs.cs - * - * The MIT License - * - * Copyright (c) 2012-2024 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -using System; - -namespace WebSocketSharp -{ - /// - /// Represents the event data for the event. - /// - /// - /// - /// The close event occurs when the WebSocket connection has been closed. - /// - /// - /// If you would like to get the reason for the connection close, - /// you should access the or - /// property. - /// - /// - public class CloseEventArgs : EventArgs - { - #region Private Fields - - private PayloadData _payloadData; - private bool _wasClean; - - #endregion - - #region Internal Constructors - - internal CloseEventArgs (PayloadData payloadData, bool clean) - { - _payloadData = payloadData; - _wasClean = clean; - } - - #endregion - - #region Public Properties - - /// - /// Gets the status code for the connection close. - /// - /// - /// - /// A that represents the status code for - /// the connection close. - /// - /// - /// 1005 (no status) if not present. - /// - /// - public ushort Code { - get { - return _payloadData.Code; - } - } - - /// - /// Gets the reason for the connection close. - /// - /// - /// - /// A that represents the reason for - /// the connection close. - /// - /// - /// An empty string if not present. - /// - /// - public string Reason { - get { - return _payloadData.Reason; - } - } - - /// - /// Gets a value indicating whether the connection has been closed cleanly. - /// - /// - /// true if the connection has been closed cleanly; otherwise, - /// false. - /// - public bool WasClean { - get { - return _wasClean; - } - } - - #endregion - } -} diff --git a/websocket-sharp/CloseStatusCode.cs b/websocket-sharp/CloseStatusCode.cs deleted file mode 100644 index 81f3317a4..000000000 --- a/websocket-sharp/CloseStatusCode.cs +++ /dev/null @@ -1,120 +0,0 @@ -#region License -/* - * CloseStatusCode.cs - * - * The MIT License - * - * Copyright (c) 2012-2016 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -using System; - -namespace WebSocketSharp -{ - /// - /// Indicates the status code for the WebSocket connection close. - /// - /// - /// - /// The values of this enumeration are defined in - /// - /// Section 7.4 of RFC 6455. - /// - /// - /// "Reserved value" cannot be sent as a status code in - /// closing handshake by an endpoint. - /// - /// - public enum CloseStatusCode : ushort - { - /// - /// Equivalent to close status 1000. Indicates normal close. - /// - Normal = 1000, - /// - /// Equivalent to close status 1001. Indicates that an endpoint is - /// going away. - /// - Away = 1001, - /// - /// Equivalent to close status 1002. Indicates that an endpoint is - /// terminating the connection due to a protocol error. - /// - ProtocolError = 1002, - /// - /// Equivalent to close status 1003. Indicates that an endpoint is - /// terminating the connection because it has received a type of - /// data that it cannot accept. - /// - UnsupportedData = 1003, - /// - /// Equivalent to close status 1004. Still undefined. A Reserved value. - /// - Undefined = 1004, - /// - /// Equivalent to close status 1005. Indicates that no status code was - /// actually present. A Reserved value. - /// - NoStatus = 1005, - /// - /// Equivalent to close status 1006. Indicates that the connection was - /// closed abnormally. A Reserved value. - /// - Abnormal = 1006, - /// - /// Equivalent to close status 1007. Indicates that an endpoint is - /// terminating the connection because it has received a message that - /// contains data that is not consistent with the type of the message. - /// - InvalidData = 1007, - /// - /// Equivalent to close status 1008. Indicates that an endpoint is - /// terminating the connection because it has received a message that - /// violates its policy. - /// - PolicyViolation = 1008, - /// - /// Equivalent to close status 1009. Indicates that an endpoint is - /// terminating the connection because it has received a message that - /// is too big to process. - /// - TooBig = 1009, - /// - /// Equivalent to close status 1010. Indicates that a client is - /// terminating the connection because it has expected the server to - /// negotiate one or more extension, but the server did not return - /// them in the handshake response. - /// - MandatoryExtension = 1010, - /// - /// Equivalent to close status 1011. Indicates that a server is - /// terminating the connection because it has encountered an unexpected - /// condition that prevented it from fulfilling the request. - /// - ServerError = 1011, - /// - /// Equivalent to close status 1015. Indicates that the connection was - /// closed due to a failure to perform a TLS handshake. A Reserved value. - /// - TlsHandshakeFailure = 1015 - } -} diff --git a/websocket-sharp/CompressionMethod.cs b/websocket-sharp/CompressionMethod.cs deleted file mode 100644 index 42ab230a6..000000000 --- a/websocket-sharp/CompressionMethod.cs +++ /dev/null @@ -1,52 +0,0 @@ -#region License -/* - * CompressionMethod.cs - * - * The MIT License - * - * Copyright (c) 2013-2017 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -using System; - -namespace WebSocketSharp -{ - /// - /// Specifies the method for compression. - /// - /// - /// The methods are defined in - /// - /// Compression Extensions for WebSocket. - /// - public enum CompressionMethod : byte - { - /// - /// Specifies no compression. - /// - None, - /// - /// Specifies DEFLATE. - /// - Deflate - } -} diff --git a/websocket-sharp/ErrorEventArgs.cs b/websocket-sharp/ErrorEventArgs.cs deleted file mode 100644 index c02d0e000..000000000 --- a/websocket-sharp/ErrorEventArgs.cs +++ /dev/null @@ -1,115 +0,0 @@ -#region License -/* - * ErrorEventArgs.cs - * - * The MIT License - * - * Copyright (c) 2012-2022 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -#region Contributors -/* - * Contributors: - * - Frank Razenberg - */ -#endregion - -using System; - -namespace WebSocketSharp -{ - /// - /// Represents the event data for the event. - /// - /// - /// - /// The error event occurs when the interface - /// gets an error. - /// - /// - /// If you would like to get the error message, you should access - /// the property. - /// - /// - /// If the error is due to an exception, you can get it by accessing - /// the property. - /// - /// - public class ErrorEventArgs : EventArgs - { - #region Private Fields - - private Exception _exception; - private string _message; - - #endregion - - #region Internal Constructors - - internal ErrorEventArgs (string message) - : this (message, null) - { - } - - internal ErrorEventArgs (string message, Exception exception) - { - _message = message; - _exception = exception; - } - - #endregion - - #region Public Properties - - /// - /// Gets the exception that caused the error. - /// - /// - /// - /// An instance that represents - /// the cause of the error. - /// - /// - /// if not present. - /// - /// - public Exception Exception { - get { - return _exception; - } - } - - /// - /// Gets the error message. - /// - /// - /// A that represents the error message. - /// - public string Message { - get { - return _message; - } - } - - #endregion - } -} diff --git a/websocket-sharp/Ext.cs b/websocket-sharp/Ext.cs deleted file mode 100644 index 2540f803e..000000000 --- a/websocket-sharp/Ext.cs +++ /dev/null @@ -1,1902 +0,0 @@ -#region License -/* - * Ext.cs - * - * Some parts of this code are derived from Mono (http://www.mono-project.com): - * - The GetStatusDescription method is derived from HttpListenerResponse.cs (System.Net) - * - The MaybeUri method is derived from Uri.cs (System) - * - The isPredefinedScheme method is derived from Uri.cs (System) - * - * The MIT License - * - * Copyright (c) 2001 Garrett Rooney - * Copyright (c) 2003 Ian MacLean - * Copyright (c) 2003 Ben Maurer - * Copyright (c) 2003, 2005, 2009 Novell, Inc. (http://www.novell.com) - * Copyright (c) 2009 Stephane Delcroix - * Copyright (c) 2010-2025 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -#region Contributors -/* - * Contributors: - * - Liryna - * - Nikola Kovacevic - * - Chris Swiedler - */ -#endregion - -using System; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.IO; -using System.IO.Compression; -using System.Net.Sockets; -using System.Security.Cryptography.X509Certificates; -using System.Text; -using WebSocketSharp.Net; -using WebSocketSharp.Net.WebSockets; -using WebSocketSharp.Server; - -namespace WebSocketSharp -{ - /// - /// Provides a set of static methods for websocket-sharp. - /// - public static class Ext - { - #region Private Fields - - private static readonly byte[] _last = new byte[] { 0x00 }; - private static readonly int _maxRetry = 5; - private const string _tspecials = "()<>@,;:\\\"/[]?={} \t"; - - #endregion - - #region Private Methods - - private static byte[] compress (this byte[] data) - { - if (data.LongLength == 0) - return data; - - using (var input = new MemoryStream (data)) - return input.compressToArray (); - } - - private static MemoryStream compress (this Stream stream) - { - var ret = new MemoryStream (); - - if (stream.Length == 0) - return ret; - - stream.Position = 0; - - var mode = CompressionMode.Compress; - - using (var ds = new DeflateStream (ret, mode, true)) { - stream.CopyTo (ds, 1024); - ds.Close (); // BFINAL set to 1. - ret.Write (_last, 0, 1); - - ret.Position = 0; - - return ret; - } - } - - private static byte[] compressToArray (this Stream stream) - { - using (var output = stream.compress ()) { - output.Close (); - - return output.ToArray (); - } - } - - private static byte[] decompress (this byte[] data) - { - if (data.LongLength == 0) - return data; - - using (var input = new MemoryStream (data)) - return input.decompressToArray (); - } - - private static MemoryStream decompress (this Stream stream) - { - var ret = new MemoryStream (); - - if (stream.Length == 0) - return ret; - - stream.Position = 0; - - var mode = CompressionMode.Decompress; - - using (var ds = new DeflateStream (stream, mode, true)) { - ds.CopyTo (ret, 1024); - - ret.Position = 0; - - return ret; - } - } - - private static byte[] decompressToArray (this Stream stream) - { - using (var output = stream.decompress ()) { - output.Close (); - - return output.ToArray (); - } - } - - private static bool isPredefinedScheme (this string value) - { - var c = value[0]; - - if (c == 'h') - return value == "http" || value == "https"; - - if (c == 'w') - return value == "ws" || value == "wss"; - - if (c == 'f') - return value == "file" || value == "ftp"; - - if (c == 'g') - return value == "gopher"; - - if (c == 'm') - return value == "mailto"; - - if (c == 'n') { - c = value[1]; - - return c == 'e' - ? value == "news" || value == "net.pipe" || value == "net.tcp" - : value == "nntp"; - } - - return false; - } - - #endregion - - #region Internal Methods - - internal static byte[] Append (this ushort code, string reason) - { - var codeAsBytes = code.ToByteArray (ByteOrder.Big); - - if (reason == null || reason.Length == 0) - return codeAsBytes; - - var buff = new List (codeAsBytes); - var reasonAsBytes = Encoding.UTF8.GetBytes (reason); - - buff.AddRange (reasonAsBytes); - - return buff.ToArray (); - } - - internal static byte[] Compress ( - this byte[] data, - CompressionMethod method - ) - { - return method == CompressionMethod.Deflate ? data.compress () : data; - } - - internal static Stream Compress ( - this Stream stream, - CompressionMethod method - ) - { - return method == CompressionMethod.Deflate ? stream.compress () : stream; - } - - internal static bool Contains (this string value, params char[] anyOf) - { - return anyOf != null && anyOf.Length > 0 - ? value.IndexOfAny (anyOf) > -1 - : false; - } - - internal static bool Contains ( - this NameValueCollection collection, - string name - ) - { - return collection[name] != null; - } - - internal static bool Contains ( - this NameValueCollection collection, - string name, - string value, - StringComparison comparisonTypeForValue - ) - { - var val = collection[name]; - - if (val == null) - return false; - - foreach (var elm in val.Split (',')) { - if (elm.Trim ().Equals (value, comparisonTypeForValue)) - return true; - } - - return false; - } - - internal static bool Contains ( - this IEnumerable source, - Func condition - ) - { - foreach (T elm in source) { - if (condition (elm)) - return true; - } - - return false; - } - - internal static bool ContainsTwice (this string[] values) - { - var len = values.Length; - var end = len - 1; - - Func seek = null; - seek = idx => { - if (idx == end) - return false; - - var val = values[idx]; - - for (var i = idx + 1; i < len; i++) { - if (values[i] == val) - return true; - } - - return seek (++idx); - }; - - return seek (0); - } - - internal static T[] Copy (this T[] sourceArray, int length) - { - var dest = new T[length]; - - Array.Copy (sourceArray, 0, dest, 0, length); - - return dest; - } - - internal static T[] Copy (this T[] sourceArray, long length) - { - var dest = new T[length]; - - Array.Copy (sourceArray, 0, dest, 0, length); - - return dest; - } - - internal static void CopyTo ( - this Stream sourceStream, - Stream destinationStream, - int bufferLength - ) - { - var buff = new byte[bufferLength]; - - while (true) { - var nread = sourceStream.Read (buff, 0, bufferLength); - - if (nread <= 0) - break; - - destinationStream.Write (buff, 0, nread); - } - } - - internal static void CopyToAsync ( - this Stream sourceStream, - Stream destinationStream, - int bufferLength, - Action completed, - Action error - ) - { - var buff = new byte[bufferLength]; - - AsyncCallback callback = null; - callback = - ar => { - try { - var nread = sourceStream.EndRead (ar); - - if (nread <= 0) { - if (completed != null) - completed (); - - return; - } - - destinationStream.Write (buff, 0, nread); - - sourceStream.BeginRead (buff, 0, bufferLength, callback, null); - } - catch (Exception ex) { - if (error != null) - error (ex); - } - }; - - try { - sourceStream.BeginRead (buff, 0, bufferLength, callback, null); - } - catch (Exception ex) { - if (error != null) - error (ex); - } - } - - internal static byte[] Decompress ( - this byte[] data, - CompressionMethod method - ) - { - return method == CompressionMethod.Deflate ? data.decompress () : data; - } - - internal static Stream Decompress ( - this Stream stream, - CompressionMethod method - ) - { - return method == CompressionMethod.Deflate - ? stream.decompress () - : stream; - } - - internal static byte[] DecompressToArray ( - this Stream stream, - CompressionMethod method - ) - { - return method == CompressionMethod.Deflate - ? stream.decompressToArray () - : stream.ToByteArray (); - } - - internal static void Emit ( - this EventHandler eventHandler, - object sender, - EventArgs e - ) - { - if (eventHandler == null) - return; - - eventHandler (sender, e); - } - - internal static void Emit ( - this EventHandler eventHandler, - object sender, - TEventArgs e - ) - where TEventArgs : EventArgs - { - if (eventHandler == null) - return; - - eventHandler (sender, e); - } - - internal static string GetAbsolutePath (this Uri uri) - { - if (uri.IsAbsoluteUri) - return uri.AbsolutePath; - - var original = uri.OriginalString; - - if (original[0] != '/') - return null; - - var idx = original.IndexOfAny (new[] { '?', '#' }); - - return idx > 0 ? original.Substring (0, idx) : original; - } - - internal static CookieCollection GetCookies ( - this NameValueCollection headers, - bool response - ) - { - var name = response ? "Set-Cookie" : "Cookie"; - var val = headers[name]; - - return val != null - ? CookieCollection.Parse (val, response) - : new CookieCollection (); - } - - internal static string GetDnsSafeHost (this Uri uri, bool bracketIPv6) - { - return bracketIPv6 && uri.HostNameType == UriHostNameType.IPv6 - ? uri.Host - : uri.DnsSafeHost; - } - - internal static string GetErrorMessage (this ushort code) - { - switch (code) { - case 1002: - return "A protocol error has occurred."; - case 1003: - return "Unsupported data has been received."; - case 1006: - return "An abnormal error has occurred."; - case 1007: - return "Invalid data has been received."; - case 1008: - return "A policy violation has occurred."; - case 1009: - return "A too big message has been received."; - case 1010: - return "The client did not receive expected extension(s)."; - case 1011: - return "The server got an internal error."; - case 1015: - return "An error has occurred during a TLS handshake."; - default: - return String.Empty; - } - } - - internal static string GetErrorMessage (this CloseStatusCode code) - { - return ((ushort) code).GetErrorMessage (); - } - - internal static string GetName (this string nameAndValue, char separator) - { - var idx = nameAndValue.IndexOf (separator); - - return idx > 0 ? nameAndValue.Substring (0, idx).Trim () : null; - } - - internal static string GetUTF8DecodedString (this byte[] bytes) - { - try { - return Encoding.UTF8.GetString (bytes); - } - catch { - return null; - } - } - - internal static byte[] GetUTF8EncodedBytes (this string s) - { - try { - return Encoding.UTF8.GetBytes (s); - } - catch { - return null; - } - } - - internal static string GetValue (this string nameAndValue, char separator) - { - return nameAndValue.GetValue (separator, false); - } - - internal static string GetValue ( - this string nameAndValue, - char separator, - bool unquote - ) - { - var idx = nameAndValue.IndexOf (separator); - - if (idx < 0 || idx == nameAndValue.Length - 1) - return null; - - var val = nameAndValue.Substring (idx + 1).Trim (); - - return unquote ? val.Unquote () : val; - } - - internal static bool IsCompressionExtension ( - this string value, - CompressionMethod method - ) - { - var extStr = method.ToExtensionString (); - var compType = StringComparison.Ordinal; - - return value.StartsWith (extStr, compType); - } - - internal static bool IsDefined (this CloseStatusCode code) - { - return Enum.IsDefined (typeof (CloseStatusCode), code); - } - - internal static bool IsEqualTo ( - this int value, - char c, - Action beforeComparing - ) - { - beforeComparing (value); - - return value == c - 0; - } - - internal static bool IsHttpMethod (this string value) - { - return value == "GET" - || value == "HEAD" - || value == "POST" - || value == "PUT" - || value == "DELETE" - || value == "CONNECT" - || value == "OPTIONS" - || value == "TRACE"; - } - - internal static bool IsPortNumber (this int value) - { - return value > 0 && value < 65536; - } - - internal static bool IsReserved (this CloseStatusCode code) - { - return ((ushort) code).IsReservedStatusCode (); - } - - internal static bool IsReservedStatusCode (this ushort code) - { - return code == 1004 - || code == 1005 - || code == 1006 - || code == 1015; - } - - internal static bool IsSupportedOpcode (this int opcode) - { - return Enum.IsDefined (typeof (Opcode), opcode); - } - - internal static bool IsText (this string value) - { - var len = value.Length; - - for (var i = 0; i < len; i++) { - var c = value[i]; - - if (c < 0x20) { - if ("\r\n\t".IndexOf (c) == -1) - return false; - - if (c == '\n') { - i++; - - if (i == len) - break; - - c = value[i]; - - if (" \t".IndexOf (c) == -1) - return false; - } - - continue; - } - - if (c == 0x7f) - return false; - } - - return true; - } - - internal static bool IsToken (this string value) - { - foreach (var c in value) { - if (c < 0x20) - return false; - - if (c > 0x7e) - return false; - - if (_tspecials.IndexOf (c) > -1) - return false; - } - - return true; - } - - internal static bool KeepsAlive ( - this NameValueCollection headers, - Version version - ) - { - var compType = StringComparison.OrdinalIgnoreCase; - - return version > HttpVersion.Version10 - ? !headers.Contains ("Connection", "close", compType) - : headers.Contains ("Connection", "keep-alive", compType); - } - - internal static bool MaybeUri (this string value) - { - var idx = value.IndexOf (':'); - - if (idx < 2 || idx > 9) - return false; - - var schm = value.Substring (0, idx); - - return schm.isPredefinedScheme (); - } - - internal static string Quote (this string value) - { - var fmt = "\"{0}\""; - var val = value.Replace ("\"", "\\\""); - - return String.Format (fmt, val); - } - - internal static byte[] ReadBytes (this Stream stream, int length) - { - var ret = new byte[length]; - - var offset = 0; - var retry = 0; - - while (length > 0) { - var nread = stream.Read (ret, offset, length); - - if (nread <= 0) { - if (retry < _maxRetry) { - retry++; - - continue; - } - - return ret.SubArray (0, offset); - } - - retry = 0; - - offset += nread; - length -= nread; - } - - return ret; - } - - internal static byte[] ReadBytes ( - this Stream stream, - long length, - int bufferLength - ) - { - using (var dest = new MemoryStream ()) { - var buff = new byte[bufferLength]; - var retry = 0; - - while (length > 0) { - if (length < bufferLength) - bufferLength = (int) length; - - var nread = stream.Read (buff, 0, bufferLength); - - if (nread <= 0) { - if (retry < _maxRetry) { - retry++; - - continue; - } - - break; - } - - retry = 0; - - dest.Write (buff, 0, nread); - - length -= nread; - } - - dest.Close (); - - return dest.ToArray (); - } - } - - internal static void ReadBytesAsync ( - this Stream stream, - int length, - Action completed, - Action error - ) - { - var ret = new byte[length]; - - var offset = 0; - var retry = 0; - - AsyncCallback callback = null; - callback = - ar => { - try { - var nread = stream.EndRead (ar); - - if (nread <= 0) { - if (retry < _maxRetry) { - retry++; - - stream.BeginRead (ret, offset, length, callback, null); - - return; - } - - if (completed != null) - completed (ret.SubArray (0, offset)); - - return; - } - - if (nread == length) { - if (completed != null) - completed (ret); - - return; - } - - retry = 0; - - offset += nread; - length -= nread; - - stream.BeginRead (ret, offset, length, callback, null); - } - catch (Exception ex) { - if (error != null) - error (ex); - } - }; - - try { - stream.BeginRead (ret, offset, length, callback, null); - } - catch (Exception ex) { - if (error != null) - error (ex); - } - } - - internal static void ReadBytesAsync ( - this Stream stream, - long length, - int bufferLength, - Action completed, - Action error - ) - { - var dest = new MemoryStream (); - - var buff = new byte[bufferLength]; - var retry = 0; - - Action read = null; - read = - len => { - if (len < bufferLength) - bufferLength = (int) len; - - stream.BeginRead ( - buff, - 0, - bufferLength, - ar => { - try { - var nread = stream.EndRead (ar); - - if (nread <= 0) { - if (retry < _maxRetry) { - retry++; - - read (len); - - return; - } - - if (completed != null) { - dest.Close (); - - var ret = dest.ToArray (); - - completed (ret); - } - - dest.Dispose (); - - return; - } - - dest.Write (buff, 0, nread); - - if (nread == len) { - if (completed != null) { - dest.Close (); - - var ret = dest.ToArray (); - - completed (ret); - } - - dest.Dispose (); - - return; - } - - retry = 0; - - read (len - nread); - } - catch (Exception ex) { - dest.Dispose (); - - if (error != null) - error (ex); - } - }, - null - ); - }; - - try { - read (length); - } - catch (Exception ex) { - dest.Dispose (); - - if (error != null) - error (ex); - } - } - - internal static T[] Reverse (this T[] array) - { - var len = array.LongLength; - var ret = new T[len]; - - var end = len - 1; - - for (long i = 0; i <= end; i++) - ret[i] = array[end - i]; - - return ret; - } - - internal static IEnumerable SplitHeaderValue ( - this string value, - params char[] separators - ) - { - var len = value.Length; - var end = len - 1; - - var buff = new StringBuilder (32); - var escaped = false; - var quoted = false; - - for (var i = 0; i <= end; i++) { - var c = value[i]; - - buff.Append (c); - - if (c == '"') { - if (escaped) { - escaped = false; - - continue; - } - - quoted = !quoted; - - continue; - } - - if (c == '\\') { - if (i == end) - break; - - if (value[i + 1] == '"') - escaped = true; - - continue; - } - - if (Array.IndexOf (separators, c) > -1) { - if (quoted) - continue; - - buff.Length -= 1; - - yield return buff.ToString (); - - buff.Length = 0; - - continue; - } - } - - yield return buff.ToString (); - } - - internal static byte[] ToByteArray (this Stream stream) - { - stream.Position = 0; - - using (var buff = new MemoryStream ()) { - stream.CopyTo (buff, 1024); - buff.Close (); - - return buff.ToArray (); - } - } - - internal static byte[] ToByteArray (this ushort value, ByteOrder order) - { - var ret = BitConverter.GetBytes (value); - - if (!order.IsHostOrder ()) - Array.Reverse (ret); - - return ret; - } - - internal static byte[] ToByteArray (this ulong value, ByteOrder order) - { - var ret = BitConverter.GetBytes (value); - - if (!order.IsHostOrder ()) - Array.Reverse (ret); - - return ret; - } - - internal static CompressionMethod ToCompressionMethod (this string value) - { - var methods = Enum.GetValues (typeof (CompressionMethod)); - - foreach (CompressionMethod method in methods) { - if (method.ToExtensionString () == value) - return method; - } - - return CompressionMethod.None; - } - - internal static string ToExtensionString ( - this CompressionMethod method, - params string[] parameters - ) - { - if (method == CompressionMethod.None) - return String.Empty; - - var name = method.ToString ().ToLower (); - var ename = String.Format ("permessage-{0}", name); - - if (parameters == null || parameters.Length == 0) - return ename; - - var eparams = parameters.ToString ("; "); - - return String.Format ("{0}; {1}", ename, eparams); - } - - internal static int ToInt32 (this string numericString) - { - return Int32.Parse (numericString); - } - - internal static System.Net.IPAddress ToIPAddress (this string value) - { - if (value == null || value.Length == 0) - return null; - - System.Net.IPAddress addr; - - if (System.Net.IPAddress.TryParse (value, out addr)) - return addr; - - try { - var addrs = System.Net.Dns.GetHostAddresses (value); - - return addrs[0]; - } - catch { - return null; - } - } - - internal static List ToList ( - this IEnumerable source - ) - { - return new List (source); - } - - internal static string ToString ( - this System.Net.IPAddress address, - bool bracketIPv6 - ) - { - return bracketIPv6 - && address.AddressFamily == AddressFamily.InterNetworkV6 - ? String.Format ("[{0}]", address) - : address.ToString (); - } - - internal static ushort ToUInt16 (this byte[] source, ByteOrder sourceOrder) - { - var val = source.ToHostOrder (sourceOrder); - - return BitConverter.ToUInt16 (val, 0); - } - - internal static ulong ToUInt64 (this byte[] source, ByteOrder sourceOrder) - { - var val = source.ToHostOrder (sourceOrder); - - return BitConverter.ToUInt64 (val, 0); - } - - internal static Version ToVersion (this string versionString) - { - return new Version (versionString); - } - - internal static IEnumerable TrimEach ( - this IEnumerable source - ) - { - foreach (var elm in source) - yield return elm.Trim (); - } - - internal static string TrimSlashFromEnd (this string value) - { - var ret = value.TrimEnd ('/'); - - return ret.Length > 0 ? ret : "/"; - } - - internal static string TrimSlashOrBackslashFromEnd (this string value) - { - var ret = value.TrimEnd ('/', '\\'); - - return ret.Length > 0 ? ret : value[0].ToString (); - } - - internal static bool TryCreateVersion ( - this string versionString, - out Version result - ) - { - result = null; - - try { - result = new Version (versionString); - } - catch { - return false; - } - - return true; - } - - internal static bool TryCreateWebSocketUri ( - this string uriString, - out Uri result, - out string message - ) - { - result = null; - message = null; - - var uri = uriString.ToUri (); - - if (uri == null) { - message = "An invalid URI string."; - - return false; - } - - if (!uri.IsAbsoluteUri) { - message = "A relative URI."; - - return false; - } - - var schm = uri.Scheme; - var valid = schm == "ws" || schm == "wss"; - - if (!valid) { - message = "The scheme part is not \"ws\" or \"wss\"."; - - return false; - } - - var port = uri.Port; - - if (port == 0) { - message = "The port part is zero."; - - return false; - } - - if (uri.Fragment.Length > 0) { - message = "It includes the fragment component."; - - return false; - } - - if (port == -1) { - port = schm == "ws" ? 80 : 443; - uriString = String.Format ( - "{0}://{1}:{2}{3}", - schm, - uri.Host, - port, - uri.PathAndQuery - ); - - result = new Uri (uriString); - } - else { - result = uri; - } - - return true; - } - - internal static bool TryGetUTF8DecodedString ( - this byte[] bytes, - out string s - ) - { - s = null; - - try { - s = Encoding.UTF8.GetString (bytes); - } - catch { - return false; - } - - return true; - } - - internal static bool TryGetUTF8EncodedBytes ( - this string s, - out byte[] bytes - ) - { - bytes = null; - - try { - bytes = Encoding.UTF8.GetBytes (s); - } - catch { - return false; - } - - return true; - } - - internal static bool TryOpenRead ( - this FileInfo fileInfo, - out FileStream fileStream - ) - { - fileStream = null; - - try { - fileStream = fileInfo.OpenRead (); - } - catch { - return false; - } - - return true; - } - - internal static string Unquote (this string value) - { - var first = value.IndexOf ('"'); - - if (first == -1) - return value; - - var last = value.LastIndexOf ('"'); - - if (last == first) - return value; - - var len = last - first - 1; - - return len > 0 - ? value.Substring (first + 1, len).Replace ("\\\"", "\"") - : String.Empty; - } - - internal static bool Upgrades ( - this NameValueCollection headers, - string protocol - ) - { - var compType = StringComparison.OrdinalIgnoreCase; - - return headers.Contains ("Upgrade", protocol, compType) - && headers.Contains ("Connection", "Upgrade", compType); - } - - internal static string UrlDecode (this string value, Encoding encoding) - { - return value.IndexOfAny (new[] { '%', '+' }) > -1 - ? HttpUtility.UrlDecode (value, encoding) - : value; - } - - internal static string UrlEncode (this string value, Encoding encoding) - { - return HttpUtility.UrlEncode (value, encoding); - } - - internal static void WriteBytes ( - this Stream stream, - byte[] bytes, - int bufferLength - ) - { - using (var src = new MemoryStream (bytes)) - src.CopyTo (stream, bufferLength); - } - - internal static void WriteBytesAsync ( - this Stream stream, - byte[] bytes, - int bufferLength, - Action completed, - Action error - ) - { - var src = new MemoryStream (bytes); - - src.CopyToAsync ( - stream, - bufferLength, - () => { - if (completed != null) - completed (); - - src.Dispose (); - }, - ex => { - src.Dispose (); - - if (error != null) - error (ex); - } - ); - } - - #endregion - - #region Public Methods - - /// - /// Gets the description of the specified HTTP status code. - /// - /// - /// A that represents the description of - /// the HTTP status code. - /// - /// - /// - /// One of the enum values. - /// - /// - /// It specifies the HTTP status code. - /// - /// - public static string GetDescription (this HttpStatusCode code) - { - return ((int) code).GetStatusDescription (); - } - - /// - /// Gets the description of the specified HTTP status code. - /// - /// - /// - /// A that represents the description of - /// the HTTP status code. - /// - /// - /// An empty string if the description is not present. - /// - /// - /// - /// An that specifies the HTTP status code. - /// - public static string GetStatusDescription (this int code) - { - switch (code) { - case 100: return "Continue"; - case 101: return "Switching Protocols"; - case 102: return "Processing"; - case 200: return "OK"; - case 201: return "Created"; - case 202: return "Accepted"; - case 203: return "Non-Authoritative Information"; - case 204: return "No Content"; - case 205: return "Reset Content"; - case 206: return "Partial Content"; - case 207: return "Multi-Status"; - case 300: return "Multiple Choices"; - case 301: return "Moved Permanently"; - case 302: return "Found"; - case 303: return "See Other"; - case 304: return "Not Modified"; - case 305: return "Use Proxy"; - case 307: return "Temporary Redirect"; - case 400: return "Bad Request"; - case 401: return "Unauthorized"; - case 402: return "Payment Required"; - case 403: return "Forbidden"; - case 404: return "Not Found"; - case 405: return "Method Not Allowed"; - case 406: return "Not Acceptable"; - case 407: return "Proxy Authentication Required"; - case 408: return "Request Timeout"; - case 409: return "Conflict"; - case 410: return "Gone"; - case 411: return "Length Required"; - case 412: return "Precondition Failed"; - case 413: return "Request Entity Too Large"; - case 414: return "Request-Uri Too Long"; - case 415: return "Unsupported Media Type"; - case 416: return "Requested Range Not Satisfiable"; - case 417: return "Expectation Failed"; - case 422: return "Unprocessable Entity"; - case 423: return "Locked"; - case 424: return "Failed Dependency"; - case 500: return "Internal Server Error"; - case 501: return "Not Implemented"; - case 502: return "Bad Gateway"; - case 503: return "Service Unavailable"; - case 504: return "Gateway Timeout"; - case 505: return "Http Version Not Supported"; - case 507: return "Insufficient Storage"; - } - - return String.Empty; - } - - /// - /// Determines whether the specified ushort is in the range of - /// the status code for the WebSocket connection close. - /// - /// - /// - /// The ranges are the following: - /// - /// - /// - /// - /// 1000-2999: These numbers are reserved for definition by - /// the WebSocket protocol. - /// - /// - /// - /// - /// 3000-3999: These numbers are reserved for use by libraries, - /// frameworks, and applications. - /// - /// - /// - /// - /// 4000-4999: These numbers are reserved for private use. - /// - /// - /// - /// - /// - /// true if is in the range of - /// the status code for the close; otherwise, false. - /// - /// - /// A to test. - /// - public static bool IsCloseStatusCode (this ushort value) - { - return value > 999 && value < 5000; - } - - /// - /// Determines whether the specified string is enclosed in - /// the specified character. - /// - /// - /// true if is enclosed in - /// ; otherwise, false. - /// - /// - /// A to test. - /// - /// - /// A to find. - /// - public static bool IsEnclosedIn (this string value, char c) - { - if (value == null) - return false; - - var len = value.Length; - - return len > 1 ? value[0] == c && value[len - 1] == c : false; - } - - /// - /// Determines whether the specified byte order is host (this computer - /// architecture) byte order. - /// - /// - /// true if is host byte order; otherwise, - /// false. - /// - /// - /// One of the enum values to test. - /// - public static bool IsHostOrder (this ByteOrder order) - { - // true: !(true ^ true) or !(false ^ false) - // false: !(true ^ false) or !(false ^ true) - return !(BitConverter.IsLittleEndian ^ (order == ByteOrder.Little)); - } - - /// - /// Determines whether the specified IP address is a local IP address. - /// - /// - /// This local means NOT REMOTE for the current host. - /// - /// - /// true if is a local IP address; - /// otherwise, false. - /// - /// - /// A to test. - /// - /// - /// is . - /// - public static bool IsLocal (this System.Net.IPAddress address) - { - if (address == null) - throw new ArgumentNullException ("address"); - - if (address.Equals (System.Net.IPAddress.Any)) - return true; - - if (address.Equals (System.Net.IPAddress.Loopback)) - return true; - - if (Socket.OSSupportsIPv6) { - if (address.Equals (System.Net.IPAddress.IPv6Any)) - return true; - - if (address.Equals (System.Net.IPAddress.IPv6Loopback)) - return true; - } - - var name = System.Net.Dns.GetHostName (); - var addrs = System.Net.Dns.GetHostAddresses (name); - - foreach (var addr in addrs) { - if (address.Equals (addr)) - return true; - } - - return false; - } - - /// - /// Determines whether the specified string is or - /// an empty string. - /// - /// - /// true if is or - /// an empty string; otherwise, false. - /// - /// - /// A to test. - /// - public static bool IsNullOrEmpty (this string value) - { - return value == null || value.Length == 0; - } - - /// - /// Retrieves a sub-array from the specified array. A sub-array starts at - /// the specified index in the array. - /// - /// - /// An array of T that receives a sub-array. - /// - /// - /// An array of T from which to retrieve a sub-array. - /// - /// - /// An that specifies the zero-based index in the array - /// at which retrieving starts. - /// - /// - /// An that specifies the number of elements to retrieve. - /// - /// - /// The type of elements in the array. - /// - /// - /// is . - /// - /// - /// - /// is less than zero. - /// - /// - /// -or- - /// - /// - /// is greater than the end of the array. - /// - /// - /// -or- - /// - /// - /// is less than zero. - /// - /// - /// -or- - /// - /// - /// is greater than the number of elements from - /// to the end of the array. - /// - /// - public static T[] SubArray (this T[] array, int startIndex, int length) - { - if (array == null) - throw new ArgumentNullException ("array"); - - var len = array.Length; - - if (len == 0) { - if (startIndex != 0) - throw new ArgumentOutOfRangeException ("startIndex"); - - if (length != 0) - throw new ArgumentOutOfRangeException ("length"); - - return array; - } - - if (startIndex < 0 || startIndex >= len) - throw new ArgumentOutOfRangeException ("startIndex"); - - if (length < 0 || length > len - startIndex) - throw new ArgumentOutOfRangeException ("length"); - - if (length == 0) - return new T[0]; - - if (length == len) - return array; - - var ret = new T[length]; - - Array.Copy (array, startIndex, ret, 0, length); - - return ret; - } - - /// - /// Retrieves a sub-array from the specified array. A sub-array starts at - /// the specified index in the array. - /// - /// - /// An array of T that receives a sub-array. - /// - /// - /// An array of T from which to retrieve a sub-array. - /// - /// - /// A that specifies the zero-based index in the array - /// at which retrieving starts. - /// - /// - /// A that specifies the number of elements to retrieve. - /// - /// - /// The type of elements in the array. - /// - /// - /// is . - /// - /// - /// - /// is less than zero. - /// - /// - /// -or- - /// - /// - /// is greater than the end of the array. - /// - /// - /// -or- - /// - /// - /// is less than zero. - /// - /// - /// -or- - /// - /// - /// is greater than the number of elements from - /// to the end of the array. - /// - /// - public static T[] SubArray (this T[] array, long startIndex, long length) - { - if (array == null) - throw new ArgumentNullException ("array"); - - var len = array.LongLength; - - if (len == 0) { - if (startIndex != 0) - throw new ArgumentOutOfRangeException ("startIndex"); - - if (length != 0) - throw new ArgumentOutOfRangeException ("length"); - - return array; - } - - if (startIndex < 0 || startIndex >= len) - throw new ArgumentOutOfRangeException ("startIndex"); - - if (length < 0 || length > len - startIndex) - throw new ArgumentOutOfRangeException ("length"); - - if (length == 0) - return new T[0]; - - if (length == len) - return array; - - var ret = new T[length]; - - Array.Copy (array, startIndex, ret, 0, length); - - return ret; - } - - /// - /// Executes the specified delegate times. - /// - /// - /// An that specifies the number of times to execute. - /// - /// - /// - /// An Action<int> delegate to execute. - /// - /// - /// The parameter is the zero-based count of iteration. - /// - /// - public static void Times (this int n, Action action) - { - if (n <= 0) - return; - - if (action == null) - return; - - for (int i = 0; i < n; i++) - action (i); - } - - /// - /// Executes the specified delegate times. - /// - /// - /// A that specifies the number of times to execute. - /// - /// - /// - /// An Action<long> delegate to execute. - /// - /// - /// The parameter is the zero-based count of iteration. - /// - /// - public static void Times (this long n, Action action) - { - if (n <= 0) - return; - - if (action == null) - return; - - for (long i = 0; i < n; i++) - action (i); - } - - /// - /// Converts the order of elements in the specified byte array to - /// host (this computer architecture) byte order. - /// - /// - /// - /// An array of converted from - /// . - /// - /// - /// if the number of elements in - /// it is less than 2 or is - /// same as host byte order. - /// - /// - /// - /// An array of to convert. - /// - /// - /// - /// One of the enum values. - /// - /// - /// It specifies the order of elements in . - /// - /// - /// - /// is . - /// - public static byte[] ToHostOrder (this byte[] source, ByteOrder sourceOrder) - { - if (source == null) - throw new ArgumentNullException ("source"); - - if (source.Length < 2) - return source; - - if (sourceOrder.IsHostOrder ()) - return source; - - return source.Reverse (); - } - - /// - /// Converts the specified array to a string. - /// - /// - /// - /// A converted by concatenating each element of - /// across . - /// - /// - /// An empty string if is an empty array. - /// - /// - /// - /// An array of T to convert. - /// - /// - /// A used to separate each element of - /// . - /// - /// - /// The type of elements in . - /// - /// - /// is . - /// - public static string ToString (this T[] array, string separator) - { - if (array == null) - throw new ArgumentNullException ("array"); - - var len = array.Length; - - if (len == 0) - return String.Empty; - - var buff = new StringBuilder (64); - var end = len - 1; - - for (var i = 0; i < end; i++) - buff.AppendFormat ("{0}{1}", array[i], separator); - - buff.AppendFormat ("{0}", array[end]); - - return buff.ToString (); - } - - /// - /// Converts the specified string to a . - /// - /// - /// - /// A converted from . - /// - /// - /// if the conversion has failed. - /// - /// - /// - /// A to convert. - /// - public static Uri ToUri (this string value) - { - if (value == null || value.Length == 0) - return null; - - var kind = value.MaybeUri () ? UriKind.Absolute : UriKind.Relative; - Uri ret; - - Uri.TryCreate (value, kind, out ret); - - return ret; - } - - #endregion - } -} diff --git a/websocket-sharp/Fin.cs b/websocket-sharp/Fin.cs deleted file mode 100644 index 36622d7e5..000000000 --- a/websocket-sharp/Fin.cs +++ /dev/null @@ -1,52 +0,0 @@ -#region License -/* - * Fin.cs - * - * The MIT License - * - * Copyright (c) 2012-2025 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -using System; - -namespace WebSocketSharp -{ - /// - /// Indicates whether a WebSocket frame is the final frame of a message. - /// - /// - /// The values of this enumeration are defined in - /// - /// Section 5.2 of RFC 6455. - /// - internal enum Fin - { - /// - /// Equivalent to numeric value 0. Indicates more frames of a message follow. - /// - More = 0x0, - /// - /// Equivalent to numeric value 1. Indicates the final frame of a message. - /// - Final = 0x1 - } -} diff --git a/websocket-sharp/HttpBase.cs b/websocket-sharp/HttpBase.cs deleted file mode 100644 index c4a244f45..000000000 --- a/websocket-sharp/HttpBase.cs +++ /dev/null @@ -1,317 +0,0 @@ -#region License -/* - * HttpBase.cs - * - * The MIT License - * - * Copyright (c) 2012-2024 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -using System; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading; -using WebSocketSharp.Net; - -namespace WebSocketSharp -{ - internal abstract class HttpBase - { - #region Private Fields - - private NameValueCollection _headers; - private static readonly int _maxMessageHeaderLength; - private string _messageBody; - private byte[] _messageBodyData; - private Version _version; - - #endregion - - #region Protected Fields - - protected static readonly string CrLf; - protected static readonly string CrLfHt; - protected static readonly string CrLfSp; - - #endregion - - #region Static Constructor - - static HttpBase () - { - _maxMessageHeaderLength = 8192; - - CrLf = "\r\n"; - CrLfHt = "\r\n\t"; - CrLfSp = "\r\n "; - } - - #endregion - - #region Protected Constructors - - protected HttpBase (Version version, NameValueCollection headers) - { - _version = version; - _headers = headers; - } - - #endregion - - #region Internal Properties - - internal byte[] MessageBodyData { - get { - return _messageBodyData; - } - } - - #endregion - - #region Protected Properties - - protected string HeaderSection { - get { - var buff = new StringBuilder (64); - - var fmt = "{0}: {1}{2}"; - - foreach (var key in _headers.AllKeys) - buff.AppendFormat (fmt, key, _headers[key], CrLf); - - buff.Append (CrLf); - - return buff.ToString (); - } - } - - #endregion - - #region Public Properties - - public bool HasMessageBody { - get { - return _messageBodyData != null; - } - } - - public NameValueCollection Headers { - get { - return _headers; - } - } - - public string MessageBody { - get { - if (_messageBody == null) - _messageBody = getMessageBody (); - - return _messageBody; - } - } - - public abstract string MessageHeader { get; } - - public Version ProtocolVersion { - get { - return _version; - } - } - - #endregion - - #region Private Methods - - private string getMessageBody () - { - if (_messageBodyData == null || _messageBodyData.LongLength == 0) - return String.Empty; - - var contentType = _headers["Content-Type"]; - - var enc = contentType != null && contentType.Length > 0 - ? HttpUtility.GetEncoding (contentType) - : Encoding.UTF8; - - return enc.GetString (_messageBodyData); - } - - private static byte[] readMessageBodyFrom (Stream stream, string length) - { - long len; - - if (!Int64.TryParse (length, out len)) { - var msg = "It could not be parsed."; - - throw new ArgumentException (msg, "length"); - } - - if (len < 0) { - var msg = "Less than zero."; - - throw new ArgumentOutOfRangeException ("length", msg); - } - - return len > 1024 - ? stream.ReadBytes (len, 1024) - : len > 0 - ? stream.ReadBytes ((int) len) - : null; - } - - private static string[] readMessageHeaderFrom (Stream stream) - { - var buff = new List (); - var cnt = 0; - Action add = - i => { - if (i == -1) { - var msg = "The header could not be read from the data stream."; - - throw new EndOfStreamException (msg); - } - - buff.Add ((byte) i); - - cnt++; - }; - - var end = false; - - do { - end = stream.ReadByte ().IsEqualTo ('\r', add) - && stream.ReadByte ().IsEqualTo ('\n', add) - && stream.ReadByte ().IsEqualTo ('\r', add) - && stream.ReadByte ().IsEqualTo ('\n', add); - - if (cnt > _maxMessageHeaderLength) { - var msg = "The length of the header is greater than the max length."; - - throw new InvalidOperationException (msg); - } - } - while (!end); - - var bytes = buff.ToArray (); - - return Encoding.UTF8.GetString (bytes) - .Replace (CrLfSp, " ") - .Replace (CrLfHt, " ") - .Split (new[] { CrLf }, StringSplitOptions.RemoveEmptyEntries); - } - - #endregion - - #region Internal Methods - - internal void WriteTo (Stream stream) - { - var bytes = ToByteArray (); - - stream.Write (bytes, 0, bytes.Length); - } - - #endregion - - #region Protected Methods - - protected static T Read ( - Stream stream, - Func parser, - int millisecondsTimeout - ) - where T : HttpBase - { - T ret = null; - - var timeout = false; - var timer = new Timer ( - state => { - timeout = true; - - stream.Close (); - }, - null, - millisecondsTimeout, - -1 - ); - - Exception exception = null; - - try { - var header = readMessageHeaderFrom (stream); - ret = parser (header); - - var contentLen = ret.Headers["Content-Length"]; - - if (contentLen != null && contentLen.Length > 0) - ret._messageBodyData = readMessageBodyFrom (stream, contentLen); - } - catch (Exception ex) { - exception = ex; - } - finally { - timer.Change (-1, -1); - timer.Dispose (); - } - - if (timeout) { - var msg = "A timeout has occurred."; - - throw new WebSocketException (msg); - } - - if (exception != null) { - var msg = "An exception has occurred."; - - throw new WebSocketException (msg, exception); - } - - return ret; - } - - #endregion - - #region Public Methods - - public byte[] ToByteArray () - { - var headerData = Encoding.UTF8.GetBytes (MessageHeader); - - return _messageBodyData != null - ? headerData.Concat (_messageBodyData).ToArray () - : headerData; - } - - public override string ToString () - { - return _messageBodyData != null - ? MessageHeader + MessageBody - : MessageHeader; - } - - #endregion - } -} diff --git a/websocket-sharp/HttpRequest.cs b/websocket-sharp/HttpRequest.cs deleted file mode 100644 index 946024d6a..000000000 --- a/websocket-sharp/HttpRequest.cs +++ /dev/null @@ -1,253 +0,0 @@ -#region License -/* - * HttpRequest.cs - * - * The MIT License - * - * Copyright (c) 2012-2024 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -#region Contributors -/* - * Contributors: - * - David Burhans - */ -#endregion - -using System; -using System.Collections.Specialized; -using System.IO; -using System.Text; -using WebSocketSharp.Net; - -namespace WebSocketSharp -{ - internal class HttpRequest : HttpBase - { - #region Private Fields - - private CookieCollection _cookies; - private string _method; - private string _target; - - #endregion - - #region Private Constructors - - private HttpRequest ( - string method, - string target, - Version version, - NameValueCollection headers - ) - : base (version, headers) - { - _method = method; - _target = target; - } - - #endregion - - #region Internal Constructors - - internal HttpRequest (string method, string target) - : this (method, target, HttpVersion.Version11, new NameValueCollection ()) - { - Headers["User-Agent"] = "websocket-sharp/1.0"; - } - - #endregion - - #region Internal Properties - - internal string RequestLine { - get { - var fmt = "{0} {1} HTTP/{2}{3}"; - - return String.Format (fmt, _method, _target, ProtocolVersion, CrLf); - } - } - - #endregion - - #region Public Properties - - public AuthenticationResponse AuthenticationResponse { - get { - var val = Headers["Authorization"]; - - return val != null && val.Length > 0 - ? AuthenticationResponse.Parse (val) - : null; - } - } - - public CookieCollection Cookies { - get { - if (_cookies == null) - _cookies = Headers.GetCookies (false); - - return _cookies; - } - } - - public string HttpMethod { - get { - return _method; - } - } - - public bool IsWebSocketRequest { - get { - return _method == "GET" - && ProtocolVersion > HttpVersion.Version10 - && Headers.Upgrades ("websocket"); - } - } - - public override string MessageHeader { - get { - return RequestLine + HeaderSection; - } - } - - public string RequestTarget { - get { - return _target; - } - } - - #endregion - - #region Internal Methods - - internal static HttpRequest CreateConnectRequest (Uri targetUri) - { - var fmt = "{0}:{1}"; - var host = targetUri.DnsSafeHost; - var port = targetUri.Port; - var authority = String.Format (fmt, host, port); - - var ret = new HttpRequest ("CONNECT", authority); - - ret.Headers["Host"] = port != 80 ? authority : host; - - return ret; - } - - internal static HttpRequest CreateWebSocketHandshakeRequest (Uri targetUri) - { - var ret = new HttpRequest ("GET", targetUri.PathAndQuery); - - var headers = ret.Headers; - - var port = targetUri.Port; - var schm = targetUri.Scheme; - var isDefaultPort = (port == 80 && schm == "ws") - || (port == 443 && schm == "wss"); - - headers["Host"] = !isDefaultPort - ? targetUri.Authority - : targetUri.DnsSafeHost; - - headers["Upgrade"] = "websocket"; - headers["Connection"] = "Upgrade"; - - return ret; - } - - internal HttpResponse GetResponse (Stream stream, int millisecondsTimeout) - { - WriteTo (stream); - - return HttpResponse.ReadResponse (stream, millisecondsTimeout); - } - - internal static HttpRequest Parse (string[] messageHeader) - { - var len = messageHeader.Length; - - if (len == 0) { - var msg = "An empty request header."; - - throw new ArgumentException (msg); - } - - var rlParts = messageHeader[0].Split (new[] { ' ' }, 3); - - if (rlParts.Length != 3) { - var msg = "It includes an invalid request line."; - - throw new ArgumentException (msg); - } - - var method = rlParts[0]; - var target = rlParts[1]; - var ver = rlParts[2].Substring (5).ToVersion (); - - var headers = new WebHeaderCollection (); - - for (var i = 1; i < len; i++) - headers.InternalSet (messageHeader[i], false); - - return new HttpRequest (method, target, ver, headers); - } - - internal static HttpRequest ReadRequest ( - Stream stream, - int millisecondsTimeout - ) - { - return Read (stream, Parse, millisecondsTimeout); - } - - #endregion - - #region Public Methods - - public void SetCookies (CookieCollection cookies) - { - if (cookies == null || cookies.Count == 0) - return; - - var buff = new StringBuilder (64); - - foreach (var cookie in cookies.SortedList) { - if (cookie.Expired) - continue; - - buff.AppendFormat ("{0}; ", cookie); - } - - var len = buff.Length; - - if (len <= 2) - return; - - buff.Length = len - 2; - - Headers["Cookie"] = buff.ToString (); - } - - #endregion - } -} diff --git a/websocket-sharp/HttpResponse.cs b/websocket-sharp/HttpResponse.cs deleted file mode 100644 index d511c94fe..000000000 --- a/websocket-sharp/HttpResponse.cs +++ /dev/null @@ -1,274 +0,0 @@ -#region License -/* - * HttpResponse.cs - * - * The MIT License - * - * Copyright (c) 2012-2024 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -using System; -using System.Collections.Specialized; -using System.IO; -using System.Text; -using WebSocketSharp.Net; - -namespace WebSocketSharp -{ - internal class HttpResponse : HttpBase - { - #region Private Fields - - private int _code; - private string _reason; - - #endregion - - #region Private Constructors - - private HttpResponse ( - int code, - string reason, - Version version, - NameValueCollection headers - ) - : base (version, headers) - { - _code = code; - _reason = reason; - } - - #endregion - - #region Internal Constructors - - internal HttpResponse (int code) - : this (code, code.GetStatusDescription ()) - { - } - - internal HttpResponse (HttpStatusCode code) - : this ((int) code) - { - } - - internal HttpResponse (int code, string reason) - : this ( - code, - reason, - HttpVersion.Version11, - new NameValueCollection () - ) - { - Headers["Server"] = "websocket-sharp/1.0"; - } - - internal HttpResponse (HttpStatusCode code, string reason) - : this ((int) code, reason) - { - } - - #endregion - - #region Internal Properties - - internal string StatusLine { - get { - return _reason != null - ? String.Format ( - "HTTP/{0} {1} {2}{3}", - ProtocolVersion, - _code, - _reason, - CrLf - ) - : String.Format ( - "HTTP/{0} {1}{2}", - ProtocolVersion, - _code, - CrLf - ); - } - } - - #endregion - - #region Public Properties - - public bool CloseConnection { - get { - var compType = StringComparison.OrdinalIgnoreCase; - - return Headers.Contains ("Connection", "close", compType); - } - } - - public CookieCollection Cookies { - get { - return Headers.GetCookies (true); - } - } - - public bool IsProxyAuthenticationRequired { - get { - return _code == 407; - } - } - - public bool IsRedirect { - get { - return _code == 301 || _code == 302; - } - } - - public bool IsSuccess { - get { - return _code >= 200 && _code <= 299; - } - } - - public bool IsUnauthorized { - get { - return _code == 401; - } - } - - public bool IsWebSocketResponse { - get { - return ProtocolVersion > HttpVersion.Version10 - && _code == 101 - && Headers.Upgrades ("websocket"); - } - } - - public override string MessageHeader { - get { - return StatusLine + HeaderSection; - } - } - - public string Reason { - get { - return _reason; - } - } - - public int StatusCode { - get { - return _code; - } - } - - #endregion - - #region Internal Methods - - internal static HttpResponse CreateCloseResponse (HttpStatusCode code) - { - var ret = new HttpResponse (code); - - ret.Headers["Connection"] = "close"; - - return ret; - } - - internal static HttpResponse CreateUnauthorizedResponse (string challenge) - { - var ret = new HttpResponse (HttpStatusCode.Unauthorized); - - ret.Headers["WWW-Authenticate"] = challenge; - - return ret; - } - - internal static HttpResponse CreateWebSocketHandshakeResponse () - { - var ret = new HttpResponse (HttpStatusCode.SwitchingProtocols); - - var headers = ret.Headers; - - headers["Upgrade"] = "websocket"; - headers["Connection"] = "Upgrade"; - - return ret; - } - - internal static HttpResponse Parse (string[] messageHeader) - { - var len = messageHeader.Length; - - if (len == 0) { - var msg = "An empty response header."; - - throw new ArgumentException (msg); - } - - var slParts = messageHeader[0].Split (new[] { ' ' }, 3); - var plen = slParts.Length; - - if (plen < 2) { - var msg = "It includes an invalid status line."; - - throw new ArgumentException (msg); - } - - var code = slParts[1].ToInt32 (); - var reason = plen == 3 ? slParts[2] : null; - var ver = slParts[0].Substring (5).ToVersion (); - - var headers = new WebHeaderCollection (); - - for (var i = 1; i < len; i++) - headers.InternalSet (messageHeader[i], true); - - return new HttpResponse (code, reason, ver, headers); - } - - internal static HttpResponse ReadResponse ( - Stream stream, - int millisecondsTimeout - ) - { - return Read (stream, Parse, millisecondsTimeout); - } - - #endregion - - #region Public Methods - - public void SetCookies (CookieCollection cookies) - { - if (cookies == null || cookies.Count == 0) - return; - - var headers = Headers; - - foreach (var cookie in cookies.SortedList) { - var val = cookie.ToResponseString (); - - headers.Add ("Set-Cookie", val); - } - } - - #endregion - } -} diff --git a/websocket-sharp/LogData.cs b/websocket-sharp/LogData.cs deleted file mode 100644 index bb3492a9b..000000000 --- a/websocket-sharp/LogData.cs +++ /dev/null @@ -1,159 +0,0 @@ -#region License -/* - * LogData.cs - * - * The MIT License - * - * Copyright (c) 2013-2024 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -using System; -using System.Diagnostics; -using System.Text; - -namespace WebSocketSharp -{ - /// - /// Represents a log data used by the class. - /// - public class LogData - { - #region Private Fields - - private StackFrame _caller; - private DateTime _date; - private LogLevel _level; - private string _message; - - #endregion - - #region Internal Constructors - - internal LogData (LogLevel level, StackFrame caller, string message) - { - _level = level; - _caller = caller; - _message = message ?? String.Empty; - - _date = DateTime.Now; - } - - #endregion - - #region Public Properties - - /// - /// Gets the information of the logging method caller. - /// - /// - /// A that provides the information of - /// the logging method caller. - /// - public StackFrame Caller { - get { - return _caller; - } - } - - /// - /// Gets the date and time when the log data was created. - /// - /// - /// A that represents the date and time when - /// the log data was created. - /// - public DateTime Date { - get { - return _date; - } - } - - /// - /// Gets the logging level of the log data. - /// - /// - /// - /// One of the enum values. - /// - /// - /// It represents the logging level of the log data. - /// - /// - public LogLevel Level { - get { - return _level; - } - } - - /// - /// Gets the message of the log data. - /// - /// - /// A that represents the message of the log data. - /// - public string Message { - get { - return _message; - } - } - - #endregion - - #region Public Methods - - /// - /// Returns a string that represents the current instance. - /// - /// - /// A that represents the current instance. - /// - public override string ToString () - { - var date = String.Format ("[{0}]", _date); - var level = String.Format ("{0,-5}", _level.ToString ().ToUpper ()); - - var method = _caller.GetMethod (); - var type = method.DeclaringType; -#if DEBUG - var num = _caller.GetFileLineNumber (); - var caller = String.Format ("{0}.{1}:{2}", type.Name, method.Name, num); -#else - var caller = String.Format ("{0}.{1}", type.Name, method.Name); -#endif - var msgs = _message.Replace ("\r\n", "\n").TrimEnd ('\n').Split ('\n'); - - if (msgs.Length <= 1) - return String.Format ("{0} {1} {2} {3}", date, level, caller, _message); - - var buff = new StringBuilder (64); - - buff.AppendFormat ("{0} {1} {2}\n\n", date, level, caller); - - foreach (var msg in msgs) - buff.AppendFormat (" {0}\n", msg); - - return buff.ToString (); - } - - #endregion - } -} diff --git a/websocket-sharp/LogLevel.cs b/websocket-sharp/LogLevel.cs deleted file mode 100644 index 5ff1d8fed..000000000 --- a/websocket-sharp/LogLevel.cs +++ /dev/null @@ -1,67 +0,0 @@ -#region License -/* - * LogLevel.cs - * - * The MIT License - * - * Copyright (c) 2013-2022 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -using System; - -namespace WebSocketSharp -{ - /// - /// Specifies the logging level. - /// - public enum LogLevel - { - /// - /// Specifies the bottom logging level. - /// - Trace, - /// - /// Specifies the 2nd logging level from the bottom. - /// - Debug, - /// - /// Specifies the 3rd logging level from the bottom. - /// - Info, - /// - /// Specifies the 3rd logging level from the top. - /// - Warn, - /// - /// Specifies the 2nd logging level from the top. - /// - Error, - /// - /// Specifies the top logging level. - /// - Fatal, - /// - /// Specifies not to output logs. - /// - None - } -} diff --git a/websocket-sharp/Logger.cs b/websocket-sharp/Logger.cs deleted file mode 100644 index a280ce43f..000000000 --- a/websocket-sharp/Logger.cs +++ /dev/null @@ -1,345 +0,0 @@ -#region License -/* - * Logger.cs - * - * The MIT License - * - * Copyright (c) 2013-2024 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -using System; -using System.Diagnostics; -using System.IO; - -namespace WebSocketSharp -{ - /// - /// Provides a set of methods and properties for logging. - /// - /// - /// - /// If you output a log with lower than the current logging level, - /// it cannot be outputted. - /// - /// - /// The default output method writes a log to the standard output - /// stream and the text file if it has a valid path. - /// - /// - /// If you would like to use the custom output method, you should - /// specify it with the constructor or the - /// property. - /// - /// - public class Logger - { - #region Private Fields - - private volatile string _file; - private volatile LogLevel _level; - private Action _output; - private object _sync; - - #endregion - - #region Public Constructors - - /// - /// Initializes a new instance of the class. - /// - /// - /// This constructor initializes the logging level with the Error level. - /// - public Logger () - : this (LogLevel.Error, null, null) - { - } - - /// - /// Initializes a new instance of the class with - /// the specified logging level. - /// - /// - /// One of the enum values that specifies - /// the logging level. - /// - public Logger (LogLevel level) - : this (level, null, null) - { - } - - /// - /// Initializes a new instance of the class with - /// the specified logging level, path to the log file, and delegate - /// used to output a log. - /// - /// - /// One of the enum values that specifies - /// the logging level. - /// - /// - /// A that specifies the path to the log file. - /// - /// - /// An that specifies - /// the delegate used to output a log. - /// - public Logger (LogLevel level, string file, Action output) - { - _level = level; - _file = file; - _output = output ?? defaultOutput; - - _sync = new object (); - } - - #endregion - - #region Public Properties - - /// - /// Gets or sets the path to the log file. - /// - /// - /// A that represents the path to the log file if any. - /// - public string File { - get { - return _file; - } - - set { - lock (_sync) - _file = value; - } - } - - /// - /// Gets or sets the current logging level. - /// - /// - /// A log with lower than the value of this property cannot be outputted. - /// - /// - /// - /// One of the enum values. - /// - /// - /// It represents the current logging level. - /// - /// - public LogLevel Level { - get { - return _level; - } - - set { - lock (_sync) - _level = value; - } - } - - /// - /// Gets or sets the delegate used to output a log. - /// - /// - /// - /// An delegate. - /// - /// - /// It represents the delegate called when the logger outputs a log. - /// - /// - /// The string parameter passed to the delegate is the value of - /// the property. - /// - /// - /// If the value to set is , the default - /// output method is set. - /// - /// - public Action Output { - get { - return _output; - } - - set { - lock (_sync) - _output = value ?? defaultOutput; - } - } - - #endregion - - #region Private Methods - - private static void defaultOutput (LogData data, string path) - { - var val = data.ToString (); - - Console.WriteLine (val); - - if (path != null && path.Length > 0) - writeToFile (val, path); - } - - private void output (string message, LogLevel level) - { - lock (_sync) { - if (_level > level) - return; - - try { - var data = new LogData (level, new StackFrame (2, true), message); - - _output (data, _file); - } - catch (Exception ex) { - var data = new LogData ( - LogLevel.Fatal, - new StackFrame (0, true), - ex.Message - ); - - Console.WriteLine (data.ToString ()); - } - } - } - - private static void writeToFile (string value, string path) - { - using (var writer = new StreamWriter (path, true)) - using (var syncWriter = TextWriter.Synchronized (writer)) - syncWriter.WriteLine (value); - } - - #endregion - - #region Public Methods - - /// - /// Outputs the specified message as a log with the Debug level. - /// - /// - /// If the current logging level is higher than the Debug level, - /// this method does not output the message as a log. - /// - /// - /// A that specifies the message to output. - /// - public void Debug (string message) - { - if (_level > LogLevel.Debug) - return; - - output (message, LogLevel.Debug); - } - - /// - /// Outputs the specified message as a log with the Error level. - /// - /// - /// If the current logging level is higher than the Error level, - /// this method does not output the message as a log. - /// - /// - /// A that specifies the message to output. - /// - public void Error (string message) - { - if (_level > LogLevel.Error) - return; - - output (message, LogLevel.Error); - } - - /// - /// Outputs the specified message as a log with the Fatal level. - /// - /// - /// A that specifies the message to output. - /// - public void Fatal (string message) - { - if (_level > LogLevel.Fatal) - return; - - output (message, LogLevel.Fatal); - } - - /// - /// Outputs the specified message as a log with the Info level. - /// - /// - /// If the current logging level is higher than the Info level, - /// this method does not output the message as a log. - /// - /// - /// A that specifies the message to output. - /// - public void Info (string message) - { - if (_level > LogLevel.Info) - return; - - output (message, LogLevel.Info); - } - - /// - /// Outputs the specified message as a log with the Trace level. - /// - /// - /// If the current logging level is higher than the Trace level, - /// this method does not output the message as a log. - /// - /// - /// A that specifies the message to output. - /// - public void Trace (string message) - { - if (_level > LogLevel.Trace) - return; - - output (message, LogLevel.Trace); - } - - /// - /// Outputs the specified message as a log with the Warn level. - /// - /// - /// If the current logging level is higher than the Warn level, - /// this method does not output the message as a log. - /// - /// - /// A that specifies the message to output. - /// - public void Warn (string message) - { - if (_level > LogLevel.Warn) - return; - - output (message, LogLevel.Warn); - } - - #endregion - } -} diff --git a/websocket-sharp/Mask.cs b/websocket-sharp/Mask.cs deleted file mode 100644 index 2958f5a83..000000000 --- a/websocket-sharp/Mask.cs +++ /dev/null @@ -1,52 +0,0 @@ -#region License -/* - * Mask.cs - * - * The MIT License - * - * Copyright (c) 2012-2025 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -using System; - -namespace WebSocketSharp -{ - /// - /// Indicates whether the payload data of a WebSocket frame is masked. - /// - /// - /// The values of this enumeration are defined in - /// - /// Section 5.2 of RFC 6455. - /// - internal enum Mask - { - /// - /// Equivalent to numeric value 0. Indicates not masked. - /// - Off = 0x0, - /// - /// Equivalent to numeric value 1. Indicates masked. - /// - On = 0x1 - } -} diff --git a/websocket-sharp/MessageEventArgs.cs b/websocket-sharp/MessageEventArgs.cs deleted file mode 100644 index 63add90f7..000000000 --- a/websocket-sharp/MessageEventArgs.cs +++ /dev/null @@ -1,192 +0,0 @@ -#region License -/* - * MessageEventArgs.cs - * - * The MIT License - * - * Copyright (c) 2012-2022 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -using System; - -namespace WebSocketSharp -{ - /// - /// Represents the event data for the event. - /// - /// - /// - /// The message event occurs when the interface - /// receives a message or a ping if the - /// property is set to true. - /// - /// - /// If you would like to get the message data, you should access - /// the or property. - /// - /// - public class MessageEventArgs : EventArgs - { - #region Private Fields - - private string _data; - private bool _dataSet; - private Opcode _opcode; - private byte[] _rawData; - - #endregion - - #region Internal Constructors - - internal MessageEventArgs (WebSocketFrame frame) - { - _opcode = frame.Opcode; - _rawData = frame.PayloadData.ApplicationData; - } - - internal MessageEventArgs (Opcode opcode, byte[] rawData) - { - if ((ulong) rawData.LongLength > PayloadData.MaxLength) - throw new WebSocketException (CloseStatusCode.TooBig); - - _opcode = opcode; - _rawData = rawData; - } - - #endregion - - #region Internal Properties - - /// - /// Gets the opcode for the message. - /// - /// - /// , , - /// or . - /// - internal Opcode Opcode { - get { - return _opcode; - } - } - - #endregion - - #region Public Properties - - /// - /// Gets the message data as a . - /// - /// - /// - /// A that represents the message data - /// if the message type is text or ping. - /// - /// - /// if the message type is binary or - /// the message data could not be UTF-8-decoded. - /// - /// - public string Data { - get { - setData (); - - return _data; - } - } - - /// - /// Gets a value indicating whether the message type is binary. - /// - /// - /// true if the message type is binary; otherwise, false. - /// - public bool IsBinary { - get { - return _opcode == Opcode.Binary; - } - } - - /// - /// Gets a value indicating whether the message type is ping. - /// - /// - /// true if the message type is ping; otherwise, false. - /// - public bool IsPing { - get { - return _opcode == Opcode.Ping; - } - } - - /// - /// Gets a value indicating whether the message type is text. - /// - /// - /// true if the message type is text; otherwise, false. - /// - public bool IsText { - get { - return _opcode == Opcode.Text; - } - } - - /// - /// Gets the message data as an array of . - /// - /// - /// An array of that represents the message data. - /// - public byte[] RawData { - get { - setData (); - - return _rawData; - } - } - - #endregion - - #region Private Methods - - private void setData () - { - if (_dataSet) - return; - - if (_opcode == Opcode.Binary) { - _dataSet = true; - - return; - } - - string data; - - if (_rawData.TryGetUTF8DecodedString (out data)) - _data = data; - - _dataSet = true; - } - - #endregion - } -} diff --git a/websocket-sharp/Net/AuthenticationChallenge.cs b/websocket-sharp/Net/AuthenticationChallenge.cs deleted file mode 100644 index 726740801..000000000 --- a/websocket-sharp/Net/AuthenticationChallenge.cs +++ /dev/null @@ -1,280 +0,0 @@ -#region License -/* - * AuthenticationChallenge.cs - * - * The MIT License - * - * Copyright (c) 2013-2024 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -using System; -using System.Collections.Specialized; -using System.Text; - -namespace WebSocketSharp.Net -{ - internal class AuthenticationChallenge - { - #region Private Fields - - private NameValueCollection _parameters; - private AuthenticationSchemes _scheme; - - #endregion - - #region Private Constructors - - private AuthenticationChallenge ( - AuthenticationSchemes scheme, - NameValueCollection parameters - ) - { - _scheme = scheme; - _parameters = parameters; - } - - #endregion - - #region Internal Constructors - - internal AuthenticationChallenge ( - AuthenticationSchemes scheme, - string realm - ) - : this (scheme, new NameValueCollection ()) - { - _parameters["realm"] = realm; - - if (scheme == AuthenticationSchemes.Digest) { - _parameters["nonce"] = CreateNonceValue (); - _parameters["algorithm"] = "MD5"; - _parameters["qop"] = "auth"; - } - } - - #endregion - - #region Internal Properties - - internal NameValueCollection Parameters { - get { - return _parameters; - } - } - - #endregion - - #region Public Properties - - public string Algorithm { - get { - return _parameters["algorithm"]; - } - } - - public string Domain { - get { - return _parameters["domain"]; - } - } - - public string Nonce { - get { - return _parameters["nonce"]; - } - } - - public string Opaque { - get { - return _parameters["opaque"]; - } - } - - public string Qop { - get { - return _parameters["qop"]; - } - } - - public string Realm { - get { - return _parameters["realm"]; - } - } - - public AuthenticationSchemes Scheme { - get { - return _scheme; - } - } - - public string Stale { - get { - return _parameters["stale"]; - } - } - - #endregion - - #region Internal Methods - - internal static AuthenticationChallenge CreateBasicChallenge (string realm) - { - return new AuthenticationChallenge (AuthenticationSchemes.Basic, realm); - } - - internal static AuthenticationChallenge CreateDigestChallenge (string realm) - { - return new AuthenticationChallenge (AuthenticationSchemes.Digest, realm); - } - - internal static string CreateNonceValue () - { - var rand = new Random (); - var bytes = new byte[16]; - - rand.NextBytes (bytes); - - var buff = new StringBuilder (32); - - foreach (var b in bytes) - buff.Append (b.ToString ("x2")); - - return buff.ToString (); - } - - internal static AuthenticationChallenge Parse (string value) - { - var chal = value.Split (new[] { ' ' }, 2); - - if (chal.Length != 2) - return null; - - var schm = chal[0].ToLower (); - - if (schm == "basic") { - var parameters = ParseParameters (chal[1]); - - return new AuthenticationChallenge ( - AuthenticationSchemes.Basic, - parameters - ); - } - - if (schm == "digest") { - var parameters = ParseParameters (chal[1]); - - return new AuthenticationChallenge ( - AuthenticationSchemes.Digest, - parameters - ); - } - - return null; - } - - internal static NameValueCollection ParseParameters (string value) - { - var ret = new NameValueCollection (); - - foreach (var param in value.SplitHeaderValue (',')) { - var i = param.IndexOf ('='); - - var name = i > 0 ? param.Substring (0, i).Trim () : null; - var val = i < 0 - ? param.Trim ().Trim ('"') - : i < param.Length - 1 - ? param.Substring (i + 1).Trim ().Trim ('"') - : String.Empty; - - ret.Add (name, val); - } - - return ret; - } - - internal string ToBasicString () - { - return String.Format ("Basic realm=\"{0}\"", _parameters["realm"]); - } - - internal string ToDigestString () - { - var buff = new StringBuilder (128); - - var domain = _parameters["domain"]; - var realm = _parameters["realm"]; - var nonce = _parameters["nonce"]; - - if (domain != null) { - buff.AppendFormat ( - "Digest realm=\"{0}\", domain=\"{1}\", nonce=\"{2}\"", - realm, - domain, - nonce - ); - } - else { - buff.AppendFormat ("Digest realm=\"{0}\", nonce=\"{1}\"", realm, nonce); - } - - var opaque = _parameters["opaque"]; - - if (opaque != null) - buff.AppendFormat (", opaque=\"{0}\"", opaque); - - var stale = _parameters["stale"]; - - if (stale != null) - buff.AppendFormat (", stale={0}", stale); - - var algo = _parameters["algorithm"]; - - if (algo != null) - buff.AppendFormat (", algorithm={0}", algo); - - var qop = _parameters["qop"]; - - if (qop != null) - buff.AppendFormat (", qop=\"{0}\"", qop); - - return buff.ToString (); - } - - #endregion - - #region Public Methods - - public override string ToString () - { - if (_scheme == AuthenticationSchemes.Basic) - return ToBasicString (); - - if (_scheme == AuthenticationSchemes.Digest) - return ToDigestString (); - - return String.Empty; - } - - #endregion - } -} diff --git a/websocket-sharp/Net/AuthenticationResponse.cs b/websocket-sharp/Net/AuthenticationResponse.cs deleted file mode 100644 index 28fbd2236..000000000 --- a/websocket-sharp/Net/AuthenticationResponse.cs +++ /dev/null @@ -1,464 +0,0 @@ -#region License -/* - * AuthenticationResponse.cs - * - * The ParseBasicCredentials method is derived from HttpListenerContext.cs - * (System.Net) of Mono (http://www.mono-project.com). - * - * The MIT License - * - * Copyright (c) 2005 Novell, Inc. (http://www.novell.com) - * Copyright (c) 2013-2024 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -using System; -using System.Collections.Specialized; -using System.Security.Cryptography; -using System.Security.Principal; -using System.Text; - -namespace WebSocketSharp.Net -{ - internal class AuthenticationResponse - { - #region Private Fields - - private uint _nonceCount; - private NameValueCollection _parameters; - private AuthenticationSchemes _scheme; - - #endregion - - #region Private Constructors - - private AuthenticationResponse ( - AuthenticationSchemes scheme, - NameValueCollection parameters - ) - { - _scheme = scheme; - _parameters = parameters; - } - - #endregion - - #region Internal Constructors - - internal AuthenticationResponse (NetworkCredential credentials) - : this ( - AuthenticationSchemes.Basic, - new NameValueCollection (), - credentials, - 0 - ) - { - } - - internal AuthenticationResponse ( - AuthenticationChallenge challenge, - NetworkCredential credentials, - uint nonceCount - ) - : this (challenge.Scheme, challenge.Parameters, credentials, nonceCount) - { - } - - internal AuthenticationResponse ( - AuthenticationSchemes scheme, - NameValueCollection parameters, - NetworkCredential credentials, - uint nonceCount - ) - : this (scheme, parameters) - { - _parameters["username"] = credentials.Username; - _parameters["password"] = credentials.Password; - _parameters["uri"] = credentials.Domain; - _nonceCount = nonceCount; - - if (scheme == AuthenticationSchemes.Digest) - initAsDigest (); - } - - #endregion - - #region Internal Properties - - internal uint NonceCount { - get { - return _nonceCount < UInt32.MaxValue ? _nonceCount : 0; - } - } - - internal NameValueCollection Parameters { - get { - return _parameters; - } - } - - #endregion - - #region Public Properties - - public string Algorithm { - get { - return _parameters["algorithm"]; - } - } - - public string Cnonce { - get { - return _parameters["cnonce"]; - } - } - - public string Nc { - get { - return _parameters["nc"]; - } - } - - public string Nonce { - get { - return _parameters["nonce"]; - } - } - - public string Opaque { - get { - return _parameters["opaque"]; - } - } - - public string Password { - get { - return _parameters["password"]; - } - } - - public string Qop { - get { - return _parameters["qop"]; - } - } - - public string Realm { - get { - return _parameters["realm"]; - } - } - - public string Response { - get { - return _parameters["response"]; - } - } - - public AuthenticationSchemes Scheme { - get { - return _scheme; - } - } - - public string Uri { - get { - return _parameters["uri"]; - } - } - - public string UserName { - get { - return _parameters["username"]; - } - } - - #endregion - - #region Private Methods - - private static string createA1 ( - string username, - string password, - string realm - ) - { - return String.Format ("{0}:{1}:{2}", username, realm, password); - } - - private static string createA1 ( - string username, - string password, - string realm, - string nonce, - string cnonce - ) - { - var a1 = createA1 (username, password, realm); - - return String.Format ("{0}:{1}:{2}", hash (a1), nonce, cnonce); - } - - private static string createA2 (string method, string uri) - { - return String.Format ("{0}:{1}", method, uri); - } - - private static string createA2 (string method, string uri, string entity) - { - return String.Format ("{0}:{1}:{2}", method, uri, hash (entity)); - } - - private static string hash (string value) - { - var buff = new StringBuilder (64); - - var md5 = MD5.Create (); - var bytes = Encoding.UTF8.GetBytes (value); - var res = md5.ComputeHash (bytes); - - foreach (var b in res) - buff.Append (b.ToString ("x2")); - - return buff.ToString (); - } - - private void initAsDigest () - { - var qops = _parameters["qop"]; - - if (qops != null) { - var hasAuth = qops.Split (',').Contains ( - qop => qop.Trim ().ToLower () == "auth" - ); - - if (hasAuth) { - _parameters["qop"] = "auth"; - _parameters["cnonce"] = AuthenticationChallenge.CreateNonceValue (); - _parameters["nc"] = String.Format ("{0:x8}", ++_nonceCount); - } - else { - _parameters["qop"] = null; - } - } - - _parameters["method"] = "GET"; - _parameters["response"] = CreateRequestDigest (_parameters); - } - - #endregion - - #region Internal Methods - - internal static string CreateRequestDigest (NameValueCollection parameters) - { - var uname = parameters["username"]; - var passwd = parameters["password"]; - var realm = parameters["realm"]; - var nonce = parameters["nonce"]; - var uri = parameters["uri"]; - var algo = parameters["algorithm"]; - var qop = parameters["qop"]; - var cnonce = parameters["cnonce"]; - var nc = parameters["nc"]; - var method = parameters["method"]; - - var a1 = algo != null && algo.ToLower () == "md5-sess" - ? createA1 (uname, passwd, realm, nonce, cnonce) - : createA1 (uname, passwd, realm); - - var a2 = qop != null && qop.ToLower () == "auth-int" - ? createA2 (method, uri, parameters["entity"]) - : createA2 (method, uri); - - var secret = hash (a1); - var data = qop != null - ? String.Format ( - "{0}:{1}:{2}:{3}:{4}", - nonce, - nc, - cnonce, - qop, - hash (a2) - ) - : String.Format ("{0}:{1}", nonce, hash (a2)); - - var keyed = String.Format ("{0}:{1}", secret, data); - - return hash (keyed); - } - - internal static AuthenticationResponse Parse (string value) - { - try { - var cred = value.Split (new[] { ' ' }, 2); - - if (cred.Length != 2) - return null; - - var schm = cred[0].ToLower (); - - if (schm == "basic") { - var parameters = ParseBasicCredentials (cred[1]); - - return new AuthenticationResponse ( - AuthenticationSchemes.Basic, - parameters - ); - } - - if (schm == "digest") { - var parameters = AuthenticationChallenge.ParseParameters (cred[1]); - - return new AuthenticationResponse ( - AuthenticationSchemes.Digest, - parameters - ); - } - - return null; - } - catch { - return null; - } - } - - internal static NameValueCollection ParseBasicCredentials (string value) - { - var ret = new NameValueCollection (); - - // Decode the basic-credentials (a Base64 encoded string). - - var bytes = Convert.FromBase64String (value); - var userPass = Encoding.UTF8.GetString (bytes); - - // The format is [\]:. - - var idx = userPass.IndexOf (':'); - var uname = userPass.Substring (0, idx); - var passwd = idx < userPass.Length - 1 - ? userPass.Substring (idx + 1) - : String.Empty; - - // Check if exists. - - idx = uname.IndexOf ('\\'); - - if (idx > -1) - uname = uname.Substring (idx + 1); - - ret["username"] = uname; - ret["password"] = passwd; - - return ret; - } - - internal string ToBasicString () - { - var uname = _parameters["username"]; - var passwd = _parameters["password"]; - var userPass = String.Format ("{0}:{1}", uname, passwd); - - var bytes = Encoding.UTF8.GetBytes (userPass); - var cred = Convert.ToBase64String (bytes); - - return "Basic " + cred; - } - - internal string ToDigestString () - { - var buff = new StringBuilder (256); - - var uname = _parameters["username"]; - var realm = _parameters["realm"]; - var nonce = _parameters["nonce"]; - var uri = _parameters["uri"]; - var res = _parameters["response"]; - - buff.AppendFormat ( - "Digest username=\"{0}\", realm=\"{1}\", nonce=\"{2}\", uri=\"{3}\", response=\"{4}\"", - uname, - realm, - nonce, - uri, - res - ); - - var opaque = _parameters["opaque"]; - - if (opaque != null) - buff.AppendFormat (", opaque=\"{0}\"", opaque); - - var algo = _parameters["algorithm"]; - - if (algo != null) - buff.AppendFormat (", algorithm={0}", algo); - - var qop = _parameters["qop"]; - - if (qop != null) { - var cnonce = _parameters["cnonce"]; - var nc = _parameters["nc"]; - - buff.AppendFormat ( - ", qop={0}, cnonce=\"{1}\", nc={2}", - qop, - cnonce, - nc - ); - } - - return buff.ToString (); - } - - #endregion - - #region Public Methods - - public IIdentity ToIdentity () - { - if (_scheme == AuthenticationSchemes.Basic) { - var uname = _parameters["username"]; - var passwd = _parameters["password"]; - - return new HttpBasicIdentity (uname, passwd); - } - - if (_scheme == AuthenticationSchemes.Digest) - return new HttpDigestIdentity (_parameters); - - return null; - } - - public override string ToString () - { - if (_scheme == AuthenticationSchemes.Basic) - return ToBasicString (); - - if (_scheme == AuthenticationSchemes.Digest) - return ToDigestString (); - - return String.Empty; - } - - #endregion - } -} diff --git a/websocket-sharp/Net/AuthenticationSchemes.cs b/websocket-sharp/Net/AuthenticationSchemes.cs deleted file mode 100644 index ab7721a15..000000000 --- a/websocket-sharp/Net/AuthenticationSchemes.cs +++ /dev/null @@ -1,66 +0,0 @@ -#region License -/* - * AuthenticationSchemes.cs - * - * This code is derived from AuthenticationSchemes.cs (System.Net) of Mono - * (http://www.mono-project.com). - * - * The MIT License - * - * Copyright (c) 2005 Novell, Inc. (http://www.novell.com) - * Copyright (c) 2012-2016 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -#region Authors -/* - * Authors: - * - Atsushi Enomoto - */ -#endregion - -using System; - -namespace WebSocketSharp.Net -{ - /// - /// Specifies the scheme for authentication. - /// - public enum AuthenticationSchemes - { - /// - /// No authentication is allowed. - /// - None, - /// - /// Specifies digest authentication. - /// - Digest = 1, - /// - /// Specifies basic authentication. - /// - Basic = 8, - /// - /// Specifies anonymous authentication. - /// - Anonymous = 0x8000 - } -} diff --git a/websocket-sharp/Net/Chunk.cs b/websocket-sharp/Net/Chunk.cs deleted file mode 100644 index 9ed28f864..000000000 --- a/websocket-sharp/Net/Chunk.cs +++ /dev/null @@ -1,93 +0,0 @@ -#region License -/* - * Chunk.cs - * - * This code is derived from ChunkStream.cs (System.Net) of Mono - * (http://www.mono-project.com). - * - * The MIT License - * - * Copyright (c) 2003 Ximian, Inc (http://www.ximian.com) - * Copyright (c) 2014-2021 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -#region Authors -/* - * Authors: - * - Gonzalo Paniagua Javier - */ -#endregion - -using System; - -namespace WebSocketSharp.Net -{ - internal class Chunk - { - #region Private Fields - - private byte[] _data; - private int _offset; - - #endregion - - #region Public Constructors - - public Chunk (byte[] data) - { - _data = data; - } - - #endregion - - #region Public Properties - - public int ReadLeft { - get { - return _data.Length - _offset; - } - } - - #endregion - - #region Public Methods - - public int Read (byte[] buffer, int offset, int count) - { - var left = _data.Length - _offset; - - if (left == 0) - return 0; - - if (count > left) - count = left; - - Buffer.BlockCopy (_data, _offset, buffer, offset, count); - - _offset += count; - - return count; - } - - #endregion - } -} diff --git a/websocket-sharp/Net/ChunkStream.cs b/websocket-sharp/Net/ChunkStream.cs deleted file mode 100644 index 3de4374db..000000000 --- a/websocket-sharp/Net/ChunkStream.cs +++ /dev/null @@ -1,429 +0,0 @@ -#region License -/* - * ChunkStream.cs - * - * This code is derived from ChunkStream.cs (System.Net) of Mono - * (http://www.mono-project.com). - * - * The MIT License - * - * Copyright (c) 2003 Ximian, Inc (http://www.ximian.com) - * Copyright (c) 2012-2023 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -#region Authors -/* - * Authors: - * - Gonzalo Paniagua Javier - */ -#endregion - -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Net; -using System.Text; - -namespace WebSocketSharp.Net -{ - internal class ChunkStream - { - #region Private Fields - - private int _chunkRead; - private int _chunkSize; - private List _chunks; - private int _count; - private byte[] _endBuffer; - private bool _gotIt; - private WebHeaderCollection _headers; - private int _offset; - private StringBuilder _saved; - private bool _sawCr; - private InputChunkState _state; - private int _trailerState; - - #endregion - - #region Public Constructors - - public ChunkStream (WebHeaderCollection headers) - { - _headers = headers; - - _chunkSize = -1; - _chunks = new List (); - _saved = new StringBuilder (); - } - - #endregion - - #region Internal Properties - - internal int Count { - get { - return _count; - } - } - - internal byte[] EndBuffer { - get { - return _endBuffer; - } - } - - internal int Offset { - get { - return _offset; - } - } - - #endregion - - #region Public Properties - - public WebHeaderCollection Headers { - get { - return _headers; - } - } - - public bool WantsMore { - get { - return _state < InputChunkState.End; - } - } - - #endregion - - #region Private Methods - - private int read (byte[] buffer, int offset, int count) - { - var nread = 0; - var cnt = _chunks.Count; - - for (var i = 0; i < cnt; i++) { - var chunk = _chunks[i]; - - if (chunk == null) - continue; - - if (chunk.ReadLeft == 0) { - _chunks[i] = null; - - continue; - } - - nread += chunk.Read (buffer, offset + nread, count - nread); - - if (nread == count) - break; - } - - return nread; - } - - private InputChunkState seekCrLf (byte[] buffer, ref int offset, int length) - { - if (!_sawCr) { - if (buffer[offset++] != 13) - throwProtocolViolation ("CR is expected."); - - _sawCr = true; - - if (offset == length) - return InputChunkState.DataEnded; - } - - if (buffer[offset++] != 10) - throwProtocolViolation ("LF is expected."); - - return InputChunkState.None; - } - - private InputChunkState setChunkSize ( - byte[] buffer, - ref int offset, - int length - ) - { - byte b = 0; - - while (offset < length) { - b = buffer[offset++]; - - if (_sawCr) { - if (b != 10) - throwProtocolViolation ("LF is expected."); - - break; - } - - if (b == 13) { - _sawCr = true; - - continue; - } - - if (b == 10) - throwProtocolViolation ("LF is unexpected."); - - if (_gotIt) - continue; - - if (b == 32 || b == 59) { // SP or ';' - _gotIt = true; - - continue; - } - - _saved.Append ((char) b); - } - - if (_saved.Length > 20) - throwProtocolViolation ("The chunk size is too big."); - - if (b != 10) - return InputChunkState.None; - - var s = _saved.ToString (); - - try { - _chunkSize = Int32.Parse (s, NumberStyles.HexNumber); - } - catch { - throwProtocolViolation ("The chunk size cannot be parsed."); - } - - _chunkRead = 0; - - if (_chunkSize == 0) { - _trailerState = 2; - - return InputChunkState.Trailer; - } - - return InputChunkState.Data; - } - - private InputChunkState setTrailer ( - byte[] buffer, - ref int offset, - int length - ) - { - while (offset < length) { - if (_trailerState == 4) // CR LF CR LF - break; - - var b = buffer[offset++]; - - _saved.Append ((char) b); - - if (_trailerState == 1 || _trailerState == 3) { // CR or CR LF CR - if (b != 10) - throwProtocolViolation ("LF is expected."); - - _trailerState++; - - continue; - } - - if (b == 13) { - _trailerState++; - - continue; - } - - if (b == 10) - throwProtocolViolation ("LF is unexpected."); - - _trailerState = 0; - } - - var len = _saved.Length; - - if (len > 4196) - throwProtocolViolation ("The trailer is too long."); - - if (_trailerState < 4) - return InputChunkState.Trailer; - - if (len == 2) - return InputChunkState.End; - - _saved.Length = len - 2; - - var val = _saved.ToString (); - var reader = new StringReader (val); - - while (true) { - var line = reader.ReadLine (); - - if (line == null || line.Length == 0) - break; - - _headers.Add (line); - } - - return InputChunkState.End; - } - - private static void throwProtocolViolation (string message) - { - throw new WebException ( - message, - null, - WebExceptionStatus.ServerProtocolViolation, - null - ); - } - - private void write (byte[] buffer, int offset, int length) - { - if (_state == InputChunkState.End) - throwProtocolViolation ("The chunks were ended."); - - if (_state == InputChunkState.None) { - _state = setChunkSize (buffer, ref offset, length); - - if (_state == InputChunkState.None) - return; - - _saved.Length = 0; - _sawCr = false; - _gotIt = false; - } - - if (_state == InputChunkState.Data) { - if (offset >= length) - return; - - _state = writeData (buffer, ref offset, length); - - if (_state == InputChunkState.Data) - return; - } - - if (_state == InputChunkState.DataEnded) { - if (offset >= length) - return; - - _state = seekCrLf (buffer, ref offset, length); - - if (_state == InputChunkState.DataEnded) - return; - - _sawCr = false; - } - - if (_state == InputChunkState.Trailer) { - if (offset >= length) - return; - - _state = setTrailer (buffer, ref offset, length); - - if (_state == InputChunkState.Trailer) - return; - - _saved.Length = 0; - } - - if (_state == InputChunkState.End) { - _endBuffer = buffer; - _offset = offset; - _count = length - offset; - - return; - } - - if (offset >= length) - return; - - write (buffer, offset, length); - } - - private InputChunkState writeData ( - byte[] buffer, - ref int offset, - int length - ) - { - var cnt = length - offset; - var left = _chunkSize - _chunkRead; - - if (cnt > left) - cnt = left; - - var data = new byte[cnt]; - - Buffer.BlockCopy (buffer, offset, data, 0, cnt); - - var chunk = new Chunk (data); - - _chunks.Add (chunk); - - offset += cnt; - _chunkRead += cnt; - - return _chunkRead == _chunkSize - ? InputChunkState.DataEnded - : InputChunkState.Data; - } - - #endregion - - #region Internal Methods - - internal void ResetChunkStore () - { - _chunkRead = 0; - _chunkSize = -1; - - _chunks.Clear (); - } - - #endregion - - #region Public Methods - - public int Read (byte[] buffer, int offset, int count) - { - if (count <= 0) - return 0; - - return read (buffer, offset, count); - } - - public void Write (byte[] buffer, int offset, int count) - { - if (count <= 0) - return; - - write (buffer, offset, offset + count); - } - - #endregion - } -} diff --git a/websocket-sharp/Net/ChunkedRequestStream.cs b/websocket-sharp/Net/ChunkedRequestStream.cs deleted file mode 100644 index f4a583925..000000000 --- a/websocket-sharp/Net/ChunkedRequestStream.cs +++ /dev/null @@ -1,283 +0,0 @@ -#region License -/* - * ChunkedRequestStream.cs - * - * This code is derived from ChunkedInputStream.cs (System.Net) of Mono - * (http://www.mono-project.com). - * - * The MIT License - * - * Copyright (c) 2005 Novell, Inc. (http://www.novell.com) - * Copyright (c) 2012-2023 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -#region Authors -/* - * Authors: - * - Gonzalo Paniagua Javier - */ -#endregion - -using System; -using System.IO; - -namespace WebSocketSharp.Net -{ - internal class ChunkedRequestStream : RequestStream - { - #region Private Fields - - private static readonly int _bufferLength; - private HttpListenerContext _context; - private ChunkStream _decoder; - private bool _disposed; - private bool _noMoreData; - - #endregion - - #region Static Constructor - - static ChunkedRequestStream () - { - _bufferLength = 8192; - } - - #endregion - - #region Internal Constructors - - internal ChunkedRequestStream ( - Stream innerStream, - byte[] initialBuffer, - int offset, - int count, - HttpListenerContext context - ) - : base (innerStream, initialBuffer, offset, count, -1) - { - _context = context; - - _decoder = new ChunkStream ( - (WebHeaderCollection) context.Request.Headers - ); - } - - #endregion - - #region Internal Properties - - internal bool HasRemainingBuffer { - get { - return _decoder.Count + Count > 0; - } - } - - internal byte[] RemainingBuffer { - get { - using (var buff = new MemoryStream ()) { - var cnt = _decoder.Count; - - if (cnt > 0) - buff.Write (_decoder.EndBuffer, _decoder.Offset, cnt); - - cnt = Count; - - if (cnt > 0) - buff.Write (InitialBuffer, Offset, cnt); - - buff.Close (); - - return buff.ToArray (); - } - } - } - - #endregion - - #region Private Methods - - private void onRead (IAsyncResult asyncResult) - { - var rstate = (ReadBufferState) asyncResult.AsyncState; - var ares = rstate.AsyncResult; - - try { - var nread = base.EndRead (asyncResult); - - _decoder.Write (ares.Buffer, ares.Offset, nread); - - nread = _decoder.Read (rstate.Buffer, rstate.Offset, rstate.Count); - - rstate.Offset += nread; - rstate.Count -= nread; - - if (rstate.Count == 0 || !_decoder.WantsMore || nread == 0) { - _noMoreData = !_decoder.WantsMore && nread == 0; - - ares.Count = rstate.InitialCount - rstate.Count; - - ares.Complete (); - - return; - } - - base.BeginRead (ares.Buffer, ares.Offset, ares.Count, onRead, rstate); - } - catch (Exception ex) { - _context.ErrorMessage = "I/O operation aborted"; - - _context.SendError (); - - ares.Complete (ex); - } - } - - #endregion - - #region Public Methods - - public override IAsyncResult BeginRead ( - byte[] buffer, - int offset, - int count, - AsyncCallback callback, - object state - ) - { - if (_disposed) - throw new ObjectDisposedException (ObjectName); - - if (buffer == null) - throw new ArgumentNullException ("buffer"); - - if (offset < 0) { - var msg = "A negative value."; - - throw new ArgumentOutOfRangeException ("offset", msg); - } - - if (count < 0) { - var msg = "A negative value."; - - throw new ArgumentOutOfRangeException ("count", msg); - } - - var len = buffer.Length; - - if (offset + count > len) { - var msg = "The sum of offset and count is greater than the length of buffer."; - - throw new ArgumentException (msg); - } - - var ares = new HttpStreamAsyncResult (callback, state); - - if (_noMoreData) { - ares.Complete (); - - return ares; - } - - var nread = _decoder.Read (buffer, offset, count); - - offset += nread; - count -= nread; - - if (count == 0) { - ares.Count = nread; - - ares.Complete (); - - return ares; - } - - if (!_decoder.WantsMore) { - _noMoreData = nread == 0; - - ares.Count = nread; - - ares.Complete (); - - return ares; - } - - ares.Buffer = new byte[_bufferLength]; - ares.Offset = 0; - ares.Count = _bufferLength; - - var rstate = new ReadBufferState (buffer, offset, count, ares); - - rstate.InitialCount += nread; - - base.BeginRead (ares.Buffer, ares.Offset, ares.Count, onRead, rstate); - - return ares; - } - - public override void Close () - { - if (_disposed) - return; - - base.Close (); - - _disposed = true; - } - - public override int EndRead (IAsyncResult asyncResult) - { - if (_disposed) - throw new ObjectDisposedException (ObjectName); - - if (asyncResult == null) - throw new ArgumentNullException ("asyncResult"); - - var ares = asyncResult as HttpStreamAsyncResult; - - if (ares == null) { - var msg = "A wrong IAsyncResult instance."; - - throw new ArgumentException (msg, "asyncResult"); - } - - if (!ares.IsCompleted) - ares.AsyncWaitHandle.WaitOne (); - - if (ares.HasException) { - var msg = "The I/O operation has been aborted."; - - throw new HttpListenerException (995, msg); - } - - return ares.Count; - } - - public override int Read (byte[] buffer, int offset, int count) - { - var ares = BeginRead (buffer, offset, count, null, null); - - return EndRead (ares); - } - - #endregion - } -} diff --git a/websocket-sharp/Net/ClientSslConfiguration.cs b/websocket-sharp/Net/ClientSslConfiguration.cs deleted file mode 100644 index 33438a93c..000000000 --- a/websocket-sharp/Net/ClientSslConfiguration.cs +++ /dev/null @@ -1,311 +0,0 @@ -#region License -/* - * ClientSslConfiguration.cs - * - * The MIT License - * - * Copyright (c) 2014 liryna - * Copyright (c) 2014-2024 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -#region Authors -/* - * Authors: - * - Liryna - */ -#endregion - -using System; -using System.Net.Security; -using System.Security.Authentication; -using System.Security.Cryptography.X509Certificates; - -namespace WebSocketSharp.Net -{ - /// - /// Stores the parameters for an instance used by - /// a client. - /// - public class ClientSslConfiguration - { - #region Private Fields - - private bool _checkCertRevocation; - private LocalCertificateSelectionCallback _clientCertSelectionCallback; - private X509CertificateCollection _clientCerts; - private SslProtocols _enabledSslProtocols; - private RemoteCertificateValidationCallback _serverCertValidationCallback; - private string _targetHost; - - #endregion - - #region Public Constructors - - /// - /// Initializes a new instance of the - /// class with the specified target host name. - /// - /// - /// A that specifies the name of the server that - /// will share a secure connection with the client. - /// - /// - /// is an empty string. - /// - /// - /// is . - /// - public ClientSslConfiguration (string targetHost) - { - if (targetHost == null) - throw new ArgumentNullException ("targetHost"); - - if (targetHost.Length == 0) - throw new ArgumentException ("An empty string.", "targetHost"); - - _targetHost = targetHost; - - _enabledSslProtocols = SslProtocols.None; - } - - /// - /// Initializes a new instance of the - /// class copying from the specified configuration. - /// - /// - /// A from which to copy. - /// - /// - /// is . - /// - public ClientSslConfiguration (ClientSslConfiguration configuration) - { - if (configuration == null) - throw new ArgumentNullException ("configuration"); - - _checkCertRevocation = configuration._checkCertRevocation; - _clientCertSelectionCallback = configuration._clientCertSelectionCallback; - _clientCerts = configuration._clientCerts; - _enabledSslProtocols = configuration._enabledSslProtocols; - _serverCertValidationCallback = configuration._serverCertValidationCallback; - _targetHost = configuration._targetHost; - } - - #endregion - - #region Public Properties - - /// - /// Gets or sets a value indicating whether the certificate revocation - /// list is checked during authentication. - /// - /// - /// - /// true if the certificate revocation list is checked during - /// authentication; otherwise, false. - /// - /// - /// The default value is false. - /// - /// - public bool CheckCertificateRevocation { - get { - return _checkCertRevocation; - } - - set { - _checkCertRevocation = value; - } - } - - /// - /// Gets or sets the collection of the certificates from which to select - /// one to supply to the server. - /// - /// - /// - /// A that contains - /// the certificates from which to select. - /// - /// - /// if not present. - /// - /// - /// The default value is . - /// - /// - public X509CertificateCollection ClientCertificates { - get { - return _clientCerts; - } - - set { - _clientCerts = value; - } - } - - /// - /// Gets or sets the callback used to select the certificate to supply to - /// the server. - /// - /// - /// No certificate is supplied if the callback returns . - /// - /// - /// - /// A delegate. - /// - /// - /// It represents the delegate called when the client selects - /// the certificate. - /// - /// - /// The default value invokes a method that only returns - /// . - /// - /// - public LocalCertificateSelectionCallback ClientCertificateSelectionCallback { - get { - if (_clientCertSelectionCallback == null) - _clientCertSelectionCallback = defaultSelectClientCertificate; - - return _clientCertSelectionCallback; - } - - set { - _clientCertSelectionCallback = value; - } - } - - /// - /// Gets or sets the enabled versions of the SSL/TLS protocols. - /// - /// - /// - /// Any of the enum values. - /// - /// - /// It represents the enabled versions of the SSL/TLS protocols. - /// - /// - /// The default value is . - /// - /// - public SslProtocols EnabledSslProtocols { - get { - return _enabledSslProtocols; - } - - set { - _enabledSslProtocols = value; - } - } - - /// - /// Gets or sets the callback used to validate the certificate supplied by - /// the server. - /// - /// - /// The certificate is valid if the callback returns true. - /// - /// - /// - /// A delegate. - /// - /// - /// It represents the delegate called when the client validates - /// the certificate. - /// - /// - /// The default value invokes a method that only returns true. - /// - /// - public RemoteCertificateValidationCallback ServerCertificateValidationCallback { - get { - if (_serverCertValidationCallback == null) - _serverCertValidationCallback = defaultValidateServerCertificate; - - return _serverCertValidationCallback; - } - - set { - _serverCertValidationCallback = value; - } - } - - /// - /// Gets or sets the target host name. - /// - /// - /// A that represents the name of the server that - /// will share a secure connection with the client. - /// - /// - /// The value specified for a set operation is an empty string. - /// - /// - /// The value specified for a set operation is . - /// - public string TargetHost { - get { - return _targetHost; - } - - set { - if (value == null) - throw new ArgumentNullException ("value"); - - if (value.Length == 0) - throw new ArgumentException ("An empty string.", "value"); - - _targetHost = value; - } - } - - #endregion - - #region Private Methods - - private static X509Certificate defaultSelectClientCertificate ( - object sender, - string targetHost, - X509CertificateCollection clientCertificates, - X509Certificate serverCertificate, - string[] acceptableIssuers - ) - { - return null; - } - - private static bool defaultValidateServerCertificate ( - object sender, - X509Certificate certificate, - X509Chain chain, - SslPolicyErrors sslPolicyErrors - ) - { - return true; - } - - #endregion - } -} diff --git a/websocket-sharp/Net/Cookie.cs b/websocket-sharp/Net/Cookie.cs deleted file mode 100644 index 149b5041e..000000000 --- a/websocket-sharp/Net/Cookie.cs +++ /dev/null @@ -1,1032 +0,0 @@ -#region License -/* - * Cookie.cs - * - * This code is derived from Cookie.cs (System.Net) of Mono - * (http://www.mono-project.com). - * - * The MIT License - * - * Copyright (c) 2004,2009 Novell, Inc. (http://www.novell.com) - * Copyright (c) 2012-2024 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -#region Authors -/* - * Authors: - * - Lawrence Pit - * - Gonzalo Paniagua Javier - * - Daniel Nauck - * - Sebastien Pouliot - */ -#endregion - -using System; -using System.Globalization; -using System.Text; - -namespace WebSocketSharp.Net -{ - /// - /// Provides a set of methods and properties used to manage an HTTP cookie. - /// - /// - /// - /// This class refers to the following specifications: - /// - /// - /// - /// - /// - /// Netscape specification - /// - /// - /// - /// - /// RFC 2109 - /// - /// - /// - /// - /// RFC 2965 - /// - /// - /// - /// - /// RFC 6265 - /// - /// - /// - /// - /// This class cannot be inherited. - /// - /// - [Serializable] - public sealed class Cookie - { - #region Private Fields - - private string _comment; - private Uri _commentUri; - private bool _discard; - private string _domain; - private static readonly int[] _emptyPorts; - private DateTime _expires; - private bool _httpOnly; - private string _name; - private string _path; - private string _port; - private int[] _ports; - private static readonly char[] _reservedCharsForValue; - private string _sameSite; - private bool _secure; - private DateTime _timeStamp; - private string _value; - private int _version; - - #endregion - - #region Static Constructor - - static Cookie () - { - _emptyPorts = new int[0]; - _reservedCharsForValue = new[] { ';', ',' }; - } - - #endregion - - #region Internal Constructors - - internal Cookie () - { - init (String.Empty, String.Empty, String.Empty, String.Empty); - } - - #endregion - - #region Public Constructors - - /// - /// Initializes a new instance of the class with - /// the specified name and value. - /// - /// - /// - /// A that specifies the name of the cookie. - /// - /// - /// The name must be a token defined in - /// - /// RFC 2616. - /// - /// - /// - /// A that specifies the value of the cookie. - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// starts with a dollar sign. - /// - /// - /// -or- - /// - /// - /// contains an invalid character. - /// - /// - /// -or- - /// - /// - /// is a string not enclosed in double quotes - /// although it contains a reserved character. - /// - /// - /// - /// is . - /// - public Cookie (string name, string value) - : this (name, value, String.Empty, String.Empty) - { - } - - /// - /// Initializes a new instance of the class with - /// the specified name, value, and path. - /// - /// - /// - /// A that specifies the name of the cookie. - /// - /// - /// The name must be a token defined in - /// - /// RFC 2616. - /// - /// - /// - /// A that specifies the value of the cookie. - /// - /// - /// A that specifies the value of the Path - /// attribute of the cookie. - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// starts with a dollar sign. - /// - /// - /// -or- - /// - /// - /// contains an invalid character. - /// - /// - /// -or- - /// - /// - /// is a string not enclosed in double quotes - /// although it contains a reserved character. - /// - /// - /// - /// is . - /// - public Cookie (string name, string value, string path) - : this (name, value, path, String.Empty) - { - } - - /// - /// Initializes a new instance of the class with - /// the specified name, value, path, and domain. - /// - /// - /// - /// A that specifies the name of the cookie. - /// - /// - /// The name must be a token defined in - /// - /// RFC 2616. - /// - /// - /// - /// A that specifies the value of the cookie. - /// - /// - /// A that specifies the value of the Path - /// attribute of the cookie. - /// - /// - /// A that specifies the value of the Domain - /// attribute of the cookie. - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// starts with a dollar sign. - /// - /// - /// -or- - /// - /// - /// contains an invalid character. - /// - /// - /// -or- - /// - /// - /// is a string not enclosed in double quotes - /// although it contains a reserved character. - /// - /// - /// - /// is . - /// - public Cookie (string name, string value, string path, string domain) - { - if (name == null) - throw new ArgumentNullException ("name"); - - if (name.Length == 0) - throw new ArgumentException ("An empty string.", "name"); - - if (name[0] == '$') { - var msg = "It starts with a dollar sign."; - - throw new ArgumentException (msg, "name"); - } - - if (!name.IsToken ()) { - var msg = "It contains an invalid character."; - - throw new ArgumentException (msg, "name"); - } - - if (value == null) - value = String.Empty; - - if (value.Contains (_reservedCharsForValue)) { - if (!value.IsEnclosedIn ('"')) { - var msg = "A string not enclosed in double quotes."; - - throw new ArgumentException (msg, "value"); - } - } - - init (name, value, path ?? String.Empty, domain ?? String.Empty); - } - - #endregion - - #region Internal Properties - - internal bool ExactDomain { - get { - return _domain.Length == 0 || _domain[0] != '.'; - } - } - - internal int MaxAge { - get { - if (_expires == DateTime.MinValue) - return 0; - - var expires = _expires.Kind != DateTimeKind.Local - ? _expires.ToLocalTime () - : _expires; - - var span = expires - DateTime.Now; - - return span > TimeSpan.Zero - ? (int) span.TotalSeconds - : 0; - } - - set { - _expires = value > 0 - ? DateTime.Now.AddSeconds ((double) value) - : DateTime.Now; - } - } - - internal int[] Ports { - get { - return _ports ?? _emptyPorts; - } - } - - internal string SameSite { - get { - return _sameSite; - } - - set { - _sameSite = value; - } - } - - #endregion - - #region Public Properties - - /// - /// Gets the value of the Comment attribute of the cookie. - /// - /// - /// - /// A that represents the comment to document - /// intended use of the cookie. - /// - /// - /// if not present. - /// - /// - /// The default value is . - /// - /// - public string Comment { - get { - return _comment; - } - - internal set { - _comment = value; - } - } - - /// - /// Gets the value of the CommentURL attribute of the cookie. - /// - /// - /// - /// A that represents the URI that provides - /// the comment to document intended use of the cookie. - /// - /// - /// if not present. - /// - /// - /// The default value is . - /// - /// - public Uri CommentUri { - get { - return _commentUri; - } - - internal set { - _commentUri = value; - } - } - - /// - /// Gets a value indicating whether the client discards the cookie - /// unconditionally when the client terminates. - /// - /// - /// - /// true if the client discards the cookie unconditionally - /// when the client terminates; otherwise, false. - /// - /// - /// The default value is false. - /// - /// - public bool Discard { - get { - return _discard; - } - - internal set { - _discard = value; - } - } - - /// - /// Gets or sets the value of the Domain attribute of the cookie. - /// - /// - /// - /// A that represents the domain name that - /// the cookie is valid for. - /// - /// - /// An empty string if not necessary. - /// - /// - public string Domain { - get { - return _domain; - } - - set { - _domain = value ?? String.Empty; - } - } - - /// - /// Gets or sets a value indicating whether the cookie has expired. - /// - /// - /// - /// true if the cookie has expired; otherwise, false. - /// - /// - /// The default value is false. - /// - /// - public bool Expired { - get { - return _expires != DateTime.MinValue && _expires <= DateTime.Now; - } - - set { - _expires = value ? DateTime.Now : DateTime.MinValue; - } - } - - /// - /// Gets or sets the value of the Expires attribute of the cookie. - /// - /// - /// - /// A that represents the date and time that - /// the cookie expires on. - /// - /// - /// if not necessary. - /// - /// - /// The default value is . - /// - /// - public DateTime Expires { - get { - return _expires; - } - - set { - _expires = value; - } - } - - /// - /// Gets or sets a value indicating whether non-HTTP APIs can access - /// the cookie. - /// - /// - /// - /// true if non-HTTP APIs cannot access the cookie; otherwise, - /// false. - /// - /// - /// The default value is false. - /// - /// - public bool HttpOnly { - get { - return _httpOnly; - } - - set { - _httpOnly = value; - } - } - - /// - /// Gets or sets the name of the cookie. - /// - /// - /// - /// A that represents the name of the cookie. - /// - /// - /// The name must be a token defined in - /// - /// RFC 2616. - /// - /// - /// - /// - /// The value specified for a set operation is an empty string. - /// - /// - /// -or- - /// - /// - /// The value specified for a set operation starts with a dollar sign. - /// - /// - /// -or- - /// - /// - /// The value specified for a set operation contains an invalid character. - /// - /// - /// - /// The value specified for a set operation is . - /// - public string Name { - get { - return _name; - } - - set { - if (value == null) - throw new ArgumentNullException ("value"); - - if (value.Length == 0) - throw new ArgumentException ("An empty string.", "value"); - - if (value[0] == '$') { - var msg = "It starts with a dollar sign."; - - throw new ArgumentException (msg, "value"); - } - - if (!value.IsToken ()) { - var msg = "It contains an invalid character."; - - throw new ArgumentException (msg, "value"); - } - - _name = value; - } - } - - /// - /// Gets or sets the value of the Path attribute of the cookie. - /// - /// - /// A that represents the subset of URI on - /// the origin server that the cookie applies to. - /// - public string Path { - get { - return _path; - } - - set { - _path = value ?? String.Empty; - } - } - - /// - /// Gets the value of the Port attribute of the cookie. - /// - /// - /// - /// A that represents the list of TCP ports - /// that the cookie applies to. - /// - /// - /// if not present. - /// - /// - /// The default value is . - /// - /// - public string Port { - get { - return _port; - } - - internal set { - int[] ports; - - if (!tryCreatePorts (value, out ports)) - return; - - _ports = ports; - _port = value; - } - } - - /// - /// Gets or sets a value indicating whether the security level of - /// the cookie is secure. - /// - /// - /// When this property is true, the cookie may be included in - /// the request only if the request is transmitted over HTTPS. - /// - /// - /// - /// true if the security level of the cookie is secure; - /// otherwise, false. - /// - /// - /// The default value is false. - /// - /// - public bool Secure { - get { - return _secure; - } - - set { - _secure = value; - } - } - - /// - /// Gets the time when the cookie was issued. - /// - /// - /// A that represents the time when - /// the cookie was issued. - /// - public DateTime TimeStamp { - get { - return _timeStamp; - } - } - - /// - /// Gets or sets the value of the cookie. - /// - /// - /// A that represents the value of the cookie. - /// - /// - /// The value specified for a set operation is a string not enclosed in - /// double quotes although it contains a reserved character. - /// - public string Value { - get { - return _value; - } - - set { - if (value == null) - value = String.Empty; - - if (value.Contains (_reservedCharsForValue)) { - if (!value.IsEnclosedIn ('"')) { - var msg = "A string not enclosed in double quotes."; - - throw new ArgumentException (msg, "value"); - } - } - - _value = value; - } - } - - /// - /// Gets the value of the Version attribute of the cookie. - /// - /// - /// - /// An that represents the version of HTTP state - /// management that the cookie conforms to. - /// - /// - /// 0 or 1. - /// - /// - /// 0 if not present. - /// - /// - /// The default value is 0. - /// - /// - public int Version { - get { - return _version; - } - - internal set { - if (value < 0 || value > 1) - return; - - _version = value; - } - } - - #endregion - - #region Private Methods - - private static int hash (int i, int j, int k, int l, int m) - { - return i - ^ (j << 13 | j >> 19) - ^ (k << 26 | k >> 6) - ^ (l << 7 | l >> 25) - ^ (m << 20 | m >> 12); - } - - private void init (string name, string value, string path, string domain) - { - _name = name; - _value = value; - _path = path; - _domain = domain; - - _expires = DateTime.MinValue; - _timeStamp = DateTime.Now; - } - - private string toResponseStringVersion0 () - { - var buff = new StringBuilder (64); - - buff.AppendFormat ("{0}={1}", _name, _value); - - if (_expires != DateTime.MinValue) { - var expires = _expires - .ToUniversalTime () - .ToString ( - "ddd, dd'-'MMM'-'yyyy HH':'mm':'ss 'GMT'", - CultureInfo.CreateSpecificCulture ("en-US") - ); - - buff.AppendFormat ("; Expires={0}", expires); - } - - if (!_path.IsNullOrEmpty ()) - buff.AppendFormat ("; Path={0}", _path); - - if (!_domain.IsNullOrEmpty ()) - buff.AppendFormat ("; Domain={0}", _domain); - - if (!_sameSite.IsNullOrEmpty ()) - buff.AppendFormat ("; SameSite={0}", _sameSite); - - if (_secure) - buff.Append ("; Secure"); - - if (_httpOnly) - buff.Append ("; HttpOnly"); - - return buff.ToString (); - } - - private string toResponseStringVersion1 () - { - var buff = new StringBuilder (64); - - buff.AppendFormat ("{0}={1}; Version={2}", _name, _value, _version); - - if (_expires != DateTime.MinValue) - buff.AppendFormat ("; Max-Age={0}", MaxAge); - - if (!_path.IsNullOrEmpty ()) - buff.AppendFormat ("; Path={0}", _path); - - if (!_domain.IsNullOrEmpty ()) - buff.AppendFormat ("; Domain={0}", _domain); - - if (_port != null) { - if (_port != "\"\"") - buff.AppendFormat ("; Port={0}", _port); - else - buff.Append ("; Port"); - } - - if (_comment != null) { - var comment = HttpUtility.UrlEncode (_comment); - - buff.AppendFormat ("; Comment={0}", comment); - } - - if (_commentUri != null) { - var url = _commentUri.OriginalString; - - buff.AppendFormat ( - "; CommentURL={0}", - !url.IsToken () ? url.Quote () : url - ); - } - - if (_discard) - buff.Append ("; Discard"); - - if (_secure) - buff.Append ("; Secure"); - - return buff.ToString (); - } - - private static bool tryCreatePorts (string value, out int[] result) - { - result = null; - - var arr = value.Trim ('"').Split (','); - var len = arr.Length; - var res = new int[len]; - - for (var i = 0; i < len; i++) { - var s = arr[i].Trim (); - - if (s.Length == 0) { - res[i] = Int32.MinValue; - - continue; - } - - if (!Int32.TryParse (s, out res[i])) - return false; - } - - result = res; - - return true; - } - - #endregion - - #region Internal Methods - - internal bool EqualsWithoutValue (Cookie cookie) - { - var caseSensitive = StringComparison.InvariantCulture; - var caseInsensitive = StringComparison.InvariantCultureIgnoreCase; - - return _name.Equals (cookie._name, caseInsensitive) - && _path.Equals (cookie._path, caseSensitive) - && _domain.Equals (cookie._domain, caseInsensitive) - && _version == cookie._version; - } - - internal bool EqualsWithoutValueAndVersion (Cookie cookie) - { - var caseSensitive = StringComparison.InvariantCulture; - var caseInsensitive = StringComparison.InvariantCultureIgnoreCase; - - return _name.Equals (cookie._name, caseInsensitive) - && _path.Equals (cookie._path, caseSensitive) - && _domain.Equals (cookie._domain, caseInsensitive); - } - - internal string ToRequestString (Uri uri) - { - if (_name.Length == 0) - return String.Empty; - - if (_version == 0) - return String.Format ("{0}={1}", _name, _value); - - var buff = new StringBuilder (64); - - buff.AppendFormat ("$Version={0}; {1}={2}", _version, _name, _value); - - if (!_path.IsNullOrEmpty ()) - buff.AppendFormat ("; $Path={0}", _path); - else if (uri != null) - buff.AppendFormat ("; $Path={0}", uri.GetAbsolutePath ()); - else - buff.Append ("; $Path=/"); - - if (!_domain.IsNullOrEmpty ()) { - if (uri == null || uri.Host != _domain) - buff.AppendFormat ("; $Domain={0}", _domain); - } - - if (_port != null) { - if (_port != "\"\"") - buff.AppendFormat ("; $Port={0}", _port); - else - buff.Append ("; $Port"); - } - - return buff.ToString (); - } - - internal string ToResponseString () - { - if (_name.Length == 0) - return String.Empty; - - if (_version == 0) - return toResponseStringVersion0 (); - - return toResponseStringVersion1 (); - } - - internal static bool TryCreate ( - string name, - string value, - out Cookie result - ) - { - result = null; - - try { - result = new Cookie (name, value); - } - catch { - return false; - } - - return true; - } - - #endregion - - #region Public Methods - - /// - /// Determines whether the current cookie instance is equal to - /// the specified instance. - /// - /// - /// - /// An instance to compare with - /// the current cookie instance. - /// - /// - /// An reference to a instance. - /// - /// - /// - /// true if the current cookie instance is equal to - /// ; otherwise, false. - /// - public override bool Equals (object comparand) - { - var cookie = comparand as Cookie; - - if (cookie == null) - return false; - - var caseSensitive = StringComparison.InvariantCulture; - var caseInsensitive = StringComparison.InvariantCultureIgnoreCase; - - return _name.Equals (cookie._name, caseInsensitive) - && _value.Equals (cookie._value, caseSensitive) - && _path.Equals (cookie._path, caseSensitive) - && _domain.Equals (cookie._domain, caseInsensitive) - && _version == cookie._version; - } - - /// - /// Gets a hash code for the current cookie instance. - /// - /// - /// An that represents the hash code. - /// - public override int GetHashCode () - { - var i = StringComparer.InvariantCultureIgnoreCase.GetHashCode (_name); - var j = _value.GetHashCode (); - var k = _path.GetHashCode (); - var l = StringComparer.InvariantCultureIgnoreCase.GetHashCode (_domain); - var m = _version; - - return hash (i, j, k, l, m); - } - - /// - /// Returns a string that represents the current cookie instance. - /// - /// - /// A that is suitable for the Cookie request header. - /// - public override string ToString () - { - return ToRequestString (null); - } - - #endregion - } -} diff --git a/websocket-sharp/Net/CookieCollection.cs b/websocket-sharp/Net/CookieCollection.cs deleted file mode 100644 index 02e065580..000000000 --- a/websocket-sharp/Net/CookieCollection.cs +++ /dev/null @@ -1,883 +0,0 @@ -#region License -/* - * CookieCollection.cs - * - * This code is derived from CookieCollection.cs (System.Net) of Mono - * (http://www.mono-project.com). - * - * The MIT License - * - * Copyright (c) 2004,2009 Novell, Inc. (http://www.novell.com) - * Copyright (c) 2012-2025 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -#region Authors -/* - * Authors: - * - Lawrence Pit - * - Gonzalo Paniagua Javier - * - Sebastien Pouliot - */ -#endregion - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Globalization; -using System.Text; - -namespace WebSocketSharp.Net -{ - /// - /// Provides a collection of instances of the class. - /// - [Serializable] - public class CookieCollection : ICollection - { - #region Private Fields - - private List _list; - private bool _readOnly; - private object _syncRoot; - - #endregion - - #region Public Constructors - - /// - /// Initializes a new instance of the class. - /// - public CookieCollection () - { - _list = new List (); - _syncRoot = ((ICollection) _list).SyncRoot; - } - - #endregion - - #region Internal Properties - - internal IList List { - get { - return _list; - } - } - - internal bool ReadOnly { - get { - return _readOnly; - } - - set { - _readOnly = value; - } - } - - internal ICollection SortedList { - get { - var list = new List (_list); - - if (list.Count > 1) - list.Sort (compareForSortedList); - - return list; - } - } - - #endregion - - #region Public Properties - - /// - /// Gets the number of cookies in the collection. - /// - /// - /// An that represents the number of cookies in - /// the collection. - /// - public int Count { - get { - return _list.Count; - } - } - - /// - /// Gets a value indicating whether the collection is read-only. - /// - /// - /// - /// true if the collection is read-only; otherwise, false. - /// - /// - /// The default value is false. - /// - /// - public bool IsReadOnly { - get { - return _readOnly; - } - } - - /// - /// Gets a value indicating whether the access to the collection is - /// thread safe. - /// - /// - /// - /// true if the access to the collection is thread safe; - /// otherwise, false. - /// - /// - /// The default value is false. - /// - /// - public bool IsSynchronized { - get { - return false; - } - } - - /// - /// Gets the cookie at the specified index from the collection. - /// - /// - /// A at the specified index in the collection. - /// - /// - /// An that specifies the zero-based index of the cookie - /// to find. - /// - /// - /// is out of allowable range for the collection. - /// - public Cookie this[int index] { - get { - if (index < 0 || index >= _list.Count) - throw new ArgumentOutOfRangeException ("index"); - - return _list[index]; - } - } - - /// - /// Gets the cookie with the specified name from the collection. - /// - /// - /// - /// A with the specified name in the collection. - /// - /// - /// if not found. - /// - /// - /// - /// A that specifies the name of the cookie to find. - /// - /// - /// is . - /// - public Cookie this[string name] { - get { - if (name == null) - throw new ArgumentNullException ("name"); - - var caseInsensitive = StringComparison.InvariantCultureIgnoreCase; - - foreach (var cookie in SortedList) { - if (cookie.Name.Equals (name, caseInsensitive)) - return cookie; - } - - return null; - } - } - - /// - /// Gets an object used to synchronize access to the collection. - /// - /// - /// An used to synchronize access to the collection. - /// - public object SyncRoot { - get { - return _syncRoot; - } - } - - #endregion - - #region Private Methods - - private void add (Cookie cookie) - { - var idx = search (cookie); - - if (idx == -1) { - _list.Add (cookie); - - return; - } - - _list[idx] = cookie; - } - - private static int compareForSort (Cookie x, Cookie y) - { - return (x.Name.Length + x.Value.Length) - - (y.Name.Length + y.Value.Length); - } - - private static int compareForSortedList (Cookie x, Cookie y) - { - var ret = x.Version - y.Version; - - if (ret != 0) - return ret; - - ret = x.Name.CompareTo (y.Name); - - if (ret != 0) - return ret; - - return y.Path.Length - x.Path.Length; - } - - private static CookieCollection parseRequest (string value) - { - var ret = new CookieCollection (); - - Cookie cookie = null; - var ver = 0; - var caseInsensitive = StringComparison.InvariantCultureIgnoreCase; - - var pairs = value.SplitHeaderValue (',', ';').ToList (); - - for (var i = 0; i < pairs.Count; i++) { - var pair = pairs[i].Trim (); - - if (pair.Length == 0) - continue; - - var idx = pair.IndexOf ('='); - - if (idx == -1) { - if (cookie == null) - continue; - - if (pair.Equals ("$port", caseInsensitive)) { - cookie.Port = "\"\""; - - continue; - } - - continue; - } - - if (idx == 0) { - if (cookie != null) { - ret.add (cookie); - - cookie = null; - } - - continue; - } - - var name = pair.Substring (0, idx).TrimEnd (' '); - var val = idx < pair.Length - 1 - ? pair.Substring (idx + 1).TrimStart (' ') - : String.Empty; - - if (name.Equals ("$version", caseInsensitive)) { - if (val.Length == 0) - continue; - - var s = val.Unquote (); - - int num; - - if (!Int32.TryParse (s, out num)) - continue; - - ver = num; - - continue; - } - - if (name.Equals ("$path", caseInsensitive)) { - if (cookie == null) - continue; - - if (val.Length == 0) - continue; - - cookie.Path = val; - - continue; - } - - if (name.Equals ("$domain", caseInsensitive)) { - if (cookie == null) - continue; - - if (val.Length == 0) - continue; - - cookie.Domain = val; - - continue; - } - - if (name.Equals ("$port", caseInsensitive)) { - if (cookie == null) - continue; - - if (val.Length == 0) - continue; - - cookie.Port = val; - - continue; - } - - if (cookie != null) - ret.add (cookie); - - if (!Cookie.TryCreate (name, val, out cookie)) - continue; - - if (ver != 0) - cookie.Version = ver; - } - - if (cookie != null) - ret.add (cookie); - - return ret; - } - - private static CookieCollection parseResponse (string value) - { - var ret = new CookieCollection (); - - Cookie cookie = null; - var caseInsensitive = StringComparison.InvariantCultureIgnoreCase; - - var pairs = value.SplitHeaderValue (',', ';').ToList (); - - for (var i = 0; i < pairs.Count; i++) { - var pair = pairs[i].Trim (); - - if (pair.Length == 0) - continue; - - var idx = pair.IndexOf ('='); - - if (idx == -1) { - if (cookie == null) - continue; - - if (pair.Equals ("port", caseInsensitive)) { - cookie.Port = "\"\""; - - continue; - } - - if (pair.Equals ("discard", caseInsensitive)) { - cookie.Discard = true; - - continue; - } - - if (pair.Equals ("secure", caseInsensitive)) { - cookie.Secure = true; - - continue; - } - - if (pair.Equals ("httponly", caseInsensitive)) { - cookie.HttpOnly = true; - - continue; - } - - continue; - } - - if (idx == 0) { - if (cookie != null) { - ret.add (cookie); - - cookie = null; - } - - continue; - } - - var name = pair.Substring (0, idx).TrimEnd (' '); - var val = idx < pair.Length - 1 - ? pair.Substring (idx + 1).TrimStart (' ') - : String.Empty; - - if (name.Equals ("version", caseInsensitive)) { - if (cookie == null) - continue; - - if (val.Length == 0) - continue; - - var s = val.Unquote (); - - int num; - - if (!Int32.TryParse (s, out num)) - continue; - - cookie.Version = num; - - continue; - } - - if (name.Equals ("expires", caseInsensitive)) { - if (val.Length == 0) - continue; - - if (i == pairs.Count - 1) - break; - - i++; - - if (cookie == null) - continue; - - if (cookie.Expires != DateTime.MinValue) - continue; - - var buff = new StringBuilder (val, 32); - - buff.AppendFormat (", {0}", pairs[i].Trim ()); - - var s = buff.ToString (); - var fmts = new[] { "ddd, dd'-'MMM'-'yyyy HH':'mm':'ss 'GMT'", "r" }; - var provider = CultureInfo.CreateSpecificCulture ("en-US"); - var style = DateTimeStyles.AdjustToUniversal - | DateTimeStyles.AssumeUniversal; - - DateTime expires; - - var done = DateTime.TryParseExact ( - s, - fmts, - provider, - style, - out expires - ); - - if (!done) - continue; - - cookie.Expires = expires.ToLocalTime (); - - continue; - } - - if (name.Equals ("max-age", caseInsensitive)) { - if (cookie == null) - continue; - - if (val.Length == 0) - continue; - - var s = val.Unquote (); - - int maxAge; - - if (!Int32.TryParse (s, out maxAge)) - continue; - - cookie.MaxAge = maxAge; - - continue; - } - - if (name.Equals ("path", caseInsensitive)) { - if (cookie == null) - continue; - - if (val.Length == 0) - continue; - - cookie.Path = val; - - continue; - } - - if (name.Equals ("domain", caseInsensitive)) { - if (cookie == null) - continue; - - if (val.Length == 0) - continue; - - cookie.Domain = val; - - continue; - } - - if (name.Equals ("port", caseInsensitive)) { - if (cookie == null) - continue; - - if (val.Length == 0) - continue; - - cookie.Port = val; - - continue; - } - - if (name.Equals ("comment", caseInsensitive)) { - if (cookie == null) - continue; - - if (val.Length == 0) - continue; - - cookie.Comment = urlDecode (val, Encoding.UTF8); - - continue; - } - - if (name.Equals ("commenturl", caseInsensitive)) { - if (cookie == null) - continue; - - if (val.Length == 0) - continue; - - cookie.CommentUri = val.Unquote ().ToUri (); - - continue; - } - - if (name.Equals ("samesite", caseInsensitive)) { - if (cookie == null) - continue; - - if (val.Length == 0) - continue; - - cookie.SameSite = val.Unquote (); - - continue; - } - - if (cookie != null) - ret.add (cookie); - - Cookie.TryCreate (name, val, out cookie); - } - - if (cookie != null) - ret.add (cookie); - - return ret; - } - - private int search (Cookie cookie) - { - for (var i = _list.Count - 1; i >= 0; i--) { - if (_list[i].EqualsWithoutValue (cookie)) - return i; - } - - return -1; - } - - private static string urlDecode (string s, Encoding encoding) - { - if (s.IndexOfAny (new[] { '%', '+' }) == -1) - return s; - - try { - return HttpUtility.UrlDecode (s, encoding); - } - catch { - return null; - } - } - - #endregion - - #region Internal Methods - - internal static CookieCollection Parse (string value, bool response) - { - try { - return response ? parseResponse (value) : parseRequest (value); - } - catch (Exception ex) { - throw new CookieException ("It could not be parsed.", ex); - } - } - - internal void SetOrRemove (Cookie cookie) - { - var idx = search (cookie); - - if (idx == -1) { - if (cookie.Expired) - return; - - _list.Add (cookie); - - return; - } - - if (cookie.Expired) { - _list.RemoveAt (idx); - - return; - } - - _list[idx] = cookie; - } - - internal void SetOrRemove (CookieCollection cookies) - { - foreach (var cookie in cookies._list) - SetOrRemove (cookie); - } - - internal void Sort () - { - if (_list.Count < 2) - return; - - _list.Sort (compareForSort); - } - - #endregion - - #region Public Methods - - /// - /// Adds the specified cookie to the collection. - /// - /// - /// A to add. - /// - /// - /// is . - /// - /// - /// This method is not available if the collection is read-only. - /// - public void Add (Cookie cookie) - { - if (_readOnly) { - var msg = "The collection is read-only."; - - throw new InvalidOperationException (msg); - } - - if (cookie == null) - throw new ArgumentNullException ("cookie"); - - add (cookie); - } - - /// - /// Adds the specified cookies to the collection. - /// - /// - /// A that contains the cookies to add. - /// - /// - /// is . - /// - /// - /// This method is not available if the collection is read-only. - /// - public void Add (CookieCollection cookies) - { - if (_readOnly) { - var msg = "The collection is read-only."; - - throw new InvalidOperationException (msg); - } - - if (cookies == null) - throw new ArgumentNullException ("cookies"); - - foreach (var cookie in cookies._list) - add (cookie); - } - - /// - /// Removes all cookies from the collection. - /// - /// - /// This method is not available if the collection is read-only. - /// - public void Clear () - { - if (_readOnly) { - var msg = "The collection is read-only."; - - throw new InvalidOperationException (msg); - } - - _list.Clear (); - } - - /// - /// Determines whether the collection contains the specified cookie. - /// - /// - /// true if the cookie is found in the collection; otherwise, - /// false. - /// - /// - /// A to find. - /// - /// - /// is . - /// - public bool Contains (Cookie cookie) - { - if (cookie == null) - throw new ArgumentNullException ("cookie"); - - return search (cookie) > -1; - } - - /// - /// Copies the elements of the collection to the specified array, - /// starting at the specified index. - /// - /// - /// An array of that specifies the destination of - /// the elements copied from the collection. - /// - /// - /// An that specifies the zero-based index in - /// the array at which copying starts. - /// - /// - /// The space from to the end of - /// is not enough to copy to. - /// - /// - /// is . - /// - /// - /// is less than zero. - /// - public void CopyTo (Cookie[] array, int index) - { - if (array == null) - throw new ArgumentNullException ("array"); - - if (index < 0) { - var msg = "Less than zero."; - - throw new ArgumentOutOfRangeException ("index", msg); - } - - if (array.Length - index < _list.Count) { - var msg = "The available space of the array is not enough to copy to."; - - throw new ArgumentException (msg); - } - - _list.CopyTo (array, index); - } - - /// - /// Gets the enumerator that iterates through the collection. - /// - /// - /// An - /// instance that can be used to iterate through the collection. - /// - public IEnumerator GetEnumerator () - { - return _list.GetEnumerator (); - } - - /// - /// Removes the specified cookie from the collection. - /// - /// - /// true if the cookie is successfully found and removed; - /// otherwise, false. - /// - /// - /// A to remove. - /// - /// - /// is . - /// - /// - /// This method is not available if the collection is read-only. - /// - public bool Remove (Cookie cookie) - { - if (_readOnly) { - var msg = "The collection is read-only."; - - throw new InvalidOperationException (msg); - } - - if (cookie == null) - throw new ArgumentNullException ("cookie"); - - var idx = search (cookie); - - if (idx == -1) - return false; - - _list.RemoveAt (idx); - - return true; - } - - #endregion - - #region Explicit Interface Implementations - - /// - /// Gets the enumerator that iterates through the collection. - /// - /// - /// An instance that can be used to iterate - /// through the collection. - /// - IEnumerator IEnumerable.GetEnumerator () - { - return _list.GetEnumerator (); - } - - #endregion - } -} diff --git a/websocket-sharp/Net/CookieException.cs b/websocket-sharp/Net/CookieException.cs deleted file mode 100644 index 3c9ab3f9e..000000000 --- a/websocket-sharp/Net/CookieException.cs +++ /dev/null @@ -1,169 +0,0 @@ -#region License -/* - * CookieException.cs - * - * This code is derived from CookieException.cs (System.Net) of Mono - * (http://www.mono-project.com). - * - * The MIT License - * - * Copyright (c) 2012-2024 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -#region Authors -/* - * Authors: - * - Lawrence Pit - */ -#endregion - -using System; -using System.Runtime.Serialization; -using System.Security.Permissions; - -namespace WebSocketSharp.Net -{ - /// - /// The exception that is thrown when a gets an error. - /// - [Serializable] - public class CookieException : FormatException, ISerializable - { - #region Internal Constructors - - internal CookieException (string message) - : base (message) - { - } - - internal CookieException (string message, Exception innerException) - : base (message, innerException) - { - } - - #endregion - - #region Protected Constructors - - /// - /// Initializes a new instance of the class - /// with the specified serialized data. - /// - /// - /// A that contains the serialized - /// object data. - /// - /// - /// A that specifies the source for - /// the deserialization. - /// - /// - /// is . - /// - protected CookieException ( - SerializationInfo serializationInfo, - StreamingContext streamingContext - ) - : base (serializationInfo, streamingContext) - { - } - - #endregion - - #region Public Constructors - - /// - /// Initializes a new instance of the class. - /// - public CookieException () - : base () - { - } - - #endregion - - #region Public Methods - - /// - /// Populates the specified instance with - /// the data needed to serialize the current instance. - /// - /// - /// A that holds the serialized object data. - /// - /// - /// A that specifies the destination for - /// the serialization. - /// - /// - /// is . - /// - [ - SecurityPermission ( - SecurityAction.LinkDemand, - Flags = SecurityPermissionFlag.SerializationFormatter - ) - ] - public override void GetObjectData ( - SerializationInfo serializationInfo, - StreamingContext streamingContext - ) - { - base.GetObjectData (serializationInfo, streamingContext); - } - - #endregion - - #region Explicit Interface Implementation - - /// - /// Populates the specified instance with - /// the data needed to serialize the current instance. - /// - /// - /// A that holds the serialized object data. - /// - /// - /// A that specifies the destination for - /// the serialization. - /// - /// - /// is . - /// - [ - SecurityPermission ( - SecurityAction.LinkDemand, - Flags = SecurityPermissionFlag.SerializationFormatter, - SerializationFormatter = true - ) - ] - void ISerializable.GetObjectData ( - SerializationInfo serializationInfo, - StreamingContext streamingContext - ) - { - base.GetObjectData (serializationInfo, streamingContext); - } - - #endregion - } -} diff --git a/websocket-sharp/Net/EndPointListener.cs b/websocket-sharp/Net/EndPointListener.cs deleted file mode 100644 index 3c8b3653a..000000000 --- a/websocket-sharp/Net/EndPointListener.cs +++ /dev/null @@ -1,604 +0,0 @@ -#region License -/* - * EndPointListener.cs - * - * This code is derived from EndPointListener.cs (System.Net) of Mono - * (http://www.mono-project.com). - * - * The MIT License - * - * Copyright (c) 2005 Novell, Inc. (http://www.novell.com) - * Copyright (c) 2012-2023 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -#region Authors -/* - * Authors: - * - Gonzalo Paniagua Javier - */ -#endregion - -#region Contributors -/* - * Contributors: - * - Liryna - * - Nicholas Devenish - */ -#endregion - -using System; -using System.Collections; -using System.Collections.Generic; -using System.IO; -using System.Net; -using System.Net.Sockets; -using System.Security.Cryptography; -using System.Security.Cryptography.X509Certificates; -using System.Threading; - -namespace WebSocketSharp.Net -{ - internal sealed class EndPointListener - { - #region Private Fields - - private List _all; // host == '+' - private Dictionary _connections; - private object _connectionsSync; - private static readonly string _defaultCertFolderPath; - private IPEndPoint _endpoint; - private List _prefixes; - private bool _secure; - private Socket _socket; - private ServerSslConfiguration _sslConfig; - private List _unhandled; // host == '*' - - #endregion - - #region Static Constructor - - static EndPointListener () - { - _defaultCertFolderPath = Environment.GetFolderPath ( - Environment.SpecialFolder.ApplicationData - ); - } - - #endregion - - #region Internal Constructors - - internal EndPointListener ( - IPEndPoint endpoint, - bool secure, - string certificateFolderPath, - ServerSslConfiguration sslConfig, - bool reuseAddress - ) - { - _endpoint = endpoint; - - if (secure) { - var cert = getCertificate ( - endpoint.Port, - certificateFolderPath, - sslConfig.ServerCertificate - ); - - if (cert == null) { - var msg = "No server certificate could be found."; - - throw new ArgumentException (msg); - } - - _secure = true; - _sslConfig = new ServerSslConfiguration (sslConfig); - _sslConfig.ServerCertificate = cert; - } - - _prefixes = new List (); - _connections = new Dictionary (); - _connectionsSync = ((ICollection) _connections).SyncRoot; - - _socket = new Socket ( - endpoint.Address.AddressFamily, - SocketType.Stream, - ProtocolType.Tcp - ); - - if (reuseAddress) { - _socket.SetSocketOption ( - SocketOptionLevel.Socket, - SocketOptionName.ReuseAddress, - true - ); - } - - _socket.Bind (endpoint); - _socket.Listen (500); - _socket.BeginAccept (onAccept, this); - } - - #endregion - - #region Public Properties - - public IPAddress Address { - get { - return _endpoint.Address; - } - } - - public bool IsSecure { - get { - return _secure; - } - } - - public int Port { - get { - return _endpoint.Port; - } - } - - public ServerSslConfiguration SslConfiguration { - get { - return _sslConfig; - } - } - - #endregion - - #region Private Methods - - private static void addSpecial ( - List prefixes, - HttpListenerPrefix prefix - ) - { - var path = prefix.Path; - - foreach (var pref in prefixes) { - if (pref.Path == path) { - var msg = "The prefix is already in use."; - - throw new HttpListenerException (87, msg); - } - } - - prefixes.Add (prefix); - } - - private void clearConnections () - { - HttpConnection[] conns = null; - - lock (_connectionsSync) { - var cnt = _connections.Count; - - if (cnt == 0) - return; - - conns = new HttpConnection[cnt]; - - _connections.Values.CopyTo (conns, 0); - _connections.Clear (); - } - - foreach (var conn in conns) - conn.Close (true); - } - - private static RSACryptoServiceProvider createRSAFromFile (string path) - { - var rsa = new RSACryptoServiceProvider (); - - var key = File.ReadAllBytes (path); - - rsa.ImportCspBlob (key); - - return rsa; - } - - private static X509Certificate2 getCertificate ( - int port, - string folderPath, - X509Certificate2 defaultCertificate - ) - { - if (folderPath == null || folderPath.Length == 0) - folderPath = _defaultCertFolderPath; - - try { - var cer = Path.Combine (folderPath, String.Format ("{0}.cer", port)); - var key = Path.Combine (folderPath, String.Format ("{0}.key", port)); - - var exists = File.Exists (cer) && File.Exists (key); - - if (!exists) - return defaultCertificate; - - var cert = new X509Certificate2 (cer); - - cert.PrivateKey = createRSAFromFile (key); - - return cert; - } - catch { - return defaultCertificate; - } - } - - private void leaveIfNoPrefix () - { - if (_prefixes.Count > 0) - return; - - var prefs = _unhandled; - - if (prefs != null && prefs.Count > 0) - return; - - prefs = _all; - - if (prefs != null && prefs.Count > 0) - return; - - Close (); - } - - private static void onAccept (IAsyncResult asyncResult) - { - var lsnr = (EndPointListener) asyncResult.AsyncState; - - Socket sock = null; - - try { - sock = lsnr._socket.EndAccept (asyncResult); - } - catch (ObjectDisposedException) { - return; - } - catch (Exception) { - // TODO: Logging. - } - - try { - lsnr._socket.BeginAccept (onAccept, lsnr); - } - catch (Exception) { - // TODO: Logging. - - if (sock != null) - sock.Close (); - - return; - } - - if (sock == null) - return; - - processAccepted (sock, lsnr); - } - - private static void processAccepted ( - Socket socket, - EndPointListener listener - ) - { - HttpConnection conn = null; - - try { - conn = new HttpConnection (socket, listener); - } - catch (Exception) { - // TODO: Logging. - - socket.Close (); - - return; - } - - lock (listener._connectionsSync) - listener._connections.Add (conn, conn); - - conn.BeginReadRequest (); - } - - private static bool removeSpecial ( - List prefixes, - HttpListenerPrefix prefix - ) - { - var path = prefix.Path; - var cnt = prefixes.Count; - - for (var i = 0; i < cnt; i++) { - if (prefixes[i].Path == path) { - prefixes.RemoveAt (i); - - return true; - } - } - - return false; - } - - private static HttpListener searchHttpListenerFromSpecial ( - string path, - List prefixes - ) - { - if (prefixes == null) - return null; - - HttpListener ret = null; - - var bestLen = -1; - - foreach (var pref in prefixes) { - var prefPath = pref.Path; - var len = prefPath.Length; - - if (len < bestLen) - continue; - - var match = path.StartsWith (prefPath, StringComparison.Ordinal); - - if (!match) - continue; - - bestLen = len; - ret = pref.Listener; - } - - return ret; - } - - #endregion - - #region Internal Methods - - internal static bool CertificateExists (int port, string folderPath) - { - if (folderPath == null || folderPath.Length == 0) - folderPath = _defaultCertFolderPath; - - var cer = Path.Combine (folderPath, String.Format ("{0}.cer", port)); - var key = Path.Combine (folderPath, String.Format ("{0}.key", port)); - - return File.Exists (cer) && File.Exists (key); - } - - internal void RemoveConnection (HttpConnection connection) - { - lock (_connectionsSync) - _connections.Remove (connection); - } - - internal bool TrySearchHttpListener (Uri uri, out HttpListener listener) - { - listener = null; - - if (uri == null) - return false; - - var host = uri.Host; - var dns = Uri.CheckHostName (host) == UriHostNameType.Dns; - var port = uri.Port.ToString (); - var path = HttpUtility.UrlDecode (uri.AbsolutePath); - - if (path[path.Length - 1] != '/') - path += "/"; - - if (host != null && host.Length > 0) { - var prefs = _prefixes; - var bestLen = -1; - - foreach (var pref in prefs) { - if (dns) { - var prefHost = pref.Host; - var prefDns = Uri.CheckHostName (prefHost) == UriHostNameType.Dns; - - if (prefDns) { - if (prefHost != host) - continue; - } - } - - if (pref.Port != port) - continue; - - var prefPath = pref.Path; - var len = prefPath.Length; - - if (len < bestLen) - continue; - - var match = path.StartsWith (prefPath, StringComparison.Ordinal); - - if (!match) - continue; - - bestLen = len; - listener = pref.Listener; - } - - if (bestLen != -1) - return true; - } - - listener = searchHttpListenerFromSpecial (path, _unhandled); - - if (listener != null) - return true; - - listener = searchHttpListenerFromSpecial (path, _all); - - return listener != null; - } - - #endregion - - #region Public Methods - - public void AddPrefix (HttpListenerPrefix prefix) - { - List current, future; - - if (prefix.Host == "*") { - do { - current = _unhandled; - future = current != null - ? new List (current) - : new List (); - - addSpecial (future, prefix); - } - while ( - Interlocked.CompareExchange (ref _unhandled, future, current) - != current - ); - - return; - } - - if (prefix.Host == "+") { - do { - current = _all; - future = current != null - ? new List (current) - : new List (); - - addSpecial (future, prefix); - } - while ( - Interlocked.CompareExchange (ref _all, future, current) - != current - ); - - return; - } - - do { - current = _prefixes; - - var idx = current.IndexOf (prefix); - - if (idx > -1) { - if (current[idx].Listener != prefix.Listener) { - var fmt = "There is another listener for {0}."; - var msg = String.Format (fmt, prefix); - - throw new HttpListenerException (87, msg); - } - - return; - } - - future = new List (current); - - future.Add (prefix); - } - while ( - Interlocked.CompareExchange (ref _prefixes, future, current) - != current - ); - } - - public void Close () - { - _socket.Close (); - - clearConnections (); - EndPointManager.RemoveEndPoint (_endpoint); - } - - public void RemovePrefix (HttpListenerPrefix prefix) - { - List current, future; - - if (prefix.Host == "*") { - do { - current = _unhandled; - - if (current == null) - break; - - future = new List (current); - - if (!removeSpecial (future, prefix)) - break; - } - while ( - Interlocked.CompareExchange (ref _unhandled, future, current) - != current - ); - - leaveIfNoPrefix (); - - return; - } - - if (prefix.Host == "+") { - do { - current = _all; - - if (current == null) - break; - - future = new List (current); - - if (!removeSpecial (future, prefix)) - break; - } - while ( - Interlocked.CompareExchange (ref _all, future, current) - != current - ); - - leaveIfNoPrefix (); - - return; - } - - do { - current = _prefixes; - - if (!current.Contains (prefix)) - break; - - future = new List (current); - - future.Remove (prefix); - } - while ( - Interlocked.CompareExchange (ref _prefixes, future, current) - != current - ); - - leaveIfNoPrefix (); - } - - #endregion - } -} diff --git a/websocket-sharp/Net/EndPointManager.cs b/websocket-sharp/Net/EndPointManager.cs deleted file mode 100644 index ac4582b24..000000000 --- a/websocket-sharp/Net/EndPointManager.cs +++ /dev/null @@ -1,261 +0,0 @@ -#region License -/* - * EndPointManager.cs - * - * This code is derived from EndPointManager.cs (System.Net) of Mono - * (http://www.mono-project.com). - * - * The MIT License - * - * Copyright (c) 2005 Novell, Inc. (http://www.novell.com) - * Copyright (c) 2012-2020 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -#region Authors -/* - * Authors: - * - Gonzalo Paniagua Javier - */ -#endregion - -#region Contributors -/* - * Contributors: - * - Liryna - */ -#endregion - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Net; - -namespace WebSocketSharp.Net -{ - internal sealed class EndPointManager - { - #region Private Fields - - private static readonly Dictionary _endpoints; - - #endregion - - #region Static Constructor - - static EndPointManager () - { - _endpoints = new Dictionary (); - } - - #endregion - - #region Private Constructors - - private EndPointManager () - { - } - - #endregion - - #region Private Methods - - private static void addPrefix (string uriPrefix, HttpListener listener) - { - var pref = new HttpListenerPrefix (uriPrefix, listener); - - var addr = convertToIPAddress (pref.Host); - - if (addr == null) { - var msg = "The URI prefix includes an invalid host."; - - throw new HttpListenerException (87, msg); - } - - if (!addr.IsLocal ()) { - var msg = "The URI prefix includes an invalid host."; - - throw new HttpListenerException (87, msg); - } - - int port; - - if (!Int32.TryParse (pref.Port, out port)) { - var msg = "The URI prefix includes an invalid port."; - - throw new HttpListenerException (87, msg); - } - - if (!port.IsPortNumber ()) { - var msg = "The URI prefix includes an invalid port."; - - throw new HttpListenerException (87, msg); - } - - var path = pref.Path; - - if (path.IndexOf ('%') != -1) { - var msg = "The URI prefix includes an invalid path."; - - throw new HttpListenerException (87, msg); - } - - if (path.IndexOf ("//", StringComparison.Ordinal) != -1) { - var msg = "The URI prefix includes an invalid path."; - - throw new HttpListenerException (87, msg); - } - - var endpoint = new IPEndPoint (addr, port); - - EndPointListener lsnr; - - if (_endpoints.TryGetValue (endpoint, out lsnr)) { - if (lsnr.IsSecure ^ pref.IsSecure) { - var msg = "The URI prefix includes an invalid scheme."; - - throw new HttpListenerException (87, msg); - } - } - else { - lsnr = new EndPointListener ( - endpoint, - pref.IsSecure, - listener.CertificateFolderPath, - listener.SslConfiguration, - listener.ReuseAddress - ); - - _endpoints.Add (endpoint, lsnr); - } - - lsnr.AddPrefix (pref); - } - - private static IPAddress convertToIPAddress (string hostname) - { - if (hostname == "*") - return IPAddress.Any; - - if (hostname == "+") - return IPAddress.Any; - - return hostname.ToIPAddress (); - } - - private static void removePrefix (string uriPrefix, HttpListener listener) - { - var pref = new HttpListenerPrefix (uriPrefix, listener); - - var addr = convertToIPAddress (pref.Host); - - if (addr == null) - return; - - if (!addr.IsLocal ()) - return; - - int port; - - if (!Int32.TryParse (pref.Port, out port)) - return; - - if (!port.IsPortNumber ()) - return; - - var path = pref.Path; - - if (path.IndexOf ('%') != -1) - return; - - if (path.IndexOf ("//", StringComparison.Ordinal) != -1) - return; - - var endpoint = new IPEndPoint (addr, port); - - EndPointListener lsnr; - - if (!_endpoints.TryGetValue (endpoint, out lsnr)) - return; - - if (lsnr.IsSecure ^ pref.IsSecure) - return; - - lsnr.RemovePrefix (pref); - } - - #endregion - - #region Internal Methods - - internal static bool RemoveEndPoint (IPEndPoint endpoint) - { - lock (((ICollection) _endpoints).SyncRoot) - return _endpoints.Remove (endpoint); - } - - #endregion - - #region Public Methods - - public static void AddListener (HttpListener listener) - { - var added = new List (); - - lock (((ICollection) _endpoints).SyncRoot) { - try { - foreach (var pref in listener.Prefixes) { - addPrefix (pref, listener); - added.Add (pref); - } - } - catch { - foreach (var pref in added) - removePrefix (pref, listener); - - throw; - } - } - } - - public static void AddPrefix (string uriPrefix, HttpListener listener) - { - lock (((ICollection) _endpoints).SyncRoot) - addPrefix (uriPrefix, listener); - } - - public static void RemoveListener (HttpListener listener) - { - lock (((ICollection) _endpoints).SyncRoot) { - foreach (var pref in listener.Prefixes) - removePrefix (pref, listener); - } - } - - public static void RemovePrefix (string uriPrefix, HttpListener listener) - { - lock (((ICollection) _endpoints).SyncRoot) - removePrefix (uriPrefix, listener); - } - - #endregion - } -} diff --git a/websocket-sharp/Net/HttpBasicIdentity.cs b/websocket-sharp/Net/HttpBasicIdentity.cs deleted file mode 100644 index d26b29f69..000000000 --- a/websocket-sharp/Net/HttpBasicIdentity.cs +++ /dev/null @@ -1,82 +0,0 @@ -#region License -/* - * HttpBasicIdentity.cs - * - * This code is derived from HttpListenerBasicIdentity.cs (System.Net) of - * Mono (http://www.mono-project.com). - * - * The MIT License - * - * Copyright (c) 2005 Novell, Inc. (http://www.novell.com) - * Copyright (c) 2014-2017 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -#region Authors -/* - * Authors: - * - Gonzalo Paniagua Javier - */ -#endregion - -using System; -using System.Security.Principal; - -namespace WebSocketSharp.Net -{ - /// - /// Holds the username and password from an HTTP Basic authentication attempt. - /// - public class HttpBasicIdentity : GenericIdentity - { - #region Private Fields - - private string _password; - - #endregion - - #region Internal Constructors - - internal HttpBasicIdentity (string username, string password) - : base (username, "Basic") - { - _password = password; - } - - #endregion - - #region Public Properties - - /// - /// Gets the password from a basic authentication attempt. - /// - /// - /// A that represents the password. - /// - public virtual string Password { - get { - return _password; - } - } - - #endregion - } -} diff --git a/websocket-sharp/Net/HttpConnection.cs b/websocket-sharp/Net/HttpConnection.cs deleted file mode 100644 index ec57a22af..000000000 --- a/websocket-sharp/Net/HttpConnection.cs +++ /dev/null @@ -1,653 +0,0 @@ -#region License -/* - * HttpConnection.cs - * - * This code is derived from HttpConnection.cs (System.Net) of Mono - * (http://www.mono-project.com). - * - * The MIT License - * - * Copyright (c) 2005 Novell, Inc. (http://www.novell.com) - * Copyright (c) 2012-2026 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -#region Authors -/* - * Authors: - * - Gonzalo Paniagua Javier - */ -#endregion - -#region Contributors -/* - * Contributors: - * - Liryna - * - Rohan Singh - */ -#endregion - -using System; -using System.Collections.Generic; -using System.IO; -using System.Net; -using System.Net.Security; -using System.Net.Sockets; -using System.Text; -using System.Threading; - -namespace WebSocketSharp.Net -{ - internal sealed class HttpConnection - { - #region Private Fields - - private int _attempts; - private byte[] _buffer; - private static readonly int _bufferLength; - private HttpListenerContext _context; - private StringBuilder _currentLine; - private EndPointListener _endPointListener; - private InputState _inputState; - private RequestStream _inputStream; - private LineState _lineState; - private EndPoint _localEndPoint; - private static readonly int _maxInputLength; - private ResponseStream _outputStream; - private int _position; - private EndPoint _remoteEndPoint; - private MemoryStream _requestBuffer; - private int _reuses; - private bool _secure; - private Socket _socket; - private Stream _stream; - private object _sync; - private int _timeout; - private Dictionary _timeoutCanceled; - private Timer _timer; - - #endregion - - #region Static Constructor - - static HttpConnection () - { - _bufferLength = 8192; - _maxInputLength = 32768; - } - - #endregion - - #region Internal Constructors - - internal HttpConnection (Socket socket, EndPointListener listener) - { - _socket = socket; - _endPointListener = listener; - - var netStream = new NetworkStream (socket, false); - - if (listener.IsSecure) { - var sslConf = listener.SslConfiguration; - var sslStream = new SslStream ( - netStream, - false, - sslConf.ClientCertificateValidationCallback - ); - - sslStream.AuthenticateAsServer ( - sslConf.ServerCertificate, - sslConf.ClientCertificateRequired, - sslConf.EnabledSslProtocols, - sslConf.CheckCertificateRevocation - ); - - _secure = true; - _stream = sslStream; - } - else { - _stream = netStream; - } - - _buffer = new byte[_bufferLength]; - _localEndPoint = socket.LocalEndPoint; - _remoteEndPoint = socket.RemoteEndPoint; - _sync = new object (); - _timeoutCanceled = new Dictionary (); - _timer = new Timer (onTimeout, this, Timeout.Infinite, Timeout.Infinite); - - // 90k ms for first request, 15k ms from then on. - init (new MemoryStream (), 90000); - } - - #endregion - - #region Public Properties - - public bool IsClosed { - get { - return _socket == null; - } - } - - public bool IsLocal { - get { - return ((IPEndPoint) _remoteEndPoint).Address.IsLocal (); - } - } - - public bool IsSecure { - get { - return _secure; - } - } - - public IPEndPoint LocalEndPoint { - get { - return (IPEndPoint) _localEndPoint; - } - } - - public IPEndPoint RemoteEndPoint { - get { - return (IPEndPoint) _remoteEndPoint; - } - } - - public int Reuses { - get { - return _reuses; - } - } - - public Socket Socket { - get { - return _socket; - } - } - - public Stream Stream { - get { - return _stream; - } - } - - #endregion - - #region Private Methods - - private void close () - { - lock (_sync) { - if (_socket == null) - return; - - disposeTimer (); - disposeRequestBuffer (); - disposeStream (); - closeSocket (); - } - - _context.Unregister (); - _endPointListener.RemoveConnection (this); - } - - private void closeSocket () - { - try { - _socket.Shutdown (SocketShutdown.Both); - } - catch { - } - - _socket.Close (); - - _socket = null; - } - - private static MemoryStream createRequestBuffer ( - RequestStream inputStream - ) - { - var ret = new MemoryStream (); - - if (inputStream is ChunkedRequestStream) { - var crs = (ChunkedRequestStream) inputStream; - - if (crs.HasRemainingBuffer) { - var buff = crs.RemainingBuffer; - - ret.Write (buff, 0, buff.Length); - } - - return ret; - } - - var cnt = inputStream.Count; - - if (cnt > 0) - ret.Write (inputStream.InitialBuffer, inputStream.Offset, cnt); - - return ret; - } - - private void disposeRequestBuffer () - { - if (_requestBuffer == null) - return; - - _requestBuffer.Dispose (); - - _requestBuffer = null; - } - - private void disposeStream () - { - if (_stream == null) - return; - - _stream.Dispose (); - - _stream = null; - } - - private void disposeTimer () - { - if (_timer == null) - return; - - try { - _timer.Change (Timeout.Infinite, Timeout.Infinite); - } - catch { - } - - _timer.Dispose (); - - _timer = null; - } - - private void init (MemoryStream requestBuffer, int timeout) - { - _requestBuffer = requestBuffer; - _timeout = timeout; - - _context = new HttpListenerContext (this); - _currentLine = new StringBuilder (64); - _inputState = InputState.RequestLine; - _inputStream = null; - _lineState = LineState.None; - _outputStream = null; - _position = 0; - } - - private static void onRead (IAsyncResult asyncResult) - { - var conn = (HttpConnection) asyncResult.AsyncState; - var current = conn._attempts; - - if (conn._socket == null) - return; - - lock (conn._sync) { - if (conn._socket == null) - return; - - conn._timer.Change (Timeout.Infinite, Timeout.Infinite); - conn._timeoutCanceled[current] = true; - - var nread = 0; - - try { - nread = conn._stream.EndRead (asyncResult); - } - catch (Exception) { - // TODO: Logging. - - conn.close (); - - return; - } - - if (nread <= 0) { - conn.close (); - - return; - } - - conn._requestBuffer.Write (conn._buffer, 0, nread); - - if (conn.processRequestBuffer ()) - return; - - conn.BeginReadRequest (); - } - } - - private static void onTimeout (object state) - { - var conn = (HttpConnection) state; - var current = conn._attempts; - - if (conn._socket == null) - return; - - lock (conn._sync) { - if (conn._socket == null) - return; - - if (conn._timeoutCanceled[current]) - return; - - conn._context.SendError (408); - } - } - - private bool processInput (byte[] data, int length) - { - // This method returns a bool: - // - true Done processing - // - false Need more input - - var req = _context.Request; - - try { - while (true) { - int nread; - var line = readLineFrom (data, _position, length, out nread); - - _position += nread; - - if (line == null) - break; - - if (line.Length == 0) { - if (_inputState == InputState.RequestLine) - continue; - - if (_position > _maxInputLength) - _context.ErrorMessage = "Headers too long"; - - return true; - } - - if (_inputState == InputState.RequestLine) { - req.SetRequestLine (line); - - _inputState = InputState.Headers; - } - else { - req.AddHeader (line); - } - - if (_context.HasErrorMessage) - return true; - } - } - catch (Exception) { - // TODO: Logging. - - _context.ErrorMessage = "Processing failure"; - - return true; - } - - if (_position >= _maxInputLength) { - _context.ErrorMessage = "Headers too long"; - - return true; - } - - return false; - } - - private bool processRequestBuffer () - { - // This method returns a bool: - // - true Done processing - // - false Need more write - - var data = _requestBuffer.GetBuffer (); - var len = (int) _requestBuffer.Length; - - if (!processInput (data, len)) - return false; - - var req = _context.Request; - - if (!_context.HasErrorMessage) - req.FinishInitialization (); - - if (_context.HasErrorMessage) { - _context.SendError (); - - return true; - } - - var uri = req.Url; - HttpListener httplsnr; - - if (!_endPointListener.TrySearchHttpListener (uri, out httplsnr)) { - _context.SendError (404); - - return true; - } - - httplsnr.RegisterContext (_context); - - return true; - } - - private string readLineFrom ( - byte[] buffer, - int offset, - int length, - out int nread - ) - { - nread = 0; - - for (var i = offset; i < length; i++) { - nread++; - - var b = buffer[i]; - - if (b == 13) { - _lineState = LineState.Cr; - - continue; - } - - if (b == 10) { - _lineState = LineState.Lf; - - break; - } - - _currentLine.Append ((char) b); - } - - if (_lineState != LineState.Lf) - return null; - - var ret = _currentLine.ToString (); - - _currentLine.Length = 0; - _lineState = LineState.None; - - return ret; - } - - private MemoryStream takeOverRequestBuffer () - { - if (_inputStream != null) - return createRequestBuffer (_inputStream); - - var ret = new MemoryStream (); - - var buff = _requestBuffer.GetBuffer (); - var len = (int) _requestBuffer.Length; - var cnt = len - _position; - - if (cnt > 0) - ret.Write (buff, _position, cnt); - - disposeRequestBuffer (); - - return ret; - } - - #endregion - - #region Internal Methods - - internal void BeginReadRequest () - { - _attempts++; - - _timeoutCanceled.Add (_attempts, false); - _timer.Change (_timeout, Timeout.Infinite); - - try { - _stream.BeginRead (_buffer, 0, _bufferLength, onRead, this); - } - catch (Exception) { - // TODO: Logging. - - close (); - } - } - - internal void Close (bool force) - { - if (_socket == null) - return; - - lock (_sync) { - if (_socket == null) - return; - - if (force) { - if (_outputStream != null) - _outputStream.Close (true); - - close (); - - return; - } - - GetResponseStream ().Close (false); - - if (_context.Response.CloseConnection) { - close (); - - return; - } - - if (!_context.Request.FlushInput ()) { - close (); - - return; - } - - _context.Unregister (); - - _reuses++; - - var buff = takeOverRequestBuffer (); - var len = buff.Length; - - init (buff, 15000); - - if (len > 0) { - if (processRequestBuffer ()) - return; - } - - BeginReadRequest (); - } - } - - #endregion - - #region Public Methods - - public void Close () - { - Close (false); - } - - public RequestStream GetRequestStream (long contentLength, bool chunked) - { - lock (_sync) { - if (_socket == null) - return null; - - if (_inputStream != null) - return _inputStream; - - var buff = _requestBuffer.GetBuffer (); - var len = (int) _requestBuffer.Length; - var cnt = len - _position; - - _inputStream = chunked - ? new ChunkedRequestStream ( - _stream, - buff, - _position, - cnt, - _context - ) - : new RequestStream ( - _stream, - buff, - _position, - cnt, - contentLength - ); - - disposeRequestBuffer (); - - return _inputStream; - } - } - - public ResponseStream GetResponseStream () - { - lock (_sync) { - if (_socket == null) - return null; - - if (_outputStream != null) - return _outputStream; - - var lsnr = _context.Listener; - var ignore = lsnr != null ? lsnr.IgnoreWriteExceptions : true; - - _outputStream = new ResponseStream (_stream, _context.Response, ignore); - - return _outputStream; - } - } - - #endregion - } -} diff --git a/websocket-sharp/Net/HttpDigestIdentity.cs b/websocket-sharp/Net/HttpDigestIdentity.cs deleted file mode 100644 index e2863aa9c..000000000 --- a/websocket-sharp/Net/HttpDigestIdentity.cs +++ /dev/null @@ -1,192 +0,0 @@ -#region License -/* - * HttpDigestIdentity.cs - * - * The MIT License - * - * Copyright (c) 2014-2024 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -using System; -using System.Collections.Specialized; -using System.Security.Principal; - -namespace WebSocketSharp.Net -{ - /// - /// Holds the username and other parameters from an HTTP Digest - /// authentication attempt. - /// - public class HttpDigestIdentity : GenericIdentity - { - #region Private Fields - - private NameValueCollection _parameters; - - #endregion - - #region Internal Constructors - - internal HttpDigestIdentity (NameValueCollection parameters) - : base (parameters["username"], "Digest") - { - _parameters = parameters; - } - - #endregion - - #region Public Properties - - /// - /// Gets the algorithm parameter from a digest authentication attempt. - /// - /// - /// A that represents the algorithm parameter. - /// - public string Algorithm { - get { - return _parameters["algorithm"]; - } - } - - /// - /// Gets the cnonce parameter from a digest authentication attempt. - /// - /// - /// A that represents the cnonce parameter. - /// - public string Cnonce { - get { - return _parameters["cnonce"]; - } - } - - /// - /// Gets the nc parameter from a digest authentication attempt. - /// - /// - /// A that represents the nc parameter. - /// - public string Nc { - get { - return _parameters["nc"]; - } - } - - /// - /// Gets the nonce parameter from a digest authentication attempt. - /// - /// - /// A that represents the nonce parameter. - /// - public string Nonce { - get { - return _parameters["nonce"]; - } - } - - /// - /// Gets the opaque parameter from a digest authentication attempt. - /// - /// - /// A that represents the opaque parameter. - /// - public string Opaque { - get { - return _parameters["opaque"]; - } - } - - /// - /// Gets the qop parameter from a digest authentication attempt. - /// - /// - /// A that represents the qop parameter. - /// - public string Qop { - get { - return _parameters["qop"]; - } - } - - /// - /// Gets the realm parameter from a digest authentication attempt. - /// - /// - /// A that represents the realm parameter. - /// - public string Realm { - get { - return _parameters["realm"]; - } - } - - /// - /// Gets the response parameter from a digest authentication attempt. - /// - /// - /// A that represents the response parameter. - /// - public string Response { - get { - return _parameters["response"]; - } - } - - /// - /// Gets the uri parameter from a digest authentication attempt. - /// - /// - /// A that represents the uri parameter. - /// - public string Uri { - get { - return _parameters["uri"]; - } - } - - #endregion - - #region Internal Methods - - internal bool IsValid ( - string password, - string realm, - string method, - string entity - ) - { - var parameters = new NameValueCollection (_parameters); - - parameters["password"] = password; - parameters["realm"] = realm; - parameters["method"] = method; - parameters["entity"] = entity; - - var expectedDigest = AuthenticationResponse.CreateRequestDigest (parameters); - - return _parameters["response"] == expectedDigest; - } - - #endregion - } -} diff --git a/websocket-sharp/Net/HttpHeaderInfo.cs b/websocket-sharp/Net/HttpHeaderInfo.cs deleted file mode 100644 index 2b77e3d9d..000000000 --- a/websocket-sharp/Net/HttpHeaderInfo.cs +++ /dev/null @@ -1,128 +0,0 @@ -#region License -/* - * HttpHeaderInfo.cs - * - * The MIT License - * - * Copyright (c) 2013-2020 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -using System; - -namespace WebSocketSharp.Net -{ - internal class HttpHeaderInfo - { - #region Private Fields - - private string _headerName; - private HttpHeaderType _headerType; - - #endregion - - #region Internal Constructors - - internal HttpHeaderInfo (string headerName, HttpHeaderType headerType) - { - _headerName = headerName; - _headerType = headerType; - } - - #endregion - - #region Internal Properties - - internal bool IsMultiValueInRequest { - get { - var headerType = _headerType & HttpHeaderType.MultiValueInRequest; - - return headerType == HttpHeaderType.MultiValueInRequest; - } - } - - internal bool IsMultiValueInResponse { - get { - var headerType = _headerType & HttpHeaderType.MultiValueInResponse; - - return headerType == HttpHeaderType.MultiValueInResponse; - } - } - - #endregion - - #region Public Properties - - public string HeaderName { - get { - return _headerName; - } - } - - public HttpHeaderType HeaderType { - get { - return _headerType; - } - } - - public bool IsRequest { - get { - var headerType = _headerType & HttpHeaderType.Request; - - return headerType == HttpHeaderType.Request; - } - } - - public bool IsResponse { - get { - var headerType = _headerType & HttpHeaderType.Response; - - return headerType == HttpHeaderType.Response; - } - } - - #endregion - - #region Public Methods - - public bool IsMultiValue (bool response) - { - var headerType = _headerType & HttpHeaderType.MultiValue; - - if (headerType != HttpHeaderType.MultiValue) - return response ? IsMultiValueInResponse : IsMultiValueInRequest; - - return response ? IsResponse : IsRequest; - } - - public bool IsRestricted (bool response) - { - var headerType = _headerType & HttpHeaderType.Restricted; - - if (headerType != HttpHeaderType.Restricted) - return false; - - return response ? IsResponse : IsRequest; - } - - #endregion - } -} diff --git a/websocket-sharp/Net/HttpHeaderType.cs b/websocket-sharp/Net/HttpHeaderType.cs deleted file mode 100644 index 113fb63b6..000000000 --- a/websocket-sharp/Net/HttpHeaderType.cs +++ /dev/null @@ -1,44 +0,0 @@ -#region License -/* - * HttpHeaderType.cs - * - * The MIT License - * - * Copyright (c) 2013-2014 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -using System; - -namespace WebSocketSharp.Net -{ - [Flags] - internal enum HttpHeaderType - { - Unspecified = 0, - Request = 1, - Response = 1 << 1, - Restricted = 1 << 2, - MultiValue = 1 << 3, - MultiValueInRequest = 1 << 4, - MultiValueInResponse = 1 << 5 - } -} diff --git a/websocket-sharp/Net/HttpListener.cs b/websocket-sharp/Net/HttpListener.cs deleted file mode 100644 index 44b964c0c..000000000 --- a/websocket-sharp/Net/HttpListener.cs +++ /dev/null @@ -1,1028 +0,0 @@ -#region License -/* - * HttpListener.cs - * - * This code is derived from HttpListener.cs (System.Net) of Mono - * (http://www.mono-project.com). - * - * The MIT License - * - * Copyright (c) 2005 Novell, Inc. (http://www.novell.com) - * Copyright (c) 2012-2024 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -#region Authors -/* - * Authors: - * - Gonzalo Paniagua Javier - */ -#endregion - -#region Contributors -/* - * Contributors: - * - Liryna - */ -#endregion - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Security.Cryptography.X509Certificates; -using System.Security.Principal; -using System.Threading; - -// TODO: Logging. -namespace WebSocketSharp.Net -{ - /// - /// Provides a simple, programmatically controlled HTTP listener. - /// - /// - /// - /// The listener supports HTTP/1.1 version request and response. - /// - /// - /// And the listener allows to accept WebSocket handshake requests. - /// - /// - /// This class cannot be inherited. - /// - /// - public sealed class HttpListener : IDisposable - { - #region Private Fields - - private AuthenticationSchemes _authSchemes; - private Func _authSchemeSelector; - private string _certFolderPath; - private Queue _contextQueue; - private LinkedList _contextRegistry; - private object _contextRegistrySync; - private static readonly string _defaultRealm; - private bool _disposed; - private bool _ignoreWriteExceptions; - private volatile bool _isListening; - private Logger _log; - private HttpListenerPrefixCollection _prefixes; - private string _realm; - private bool _reuseAddress; - private ServerSslConfiguration _sslConfig; - private object _sync; - private Func _userCredFinder; - private Queue _waitQueue; - - #endregion - - #region Static Constructor - - static HttpListener () - { - _defaultRealm = "SECRET AREA"; - } - - #endregion - - #region Public Constructors - - /// - /// Initializes a new instance of the class. - /// - public HttpListener () - { - _authSchemes = AuthenticationSchemes.Anonymous; - _contextQueue = new Queue (); - _contextRegistry = new LinkedList (); - _contextRegistrySync = ((ICollection) _contextRegistry).SyncRoot; - _log = new Logger (); - _prefixes = new HttpListenerPrefixCollection (this); - _sync = new object (); - _waitQueue = new Queue (); - } - - #endregion - - #region Internal Properties - - internal string ObjectName { - get { - return GetType ().ToString (); - } - } - - internal bool ReuseAddress { - get { - return _reuseAddress; - } - - set { - _reuseAddress = value; - } - } - - #endregion - - #region Public Properties - - /// - /// Gets or sets the scheme used to authenticate the clients. - /// - /// - /// - /// One of the - /// enum values. - /// - /// - /// It represents the scheme used to authenticate the clients. - /// - /// - /// The default value is - /// . - /// - /// - /// - /// This listener has been closed. - /// - public AuthenticationSchemes AuthenticationSchemes { - get { - if (_disposed) - throw new ObjectDisposedException (ObjectName); - - return _authSchemes; - } - - set { - if (_disposed) - throw new ObjectDisposedException (ObjectName); - - _authSchemes = value; - } - } - - /// - /// Gets or sets the delegate called to determine the scheme used to - /// authenticate the clients. - /// - /// - /// - /// If this property is set, the listener uses the authentication - /// scheme selected by the delegate for each request. - /// - /// - /// Or if this property is not set, the listener uses the value of - /// the property - /// as the authentication scheme for all requests. - /// - /// - /// - /// - /// A - /// delegate. - /// - /// - /// It represents the delegate called when the listener selects - /// an authentication scheme. - /// - /// - /// if not necessary. - /// - /// - /// The default value is . - /// - /// - /// - /// This listener has been closed. - /// - public Func AuthenticationSchemeSelector { - get { - if (_disposed) - throw new ObjectDisposedException (ObjectName); - - return _authSchemeSelector; - } - - set { - if (_disposed) - throw new ObjectDisposedException (ObjectName); - - _authSchemeSelector = value; - } - } - - /// - /// Gets or sets the path to the folder in which stores the certificate - /// files used to authenticate the server on the secure connection. - /// - /// - /// - /// This property represents the path to the folder in which stores - /// the certificate files associated with each port number of added - /// URI prefixes. - /// - /// - /// A set of the certificate files is a pair of <port number>.cer - /// (DER) and <port number>.key (DER, RSA Private Key). - /// - /// - /// If this property is or an empty string, - /// the result of the - /// with the method is used as - /// the default path. - /// - /// - /// - /// - /// A that represents the path to the folder - /// in which stores the certificate files. - /// - /// - /// The default value is . - /// - /// - /// - /// This listener has been closed. - /// - public string CertificateFolderPath { - get { - if (_disposed) - throw new ObjectDisposedException (ObjectName); - - return _certFolderPath; - } - - set { - if (_disposed) - throw new ObjectDisposedException (ObjectName); - - _certFolderPath = value; - } - } - - /// - /// Gets or sets a value indicating whether the listener returns - /// exceptions that occur when sending the response to the client. - /// - /// - /// - /// true if the listener should not return those exceptions; - /// otherwise, false. - /// - /// - /// The default value is false. - /// - /// - /// - /// This listener has been closed. - /// - public bool IgnoreWriteExceptions { - get { - if (_disposed) - throw new ObjectDisposedException (ObjectName); - - return _ignoreWriteExceptions; - } - - set { - if (_disposed) - throw new ObjectDisposedException (ObjectName); - - _ignoreWriteExceptions = value; - } - } - - /// - /// Gets a value indicating whether the listener has been started. - /// - /// - /// true if the listener has been started; otherwise, false. - /// - public bool IsListening { - get { - return _isListening; - } - } - - /// - /// Gets a value indicating whether the listener can be used with - /// the current operating system. - /// - /// - /// true. - /// - public static bool IsSupported { - get { - return true; - } - } - - /// - /// Gets the logging functions. - /// - /// - /// - /// The default logging level is . - /// - /// - /// If you would like to change it, you should set the Log.Level - /// property to any of the enum values. - /// - /// - /// - /// A that provides the logging functions. - /// - /// - /// This listener has been closed. - /// - public Logger Log { - get { - if (_disposed) - throw new ObjectDisposedException (ObjectName); - - return _log; - } - } - - /// - /// Gets the URI prefixes handled by the listener. - /// - /// - /// A that contains the URI - /// prefixes. - /// - /// - /// This listener has been closed. - /// - public HttpListenerPrefixCollection Prefixes { - get { - if (_disposed) - throw new ObjectDisposedException (ObjectName); - - return _prefixes; - } - } - - /// - /// Gets or sets the name of the realm associated with the listener. - /// - /// - /// If this property is or an empty string, - /// "SECRET AREA" is used as the name of the realm. - /// - /// - /// - /// A that represents the name of the realm. - /// - /// - /// The default value is . - /// - /// - /// - /// This listener has been closed. - /// - public string Realm { - get { - if (_disposed) - throw new ObjectDisposedException (ObjectName); - - return _realm; - } - - set { - if (_disposed) - throw new ObjectDisposedException (ObjectName); - - _realm = value; - } - } - - /// - /// Gets the configuration for secure connection. - /// - /// - /// A that represents the - /// configuration used to provide secure connections. - /// - /// - /// This listener has been closed. - /// - public ServerSslConfiguration SslConfiguration { - get { - if (_disposed) - throw new ObjectDisposedException (ObjectName); - - if (_sslConfig == null) - _sslConfig = new ServerSslConfiguration (); - - return _sslConfig; - } - } - - /// - /// Gets or sets a value indicating whether, when NTLM authentication is used, - /// the authentication information of first request is used to authenticate - /// additional requests on the same connection. - /// - /// - /// This property is not currently supported and always throws - /// a . - /// - /// - /// true if the authentication information of first request is used; - /// otherwise, false. - /// - /// - /// Any use of this property. - /// - public bool UnsafeConnectionNtlmAuthentication { - get { - throw new NotSupportedException (); - } - - set { - throw new NotSupportedException (); - } - } - - /// - /// Gets or sets the delegate called to find the credentials for - /// an identity used to authenticate a client. - /// - /// - /// - /// A - /// delegate. - /// - /// - /// It represents the delegate called when the listener finds - /// the credentials used to authenticate a client. - /// - /// - /// It must return if the credentials - /// are not found. - /// - /// - /// if not necessary. - /// - /// - /// The default value is . - /// - /// - /// - /// This listener has been closed. - /// - public Func UserCredentialsFinder { - get { - if (_disposed) - throw new ObjectDisposedException (ObjectName); - - return _userCredFinder; - } - - set { - if (_disposed) - throw new ObjectDisposedException (ObjectName); - - _userCredFinder = value; - } - } - - #endregion - - #region Private Methods - - private bool authenticateClient (HttpListenerContext context) - { - var schm = selectAuthenticationScheme (context.Request); - - if (schm == AuthenticationSchemes.Anonymous) - return true; - - if (schm == AuthenticationSchemes.None) { - var msg = "Authentication not allowed"; - - context.SendError (403, msg); - - return false; - } - - var realm = getRealm (); - - if (!context.SetUser (schm, realm, _userCredFinder)) { - context.SendAuthenticationChallenge (schm, realm); - - return false; - } - - return true; - } - - private HttpListenerAsyncResult beginGetContext ( - AsyncCallback callback, - object state - ) - { - lock (_contextRegistrySync) { - if (!_isListening) { - var msg = "The method is canceled."; - - throw new HttpListenerException (995, msg); - } - - var ares = new HttpListenerAsyncResult (callback, state, _log); - - if (_contextQueue.Count == 0) { - _waitQueue.Enqueue (ares); - - return ares; - } - - var ctx = _contextQueue.Dequeue (); - - ares.Complete (ctx, true); - - return ares; - } - } - - private void cleanupContextQueue (bool force) - { - if (_contextQueue.Count == 0) - return; - - if (force) { - _contextQueue.Clear (); - - return; - } - - var ctxs = _contextQueue.ToArray (); - - _contextQueue.Clear (); - - foreach (var ctx in ctxs) - ctx.SendError (503); - } - - private void cleanupContextRegistry () - { - var cnt = _contextRegistry.Count; - - if (cnt == 0) - return; - - var ctxs = new HttpListenerContext[cnt]; - - lock (_contextRegistrySync) { - _contextRegistry.CopyTo (ctxs, 0); - _contextRegistry.Clear (); - } - - foreach (var ctx in ctxs) - ctx.Connection.Close (true); - } - - private void cleanupWaitQueue (string message) - { - if (_waitQueue.Count == 0) - return; - - var aress = _waitQueue.ToArray (); - - _waitQueue.Clear (); - - foreach (var ares in aress) { - var ex = new HttpListenerException (995, message); - - ares.Complete (ex); - } - } - - private void close (bool force) - { - lock (_sync) { - if (_disposed) - return; - - lock (_contextRegistrySync) { - if (!_isListening) { - _disposed = true; - - return; - } - - _isListening = false; - } - - cleanupContextQueue (force); - cleanupContextRegistry (); - - var msg = "The listener is closed."; - - cleanupWaitQueue (msg); - - EndPointManager.RemoveListener (this); - - _disposed = true; - } - } - - private string getRealm () - { - var realm = _realm; - - return realm != null && realm.Length > 0 ? realm : _defaultRealm; - } - - private bool registerContext (HttpListenerContext context) - { - if (!_isListening) - return false; - - lock (_contextRegistrySync) { - if (!_isListening) - return false; - - context.Listener = this; - - _contextRegistry.AddLast (context); - - if (_waitQueue.Count == 0) { - _contextQueue.Enqueue (context); - - return true; - } - - var ares = _waitQueue.Dequeue (); - - ares.Complete (context, false); - - return true; - } - } - - private AuthenticationSchemes selectAuthenticationScheme ( - HttpListenerRequest request - ) - { - var selector = _authSchemeSelector; - - if (selector == null) - return _authSchemes; - - try { - return selector (request); - } - catch { - return AuthenticationSchemes.None; - } - } - - #endregion - - #region Internal Methods - - internal void CheckDisposed () - { - if (_disposed) - throw new ObjectDisposedException (ObjectName); - } - - internal bool RegisterContext (HttpListenerContext context) - { - if (!authenticateClient (context)) - return false; - - if (!registerContext (context)) { - context.SendError (503); - - return false; - } - - return true; - } - - internal void UnregisterContext (HttpListenerContext context) - { - lock (_contextRegistrySync) - _contextRegistry.Remove (context); - } - - #endregion - - #region Public Methods - - /// - /// Shuts down the listener immediately. - /// - public void Abort () - { - if (_disposed) - return; - - close (true); - } - - /// - /// Begins getting an incoming request asynchronously. - /// - /// - /// - /// This asynchronous operation must be ended by calling - /// the method. - /// - /// - /// Typically, the method is called by - /// . - /// - /// - /// - /// An instance that represents the status of - /// the asynchronous operation. - /// - /// - /// - /// An delegate. - /// - /// - /// It specifies the delegate called when the asynchronous operation is - /// complete. - /// - /// - /// - /// An that specifies a user defined object to pass to - /// . - /// - /// - /// This method is canceled. - /// - /// - /// - /// This listener has not been started or is currently stopped. - /// - /// - /// -or- - /// - /// - /// This listener has no URI prefix on which listens. - /// - /// - /// - /// This listener has been closed. - /// - public IAsyncResult BeginGetContext (AsyncCallback callback, object state) - { - if (_disposed) - throw new ObjectDisposedException (ObjectName); - - if (!_isListening) { - var msg = "The listener has not been started."; - - throw new InvalidOperationException (msg); - } - - if (_prefixes.Count == 0) { - var msg = "The listener has no URI prefix on which listens."; - - throw new InvalidOperationException (msg); - } - - return beginGetContext (callback, state); - } - - /// - /// Shuts down the listener. - /// - public void Close () - { - if (_disposed) - return; - - close (false); - } - - /// - /// Ends an asynchronous operation to get an incoming request. - /// - /// - /// This method ends an asynchronous operation started by calling - /// the method. - /// - /// - /// A that represents a request. - /// - /// - /// An instance obtained by calling - /// the method. - /// - /// - /// was not obtained by calling - /// the method. - /// - /// - /// is . - /// - /// - /// This method is canceled. - /// - /// - /// - /// This listener has not been started or is currently stopped. - /// - /// - /// -or- - /// - /// - /// This method was already called for . - /// - /// - /// - /// This listener has been closed. - /// - public HttpListenerContext EndGetContext (IAsyncResult asyncResult) - { - if (_disposed) - throw new ObjectDisposedException (ObjectName); - - if (!_isListening) { - var msg = "The listener has not been started."; - - throw new InvalidOperationException (msg); - } - - if (asyncResult == null) - throw new ArgumentNullException ("asyncResult"); - - var ares = asyncResult as HttpListenerAsyncResult; - - if (ares == null) { - var msg = "A wrong IAsyncResult instance."; - - throw new ArgumentException (msg, "asyncResult"); - } - - lock (ares.SyncRoot) { - if (ares.EndCalled) { - var msg = "This IAsyncResult instance cannot be reused."; - - throw new InvalidOperationException (msg); - } - - ares.EndCalled = true; - } - - if (!ares.IsCompleted) - ares.AsyncWaitHandle.WaitOne (); - - return ares.Context; - } - - /// - /// Gets an incoming request. - /// - /// - /// This method waits for an incoming request and returns when - /// a request is received. - /// - /// - /// A that represents a request. - /// - /// - /// This method is canceled. - /// - /// - /// - /// This listener has not been started or is currently stopped. - /// - /// - /// -or- - /// - /// - /// This listener has no URI prefix on which listens. - /// - /// - /// - /// This listener has been closed. - /// - public HttpListenerContext GetContext () - { - if (_disposed) - throw new ObjectDisposedException (ObjectName); - - if (!_isListening) { - var msg = "The listener has not been started."; - - throw new InvalidOperationException (msg); - } - - if (_prefixes.Count == 0) { - var msg = "The listener has no URI prefix on which listens."; - - throw new InvalidOperationException (msg); - } - - var ares = beginGetContext (null, null); - - ares.EndCalled = true; - - if (!ares.IsCompleted) - ares.AsyncWaitHandle.WaitOne (); - - return ares.Context; - } - - /// - /// Starts receiving incoming requests. - /// - /// - /// This listener has been closed. - /// - public void Start () - { - if (_disposed) - throw new ObjectDisposedException (ObjectName); - - lock (_sync) { - if (_disposed) - throw new ObjectDisposedException (ObjectName); - - lock (_contextRegistrySync) { - if (_isListening) - return; - - EndPointManager.AddListener (this); - - _isListening = true; - } - } - } - - /// - /// Stops receiving incoming requests. - /// - /// - /// This listener has been closed. - /// - public void Stop () - { - if (_disposed) - throw new ObjectDisposedException (ObjectName); - - lock (_sync) { - if (_disposed) - throw new ObjectDisposedException (ObjectName); - - lock (_contextRegistrySync) { - if (!_isListening) - return; - - _isListening = false; - } - - cleanupContextQueue (false); - cleanupContextRegistry (); - - var msg = "The listener is stopped."; - - cleanupWaitQueue (msg); - - EndPointManager.RemoveListener (this); - } - } - - #endregion - - #region Explicit Interface Implementations - - /// - /// Releases all resources used by the listener. - /// - void IDisposable.Dispose () - { - if (_disposed) - return; - - close (true); - } - - #endregion - } -} diff --git a/websocket-sharp/Net/HttpListenerAsyncResult.cs b/websocket-sharp/Net/HttpListenerAsyncResult.cs deleted file mode 100644 index e4742d77b..000000000 --- a/websocket-sharp/Net/HttpListenerAsyncResult.cs +++ /dev/null @@ -1,202 +0,0 @@ -#region License -/* - * HttpListenerAsyncResult.cs - * - * This code is derived from ListenerAsyncResult.cs (System.Net) of Mono - * (http://www.mono-project.com). - * - * The MIT License - * - * Copyright (c) 2005 Ximian, Inc. (http://www.ximian.com) - * Copyright (c) 2012-2024 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -#region Authors -/* - * Authors: - * - Gonzalo Paniagua Javier - */ -#endregion - -#region Contributors -/* - * Contributors: - * - Nicholas Devenish - */ -#endregion - -using System; -using System.Threading; - -namespace WebSocketSharp.Net -{ - internal class HttpListenerAsyncResult : IAsyncResult - { - #region Private Fields - - private AsyncCallback _callback; - private bool _completed; - private bool _completedSynchronously; - private HttpListenerContext _context; - private bool _endCalled; - private Exception _exception; - private Logger _log; - private object _state; - private object _sync; - private ManualResetEvent _waitHandle; - - #endregion - - #region Internal Constructors - - internal HttpListenerAsyncResult ( - AsyncCallback callback, - object state, - Logger log - ) - { - _callback = callback; - _state = state; - _log = log; - - _sync = new object (); - } - - #endregion - - #region Internal Properties - - internal HttpListenerContext Context - { - get { - if (_exception != null) - throw _exception; - - return _context; - } - } - - internal bool EndCalled { - get { - return _endCalled; - } - - set { - _endCalled = value; - } - } - - internal object SyncRoot { - get { - return _sync; - } - } - - #endregion - - #region Public Properties - - public object AsyncState { - get { - return _state; - } - } - - public WaitHandle AsyncWaitHandle { - get { - lock (_sync) { - if (_waitHandle == null) - _waitHandle = new ManualResetEvent (_completed); - - return _waitHandle; - } - } - } - - public bool CompletedSynchronously { - get { - return _completedSynchronously; - } - } - - public bool IsCompleted { - get { - lock (_sync) - return _completed; - } - } - - #endregion - - #region Private Methods - - private void complete () - { - lock (_sync) { - _completed = true; - - if (_waitHandle != null) - _waitHandle.Set (); - } - - if (_callback == null) - return; - - ThreadPool.QueueUserWorkItem ( - state => { - try { - _callback (this); - } - catch (Exception ex) { - _log.Error (ex.Message); - _log.Debug (ex.ToString ()); - } - }, - null - ); - } - - #endregion - - #region Internal Methods - - internal void Complete (Exception exception) - { - _exception = exception; - - complete (); - } - - internal void Complete ( - HttpListenerContext context, - bool completedSynchronously - ) - { - _context = context; - _completedSynchronously = completedSynchronously; - - complete (); - } - - #endregion - } -} diff --git a/websocket-sharp/Net/HttpListenerContext.cs b/websocket-sharp/Net/HttpListenerContext.cs deleted file mode 100644 index 82ea0176c..000000000 --- a/websocket-sharp/Net/HttpListenerContext.cs +++ /dev/null @@ -1,452 +0,0 @@ -#region License -/* - * HttpListenerContext.cs - * - * This code is derived from HttpListenerContext.cs (System.Net) of Mono - * (http://www.mono-project.com). - * - * The MIT License - * - * Copyright (c) 2005 Novell, Inc. (http://www.novell.com) - * Copyright (c) 2012-2024 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -#region Authors -/* - * Authors: - * - Gonzalo Paniagua Javier - */ -#endregion - -using System; -using System.Security.Principal; -using System.Text; -using WebSocketSharp.Net.WebSockets; - -namespace WebSocketSharp.Net -{ - /// - /// Provides the access to the HTTP request and response objects used by - /// the class. - /// - /// - /// This class cannot be inherited. - /// - public sealed class HttpListenerContext - { - #region Private Fields - - private HttpConnection _connection; - private string _errorMessage; - private int _errorStatusCode; - private HttpListener _listener; - private HttpListenerRequest _request; - private HttpListenerResponse _response; - private IPrincipal _user; - private HttpListenerWebSocketContext _websocketContext; - - #endregion - - #region Internal Constructors - - internal HttpListenerContext (HttpConnection connection) - { - _connection = connection; - - _errorStatusCode = 400; - _request = new HttpListenerRequest (this); - _response = new HttpListenerResponse (this); - } - - #endregion - - #region Internal Properties - - internal HttpConnection Connection { - get { - return _connection; - } - } - - internal string ErrorMessage { - get { - return _errorMessage; - } - - set { - _errorMessage = value; - } - } - - internal int ErrorStatusCode { - get { - return _errorStatusCode; - } - - set { - _errorStatusCode = value; - } - } - - internal bool HasErrorMessage { - get { - return _errorMessage != null; - } - } - - internal HttpListener Listener { - get { - return _listener; - } - - set { - _listener = value; - } - } - - #endregion - - #region Public Properties - - /// - /// Gets the HTTP request object that represents a client request. - /// - /// - /// A that represents the client request. - /// - public HttpListenerRequest Request { - get { - return _request; - } - } - - /// - /// Gets the HTTP response object used to send a response to the client. - /// - /// - /// A that represents a response to - /// the client request. - /// - public HttpListenerResponse Response { - get { - return _response; - } - } - - /// - /// Gets the client information. - /// - /// - /// - /// A instance that represents identity, - /// authentication, and security roles for the client. - /// - /// - /// if the client is not authenticated. - /// - /// - public IPrincipal User { - get { - return _user; - } - } - - #endregion - - #region Private Methods - - private static string createErrorContent ( - int statusCode, - string statusDescription, - string message - ) - { - return message != null && message.Length > 0 - ? String.Format ( - "

{0} {1} ({2})

", - statusCode, - statusDescription, - message - ) - : String.Format ( - "

{0} {1}

", - statusCode, - statusDescription - ); - } - - #endregion - - #region Internal Methods - - internal HttpListenerWebSocketContext GetWebSocketContext (string protocol) - { - _websocketContext = new HttpListenerWebSocketContext (this, protocol); - - return _websocketContext; - } - - internal void SendAuthenticationChallenge ( - AuthenticationSchemes scheme, - string realm - ) - { - _response.StatusCode = 401; - - var val = new AuthenticationChallenge (scheme, realm).ToString (); - - _response.Headers.InternalSet ("WWW-Authenticate", val, true); - - _response.Close (); - } - - internal void SendError () - { - try { - _response.StatusCode = _errorStatusCode; - _response.ContentType = "text/html"; - - var content = createErrorContent ( - _errorStatusCode, - _response.StatusDescription, - _errorMessage - ); - - var enc = Encoding.UTF8; - var entity = enc.GetBytes (content); - - _response.ContentEncoding = enc; - _response.ContentLength64 = entity.LongLength; - - _response.Close (entity, true); - } - catch { - _connection.Close (true); - } - } - - internal void SendError (int statusCode) - { - _errorStatusCode = statusCode; - - SendError (); - } - - internal void SendError (int statusCode, string message) - { - _errorStatusCode = statusCode; - _errorMessage = message; - - SendError (); - } - - internal bool SetUser ( - AuthenticationSchemes scheme, - string realm, - Func credentialsFinder - ) - { - var user = HttpUtility.CreateUser ( - _request.Headers["Authorization"], - scheme, - realm, - _request.HttpMethod, - credentialsFinder - ); - - if (user == null) - return false; - - if (!user.Identity.IsAuthenticated) - return false; - - _user = user; - - return true; - } - - internal void Unregister () - { - if (_listener == null) - return; - - _listener.UnregisterContext (this); - } - - #endregion - - #region Public Methods - - /// - /// Accepts a WebSocket connection. - /// - /// - /// A that represents - /// the WebSocket handshake request. - /// - /// - /// - /// A that specifies the name of the subprotocol - /// supported on the WebSocket connection. - /// - /// - /// if not necessary. - /// - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// contains an invalid character. - /// - /// - /// - /// - /// This method has already been done. - /// - /// - /// -or- - /// - /// - /// The client request is not a WebSocket handshake request. - /// - /// - public HttpListenerWebSocketContext AcceptWebSocket (string protocol) - { - return AcceptWebSocket (protocol, null); - } - - /// - /// Accepts a WebSocket connection with initializing the WebSocket - /// interface. - /// - /// - /// A that represents - /// the WebSocket handshake request. - /// - /// - /// - /// A that specifies the name of the subprotocol - /// supported on the WebSocket connection. - /// - /// - /// if not necessary. - /// - /// - /// - /// - /// An delegate. - /// - /// - /// It specifies the delegate called when a new WebSocket instance is - /// initialized. - /// - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// contains an invalid character. - /// - /// - /// -or- - /// - /// - /// caused an exception. - /// - /// - /// - /// - /// This method has already been done. - /// - /// - /// -or- - /// - /// - /// The client request is not a WebSocket handshake request. - /// - /// - public HttpListenerWebSocketContext AcceptWebSocket ( - string protocol, - Action initializer - ) - { - if (_websocketContext != null) { - var msg = "The method has already been done."; - - throw new InvalidOperationException (msg); - } - - if (!_request.IsWebSocketRequest) { - var msg = "The request is not a WebSocket handshake request."; - - throw new InvalidOperationException (msg); - } - - if (protocol != null) { - if (protocol.Length == 0) { - var msg = "An empty string."; - - throw new ArgumentException (msg, "protocol"); - } - - if (!protocol.IsToken ()) { - var msg = "It contains an invalid character."; - - throw new ArgumentException (msg, "protocol"); - } - } - - var ret = GetWebSocketContext (protocol); - - var ws = ret.WebSocket; - - if (initializer != null) { - try { - initializer (ws); - } - catch (Exception ex) { - if (ws.ReadyState == WebSocketState.New) - _websocketContext = null; - - var msg = "It caused an exception."; - - throw new ArgumentException (msg, "initializer", ex); - } - } - - ws.Accept (); - - return ret; - } - - #endregion - } -} diff --git a/websocket-sharp/Net/HttpListenerException.cs b/websocket-sharp/Net/HttpListenerException.cs deleted file mode 100644 index dec858d53..000000000 --- a/websocket-sharp/Net/HttpListenerException.cs +++ /dev/null @@ -1,140 +0,0 @@ -#region License -/* - * HttpListenerException.cs - * - * This code is derived from HttpListenerException.cs (System.Net) of Mono - * (http://www.mono-project.com). - * - * The MIT License - * - * Copyright (c) 2005 Novell, Inc. (http://www.novell.com) - * Copyright (c) 2012-2024 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -#region Authors -/* - * Authors: - * - Gonzalo Paniagua Javier - */ -#endregion - -using System; -using System.ComponentModel; -using System.Runtime.Serialization; - -namespace WebSocketSharp.Net -{ - /// - /// The exception that is thrown when an error occurs processing - /// an HTTP request. - /// - [Serializable] - public class HttpListenerException : Win32Exception - { - #region Protected Constructors - - /// - /// Initializes a new instance of the - /// class with the specified serialized data. - /// - /// - /// A that contains the serialized - /// object data. - /// - /// - /// A that specifies the source for - /// the deserialization. - /// - /// - /// is . - /// - protected HttpListenerException ( - SerializationInfo serializationInfo, - StreamingContext streamingContext - ) - : base (serializationInfo, streamingContext) - { - } - - #endregion - - #region Public Constructors - - /// - /// Initializes a new instance of the - /// class. - /// - public HttpListenerException () - { - } - - /// - /// Initializes a new instance of the - /// class with the specified error code. - /// - /// - /// An that specifies the error code. - /// - public HttpListenerException (int errorCode) - : base (errorCode) - { - } - - /// - /// Initializes a new instance of the - /// class with the specified error code and message. - /// - /// - /// An that specifies the error code. - /// - /// - /// A that specifies the message. - /// - public HttpListenerException (int errorCode, string message) - : base (errorCode, message) - { - } - - #endregion - - #region Public Properties - - /// - /// Gets the error code that identifies the error that occurred. - /// - /// - /// - /// An that represents the error code. - /// - /// - /// It is any of the Win32 error codes. - /// - /// - public override int ErrorCode { - get { - return NativeErrorCode; - } - } - - #endregion - } -} diff --git a/websocket-sharp/Net/HttpListenerPrefix.cs b/websocket-sharp/Net/HttpListenerPrefix.cs deleted file mode 100644 index aba9d1bfd..000000000 --- a/websocket-sharp/Net/HttpListenerPrefix.cs +++ /dev/null @@ -1,243 +0,0 @@ -#region License -/* - * HttpListenerPrefix.cs - * - * This code is derived from ListenerPrefix.cs (System.Net) of Mono - * (http://www.mono-project.com). - * - * The MIT License - * - * Copyright (c) 2005 Novell, Inc. (http://www.novell.com) - * Copyright (c) 2012-2024 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -#region Authors -/* - * Authors: - * - Gonzalo Paniagua Javier - * - Oleg Mihailik - */ -#endregion - -using System; - -namespace WebSocketSharp.Net -{ - internal sealed class HttpListenerPrefix - { - #region Private Fields - - private string _host; - private bool _isSecure; - private HttpListener _listener; - private string _original; - private string _path; - private string _port; - private string _prefix; - private string _scheme; - - #endregion - - #region Internal Constructors - - internal HttpListenerPrefix (string uriPrefix, HttpListener listener) - { - _original = uriPrefix; - _listener = listener; - - parse (uriPrefix); - } - - #endregion - - #region Public Properties - - public string Host { - get { - return _host; - } - } - - public bool IsSecure { - get { - return _isSecure; - } - } - - public HttpListener Listener { - get { - return _listener; - } - } - - public string Original { - get { - return _original; - } - } - - public string Path { - get { - return _path; - } - } - - public string Port { - get { - return _port; - } - } - - public string Scheme { - get { - return _scheme; - } - } - - #endregion - - #region Private Methods - - private void parse (string uriPrefix) - { - var compType = StringComparison.Ordinal; - - _isSecure = uriPrefix.StartsWith ("https", compType); - _scheme = _isSecure ? "https" : "http"; - - var hostStartIdx = uriPrefix.IndexOf (':') + 3; - - var len = uriPrefix.Length; - var rootIdx = uriPrefix - .IndexOf ('/', hostStartIdx + 1, len - hostStartIdx - 1); - - var colonIdx = uriPrefix - .LastIndexOf (':', rootIdx - 1, rootIdx - hostStartIdx - 1); - - var hasPort = uriPrefix[rootIdx - 1] != ']' && colonIdx > hostStartIdx; - - if (hasPort) { - _host = uriPrefix.Substring (hostStartIdx, colonIdx - hostStartIdx); - _port = uriPrefix.Substring (colonIdx + 1, rootIdx - colonIdx - 1); - } - else { - _host = uriPrefix.Substring (hostStartIdx, rootIdx - hostStartIdx); - _port = _isSecure ? "443" : "80"; - } - - _path = uriPrefix.Substring (rootIdx); - - var fmt = "{0}://{1}:{2}{3}"; - - _prefix = String.Format (fmt, _scheme, _host, _port, _path); - } - - #endregion - - #region Public Methods - - public static void CheckPrefix (string uriPrefix) - { - if (uriPrefix == null) - throw new ArgumentNullException ("uriPrefix"); - - var len = uriPrefix.Length; - - if (len == 0) { - var msg = "An empty string."; - - throw new ArgumentException (msg, "uriPrefix"); - } - - var compType = StringComparison.Ordinal; - var isHttpSchm = uriPrefix.StartsWith ("http://", compType) - || uriPrefix.StartsWith ("https://", compType); - - if (!isHttpSchm) { - var msg = "The scheme is not http or https."; - - throw new ArgumentException (msg, "uriPrefix"); - } - - var endIdx = len - 1; - - if (uriPrefix[endIdx] != '/') { - var msg = "It ends without a forward slash."; - - throw new ArgumentException (msg, "uriPrefix"); - } - - var hostStartIdx = uriPrefix.IndexOf (':') + 3; - - if (hostStartIdx >= endIdx) { - var msg = "No host is specified."; - - throw new ArgumentException (msg, "uriPrefix"); - } - - if (uriPrefix[hostStartIdx] == ':') { - var msg = "No host is specified."; - - throw new ArgumentException (msg, "uriPrefix"); - } - - var rootIdx = uriPrefix.IndexOf ('/', hostStartIdx, len - hostStartIdx); - - if (rootIdx == hostStartIdx) { - var msg = "No host is specified."; - - throw new ArgumentException (msg, "uriPrefix"); - } - - if (uriPrefix[rootIdx - 1] == ':') { - var msg = "No port is specified."; - - throw new ArgumentException (msg, "uriPrefix"); - } - - if (rootIdx == endIdx - 1) { - var msg = "No path is specified."; - - throw new ArgumentException (msg, "uriPrefix"); - } - } - - public override bool Equals (object obj) - { - var pref = obj as HttpListenerPrefix; - - return pref != null && _prefix.Equals (pref._prefix); - } - - public override int GetHashCode () - { - return _prefix.GetHashCode (); - } - - public override string ToString () - { - return _prefix; - } - - #endregion - } -} diff --git a/websocket-sharp/Net/HttpListenerPrefixCollection.cs b/websocket-sharp/Net/HttpListenerPrefixCollection.cs deleted file mode 100644 index 3f5c44a2c..000000000 --- a/websocket-sharp/Net/HttpListenerPrefixCollection.cs +++ /dev/null @@ -1,295 +0,0 @@ -#region License -/* - * HttpListenerPrefixCollection.cs - * - * This code is derived from HttpListenerPrefixCollection.cs (System.Net) of Mono - * (http://www.mono-project.com). - * - * The MIT License - * - * Copyright (c) 2005 Novell, Inc. (http://www.novell.com) - * Copyright (c) 2012-2024 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -#region Authors -/* - * Authors: - * - Gonzalo Paniagua Javier - */ -#endregion - -using System; -using System.Collections; -using System.Collections.Generic; - -namespace WebSocketSharp.Net -{ - /// - /// Provides a collection used to store the URI prefixes for a instance of - /// the class. - /// - /// - /// The instance responds to the request which - /// has a requested URI that the prefixes most closely match. - /// - public class HttpListenerPrefixCollection : ICollection - { - #region Private Fields - - private HttpListener _listener; - private List _prefixes; - - #endregion - - #region Internal Constructors - - internal HttpListenerPrefixCollection (HttpListener listener) - { - _listener = listener; - - _prefixes = new List (); - } - - #endregion - - #region Public Properties - - /// - /// Gets the number of prefixes in the collection. - /// - /// - /// An that represents the number of prefixes. - /// - public int Count { - get { - return _prefixes.Count; - } - } - - /// - /// Gets a value indicating whether the access to the collection is - /// read-only. - /// - /// - /// Always returns false. - /// - public bool IsReadOnly { - get { - return false; - } - } - - /// - /// Gets a value indicating whether the access to the collection is - /// synchronized. - /// - /// - /// Always returns false. - /// - public bool IsSynchronized { - get { - return false; - } - } - - #endregion - - #region Public Methods - - /// - /// Adds the specified URI prefix to the collection. - /// - /// - /// - /// A that specifies the URI prefix to add. - /// - /// - /// It must be a well-formed URI prefix with http or https scheme, - /// and must end with a forward slash (/). - /// - /// - /// - /// is invalid. - /// - /// - /// is . - /// - /// - /// The instance associated with this - /// collection is closed. - /// - public void Add (string uriPrefix) - { - _listener.CheckDisposed (); - - HttpListenerPrefix.CheckPrefix (uriPrefix); - - if (_prefixes.Contains (uriPrefix)) - return; - - if (_listener.IsListening) - EndPointManager.AddPrefix (uriPrefix, _listener); - - _prefixes.Add (uriPrefix); - } - - /// - /// Removes all URI prefixes from the collection. - /// - /// - /// The instance associated with this - /// collection is closed. - /// - public void Clear () - { - _listener.CheckDisposed (); - - if (_listener.IsListening) - EndPointManager.RemoveListener (_listener); - - _prefixes.Clear (); - } - - /// - /// Returns a value indicating whether the collection contains the - /// specified URI prefix. - /// - /// - /// true if the collection contains the URI prefix; otherwise, - /// false. - /// - /// - /// A that specifies the URI prefix to test. - /// - /// - /// is . - /// - /// - /// The instance associated with this - /// collection is closed. - /// - public bool Contains (string uriPrefix) - { - _listener.CheckDisposed (); - - if (uriPrefix == null) - throw new ArgumentNullException ("uriPrefix"); - - return _prefixes.Contains (uriPrefix); - } - - /// - /// Copies the contents of the collection to the specified array of string. - /// - /// - /// An array of that specifies the destination of - /// the URI prefix strings copied from the collection. - /// - /// - /// An that specifies the zero-based index in - /// the array at which copying begins. - /// - /// - /// The space from to the end of - /// is not enough to copy to. - /// - /// - /// is . - /// - /// - /// is less than zero. - /// - /// - /// The instance associated with this - /// collection is closed. - /// - public void CopyTo (string[] array, int offset) - { - _listener.CheckDisposed (); - - _prefixes.CopyTo (array, offset); - } - - /// - /// Gets the enumerator that iterates through the collection. - /// - /// - /// An - /// instance that can be used to iterate through the collection. - /// - public IEnumerator GetEnumerator () - { - return _prefixes.GetEnumerator (); - } - - /// - /// Removes the specified URI prefix from the collection. - /// - /// - /// true if the URI prefix is successfully removed; otherwise, - /// false. - /// - /// - /// A that specifies the URI prefix to remove. - /// - /// - /// is . - /// - /// - /// The instance associated with this - /// collection is closed. - /// - public bool Remove (string uriPrefix) - { - _listener.CheckDisposed (); - - if (uriPrefix == null) - throw new ArgumentNullException ("uriPrefix"); - - if (!_prefixes.Contains (uriPrefix)) - return false; - - if (_listener.IsListening) - EndPointManager.RemovePrefix (uriPrefix, _listener); - - return _prefixes.Remove (uriPrefix); - } - - #endregion - - #region Explicit Interface Implementations - - /// - /// Gets the enumerator that iterates through the collection. - /// - /// - /// An instance that can be used to iterate - /// through the collection. - /// - IEnumerator IEnumerable.GetEnumerator () - { - return _prefixes.GetEnumerator (); - } - - #endregion - } -} diff --git a/websocket-sharp/Net/HttpListenerRequest.cs b/websocket-sharp/Net/HttpListenerRequest.cs deleted file mode 100644 index 9c7b1a16d..000000000 --- a/websocket-sharp/Net/HttpListenerRequest.cs +++ /dev/null @@ -1,943 +0,0 @@ -#region License -/* - * HttpListenerRequest.cs - * - * This code is derived from HttpListenerRequest.cs (System.Net) of Mono - * (http://www.mono-project.com). - * - * The MIT License - * - * Copyright (c) 2005 Novell, Inc. (http://www.novell.com) - * Copyright (c) 2012-2024 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -#region Authors -/* - * Authors: - * - Gonzalo Paniagua Javier - */ -#endregion - -using System; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.Globalization; -using System.IO; -using System.Security.Cryptography.X509Certificates; -using System.Text; - -namespace WebSocketSharp.Net -{ - /// - /// Represents an incoming HTTP request to a - /// instance. - /// - /// - /// This class cannot be inherited. - /// - public sealed class HttpListenerRequest - { - #region Private Fields - - private static readonly byte[] _100continue; - private string[] _acceptTypes; - private bool _chunked; - private HttpConnection _connection; - private Encoding _contentEncoding; - private long _contentLength; - private HttpListenerContext _context; - private CookieCollection _cookies; - private static readonly Encoding _defaultEncoding; - private WebHeaderCollection _headers; - private string _httpMethod; - private Stream _inputStream; - private Version _protocolVersion; - private NameValueCollection _queryString; - private string _rawUrl; - private Guid _requestTraceIdentifier; - private Uri _url; - private Uri _urlReferrer; - private bool _urlSet; - private string _userHostName; - private string[] _userLanguages; - - #endregion - - #region Static Constructor - - static HttpListenerRequest () - { - _100continue = Encoding.ASCII.GetBytes ("HTTP/1.1 100 Continue\r\n\r\n"); - _defaultEncoding = Encoding.UTF8; - } - - #endregion - - #region Internal Constructors - - internal HttpListenerRequest (HttpListenerContext context) - { - _context = context; - - _connection = context.Connection; - _contentLength = -1; - _headers = new WebHeaderCollection (); - _requestTraceIdentifier = Guid.NewGuid (); - } - - #endregion - - #region Public Properties - - /// - /// Gets the media types that are acceptable for the client. - /// - /// - /// - /// An array of that contains the names of - /// the media types specified in the value of the Accept header. - /// - /// - /// if the header is not present. - /// - /// - public string[] AcceptTypes { - get { - var val = _headers["Accept"]; - - if (val == null) - return null; - - if (_acceptTypes == null) { - _acceptTypes = val - .SplitHeaderValue (',') - .TrimEach () - .ToList () - .ToArray (); - } - - return _acceptTypes; - } - } - - /// - /// Gets an error code that identifies a problem with the certificate - /// provided by the client. - /// - /// - /// An that represents an error code. - /// - /// - /// This property is not supported. - /// - public int ClientCertificateError { - get { - throw new NotSupportedException (); - } - } - - /// - /// Gets the encoding for the entity body data included in the request. - /// - /// - /// - /// A converted from the charset value of the - /// Content-Type header. - /// - /// - /// if the charset value is not available. - /// - /// - public Encoding ContentEncoding { - get { - if (_contentEncoding == null) - _contentEncoding = getContentEncoding (); - - return _contentEncoding; - } - } - - /// - /// Gets the length in bytes of the entity body data included in the - /// request. - /// - /// - /// - /// A converted from the value of the Content-Length - /// header. - /// - /// - /// -1 if the header is not present. - /// - /// - public long ContentLength64 { - get { - return _contentLength; - } - } - - /// - /// Gets the media type of the entity body data included in the request. - /// - /// - /// - /// A that represents the value of the Content-Type - /// header. - /// - /// - /// if the header is not present. - /// - /// - public string ContentType { - get { - return _headers["Content-Type"]; - } - } - - /// - /// Gets the HTTP cookies included in the request. - /// - /// - /// - /// A that contains the cookies. - /// - /// - /// An empty collection if not included. - /// - /// - public CookieCollection Cookies { - get { - if (_cookies == null) - _cookies = _headers.GetCookies (false); - - return _cookies; - } - } - - /// - /// Gets a value indicating whether the request has the entity body data. - /// - /// - /// true if the request has the entity body data; otherwise, - /// false. - /// - public bool HasEntityBody { - get { - return _contentLength > 0 || _chunked; - } - } - - /// - /// Gets the HTTP headers included in the request. - /// - /// - /// A that contains the headers. - /// - public NameValueCollection Headers { - get { - return _headers; - } - } - - /// - /// Gets the HTTP method specified by the client. - /// - /// - /// A that represents the HTTP method specified in - /// the request line. - /// - public string HttpMethod { - get { - return _httpMethod; - } - } - - /// - /// Gets a stream that contains the entity body data included in - /// the request. - /// - /// - /// - /// A that contains the entity body data. - /// - /// - /// if the entity body data is not available. - /// - /// - public Stream InputStream { - get { - if (_inputStream == null) { - _inputStream = _contentLength > 0 || _chunked - ? _connection - .GetRequestStream (_contentLength, _chunked) - : Stream.Null; - } - - return _inputStream; - } - } - - /// - /// Gets a value indicating whether the client is authenticated. - /// - /// - /// true if the client is authenticated; otherwise, false. - /// - public bool IsAuthenticated { - get { - return _context.User != null; - } - } - - /// - /// Gets a value indicating whether the request is sent from the - /// local computer. - /// - /// - /// true if the request is sent from the same computer as - /// the server; otherwise, false. - /// - public bool IsLocal { - get { - return _connection.IsLocal; - } - } - - /// - /// Gets a value indicating whether a secure connection is used to send - /// the request. - /// - /// - /// true if the connection is secure; otherwise, false. - /// - public bool IsSecureConnection { - get { - return _connection.IsSecure; - } - } - - /// - /// Gets a value indicating whether the request is a WebSocket handshake - /// request. - /// - /// - /// true if the request is a WebSocket handshake request; otherwise, - /// false. - /// - public bool IsWebSocketRequest { - get { - return _httpMethod == "GET" && _headers.Upgrades ("websocket"); - } - } - - /// - /// Gets a value indicating whether a persistent connection is requested. - /// - /// - /// true if the request specifies that the connection is kept open; - /// otherwise, false. - /// - public bool KeepAlive { - get { - return _headers.KeepsAlive (_protocolVersion); - } - } - - /// - /// Gets the endpoint to which the request is sent. - /// - /// - /// A that represents the server - /// IP address and port number. - /// - public System.Net.IPEndPoint LocalEndPoint { - get { - return _connection.LocalEndPoint; - } - } - - /// - /// Gets the HTTP version specified by the client. - /// - /// - /// A that represents the HTTP version specified in - /// the request line. - /// - public Version ProtocolVersion { - get { - return _protocolVersion; - } - } - - /// - /// Gets the query string included in the request. - /// - /// - /// - /// A that contains the query - /// parameters. - /// - /// - /// Each query parameter is decoded in UTF-8. - /// - /// - /// An empty collection if not included. - /// - /// - public NameValueCollection QueryString { - get { - if (_queryString == null) { - var url = Url; - var query = url != null ? url.Query : null; - - _queryString = QueryStringCollection.Parse (query, _defaultEncoding); - } - - return _queryString; - } - } - - /// - /// Gets the raw URL specified by the client. - /// - /// - /// A that represents the request target specified in - /// the request line. - /// - public string RawUrl { - get { - return _rawUrl; - } - } - - /// - /// Gets the endpoint from which the request is sent. - /// - /// - /// A that represents the client - /// IP address and port number. - /// - public System.Net.IPEndPoint RemoteEndPoint { - get { - return _connection.RemoteEndPoint; - } - } - - /// - /// Gets the trace identifier of the request. - /// - /// - /// A that represents the trace identifier. - /// - public Guid RequestTraceIdentifier { - get { - return _requestTraceIdentifier; - } - } - - /// - /// Gets the URL requested by the client. - /// - /// - /// - /// A that represents the URL parsed from the request. - /// - /// - /// if the URL cannot be parsed. - /// - /// - public Uri Url { - get { - if (!_urlSet) { - _url = HttpUtility - .CreateRequestUrl ( - _rawUrl, - _userHostName, - IsWebSocketRequest, - IsSecureConnection - ); - - _urlSet = true; - } - - return _url; - } - } - - /// - /// Gets the URI of the resource from which the requested URL was obtained. - /// - /// - /// - /// A that represents the value of the Referer header. - /// - /// - /// if the header value is not available. - /// - /// - public Uri UrlReferrer { - get { - var val = _headers["Referer"]; - - if (val == null) - return null; - - if (_urlReferrer == null) - _urlReferrer = val.ToUri (); - - return _urlReferrer; - } - } - - /// - /// Gets the user agent from which the request is originated. - /// - /// - /// - /// A that represents the value of the User-Agent - /// header. - /// - /// - /// if the header is not present. - /// - /// - public string UserAgent { - get { - return _headers["User-Agent"]; - } - } - - /// - /// Gets the IP address and port number to which the request is sent. - /// - /// - /// A that represents the server IP address and - /// port number. - /// - public string UserHostAddress { - get { - return _connection.LocalEndPoint.ToString (); - } - } - - /// - /// Gets the server host name requested by the client. - /// - /// - /// - /// A that represents the value of the Host header. - /// - /// - /// It includes the port number if provided. - /// - /// - public string UserHostName { - get { - return _userHostName; - } - } - - /// - /// Gets the natural languages that are acceptable for the client. - /// - /// - /// - /// An array of that contains the names of the - /// natural languages specified in the value of the Accept-Language - /// header. - /// - /// - /// if the header is not present. - /// - /// - public string[] UserLanguages { - get { - var val = _headers["Accept-Language"]; - - if (val == null) - return null; - - if (_userLanguages == null) - _userLanguages = val.Split (',').TrimEach ().ToList ().ToArray (); - - return _userLanguages; - } - } - - #endregion - - #region Private Methods - - private Encoding getContentEncoding () - { - var val = _headers["Content-Type"]; - - if (val == null) - return _defaultEncoding; - - Encoding ret; - - return HttpUtility.TryGetEncoding (val, out ret) - ? ret - : _defaultEncoding; - } - - #endregion - - #region Internal Methods - - internal void AddHeader (string headerField) - { - var start = headerField[0]; - - if (start == ' ' || start == '\t') { - _context.ErrorMessage = "Invalid header field"; - - return; - } - - var colon = headerField.IndexOf (':'); - - if (colon < 1) { - _context.ErrorMessage = "Invalid header field"; - - return; - } - - var name = headerField.Substring (0, colon).Trim (); - - if (name.Length == 0 || !name.IsToken ()) { - _context.ErrorMessage = "Invalid header name"; - - return; - } - - var val = colon < headerField.Length - 1 - ? headerField.Substring (colon + 1).Trim () - : String.Empty; - - _headers.InternalSet (name, val, false); - - var lower = name.ToLower (CultureInfo.InvariantCulture); - - if (lower == "host") { - if (_userHostName != null) { - _context.ErrorMessage = "Invalid Host header"; - - return; - } - - if (val.Length == 0) { - _context.ErrorMessage = "Invalid Host header"; - - return; - } - - _userHostName = val; - - return; - } - - if (lower == "content-length") { - if (_contentLength > -1) { - _context.ErrorMessage = "Invalid Content-Length header"; - - return; - } - - long len; - - if (!Int64.TryParse (val, out len)) { - _context.ErrorMessage = "Invalid Content-Length header"; - - return; - } - - if (len < 0) { - _context.ErrorMessage = "Invalid Content-Length header"; - - return; - } - - _contentLength = len; - - return; - } - } - - internal void FinishInitialization () - { - if (_userHostName == null) { - _context.ErrorMessage = "Host header required"; - - return; - } - - var transferEnc = _headers["Transfer-Encoding"]; - - if (transferEnc != null) { - var compType = StringComparison.OrdinalIgnoreCase; - - if (!transferEnc.Equals ("chunked", compType)) { - _context.ErrorStatusCode = 501; - _context.ErrorMessage = "Invalid Transfer-Encoding header"; - - return; - } - - _chunked = true; - } - - if (_httpMethod == "POST" || _httpMethod == "PUT") { - if (_contentLength == -1 && !_chunked) { - _context.ErrorStatusCode = 411; - _context.ErrorMessage = "Content-Length header required"; - - return; - } - - if (_contentLength == 0 && !_chunked) { - _context.ErrorStatusCode = 411; - _context.ErrorMessage = "Invalid Content-Length header"; - - return; - } - } - - var expect = _headers["Expect"]; - - if (expect != null) { - var compType = StringComparison.OrdinalIgnoreCase; - - if (!expect.Equals ("100-continue", compType)) { - _context.ErrorStatusCode = 417; - _context.ErrorMessage = "Invalid Expect header"; - - return; - } - - var output = _connection.GetResponseStream (); - - output.InternalWrite (_100continue, 0, _100continue.Length); - } - } - - internal bool FlushInput () - { - var input = InputStream; - - if (input == Stream.Null) - return true; - - var len = 2048; - - if (_contentLength > 0 && _contentLength < len) - len = (int) _contentLength; - - var buff = new byte[len]; - - while (true) { - try { - var ares = input.BeginRead (buff, 0, len, null, null); - - if (!ares.IsCompleted) { - var timeout = 100; - - if (!ares.AsyncWaitHandle.WaitOne (timeout)) - return false; - } - - if (input.EndRead (ares) <= 0) - return true; - } - catch { - return false; - } - } - } - - internal bool IsUpgradeRequest (string protocol) - { - return _headers.Upgrades (protocol); - } - - internal void SetRequestLine (string requestLine) - { - var parts = requestLine.Split (new[] { ' ' }, 3); - - if (parts.Length < 3) { - _context.ErrorMessage = "Invalid request line (parts)"; - - return; - } - - var method = parts[0]; - - if (method.Length == 0) { - _context.ErrorMessage = "Invalid request line (method)"; - - return; - } - - if (!method.IsHttpMethod ()) { - _context.ErrorStatusCode = 501; - _context.ErrorMessage = "Invalid request line (method)"; - - return; - } - - var target = parts[1]; - - if (target.Length == 0) { - _context.ErrorMessage = "Invalid request line (target)"; - - return; - } - - var rawVer = parts[2]; - - if (rawVer.Length != 8) { - _context.ErrorMessage = "Invalid request line (version)"; - - return; - } - - if (!rawVer.StartsWith ("HTTP/", StringComparison.Ordinal)) { - _context.ErrorMessage = "Invalid request line (version)"; - - return; - } - - Version ver; - - if (!rawVer.Substring (5).TryCreateVersion (out ver)) { - _context.ErrorMessage = "Invalid request line (version)"; - - return; - } - - if (ver != HttpVersion.Version11) { - _context.ErrorStatusCode = 505; - _context.ErrorMessage = "Invalid request line (version)"; - - return; - } - - _httpMethod = method; - _rawUrl = target; - _protocolVersion = ver; - } - - #endregion - - #region Public Methods - - /// - /// Begins getting the certificate provided by the client asynchronously. - /// - /// - /// An instance that represents the status of - /// the asynchronous operation. - /// - /// - /// - /// An delegate. - /// - /// - /// It specifies the delegate called when the asynchronous operation is - /// complete. - /// - /// - /// - /// An that specifies a user defined object to pass to - /// . - /// - /// - /// This method is not supported. - /// - public IAsyncResult BeginGetClientCertificate ( - AsyncCallback requestCallback, - object state - ) - { - throw new NotSupportedException (); - } - - /// - /// Ends an asynchronous operation to get the certificate provided by - /// the client. - /// - /// - /// A that represents an X.509 certificate - /// provided by the client. - /// - /// - /// An instance obtained by calling - /// the method. - /// - /// - /// This method is not supported. - /// - public X509Certificate2 EndGetClientCertificate (IAsyncResult asyncResult) - { - throw new NotSupportedException (); - } - - /// - /// Gets the certificate provided by the client. - /// - /// - /// A that represents an X.509 certificate - /// provided by the client. - /// - /// - /// This method is not supported. - /// - public X509Certificate2 GetClientCertificate () - { - throw new NotSupportedException (); - } - - /// - /// Returns a string that represents the current instance. - /// - /// - /// A that contains the request line and headers - /// included in the request. - /// - public override string ToString () - { - var buff = new StringBuilder (64); - - var fmt = "{0} {1} HTTP/{2}\r\n"; - var headers = _headers.ToString (); - - buff - .AppendFormat (fmt, _httpMethod, _rawUrl, _protocolVersion) - .Append (headers); - - return buff.ToString (); - } - - #endregion - } -} diff --git a/websocket-sharp/Net/HttpListenerResponse.cs b/websocket-sharp/Net/HttpListenerResponse.cs deleted file mode 100644 index 500d42827..000000000 --- a/websocket-sharp/Net/HttpListenerResponse.cs +++ /dev/null @@ -1,1201 +0,0 @@ -#region License -/* - * HttpListenerResponse.cs - * - * This code is derived from HttpListenerResponse.cs (System.Net) of Mono - * (http://www.mono-project.com). - * - * The MIT License - * - * Copyright (c) 2005 Novell, Inc. (http://www.novell.com) - * Copyright (c) 2012-2024 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -#region Authors -/* - * Authors: - * - Gonzalo Paniagua Javier - */ -#endregion - -#region Contributors -/* - * Contributors: - * - Nicholas Devenish - */ -#endregion - -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Text; - -namespace WebSocketSharp.Net -{ - /// - /// Represents an HTTP response to an HTTP request received by - /// a instance. - /// - /// - /// This class cannot be inherited. - /// - public sealed class HttpListenerResponse : IDisposable - { - #region Private Fields - - private bool _closeConnection; - private Encoding _contentEncoding; - private long _contentLength; - private string _contentType; - private HttpListenerContext _context; - private CookieCollection _cookies; - private static readonly string _defaultProductName; - private bool _disposed; - private WebHeaderCollection _headers; - private bool _headersSent; - private bool _keepAlive; - private ResponseStream _outputStream; - private Uri _redirectLocation; - private bool _sendChunked; - private int _statusCode; - private string _statusDescription; - private Version _version; - - #endregion - - #region Static Constructor - - static HttpListenerResponse () - { - _defaultProductName = "websocket-sharp/1.0"; - } - - #endregion - - #region Internal Constructors - - internal HttpListenerResponse (HttpListenerContext context) - { - _context = context; - - _keepAlive = true; - _statusCode = 200; - _statusDescription = "OK"; - _version = HttpVersion.Version11; - } - - #endregion - - #region Internal Properties - - internal bool CloseConnection { - get { - return _closeConnection; - } - - set { - _closeConnection = value; - } - } - - internal WebHeaderCollection FullHeaders { - get { - var headers = new WebHeaderCollection (HttpHeaderType.Response, true); - - if (_headers != null) - headers.Add (_headers); - - if (_contentType != null) { - var val = createContentTypeHeaderText (_contentType, _contentEncoding); - - headers.InternalSet ("Content-Type", val, true); - } - - if (headers["Server"] == null) - headers.InternalSet ("Server", _defaultProductName, true); - - if (headers["Date"] == null) { - var val = DateTime.UtcNow.ToString ("r", CultureInfo.InvariantCulture); - - headers.InternalSet ("Date", val, true); - } - - if (_sendChunked) { - headers.InternalSet ("Transfer-Encoding", "chunked", true); - } - else { - var val = _contentLength.ToString (CultureInfo.InvariantCulture); - - headers.InternalSet ("Content-Length", val, true); - } - - /* - * Apache forces closing the connection for these status codes: - * - 400 Bad Request - * - 408 Request Timeout - * - 411 Length Required - * - 413 Request Entity Too Large - * - 414 Request-Uri Too Long - * - 500 Internal Server Error - * - 503 Service Unavailable - */ - - var reuses = _context.Connection.Reuses; - var closeConn = !_context.Request.KeepAlive - || !_keepAlive - || reuses >= 100 - || _statusCode == 400 - || _statusCode == 408 - || _statusCode == 411 - || _statusCode == 413 - || _statusCode == 414 - || _statusCode == 500 - || _statusCode == 503; - - if (closeConn) { - headers.InternalSet ("Connection", "close", true); - } - else { - var fmt = "timeout=15,max={0}"; - var max = 100 - reuses; - var val = String.Format (fmt, max); - - headers.InternalSet ("Keep-Alive", val, true); - } - - if (_redirectLocation != null) - headers.InternalSet ("Location", _redirectLocation.AbsoluteUri, true); - - if (_cookies != null) { - foreach (var cookie in _cookies) { - var val = cookie.ToResponseString (); - - headers.InternalSet ("Set-Cookie", val, true); - } - } - - return headers; - } - } - - internal bool HeadersSent { - get { - return _headersSent; - } - - set { - _headersSent = value; - } - } - - internal string ObjectName { - get { - return GetType ().ToString (); - } - } - - internal string StatusLine { - get { - var fmt = "HTTP/{0} {1} {2}\r\n"; - - return String.Format (fmt, _version, _statusCode, _statusDescription); - } - } - - #endregion - - #region Public Properties - - /// - /// Gets or sets the encoding for the entity body data included in - /// the response. - /// - /// - /// - /// A that represents the encoding for - /// the entity body data. - /// - /// - /// if no encoding is specified. - /// - /// - /// The default value is . - /// - /// - /// - /// The response is already being sent. - /// - /// - /// This instance is closed. - /// - public Encoding ContentEncoding { - get { - return _contentEncoding; - } - - set { - if (_disposed) - throw new ObjectDisposedException (ObjectName); - - if (_headersSent) { - var msg = "The response is already being sent."; - - throw new InvalidOperationException (msg); - } - - _contentEncoding = value; - } - } - - /// - /// Gets or sets the number of bytes in the entity body data included in - /// the response. - /// - /// - /// - /// A that represents the number of bytes in - /// the entity body data. - /// - /// - /// It is used for the value of the Content-Length header. - /// - /// - /// The default value is zero. - /// - /// - /// - /// The value specified for a set operation is less than zero. - /// - /// - /// The response is already being sent. - /// - /// - /// This instance is closed. - /// - public long ContentLength64 { - get { - return _contentLength; - } - - set { - if (_disposed) - throw new ObjectDisposedException (ObjectName); - - if (_headersSent) { - var msg = "The response is already being sent."; - - throw new InvalidOperationException (msg); - } - - if (value < 0) { - var msg = "Less than zero."; - - throw new ArgumentOutOfRangeException (msg, "value"); - } - - _contentLength = value; - } - } - - /// - /// Gets or sets the media type of the entity body included in - /// the response. - /// - /// - /// - /// A that represents the media type of - /// the entity body. - /// - /// - /// It is used for the value of the Content-Type header. - /// - /// - /// if no media type is specified. - /// - /// - /// The default value is . - /// - /// - /// - /// - /// The value specified for a set operation is an empty string. - /// - /// - /// -or- - /// - /// - /// The value specified for a set operation contains - /// an invalid character. - /// - /// - /// - /// The response is already being sent. - /// - /// - /// This instance is closed. - /// - public string ContentType { - get { - return _contentType; - } - - set { - if (_disposed) - throw new ObjectDisposedException (ObjectName); - - if (_headersSent) { - var msg = "The response is already being sent."; - - throw new InvalidOperationException (msg); - } - - if (value == null) { - _contentType = null; - - return; - } - - if (value.Length == 0) - throw new ArgumentException ("An empty string.", "value"); - - if (!isValidForContentType (value)) { - var msg = "It contains an invalid character."; - - throw new ArgumentException (msg, "value"); - } - - _contentType = value; - } - } - - /// - /// Gets or sets the collection of the HTTP cookies sent with the response. - /// - /// - /// A that contains the cookies sent with - /// the response. - /// - public CookieCollection Cookies { - get { - if (_cookies == null) - _cookies = new CookieCollection (); - - return _cookies; - } - - set { - _cookies = value; - } - } - - /// - /// Gets or sets the collection of the HTTP headers sent to the client. - /// - /// - /// A that contains the headers sent to - /// the client. - /// - /// - /// The value specified for a set operation is not valid for a response. - /// - public WebHeaderCollection Headers { - get { - if (_headers == null) - _headers = new WebHeaderCollection (HttpHeaderType.Response, false); - - return _headers; - } - - set { - if (value == null) { - _headers = null; - - return; - } - - if (value.State != HttpHeaderType.Response) { - var msg = "The value is not valid for a response."; - - throw new InvalidOperationException (msg); - } - - _headers = value; - } - } - - /// - /// Gets or sets a value indicating whether the server requests - /// a persistent connection. - /// - /// - /// - /// true if the server requests a persistent connection; - /// otherwise, false. - /// - /// - /// The default value is true. - /// - /// - /// - /// The response is already being sent. - /// - /// - /// This instance is closed. - /// - public bool KeepAlive { - get { - return _keepAlive; - } - - set { - if (_disposed) - throw new ObjectDisposedException (ObjectName); - - if (_headersSent) { - var msg = "The response is already being sent."; - - throw new InvalidOperationException (msg); - } - - _keepAlive = value; - } - } - - /// - /// Gets a stream instance to which the entity body data can be written. - /// - /// - /// A instance to which the entity body data can be - /// written. - /// - /// - /// This instance is closed. - /// - public Stream OutputStream { - get { - if (_disposed) - throw new ObjectDisposedException (ObjectName); - - if (_outputStream == null) - _outputStream = _context.Connection.GetResponseStream (); - - return _outputStream; - } - } - - /// - /// Gets the HTTP version used for the response. - /// - /// - /// - /// A that represents the HTTP version used for - /// the response. - /// - /// - /// Always returns same as 1.1. - /// - /// - public Version ProtocolVersion { - get { - return _version; - } - } - - /// - /// Gets or sets the URL to which the client is redirected to locate - /// a requested resource. - /// - /// - /// - /// A that represents the absolute URL for - /// the redirect location. - /// - /// - /// It is used for the value of the Location header. - /// - /// - /// if no redirect location is specified. - /// - /// - /// The default value is . - /// - /// - /// - /// - /// The value specified for a set operation is an empty string. - /// - /// - /// -or- - /// - /// - /// The value specified for a set operation is not an absolute URL. - /// - /// - /// - /// The response is already being sent. - /// - /// - /// This instance is closed. - /// - public string RedirectLocation { - get { - return _redirectLocation != null - ? _redirectLocation.OriginalString - : null; - } - - set { - if (_disposed) - throw new ObjectDisposedException (ObjectName); - - if (_headersSent) { - var msg = "The response is already being sent."; - - throw new InvalidOperationException (msg); - } - - if (value == null) { - _redirectLocation = null; - - return; - } - - if (value.Length == 0) - throw new ArgumentException ("An empty string.", "value"); - - Uri uri; - - if (!Uri.TryCreate (value, UriKind.Absolute, out uri)) { - var msg = "Not an absolute URL."; - - throw new ArgumentException (msg, "value"); - } - - _redirectLocation = uri; - } - } - - /// - /// Gets or sets a value indicating whether the response uses the chunked - /// transfer encoding. - /// - /// - /// - /// true if the response uses the chunked transfer encoding; - /// otherwise, false. - /// - /// - /// The default value is false. - /// - /// - /// - /// The response is already being sent. - /// - /// - /// This instance is closed. - /// - public bool SendChunked { - get { - return _sendChunked; - } - - set { - if (_disposed) - throw new ObjectDisposedException (ObjectName); - - if (_headersSent) { - var msg = "The response is already being sent."; - - throw new InvalidOperationException (msg); - } - - _sendChunked = value; - } - } - - /// - /// Gets or sets the HTTP status code returned to the client. - /// - /// - /// - /// An that represents the HTTP status code for - /// the response to the request. - /// - /// - /// The default value is 200. It indicates that the request has - /// succeeded. - /// - /// - /// - /// The response is already being sent. - /// - /// - /// This instance is closed. - /// - /// - /// - /// The value specified for a set operation is invalid. - /// - /// - /// Valid values are between 100 and 999 inclusive. - /// - /// - public int StatusCode { - get { - return _statusCode; - } - - set { - if (_disposed) - throw new ObjectDisposedException (ObjectName); - - if (_headersSent) { - var msg = "The response is already being sent."; - - throw new InvalidOperationException (msg); - } - - if (value < 100 || value > 999) { - var msg = "A value is not between 100 and 999 inclusive."; - - throw new System.Net.ProtocolViolationException (msg); - } - - _statusCode = value; - _statusDescription = value.GetStatusDescription (); - } - } - - /// - /// Gets or sets the description of the HTTP status code returned to - /// the client. - /// - /// - /// - /// A that represents the description of - /// the HTTP status code for the response to the request. - /// - /// - /// The default value is - /// the - /// RFC 2616 description for the - /// property value. - /// - /// - /// An empty string if an RFC 2616 description does not exist. - /// - /// - /// - /// The value specified for a set operation contains an invalid character. - /// - /// - /// The value specified for a set operation is . - /// - /// - /// The response is already being sent. - /// - /// - /// This instance is closed. - /// - public string StatusDescription { - get { - return _statusDescription; - } - - set { - if (_disposed) - throw new ObjectDisposedException (ObjectName); - - if (_headersSent) { - var msg = "The response is already being sent."; - - throw new InvalidOperationException (msg); - } - - if (value == null) - throw new ArgumentNullException ("value"); - - if (value.Length == 0) { - _statusDescription = _statusCode.GetStatusDescription (); - - return; - } - - if (!isValidForStatusDescription (value)) { - var msg = "It contains an invalid character."; - - throw new ArgumentException (msg, "value"); - } - - _statusDescription = value; - } - } - - #endregion - - #region Private Methods - - private bool canSetCookie (Cookie cookie) - { - var res = findCookie (cookie).ToList (); - - if (res.Count == 0) - return true; - - var ver = cookie.Version; - - foreach (var c in res) { - if (c.Version == ver) - return true; - } - - return false; - } - - private void close (bool force) - { - _disposed = true; - - _context.Connection.Close (force); - } - - private void close (byte[] responseEntity, int bufferLength, bool willBlock) - { - if (willBlock) { - OutputStream.WriteBytes (responseEntity, bufferLength); - close (false); - - return; - } - - OutputStream.WriteBytesAsync ( - responseEntity, - bufferLength, - () => close (false), - null - ); - } - - private static string createContentTypeHeaderText ( - string value, - Encoding encoding - ) - { - if (value.Contains ("charset=")) - return value; - - if (encoding == null) - return value; - - var fmt = "{0}; charset={1}"; - - return String.Format (fmt, value, encoding.WebName); - } - - private IEnumerable findCookie (Cookie cookie) - { - if (_cookies == null || _cookies.Count == 0) - yield break; - - foreach (var c in _cookies) { - if (c.EqualsWithoutValueAndVersion (cookie)) - yield return c; - } - } - - private static bool isValidForContentType (string value) - { - foreach (var c in value) { - if (c < 0x20) - return false; - - if (c > 0x7e) - return false; - - if ("()<>@:\\[]?{}".IndexOf (c) > -1) - return false; - } - - return true; - } - - private static bool isValidForStatusDescription (string value) - { - foreach (var c in value) { - if (c < 0x20) - return false; - - if (c > 0x7e) - return false; - } - - return true; - } - - #endregion - - #region Public Methods - - /// - /// Closes the connection to the client without sending a response. - /// - public void Abort () - { - if (_disposed) - return; - - close (true); - } - - /// - /// Appends an HTTP cookie to the cookies sent with the response. - /// - /// - /// A to append. - /// - /// - /// is . - /// - public void AppendCookie (Cookie cookie) - { - Cookies.Add (cookie); - } - - /// - /// Appends an HTTP header with the specified name and value to - /// the headers for the response. - /// - /// - /// A that specifies the name of the header to - /// append. - /// - /// - /// A that specifies the value of the header to - /// append. - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// is a string of spaces. - /// - /// - /// -or- - /// - /// - /// contains an invalid character. - /// - /// - /// -or- - /// - /// - /// contains an invalid character. - /// - /// - /// -or- - /// - /// - /// is a restricted header name. - /// - /// - /// - /// is . - /// - /// - /// The length of is greater than 65,535 - /// characters. - /// - /// - /// The current headers do not allow the header. - /// - public void AppendHeader (string name, string value) - { - Headers.Add (name, value); - } - - /// - /// Sends the response to the client and releases the resources used by - /// this instance. - /// - public void Close () - { - if (_disposed) - return; - - close (false); - } - - /// - /// Sends the response with the specified entity body data to the client - /// and releases the resources used by this instance. - /// - /// - /// An array of that contains the entity body data. - /// - /// - /// A : true if this method blocks execution while - /// flushing the stream to the client; otherwise, false. - /// - /// - /// is . - /// - /// - /// This instance is closed. - /// - public void Close (byte[] responseEntity, bool willBlock) - { - if (_disposed) - throw new ObjectDisposedException (ObjectName); - - if (responseEntity == null) - throw new ArgumentNullException ("responseEntity"); - - var len = responseEntity.LongLength; - - if (len > Int32.MaxValue) { - close (responseEntity, 1024, willBlock); - - return; - } - - var stream = OutputStream; - - if (willBlock) { - stream.Write (responseEntity, 0, (int) len); - close (false); - - return; - } - - stream.BeginWrite ( - responseEntity, - 0, - (int) len, - ar => { - stream.EndWrite (ar); - close (false); - }, - null - ); - } - - /// - /// Copies some properties from the specified response instance to - /// this instance. - /// - /// - /// A to copy. - /// - /// - /// is . - /// - public void CopyFrom (HttpListenerResponse templateResponse) - { - if (templateResponse == null) - throw new ArgumentNullException ("templateResponse"); - - var headers = templateResponse._headers; - - if (headers != null) { - if (_headers != null) - _headers.Clear (); - - Headers.Add (headers); - } - else { - _headers = null; - } - - _contentLength = templateResponse._contentLength; - _statusCode = templateResponse._statusCode; - _statusDescription = templateResponse._statusDescription; - _keepAlive = templateResponse._keepAlive; - _version = templateResponse._version; - } - - /// - /// Configures the response to redirect the client's request to - /// the specified URL. - /// - /// - /// This method sets the property to - /// , the property to - /// 302, and the property to "Found". - /// - /// - /// A that specifies the absolute URL to which - /// the client is redirected to locate a requested resource. - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// is not an absolute URL. - /// - /// - /// - /// is . - /// - /// - /// The response is already being sent. - /// - /// - /// This instance is closed. - /// - public void Redirect (string url) - { - if (_disposed) - throw new ObjectDisposedException (ObjectName); - - if (_headersSent) { - var msg = "The response is already being sent."; - - throw new InvalidOperationException (msg); - } - - if (url == null) - throw new ArgumentNullException ("url"); - - if (url.Length == 0) - throw new ArgumentException ("An empty string.", "url"); - - Uri uri; - - if (!Uri.TryCreate (url, UriKind.Absolute, out uri)) { - var msg = "Not an absolute URL."; - - throw new ArgumentException (msg, "url"); - } - - _redirectLocation = uri; - _statusCode = 302; - _statusDescription = "Found"; - } - - /// - /// Adds or updates an HTTP cookie in the cookies sent with the response. - /// - /// - /// A to set. - /// - /// - /// already exists in the cookies but - /// it cannot be updated. - /// - /// - /// is . - /// - public void SetCookie (Cookie cookie) - { - if (cookie == null) - throw new ArgumentNullException ("cookie"); - - if (!canSetCookie (cookie)) { - var msg = "It cannot be updated."; - - throw new ArgumentException (msg, "cookie"); - } - - Cookies.Add (cookie); - } - - /// - /// Adds or updates an HTTP header with the specified name and value in - /// the headers for the response. - /// - /// - /// A that specifies the name of the header to set. - /// - /// - /// A that specifies the value of the header to set. - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// is a string of spaces. - /// - /// - /// -or- - /// - /// - /// contains an invalid character. - /// - /// - /// -or- - /// - /// - /// contains an invalid character. - /// - /// - /// -or- - /// - /// - /// is a restricted header name. - /// - /// - /// - /// is . - /// - /// - /// The length of is greater than 65,535 - /// characters. - /// - /// - /// The current headers do not allow the header. - /// - public void SetHeader (string name, string value) - { - Headers.Set (name, value); - } - - #endregion - - #region Explicit Interface Implementations - - /// - /// Releases all resources used by this instance. - /// - void IDisposable.Dispose () - { - if (_disposed) - return; - - close (true); - } - - #endregion - } -} diff --git a/websocket-sharp/Net/HttpRequestHeader.cs b/websocket-sharp/Net/HttpRequestHeader.cs deleted file mode 100644 index aab3497ff..000000000 --- a/websocket-sharp/Net/HttpRequestHeader.cs +++ /dev/null @@ -1,233 +0,0 @@ -#region License -/* - * HttpRequestHeader.cs - * - * This code is derived from HttpRequestHeader.cs (System.Net) of Mono - * (http://www.mono-project.com). - * - * The MIT License - * - * Copyright (c) 2005 Novell, Inc. (http://www.novell.com) - * Copyright (c) 2014-2020 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -#region Authors -/* - * Authors: - * - Gonzalo Paniagua Javier - */ -#endregion - -namespace WebSocketSharp.Net -{ - /// - /// Indicates the HTTP header that may be specified in a client request. - /// - /// - /// The headers of this enumeration are defined in - /// RFC 2616 or - /// RFC 6455. - /// - public enum HttpRequestHeader - { - /// - /// Indicates the Cache-Control header. - /// - CacheControl, - /// - /// Indicates the Connection header. - /// - Connection, - /// - /// Indicates the Date header. - /// - Date, - /// - /// Indicates the Keep-Alive header. - /// - KeepAlive, - /// - /// Indicates the Pragma header. - /// - Pragma, - /// - /// Indicates the Trailer header. - /// - Trailer, - /// - /// Indicates the Transfer-Encoding header. - /// - TransferEncoding, - /// - /// Indicates the Upgrade header. - /// - Upgrade, - /// - /// Indicates the Via header. - /// - Via, - /// - /// Indicates the Warning header. - /// - Warning, - /// - /// Indicates the Allow header. - /// - Allow, - /// - /// Indicates the Content-Length header. - /// - ContentLength, - /// - /// Indicates the Content-Type header. - /// - ContentType, - /// - /// Indicates the Content-Encoding header. - /// - ContentEncoding, - /// - /// Indicates the Content-Language header. - /// - ContentLanguage, - /// - /// Indicates the Content-Location header. - /// - ContentLocation, - /// - /// Indicates the Content-MD5 header. - /// - ContentMd5, - /// - /// Indicates the Content-Range header. - /// - ContentRange, - /// - /// Indicates the Expires header. - /// - Expires, - /// - /// Indicates the Last-Modified header. - /// - LastModified, - /// - /// Indicates the Accept header. - /// - Accept, - /// - /// Indicates the Accept-Charset header. - /// - AcceptCharset, - /// - /// Indicates the Accept-Encoding header. - /// - AcceptEncoding, - /// - /// Indicates the Accept-Language header. - /// - AcceptLanguage, - /// - /// Indicates the Authorization header. - /// - Authorization, - /// - /// Indicates the Cookie header. - /// - Cookie, - /// - /// Indicates the Expect header. - /// - Expect, - /// - /// Indicates the From header. - /// - From, - /// - /// Indicates the Host header. - /// - Host, - /// - /// Indicates the If-Match header. - /// - IfMatch, - /// - /// Indicates the If-Modified-Since header. - /// - IfModifiedSince, - /// - /// Indicates the If-None-Match header. - /// - IfNoneMatch, - /// - /// Indicates the If-Range header. - /// - IfRange, - /// - /// Indicates the If-Unmodified-Since header. - /// - IfUnmodifiedSince, - /// - /// Indicates the Max-Forwards header. - /// - MaxForwards, - /// - /// Indicates the Proxy-Authorization header. - /// - ProxyAuthorization, - /// - /// Indicates the Referer header. - /// - Referer, - /// - /// Indicates the Range header. - /// - Range, - /// - /// Indicates the TE header. - /// - Te, - /// - /// Indicates the Translate header. - /// - Translate, - /// - /// Indicates the User-Agent header. - /// - UserAgent, - /// - /// Indicates the Sec-WebSocket-Key header. - /// - SecWebSocketKey, - /// - /// Indicates the Sec-WebSocket-Extensions header. - /// - SecWebSocketExtensions, - /// - /// Indicates the Sec-WebSocket-Protocol header. - /// - SecWebSocketProtocol, - /// - /// Indicates the Sec-WebSocket-Version header. - /// - SecWebSocketVersion - } -} diff --git a/websocket-sharp/Net/HttpResponseHeader.cs b/websocket-sharp/Net/HttpResponseHeader.cs deleted file mode 100644 index d32afe6c9..000000000 --- a/websocket-sharp/Net/HttpResponseHeader.cs +++ /dev/null @@ -1,189 +0,0 @@ -#region License -/* - * HttpResponseHeader.cs - * - * This code is derived from HttpResponseHeader.cs (System.Net) of Mono - * (http://www.mono-project.com). - * - * The MIT License - * - * Copyright (c) 2005 Novell, Inc. (http://www.novell.com) - * Copyright (c) 2014-2020 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -#region Authors -/* - * Authors: - * - Gonzalo Paniagua Javier - */ -#endregion - -namespace WebSocketSharp.Net -{ - /// - /// Indicates the HTTP header that can be specified in a server response. - /// - /// - /// The headers of this enumeration are defined in - /// RFC 2616 or - /// RFC 6455. - /// - public enum HttpResponseHeader - { - /// - /// Indicates the Cache-Control header. - /// - CacheControl, - /// - /// Indicates the Connection header. - /// - Connection, - /// - /// Indicates the Date header. - /// - Date, - /// - /// Indicates the Keep-Alive header. - /// - KeepAlive, - /// - /// Indicates the Pragma header. - /// - Pragma, - /// - /// Indicates the Trailer header. - /// - Trailer, - /// - /// Indicates the Transfer-Encoding header. - /// - TransferEncoding, - /// - /// Indicates the Upgrade header. - /// - Upgrade, - /// - /// Indicates the Via header. - /// - Via, - /// - /// Indicates the Warning header. - /// - Warning, - /// - /// Indicates the Allow header. - /// - Allow, - /// - /// Indicates the Content-Length header. - /// - ContentLength, - /// - /// Indicates the Content-Type header. - /// - ContentType, - /// - /// Indicates the Content-Encoding header. - /// - ContentEncoding, - /// - /// Indicates the Content-Language header. - /// - ContentLanguage, - /// - /// Indicates the Content-Location header. - /// - ContentLocation, - /// - /// Indicates the Content-MD5 header. - /// - ContentMd5, - /// - /// Indicates the Content-Range header. - /// - ContentRange, - /// - /// Indicates the Expires header. - /// - Expires, - /// - /// Indicates the Last-Modified header. - /// - LastModified, - /// - /// Indicates the Accept-Ranges header. - /// - AcceptRanges, - /// - /// Indicates the Age header. - /// - Age, - /// - /// Indicates the ETag header. - /// - ETag, - /// - /// Indicates the Location header. - /// - Location, - /// - /// Indicates the Proxy-Authenticate header. - /// - ProxyAuthenticate, - /// - /// Indicates the Retry-After header. - /// - RetryAfter, - /// - /// Indicates the Server header. - /// - Server, - /// - /// Indicates the Set-Cookie header. - /// - SetCookie, - /// - /// Indicates the Vary header. - /// - Vary, - /// - /// Indicates the WWW-Authenticate header. - /// - WwwAuthenticate, - /// - /// Indicates the Sec-WebSocket-Extensions header. - /// - SecWebSocketExtensions, - /// - /// Indicates the Sec-WebSocket-Accept header. - /// - SecWebSocketAccept, - /// - /// Indicates the Sec-WebSocket-Protocol header. - /// - SecWebSocketProtocol, - /// - /// Indicates the Sec-WebSocket-Version header. - /// - SecWebSocketVersion - } -} diff --git a/websocket-sharp/Net/HttpStatusCode.cs b/websocket-sharp/Net/HttpStatusCode.cs deleted file mode 100644 index b773a4964..000000000 --- a/websocket-sharp/Net/HttpStatusCode.cs +++ /dev/null @@ -1,346 +0,0 @@ -#region License -/* - * HttpStatusCode.cs - * - * This code is derived from HttpStatusCode.cs (System.Net) of Mono - * (http://www.mono-project.com). - * - * It was automatically generated from ECMA CLI XML Library Specification. - * Generator: libgen.xsl [1.0; (C) Sergey Chaban (serge@wildwestsoftware.com)] - * Created: Wed, 5 Sep 2001 06:32:05 UTC - * Source file: AllTypes.xml - * URL: http://msdn.microsoft.com/net/ecma/AllTypes.xml - * - * The MIT License - * - * Copyright (c) 2001 Ximian, Inc. (http://www.ximian.com) - * Copyright (c) 2012-2020 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -namespace WebSocketSharp.Net -{ - /// - /// Indicates the HTTP status code that can be specified in a server response. - /// - /// - /// The values of this enumeration are defined in - /// RFC 2616. - /// - public enum HttpStatusCode - { - /// - /// Equivalent to status code 100. Indicates that the client should continue - /// with its request. - /// - Continue = 100, - /// - /// Equivalent to status code 101. Indicates that the server is switching - /// the HTTP version or protocol on the connection. - /// - SwitchingProtocols = 101, - /// - /// Equivalent to status code 200. Indicates that the client's request has - /// succeeded. - /// - OK = 200, - /// - /// Equivalent to status code 201. Indicates that the client's request has - /// been fulfilled and resulted in a new resource being created. - /// - Created = 201, - /// - /// Equivalent to status code 202. Indicates that the client's request has - /// been accepted for processing, but the processing has not been completed. - /// - Accepted = 202, - /// - /// Equivalent to status code 203. Indicates that the returned metainformation - /// is from a local or a third-party copy instead of the origin server. - /// - NonAuthoritativeInformation = 203, - /// - /// Equivalent to status code 204. Indicates that the server has fulfilled - /// the client's request but does not need to return an entity-body. - /// - NoContent = 204, - /// - /// Equivalent to status code 205. Indicates that the server has fulfilled - /// the client's request, and the user agent should reset the document view - /// which caused the request to be sent. - /// - ResetContent = 205, - /// - /// Equivalent to status code 206. Indicates that the server has fulfilled - /// the partial GET request for the resource. - /// - PartialContent = 206, - /// - /// - /// Equivalent to status code 300. Indicates that the requested resource - /// corresponds to any of multiple representations. - /// - /// - /// MultipleChoices is a synonym for Ambiguous. - /// - /// - MultipleChoices = 300, - /// - /// - /// Equivalent to status code 300. Indicates that the requested resource - /// corresponds to any of multiple representations. - /// - /// - /// Ambiguous is a synonym for MultipleChoices. - /// - /// - Ambiguous = 300, - /// - /// - /// Equivalent to status code 301. Indicates that the requested resource - /// has been assigned a new permanent URI and any future references to - /// this resource should use one of the returned URIs. - /// - /// - /// MovedPermanently is a synonym for Moved. - /// - /// - MovedPermanently = 301, - /// - /// - /// Equivalent to status code 301. Indicates that the requested resource - /// has been assigned a new permanent URI and any future references to - /// this resource should use one of the returned URIs. - /// - /// - /// Moved is a synonym for MovedPermanently. - /// - /// - Moved = 301, - /// - /// - /// Equivalent to status code 302. Indicates that the requested resource - /// is located temporarily under a different URI. - /// - /// - /// Found is a synonym for Redirect. - /// - /// - Found = 302, - /// - /// - /// Equivalent to status code 302. Indicates that the requested resource - /// is located temporarily under a different URI. - /// - /// - /// Redirect is a synonym for Found. - /// - /// - Redirect = 302, - /// - /// - /// Equivalent to status code 303. Indicates that the response to - /// the request can be found under a different URI and should be - /// retrieved using a GET method on that resource. - /// - /// - /// SeeOther is a synonym for RedirectMethod. - /// - /// - SeeOther = 303, - /// - /// - /// Equivalent to status code 303. Indicates that the response to - /// the request can be found under a different URI and should be - /// retrieved using a GET method on that resource. - /// - /// - /// RedirectMethod is a synonym for SeeOther. - /// - /// - RedirectMethod = 303, - /// - /// Equivalent to status code 304. Indicates that the client has performed - /// a conditional GET request and access is allowed, but the document has - /// not been modified. - /// - NotModified = 304, - /// - /// Equivalent to status code 305. Indicates that the requested resource - /// must be accessed through the proxy given by the Location field. - /// - UseProxy = 305, - /// - /// Equivalent to status code 306. This status code was used in a previous - /// version of the specification, is no longer used, and is reserved for - /// future use. - /// - Unused = 306, - /// - /// - /// Equivalent to status code 307. Indicates that the requested resource - /// is located temporarily under a different URI. - /// - /// - /// TemporaryRedirect is a synonym for RedirectKeepVerb. - /// - /// - TemporaryRedirect = 307, - /// - /// - /// Equivalent to status code 307. Indicates that the requested resource - /// is located temporarily under a different URI. - /// - /// - /// RedirectKeepVerb is a synonym for TemporaryRedirect. - /// - /// - RedirectKeepVerb = 307, - /// - /// Equivalent to status code 400. Indicates that the client's request could - /// not be understood by the server due to malformed syntax. - /// - BadRequest = 400, - /// - /// Equivalent to status code 401. Indicates that the client's request - /// requires user authentication. - /// - Unauthorized = 401, - /// - /// Equivalent to status code 402. This status code is reserved for future - /// use. - /// - PaymentRequired = 402, - /// - /// Equivalent to status code 403. Indicates that the server understood - /// the client's request but is refusing to fulfill it. - /// - Forbidden = 403, - /// - /// Equivalent to status code 404. Indicates that the server has not found - /// anything matching the request URI. - /// - NotFound = 404, - /// - /// Equivalent to status code 405. Indicates that the method specified - /// in the request line is not allowed for the resource identified by - /// the request URI. - /// - MethodNotAllowed = 405, - /// - /// Equivalent to status code 406. Indicates that the server does not - /// have the appropriate resource to respond to the Accept headers in - /// the client's request. - /// - NotAcceptable = 406, - /// - /// Equivalent to status code 407. Indicates that the client must first - /// authenticate itself with the proxy. - /// - ProxyAuthenticationRequired = 407, - /// - /// Equivalent to status code 408. Indicates that the client did not produce - /// a request within the time that the server was prepared to wait. - /// - RequestTimeout = 408, - /// - /// Equivalent to status code 409. Indicates that the client's request could - /// not be completed due to a conflict on the server. - /// - Conflict = 409, - /// - /// Equivalent to status code 410. Indicates that the requested resource is - /// no longer available at the server and no forwarding address is known. - /// - Gone = 410, - /// - /// Equivalent to status code 411. Indicates that the server refuses to - /// accept the client's request without a defined Content-Length. - /// - LengthRequired = 411, - /// - /// Equivalent to status code 412. Indicates that the precondition given in - /// one or more of the request headers evaluated to false when it was tested - /// on the server. - /// - PreconditionFailed = 412, - /// - /// Equivalent to status code 413. Indicates that the entity of the client's - /// request is larger than the server is willing or able to process. - /// - RequestEntityTooLarge = 413, - /// - /// Equivalent to status code 414. Indicates that the request URI is longer - /// than the server is willing to interpret. - /// - RequestUriTooLong = 414, - /// - /// Equivalent to status code 415. Indicates that the entity of the client's - /// request is in a format not supported by the requested resource for the - /// requested method. - /// - UnsupportedMediaType = 415, - /// - /// Equivalent to status code 416. Indicates that none of the range - /// specifier values in a Range request header overlap the current - /// extent of the selected resource. - /// - RequestedRangeNotSatisfiable = 416, - /// - /// Equivalent to status code 417. Indicates that the expectation given in - /// an Expect request header could not be met by the server. - /// - ExpectationFailed = 417, - /// - /// Equivalent to status code 500. Indicates that the server encountered - /// an unexpected condition which prevented it from fulfilling the client's - /// request. - /// - InternalServerError = 500, - /// - /// Equivalent to status code 501. Indicates that the server does not - /// support the functionality required to fulfill the client's request. - /// - NotImplemented = 501, - /// - /// Equivalent to status code 502. Indicates that a gateway or proxy server - /// received an invalid response from the upstream server. - /// - BadGateway = 502, - /// - /// Equivalent to status code 503. Indicates that the server is currently - /// unable to handle the client's request due to a temporary overloading - /// or maintenance of the server. - /// - ServiceUnavailable = 503, - /// - /// Equivalent to status code 504. Indicates that a gateway or proxy server - /// did not receive a timely response from the upstream server or some other - /// auxiliary server. - /// - GatewayTimeout = 504, - /// - /// Equivalent to status code 505. Indicates that the server does not - /// support the HTTP version used in the client's request. - /// - HttpVersionNotSupported = 505, - } -} diff --git a/websocket-sharp/Net/HttpStreamAsyncResult.cs b/websocket-sharp/Net/HttpStreamAsyncResult.cs deleted file mode 100644 index 09447ea21..000000000 --- a/websocket-sharp/Net/HttpStreamAsyncResult.cs +++ /dev/null @@ -1,201 +0,0 @@ -#region License -/* - * HttpStreamAsyncResult.cs - * - * This code is derived from HttpStreamAsyncResult.cs (System.Net) of Mono - * (http://www.mono-project.com). - * - * The MIT License - * - * Copyright (c) 2005 Novell, Inc. (http://www.novell.com) - * Copyright (c) 2012-2021 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -#region Authors -/* - * Authors: - * - Gonzalo Paniagua Javier - */ -#endregion - -using System; -using System.Threading; - -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) - { - _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) { - if (_waitHandle == null) - _waitHandle = new ManualResetEvent (_completed); - - return _waitHandle; - } - } - } - - 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) - { - lock (_sync) { - if (_completed) - return; - - _completed = true; - _exception = exception; - - if (_waitHandle != null) - _waitHandle.Set (); - - if (_callback != null) - _callback.BeginInvoke (this, ar => _callback.EndInvoke (ar), null); - } - } - - #endregion - } -} diff --git a/websocket-sharp/Net/HttpUtility.cs b/websocket-sharp/Net/HttpUtility.cs deleted file mode 100644 index b7db7b922..000000000 --- a/websocket-sharp/Net/HttpUtility.cs +++ /dev/null @@ -1,1237 +0,0 @@ -#region License -/* - * HttpUtility.cs - * - * This code is derived from HttpUtility.cs (System.Net) of Mono - * (http://www.mono-project.com). - * - * The MIT License - * - * Copyright (c) 2005-2009 Novell, Inc. (http://www.novell.com) - * Copyright (c) 2012-2024 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -#region Authors -/* - * Authors: - * - Patrik Torstensson - * - Wictor Wilén (decode/encode functions) - * - Tim Coleman - * - Gonzalo Paniagua Javier - */ -#endregion - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.Globalization; -using System.IO; -using System.Security.Principal; -using System.Text; - -namespace WebSocketSharp.Net -{ - internal static class HttpUtility - { - #region Private Fields - - private static Dictionary _entities; - private static char[] _hexChars; - private static object _sync; - - #endregion - - #region Static Constructor - - static HttpUtility () - { - _hexChars = "0123456789ABCDEF".ToCharArray (); - _sync = new object (); - } - - #endregion - - #region Private Methods - - private static Dictionary getEntities () - { - lock (_sync) { - if (_entities == null) - initEntities (); - - return _entities; - } - } - - private static int getNumber (char c) - { - if (c >= '0' && c <= '9') - return c - '0'; - - if (c >= 'A' && c <= 'F') - return c - 'A' + 10; - - if (c >= 'a' && c <= 'f') - return c - 'a' + 10; - - return -1; - } - - private static int getNumber (byte[] bytes, int offset, int count) - { - var ret = 0; - - var end = offset + count - 1; - - for (var i = offset; i <= end; i++) { - var c = (char) bytes[i]; - var n = getNumber (c); - - if (n == -1) - return -1; - - ret = (ret << 4) + n; - } - - return ret; - } - - private static int getNumber (string s, int offset, int count) - { - var ret = 0; - - var end = offset + count - 1; - - for (var i = offset; i <= end; i++) { - var c = s[i]; - var n = getNumber (c); - - if (n == -1) - return -1; - - ret = (ret << 4) + n; - } - - return ret; - } - - private static string htmlDecode (string s) - { - var buff = new StringBuilder (); - - // 0: None - // 1: Right after '&' - // 2: Between '&' and ';' but no NCR - // 3: '#' found after '&' and getting numbers - // 4: 'x' found after '#' and getting numbers - var state = 0; - - var reference = new StringBuilder (); - var num = 0; - - foreach (var c in s) { - if (state == 0) { - if (c == '&') { - reference.Append ('&'); - - state = 1; - - continue; - } - - buff.Append (c); - - continue; - } - - if (c == '&') { - buff.Append (reference.ToString ()); - - reference.Length = 0; - - reference.Append ('&'); - - state = 1; - - continue; - } - - reference.Append (c); - - if (state == 1) { - if (c == ';') { - buff.Append (reference.ToString ()); - - reference.Length = 0; - state = 0; - - continue; - } - - num = 0; - state = c == '#' ? 3 : 2; - - continue; - } - - if (state == 2) { - if (c == ';') { - var entity = reference.ToString (); - var name = entity.Substring (1, entity.Length - 2); - - var entities = getEntities (); - - if (entities.ContainsKey (name)) - buff.Append (entities[name]); - else - buff.Append (entity); - - reference.Length = 0; - state = 0; - - continue; - } - - continue; - } - - if (state == 3) { - if (c == ';') { - if (reference.Length > 3 && num < 65536) - buff.Append ((char) num); - else - buff.Append (reference.ToString ()); - - reference.Length = 0; - state = 0; - - continue; - } - - if (c == 'x') { - state = reference.Length == 3 ? 4 : 2; - - continue; - } - - if (!isNumeric (c)) { - state = 2; - - continue; - } - - num = num * 10 + (c - '0'); - - continue; - } - - if (state == 4) { - if (c == ';') { - if (reference.Length > 4 && num < 65536) - buff.Append ((char) num); - else - buff.Append (reference.ToString ()); - - reference.Length = 0; - state = 0; - - continue; - } - - var n = getNumber (c); - - if (n == -1) { - state = 2; - - continue; - } - - num = (num << 4) + n; - } - } - - if (reference.Length > 0) - buff.Append (reference.ToString ()); - - return buff.ToString (); - } - - /// - /// Converts the specified string to an HTML-encoded string. - /// - /// - /// - /// This method starts encoding with a NCR from the character code 160 - /// but does not stop at the character code 255. - /// - /// - /// One reason is the unicode characters < and > that - /// look like < and >. - /// - /// - /// - /// A that represents an encoded string. - /// - /// - /// A to encode. - /// - /// - /// A : true if encodes without a NCR; - /// otherwise, false. - /// - private static string htmlEncode (string s, bool minimal) - { - var buff = new StringBuilder (); - - foreach (var c in s) { - if (c == '"') { - buff.Append ("""); - - continue; - } - - if (c == '&') { - buff.Append ("&"); - - continue; - } - - if (c == '<') { - buff.Append ("<"); - - continue; - } - - if (c == '>') { - buff.Append (">"); - - continue; - } - - if (c > 159) { - if (!minimal) { - var val = String.Format ("&#{0};", (int) c); - - buff.Append (val); - - continue; - } - } - - buff.Append (c); - } - - return buff.ToString (); - } - - /// - /// Initializes the _entities field. - /// - /// - /// This method builds a dictionary of HTML character entity references. - /// This dictionary comes from the HTML 4.01 W3C recommendation. - /// - private static void initEntities () - { - _entities = new Dictionary (); - - _entities.Add ("nbsp", '\u00A0'); - _entities.Add ("iexcl", '\u00A1'); - _entities.Add ("cent", '\u00A2'); - _entities.Add ("pound", '\u00A3'); - _entities.Add ("curren", '\u00A4'); - _entities.Add ("yen", '\u00A5'); - _entities.Add ("brvbar", '\u00A6'); - _entities.Add ("sect", '\u00A7'); - _entities.Add ("uml", '\u00A8'); - _entities.Add ("copy", '\u00A9'); - _entities.Add ("ordf", '\u00AA'); - _entities.Add ("laquo", '\u00AB'); - _entities.Add ("not", '\u00AC'); - _entities.Add ("shy", '\u00AD'); - _entities.Add ("reg", '\u00AE'); - _entities.Add ("macr", '\u00AF'); - _entities.Add ("deg", '\u00B0'); - _entities.Add ("plusmn", '\u00B1'); - _entities.Add ("sup2", '\u00B2'); - _entities.Add ("sup3", '\u00B3'); - _entities.Add ("acute", '\u00B4'); - _entities.Add ("micro", '\u00B5'); - _entities.Add ("para", '\u00B6'); - _entities.Add ("middot", '\u00B7'); - _entities.Add ("cedil", '\u00B8'); - _entities.Add ("sup1", '\u00B9'); - _entities.Add ("ordm", '\u00BA'); - _entities.Add ("raquo", '\u00BB'); - _entities.Add ("frac14", '\u00BC'); - _entities.Add ("frac12", '\u00BD'); - _entities.Add ("frac34", '\u00BE'); - _entities.Add ("iquest", '\u00BF'); - _entities.Add ("Agrave", '\u00C0'); - _entities.Add ("Aacute", '\u00C1'); - _entities.Add ("Acirc", '\u00C2'); - _entities.Add ("Atilde", '\u00C3'); - _entities.Add ("Auml", '\u00C4'); - _entities.Add ("Aring", '\u00C5'); - _entities.Add ("AElig", '\u00C6'); - _entities.Add ("Ccedil", '\u00C7'); - _entities.Add ("Egrave", '\u00C8'); - _entities.Add ("Eacute", '\u00C9'); - _entities.Add ("Ecirc", '\u00CA'); - _entities.Add ("Euml", '\u00CB'); - _entities.Add ("Igrave", '\u00CC'); - _entities.Add ("Iacute", '\u00CD'); - _entities.Add ("Icirc", '\u00CE'); - _entities.Add ("Iuml", '\u00CF'); - _entities.Add ("ETH", '\u00D0'); - _entities.Add ("Ntilde", '\u00D1'); - _entities.Add ("Ograve", '\u00D2'); - _entities.Add ("Oacute", '\u00D3'); - _entities.Add ("Ocirc", '\u00D4'); - _entities.Add ("Otilde", '\u00D5'); - _entities.Add ("Ouml", '\u00D6'); - _entities.Add ("times", '\u00D7'); - _entities.Add ("Oslash", '\u00D8'); - _entities.Add ("Ugrave", '\u00D9'); - _entities.Add ("Uacute", '\u00DA'); - _entities.Add ("Ucirc", '\u00DB'); - _entities.Add ("Uuml", '\u00DC'); - _entities.Add ("Yacute", '\u00DD'); - _entities.Add ("THORN", '\u00DE'); - _entities.Add ("szlig", '\u00DF'); - _entities.Add ("agrave", '\u00E0'); - _entities.Add ("aacute", '\u00E1'); - _entities.Add ("acirc", '\u00E2'); - _entities.Add ("atilde", '\u00E3'); - _entities.Add ("auml", '\u00E4'); - _entities.Add ("aring", '\u00E5'); - _entities.Add ("aelig", '\u00E6'); - _entities.Add ("ccedil", '\u00E7'); - _entities.Add ("egrave", '\u00E8'); - _entities.Add ("eacute", '\u00E9'); - _entities.Add ("ecirc", '\u00EA'); - _entities.Add ("euml", '\u00EB'); - _entities.Add ("igrave", '\u00EC'); - _entities.Add ("iacute", '\u00ED'); - _entities.Add ("icirc", '\u00EE'); - _entities.Add ("iuml", '\u00EF'); - _entities.Add ("eth", '\u00F0'); - _entities.Add ("ntilde", '\u00F1'); - _entities.Add ("ograve", '\u00F2'); - _entities.Add ("oacute", '\u00F3'); - _entities.Add ("ocirc", '\u00F4'); - _entities.Add ("otilde", '\u00F5'); - _entities.Add ("ouml", '\u00F6'); - _entities.Add ("divide", '\u00F7'); - _entities.Add ("oslash", '\u00F8'); - _entities.Add ("ugrave", '\u00F9'); - _entities.Add ("uacute", '\u00FA'); - _entities.Add ("ucirc", '\u00FB'); - _entities.Add ("uuml", '\u00FC'); - _entities.Add ("yacute", '\u00FD'); - _entities.Add ("thorn", '\u00FE'); - _entities.Add ("yuml", '\u00FF'); - _entities.Add ("fnof", '\u0192'); - _entities.Add ("Alpha", '\u0391'); - _entities.Add ("Beta", '\u0392'); - _entities.Add ("Gamma", '\u0393'); - _entities.Add ("Delta", '\u0394'); - _entities.Add ("Epsilon", '\u0395'); - _entities.Add ("Zeta", '\u0396'); - _entities.Add ("Eta", '\u0397'); - _entities.Add ("Theta", '\u0398'); - _entities.Add ("Iota", '\u0399'); - _entities.Add ("Kappa", '\u039A'); - _entities.Add ("Lambda", '\u039B'); - _entities.Add ("Mu", '\u039C'); - _entities.Add ("Nu", '\u039D'); - _entities.Add ("Xi", '\u039E'); - _entities.Add ("Omicron", '\u039F'); - _entities.Add ("Pi", '\u03A0'); - _entities.Add ("Rho", '\u03A1'); - _entities.Add ("Sigma", '\u03A3'); - _entities.Add ("Tau", '\u03A4'); - _entities.Add ("Upsilon", '\u03A5'); - _entities.Add ("Phi", '\u03A6'); - _entities.Add ("Chi", '\u03A7'); - _entities.Add ("Psi", '\u03A8'); - _entities.Add ("Omega", '\u03A9'); - _entities.Add ("alpha", '\u03B1'); - _entities.Add ("beta", '\u03B2'); - _entities.Add ("gamma", '\u03B3'); - _entities.Add ("delta", '\u03B4'); - _entities.Add ("epsilon", '\u03B5'); - _entities.Add ("zeta", '\u03B6'); - _entities.Add ("eta", '\u03B7'); - _entities.Add ("theta", '\u03B8'); - _entities.Add ("iota", '\u03B9'); - _entities.Add ("kappa", '\u03BA'); - _entities.Add ("lambda", '\u03BB'); - _entities.Add ("mu", '\u03BC'); - _entities.Add ("nu", '\u03BD'); - _entities.Add ("xi", '\u03BE'); - _entities.Add ("omicron", '\u03BF'); - _entities.Add ("pi", '\u03C0'); - _entities.Add ("rho", '\u03C1'); - _entities.Add ("sigmaf", '\u03C2'); - _entities.Add ("sigma", '\u03C3'); - _entities.Add ("tau", '\u03C4'); - _entities.Add ("upsilon", '\u03C5'); - _entities.Add ("phi", '\u03C6'); - _entities.Add ("chi", '\u03C7'); - _entities.Add ("psi", '\u03C8'); - _entities.Add ("omega", '\u03C9'); - _entities.Add ("thetasym", '\u03D1'); - _entities.Add ("upsih", '\u03D2'); - _entities.Add ("piv", '\u03D6'); - _entities.Add ("bull", '\u2022'); - _entities.Add ("hellip", '\u2026'); - _entities.Add ("prime", '\u2032'); - _entities.Add ("Prime", '\u2033'); - _entities.Add ("oline", '\u203E'); - _entities.Add ("frasl", '\u2044'); - _entities.Add ("weierp", '\u2118'); - _entities.Add ("image", '\u2111'); - _entities.Add ("real", '\u211C'); - _entities.Add ("trade", '\u2122'); - _entities.Add ("alefsym", '\u2135'); - _entities.Add ("larr", '\u2190'); - _entities.Add ("uarr", '\u2191'); - _entities.Add ("rarr", '\u2192'); - _entities.Add ("darr", '\u2193'); - _entities.Add ("harr", '\u2194'); - _entities.Add ("crarr", '\u21B5'); - _entities.Add ("lArr", '\u21D0'); - _entities.Add ("uArr", '\u21D1'); - _entities.Add ("rArr", '\u21D2'); - _entities.Add ("dArr", '\u21D3'); - _entities.Add ("hArr", '\u21D4'); - _entities.Add ("forall", '\u2200'); - _entities.Add ("part", '\u2202'); - _entities.Add ("exist", '\u2203'); - _entities.Add ("empty", '\u2205'); - _entities.Add ("nabla", '\u2207'); - _entities.Add ("isin", '\u2208'); - _entities.Add ("notin", '\u2209'); - _entities.Add ("ni", '\u220B'); - _entities.Add ("prod", '\u220F'); - _entities.Add ("sum", '\u2211'); - _entities.Add ("minus", '\u2212'); - _entities.Add ("lowast", '\u2217'); - _entities.Add ("radic", '\u221A'); - _entities.Add ("prop", '\u221D'); - _entities.Add ("infin", '\u221E'); - _entities.Add ("ang", '\u2220'); - _entities.Add ("and", '\u2227'); - _entities.Add ("or", '\u2228'); - _entities.Add ("cap", '\u2229'); - _entities.Add ("cup", '\u222A'); - _entities.Add ("int", '\u222B'); - _entities.Add ("there4", '\u2234'); - _entities.Add ("sim", '\u223C'); - _entities.Add ("cong", '\u2245'); - _entities.Add ("asymp", '\u2248'); - _entities.Add ("ne", '\u2260'); - _entities.Add ("equiv", '\u2261'); - _entities.Add ("le", '\u2264'); - _entities.Add ("ge", '\u2265'); - _entities.Add ("sub", '\u2282'); - _entities.Add ("sup", '\u2283'); - _entities.Add ("nsub", '\u2284'); - _entities.Add ("sube", '\u2286'); - _entities.Add ("supe", '\u2287'); - _entities.Add ("oplus", '\u2295'); - _entities.Add ("otimes", '\u2297'); - _entities.Add ("perp", '\u22A5'); - _entities.Add ("sdot", '\u22C5'); - _entities.Add ("lceil", '\u2308'); - _entities.Add ("rceil", '\u2309'); - _entities.Add ("lfloor", '\u230A'); - _entities.Add ("rfloor", '\u230B'); - _entities.Add ("lang", '\u2329'); - _entities.Add ("rang", '\u232A'); - _entities.Add ("loz", '\u25CA'); - _entities.Add ("spades", '\u2660'); - _entities.Add ("clubs", '\u2663'); - _entities.Add ("hearts", '\u2665'); - _entities.Add ("diams", '\u2666'); - _entities.Add ("quot", '\u0022'); - _entities.Add ("amp", '\u0026'); - _entities.Add ("lt", '\u003C'); - _entities.Add ("gt", '\u003E'); - _entities.Add ("OElig", '\u0152'); - _entities.Add ("oelig", '\u0153'); - _entities.Add ("Scaron", '\u0160'); - _entities.Add ("scaron", '\u0161'); - _entities.Add ("Yuml", '\u0178'); - _entities.Add ("circ", '\u02C6'); - _entities.Add ("tilde", '\u02DC'); - _entities.Add ("ensp", '\u2002'); - _entities.Add ("emsp", '\u2003'); - _entities.Add ("thinsp", '\u2009'); - _entities.Add ("zwnj", '\u200C'); - _entities.Add ("zwj", '\u200D'); - _entities.Add ("lrm", '\u200E'); - _entities.Add ("rlm", '\u200F'); - _entities.Add ("ndash", '\u2013'); - _entities.Add ("mdash", '\u2014'); - _entities.Add ("lsquo", '\u2018'); - _entities.Add ("rsquo", '\u2019'); - _entities.Add ("sbquo", '\u201A'); - _entities.Add ("ldquo", '\u201C'); - _entities.Add ("rdquo", '\u201D'); - _entities.Add ("bdquo", '\u201E'); - _entities.Add ("dagger", '\u2020'); - _entities.Add ("Dagger", '\u2021'); - _entities.Add ("permil", '\u2030'); - _entities.Add ("lsaquo", '\u2039'); - _entities.Add ("rsaquo", '\u203A'); - _entities.Add ("euro", '\u20AC'); - } - - private static bool isAlphabet (char c) - { - return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); - } - - private static bool isNumeric (char c) - { - return c >= '0' && c <= '9'; - } - - private static bool isUnreserved (char c) - { - return c == '*' - || c == '-' - || c == '.' - || c == '_'; - } - - private static bool isUnreservedInRfc2396 (char c) - { - return c == '!' - || c == '\'' - || c == '(' - || c == ')' - || c == '*' - || c == '-' - || c == '.' - || c == '_' - || c == '~'; - } - - private static bool isUnreservedInRfc3986 (char c) - { - return c == '-' - || c == '.' - || c == '_' - || c == '~'; - } - - private static byte[] urlDecodeToBytes (byte[] bytes, int offset, int count) - { - using (var buff = new MemoryStream ()) { - var end = offset + count - 1; - - for (var i = offset; i <= end; i++) { - var b = bytes[i]; - var c = (char) b; - - if (c == '%') { - if (i > end - 2) - break; - - var num = getNumber (bytes, i + 1, 2); - - if (num == -1) - break; - - buff.WriteByte ((byte) num); - - i += 2; - - continue; - } - - if (c == '+') { - buff.WriteByte ((byte) ' '); - - continue; - } - - buff.WriteByte (b); - } - - buff.Close (); - - return buff.ToArray (); - } - } - - private static void urlEncode (byte b, Stream output) - { - if (b > 31 && b < 127) { - var c = (char) b; - - if (c == ' ') { - output.WriteByte ((byte) '+'); - - return; - } - - if (isNumeric (c)) { - output.WriteByte (b); - - return; - } - - if (isAlphabet (c)) { - output.WriteByte (b); - - return; - } - - if (isUnreserved (c)) { - output.WriteByte (b); - - return; - } - } - - var i = (int) b; - var bytes = new byte[] { - (byte) '%', - (byte) _hexChars[i >> 4], - (byte) _hexChars[i & 0x0F] - }; - - output.Write (bytes, 0, 3); - } - - private static byte[] urlEncodeToBytes (byte[] bytes, int offset, int count) - { - using (var buff = new MemoryStream ()) { - var end = offset + count - 1; - - for (var i = offset; i <= end; i++) - urlEncode (bytes[i], buff); - - buff.Close (); - - return buff.ToArray (); - } - } - - #endregion - - #region Internal Methods - - internal static Uri CreateRequestUrl ( - string requestUri, - string host, - bool websocketRequest, - bool secure - ) - { - if (requestUri == null || requestUri.Length == 0) - return null; - - if (host == null || host.Length == 0) - return null; - - string schm = null; - string path = null; - - if (requestUri.IndexOf ('/') == 0) { - path = requestUri; - } - else if (requestUri.MaybeUri ()) { - Uri uri; - - if (!Uri.TryCreate (requestUri, UriKind.Absolute, out uri)) - return null; - - schm = uri.Scheme; - var valid = websocketRequest - ? schm == "ws" || schm == "wss" - : schm == "http" || schm == "https"; - - if (!valid) - return null; - - host = uri.Authority; - path = uri.PathAndQuery; - } - else if (requestUri == "*") { - } - else { - // As the authority form. - - host = requestUri; - } - - if (schm == null) { - schm = websocketRequest - ? (secure ? "wss" : "ws") - : (secure ? "https" : "http"); - } - - if (host.IndexOf (':') == -1) - host = String.Format ("{0}:{1}", host, secure ? 443 : 80); - - var url = String.Format ("{0}://{1}{2}", schm, host, path); - Uri ret; - - return Uri.TryCreate (url, UriKind.Absolute, out ret) ? ret : null; - } - - internal static IPrincipal CreateUser ( - string response, - AuthenticationSchemes scheme, - string realm, - string method, - Func credentialsFinder - ) - { - if (response == null || response.Length == 0) - return null; - - if (scheme == AuthenticationSchemes.Digest) { - if (realm == null || realm.Length == 0) - return null; - - if (method == null || method.Length == 0) - return null; - } - else { - if (scheme != AuthenticationSchemes.Basic) - return null; - } - - if (credentialsFinder == null) - return null; - - var compType = StringComparison.OrdinalIgnoreCase; - - if (!response.StartsWith (scheme.ToString (), compType)) - return null; - - var res = AuthenticationResponse.Parse (response); - - if (res == null) - return null; - - var id = res.ToIdentity (); - - if (id == null) - return null; - - NetworkCredential cred = null; - - try { - cred = credentialsFinder (id); - } - catch { - } - - if (cred == null) - return null; - - if (scheme == AuthenticationSchemes.Basic) { - var basicId = (HttpBasicIdentity) id; - - return basicId.Password == cred.Password - ? new GenericPrincipal (id, cred.Roles) - : null; - } - - var digestId = (HttpDigestIdentity) id; - - return digestId.IsValid (cred.Password, realm, method, null) - ? new GenericPrincipal (id, cred.Roles) - : null; - } - - internal static Encoding GetEncoding (string contentType) - { - var name = "charset="; - var compType = StringComparison.OrdinalIgnoreCase; - - foreach (var elm in contentType.SplitHeaderValue (';')) { - var part = elm.Trim (); - - if (!part.StartsWith (name, compType)) - continue; - - var val = part.GetValue ('=', true); - - if (val == null || val.Length == 0) - return null; - - return Encoding.GetEncoding (val); - } - - return null; - } - - internal static bool TryGetEncoding ( - string contentType, - out Encoding result - ) - { - result = null; - - try { - result = GetEncoding (contentType); - } - catch { - return false; - } - - return result != null; - } - - #endregion - - #region Public Methods - - public static string HtmlAttributeEncode (string s) - { - if (s == null) - throw new ArgumentNullException ("s"); - - return s.Length > 0 ? htmlEncode (s, true) : s; - } - - public static void HtmlAttributeEncode (string s, TextWriter output) - { - if (s == null) - throw new ArgumentNullException ("s"); - - if (output == null) - throw new ArgumentNullException ("output"); - - if (s.Length == 0) - return; - - var encodedS = htmlEncode (s, true); - - output.Write (encodedS); - } - - public static string HtmlDecode (string s) - { - if (s == null) - throw new ArgumentNullException ("s"); - - return s.Length > 0 ? htmlDecode (s) : s; - } - - public static void HtmlDecode (string s, TextWriter output) - { - if (s == null) - throw new ArgumentNullException ("s"); - - if (output == null) - throw new ArgumentNullException ("output"); - - if (s.Length == 0) - return; - - var decodedS = htmlDecode (s); - - output.Write (decodedS); - } - - public static string HtmlEncode (string s) - { - if (s == null) - throw new ArgumentNullException ("s"); - - return s.Length > 0 ? htmlEncode (s, false) : s; - } - - public static void HtmlEncode (string s, TextWriter output) - { - if (s == null) - throw new ArgumentNullException ("s"); - - if (output == null) - throw new ArgumentNullException ("output"); - - if (s.Length == 0) - return; - - var encodedS = htmlEncode (s, false); - - output.Write (encodedS); - } - - public static string UrlDecode (string s) - { - return UrlDecode (s, Encoding.UTF8); - } - - public static string UrlDecode (byte[] bytes, Encoding encoding) - { - if (bytes == null) - throw new ArgumentNullException ("bytes"); - - var len = bytes.Length; - - if (len == 0) - return String.Empty; - - var decodedBytes = urlDecodeToBytes (bytes, 0, len); - - return (encoding ?? Encoding.UTF8).GetString (decodedBytes); - } - - public static string UrlDecode (string s, Encoding encoding) - { - if (s == null) - throw new ArgumentNullException ("s"); - - if (s.Length == 0) - return s; - - var bytes = Encoding.ASCII.GetBytes (s); - var decodedBytes = urlDecodeToBytes (bytes, 0, bytes.Length); - - return (encoding ?? Encoding.UTF8).GetString (decodedBytes); - } - - public static string UrlDecode ( - byte[] bytes, - int offset, - int count, - Encoding encoding - ) - { - if (bytes == null) - throw new ArgumentNullException ("bytes"); - - var len = bytes.Length; - - if (len == 0) { - if (offset != 0) - throw new ArgumentOutOfRangeException ("offset"); - - if (count != 0) - throw new ArgumentOutOfRangeException ("count"); - - return String.Empty; - } - - if (offset < 0 || offset >= len) - throw new ArgumentOutOfRangeException ("offset"); - - if (count < 0 || count > len - offset) - throw new ArgumentOutOfRangeException ("count"); - - if (count == 0) - return String.Empty; - - var decodedBytes = urlDecodeToBytes (bytes, offset, count); - - return (encoding ?? Encoding.UTF8).GetString (decodedBytes); - } - - public static byte[] UrlDecodeToBytes (byte[] bytes) - { - if (bytes == null) - throw new ArgumentNullException ("bytes"); - - var len = bytes.Length; - - return len > 0 ? urlDecodeToBytes (bytes, 0, len) : bytes; - } - - public static byte[] UrlDecodeToBytes (string s) - { - if (s == null) - throw new ArgumentNullException ("s"); - - if (s.Length == 0) - return new byte[0]; - - var bytes = Encoding.ASCII.GetBytes (s); - - return urlDecodeToBytes (bytes, 0, bytes.Length); - } - - public static byte[] UrlDecodeToBytes (byte[] bytes, int offset, int count) - { - if (bytes == null) - throw new ArgumentNullException ("bytes"); - - var len = bytes.Length; - - if (len == 0) { - if (offset != 0) - throw new ArgumentOutOfRangeException ("offset"); - - if (count != 0) - throw new ArgumentOutOfRangeException ("count"); - - return bytes; - } - - if (offset < 0 || offset >= len) - throw new ArgumentOutOfRangeException ("offset"); - - if (count < 0 || count > len - offset) - throw new ArgumentOutOfRangeException ("count"); - - return count > 0 ? urlDecodeToBytes (bytes, offset, count) : new byte[0]; - } - - public static string UrlEncode (byte[] bytes) - { - if (bytes == null) - throw new ArgumentNullException ("bytes"); - - var len = bytes.Length; - - if (len == 0) - return String.Empty; - - var encodedBytes = urlEncodeToBytes (bytes, 0, len); - - return Encoding.ASCII.GetString (encodedBytes); - } - - public static string UrlEncode (string s) - { - return UrlEncode (s, Encoding.UTF8); - } - - public static string UrlEncode (string s, Encoding encoding) - { - if (s == null) - throw new ArgumentNullException ("s"); - - var len = s.Length; - - if (len == 0) - return s; - - if (encoding == null) - encoding = Encoding.UTF8; - - var maxCnt = encoding.GetMaxByteCount (len); - var bytes = new byte[maxCnt]; - var cnt = encoding.GetBytes (s, 0, len, bytes, 0); - var encodedBytes = urlEncodeToBytes (bytes, 0, cnt); - - return Encoding.ASCII.GetString (encodedBytes); - } - - public static string UrlEncode (byte[] bytes, int offset, int count) - { - if (bytes == null) - throw new ArgumentNullException ("bytes"); - - var len = bytes.Length; - - if (len == 0) { - if (offset != 0) - throw new ArgumentOutOfRangeException ("offset"); - - if (count != 0) - throw new ArgumentOutOfRangeException ("count"); - - return String.Empty; - } - - if (offset < 0 || offset >= len) - throw new ArgumentOutOfRangeException ("offset"); - - if (count < 0 || count > len - offset) - throw new ArgumentOutOfRangeException ("count"); - - if (count == 0) - return String.Empty; - - var encodedBytes = urlEncodeToBytes (bytes, offset, count); - - return Encoding.ASCII.GetString (encodedBytes); - } - - public static byte[] UrlEncodeToBytes (byte[] bytes) - { - if (bytes == null) - throw new ArgumentNullException ("bytes"); - - var len = bytes.Length; - - return len > 0 ? urlEncodeToBytes (bytes, 0, len) : bytes; - } - - public static byte[] UrlEncodeToBytes (string s) - { - return UrlEncodeToBytes (s, Encoding.UTF8); - } - - public static byte[] UrlEncodeToBytes (string s, Encoding encoding) - { - if (s == null) - throw new ArgumentNullException ("s"); - - if (s.Length == 0) - return new byte[0]; - - var bytes = (encoding ?? Encoding.UTF8).GetBytes (s); - - return urlEncodeToBytes (bytes, 0, bytes.Length); - } - - public static byte[] UrlEncodeToBytes (byte[] bytes, int offset, int count) - { - if (bytes == null) - throw new ArgumentNullException ("bytes"); - - var len = bytes.Length; - - if (len == 0) { - if (offset != 0) - throw new ArgumentOutOfRangeException ("offset"); - - if (count != 0) - throw new ArgumentOutOfRangeException ("count"); - - return bytes; - } - - if (offset < 0 || offset >= len) - throw new ArgumentOutOfRangeException ("offset"); - - if (count < 0 || count > len - offset) - throw new ArgumentOutOfRangeException ("count"); - - return count > 0 ? urlEncodeToBytes (bytes, offset, count) : new byte[0]; - } - - #endregion - } -} diff --git a/websocket-sharp/Net/HttpVersion.cs b/websocket-sharp/Net/HttpVersion.cs deleted file mode 100644 index 95f8f0a38..000000000 --- a/websocket-sharp/Net/HttpVersion.cs +++ /dev/null @@ -1,73 +0,0 @@ -#region License -/* - * HttpVersion.cs - * - * This code is derived from HttpVersion.cs (System.Net) of Mono - * (http://www.mono-project.com). - * - * The MIT License - * - * Copyright (c) 2012-2024 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -#region Authors -/* - * Authors: - * - Lawrence Pit - */ -#endregion - -using System; - -namespace WebSocketSharp.Net -{ - /// - /// Provides the HTTP version numbers. - /// - public class HttpVersion - { - #region Public Fields - - /// - /// Provides a instance for the HTTP/1.0. - /// - public static readonly Version Version10 = new Version (1, 0); - - /// - /// Provides a instance for the HTTP/1.1. - /// - public static readonly Version Version11 = new Version (1, 1); - - #endregion - - #region Public Constructors - - /// - /// Initializes a new instance of the class. - /// - public HttpVersion () - { - } - - #endregion - } -} diff --git a/websocket-sharp/Net/InputChunkState.cs b/websocket-sharp/Net/InputChunkState.cs deleted file mode 100644 index f50ad6b7a..000000000 --- a/websocket-sharp/Net/InputChunkState.cs +++ /dev/null @@ -1,52 +0,0 @@ -#region License -/* - * InputChunkState.cs - * - * This code is derived from ChunkStream.cs (System.Net) of Mono - * (http://www.mono-project.com). - * - * The MIT License - * - * Copyright (c) 2003 Ximian, Inc (http://www.ximian.com) - * Copyright (c) 2014-2015 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -#region Authors -/* - * Authors: - * - Gonzalo Paniagua Javier - */ -#endregion - -using System; - -namespace WebSocketSharp.Net -{ - internal enum InputChunkState - { - None, - Data, - DataEnded, - Trailer, - End - } -} diff --git a/websocket-sharp/Net/InputState.cs b/websocket-sharp/Net/InputState.cs deleted file mode 100644 index 9f566d246..000000000 --- a/websocket-sharp/Net/InputState.cs +++ /dev/null @@ -1,49 +0,0 @@ -#region License -/* - * InputState.cs - * - * This code is derived from HttpConnection.cs (System.Net) of Mono - * (http://www.mono-project.com). - * - * The MIT License - * - * Copyright (c) 2005 Novell, Inc. (http://www.novell.com) - * Copyright (c) 2014-2015 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -#region Authors -/* - * Authors: - * - Gonzalo Paniagua Javier - */ -#endregion - -using System; - -namespace WebSocketSharp.Net -{ - internal enum InputState - { - RequestLine, - Headers - } -} diff --git a/websocket-sharp/Net/LineState.cs b/websocket-sharp/Net/LineState.cs deleted file mode 100644 index 84e271a7b..000000000 --- a/websocket-sharp/Net/LineState.cs +++ /dev/null @@ -1,50 +0,0 @@ -#region License -/* - * LineState.cs - * - * This code is derived from HttpConnection.cs (System.Net) of Mono - * (http://www.mono-project.com). - * - * The MIT License - * - * Copyright (c) 2005 Novell, Inc. (http://www.novell.com) - * Copyright (c) 2014-2015 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -#region Authors -/* - * Authors: - * - Gonzalo Paniagua Javier - */ -#endregion - -using System; - -namespace WebSocketSharp.Net -{ - internal enum LineState - { - None, - Cr, - Lf - } -} diff --git a/websocket-sharp/Net/NetworkCredential.cs b/websocket-sharp/Net/NetworkCredential.cs deleted file mode 100644 index f97df971f..000000000 --- a/websocket-sharp/Net/NetworkCredential.cs +++ /dev/null @@ -1,217 +0,0 @@ -#region License -/* - * NetworkCredential.cs - * - * The MIT License - * - * Copyright (c) 2014-2024 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -using System; - -namespace WebSocketSharp.Net -{ - /// - /// Provides the credentials for the password-based authentication. - /// - public class NetworkCredential - { - #region Private Fields - - private string _domain; - private static readonly string[] _noRoles; - private string _password; - private string[] _roles; - private string _username; - - #endregion - - #region Static Constructor - - static NetworkCredential () - { - _noRoles = new string[0]; - } - - #endregion - - #region Public Constructors - - /// - /// Initializes a new instance of the class - /// with the specified username and password. - /// - /// - /// A that specifies the username associated with - /// the credentials. - /// - /// - /// A that specifies the password for the username - /// associated with the credentials. - /// - /// - /// is an empty string. - /// - /// - /// is . - /// - public NetworkCredential (string username, string password) - : this (username, password, null, null) - { - } - - /// - /// Initializes a new instance of the class - /// with the specified username, password, domain and roles. - /// - /// - /// A that specifies the username associated with - /// the credentials. - /// - /// - /// A that specifies the password for the username - /// associated with the credentials. - /// - /// - /// A that specifies the domain associated with - /// the credentials. - /// - /// - /// An array of that specifies the roles associated - /// with the credentials if any. - /// - /// - /// is an empty string. - /// - /// - /// is . - /// - public NetworkCredential ( - string username, - string password, - string domain, - params string[] roles - ) - { - if (username == null) - throw new ArgumentNullException ("username"); - - if (username.Length == 0) - throw new ArgumentException ("An empty string.", "username"); - - _username = username; - _password = password; - _domain = domain; - _roles = roles; - } - - #endregion - - #region Public Properties - - /// - /// Gets the domain associated with the credentials. - /// - /// - /// - /// A that represents the domain name - /// to which the username belongs. - /// - /// - /// An empty string if the value was initialized with - /// . - /// - /// - public string Domain { - get { - return _domain ?? String.Empty; - } - - internal set { - _domain = value; - } - } - - /// - /// Gets the password for the username associated with the credentials. - /// - /// - /// - /// A that represents the password. - /// - /// - /// An empty string if the value was initialized with - /// . - /// - /// - public string Password { - get { - return _password ?? String.Empty; - } - - internal set { - _password = value; - } - } - - /// - /// Gets the roles associated with the credentials. - /// - /// - /// - /// An array of that represents the role names - /// to which the username belongs. - /// - /// - /// An empty array if the value was initialized with - /// . - /// - /// - public string[] Roles { - get { - return _roles ?? _noRoles; - } - - internal set { - _roles = value; - } - } - - /// - /// Gets the username associated with the credentials. - /// - /// - /// A that represents the username. - /// - public string Username { - get { - return _username; - } - - internal set { - _username = value; - } - } - - #endregion - } -} diff --git a/websocket-sharp/Net/QueryStringCollection.cs b/websocket-sharp/Net/QueryStringCollection.cs deleted file mode 100644 index d5b7a8d9a..000000000 --- a/websocket-sharp/Net/QueryStringCollection.cs +++ /dev/null @@ -1,144 +0,0 @@ -#region License -/* - * QueryStringCollection.cs - * - * This code is derived from HttpUtility.cs (System.Net) of Mono - * (http://www.mono-project.com). - * - * The MIT License - * - * Copyright (c) 2005-2009 Novell, Inc. (http://www.novell.com) - * Copyright (c) 2018-2023 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -#region Authors -/* - * Authors: - * - Patrik Torstensson - * - Wictor Wilén (decode/encode functions) - * - Tim Coleman - * - Gonzalo Paniagua Javier - */ -#endregion - -using System; -using System.Collections.Specialized; -using System.Text; - -namespace WebSocketSharp.Net -{ - internal sealed class QueryStringCollection : NameValueCollection - { - #region Public Constructors - - public QueryStringCollection () - { - } - - public QueryStringCollection (int capacity) - : base (capacity) - { - } - - #endregion - - #region Public Methods - - public static QueryStringCollection Parse (string query) - { - return Parse (query, Encoding.UTF8); - } - - public static QueryStringCollection Parse (string query, Encoding encoding) - { - if (query == null) - return new QueryStringCollection (1); - - if (query.Length == 0) - return new QueryStringCollection (1); - - if (query == "?") - return new QueryStringCollection (1); - - if (query[0] == '?') - query = query.Substring (1); - - if (encoding == null) - encoding = Encoding.UTF8; - - var ret = new QueryStringCollection (); - - foreach (var component in query.Split ('&')) { - var len = component.Length; - - if (len == 0) - continue; - - if (component == "=") - continue; - - string name = null; - string val = null; - - var idx = component.IndexOf ('='); - - if (idx < 0) { - val = component.UrlDecode (encoding); - } - else if (idx == 0) { - val = component.Substring (1).UrlDecode (encoding); - } - else { - name = component.Substring (0, idx).UrlDecode (encoding); - - var start = idx + 1; - val = start < len - ? component.Substring (start).UrlDecode (encoding) - : String.Empty; - } - - ret.Add (name, val); - } - - return ret; - } - - public override string ToString () - { - if (Count == 0) - return String.Empty; - - var buff = new StringBuilder (); - - var fmt = "{0}={1}&"; - - foreach (var key in AllKeys) - buff.AppendFormat (fmt, key, this[key]); - - buff.Length--; - - return buff.ToString (); - } - - #endregion - } -} diff --git a/websocket-sharp/Net/ReadBufferState.cs b/websocket-sharp/Net/ReadBufferState.cs deleted file mode 100644 index bf0de8848..000000000 --- a/websocket-sharp/Net/ReadBufferState.cs +++ /dev/null @@ -1,129 +0,0 @@ -#region License -/* - * ReadBufferState.cs - * - * This code is derived from ChunkedInputStream.cs (System.Net) of Mono - * (http://www.mono-project.com). - * - * The MIT License - * - * Copyright (c) 2005 Novell, Inc. (http://www.novell.com) - * Copyright (c) 2014-2023 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -#region Authors -/* - * Authors: - * - Gonzalo Paniagua Javier - */ -#endregion - -using System; - -namespace WebSocketSharp.Net -{ - internal class ReadBufferState - { - #region Private Fields - - private HttpStreamAsyncResult _asyncResult; - private byte[] _buffer; - private int _count; - private int _initialCount; - private int _offset; - - #endregion - - #region Public Constructors - - public ReadBufferState ( - byte[] buffer, - int offset, - int count, - HttpStreamAsyncResult asyncResult - ) - { - _buffer = buffer; - _offset = offset; - _count = count; - _asyncResult = asyncResult; - - _initialCount = count; - } - - #endregion - - #region Public Properties - - public HttpStreamAsyncResult AsyncResult { - get { - return _asyncResult; - } - - set { - _asyncResult = value; - } - } - - public byte[] Buffer { - get { - return _buffer; - } - - set { - _buffer = value; - } - } - - public int Count { - get { - return _count; - } - - set { - _count = value; - } - } - - public int InitialCount { - get { - return _initialCount; - } - - set { - _initialCount = value; - } - } - - public int Offset { - get { - return _offset; - } - - set { - _offset = value; - } - } - - #endregion - } -} diff --git a/websocket-sharp/Net/RequestStream.cs b/websocket-sharp/Net/RequestStream.cs deleted file mode 100644 index dd40f920a..000000000 --- a/websocket-sharp/Net/RequestStream.cs +++ /dev/null @@ -1,354 +0,0 @@ -#region License -/* - * RequestStream.cs - * - * This code is derived from RequestStream.cs (System.Net) of Mono - * (http://www.mono-project.com). - * - * The MIT License - * - * Copyright (c) 2005 Novell, Inc. (http://www.novell.com) - * Copyright (c) 2012-2023 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -#region Authors -/* - * Authors: - * - Gonzalo Paniagua Javier - */ -#endregion - -using System; -using System.IO; - -namespace WebSocketSharp.Net -{ - internal class RequestStream : Stream - { - #region Private Fields - - private long _bodyLeft; - private int _count; - private bool _disposed; - private byte[] _initialBuffer; - private Stream _innerStream; - private int _offset; - - #endregion - - #region Internal Constructors - - internal RequestStream ( - Stream innerStream, - byte[] initialBuffer, - int offset, - int count, - long contentLength - ) - { - _innerStream = innerStream; - _initialBuffer = initialBuffer; - _offset = offset; - _count = count; - _bodyLeft = contentLength; - } - - #endregion - - #region Internal Properties - - internal int Count { - get { - return _count; - } - } - - internal byte[] InitialBuffer { - get { - return _initialBuffer; - } - } - - internal string ObjectName { - get { - return GetType ().ToString (); - } - } - - internal int Offset { - get { - return _offset; - } - } - - #endregion - - #region Public Properties - - public override bool CanRead { - get { - return true; - } - } - - public override bool CanSeek { - get { - return false; - } - } - - public override bool CanWrite { - get { - return false; - } - } - - public override long Length { - get { - throw new NotSupportedException (); - } - } - - public override long Position { - get { - throw new NotSupportedException (); - } - - set { - throw new NotSupportedException (); - } - } - - #endregion - - #region Private Methods - - private int fillFromInitialBuffer (byte[] buffer, int offset, int count) - { - // This method returns a int: - // - > 0 The number of bytes read from the initial buffer - // - 0 No more bytes read from the initial buffer - // - -1 No more content data - - if (_bodyLeft == 0) - return -1; - - if (_count == 0) - return 0; - - if (count > _count) - count = _count; - - if (_bodyLeft > 0 && _bodyLeft < count) - count = (int) _bodyLeft; - - Buffer.BlockCopy (_initialBuffer, _offset, buffer, offset, count); - - _offset += count; - _count -= count; - - if (_bodyLeft > 0) - _bodyLeft -= count; - - return count; - } - - #endregion - - #region Public Methods - - public override IAsyncResult BeginRead ( - byte[] buffer, - int offset, - int count, - AsyncCallback callback, - object state - ) - { - if (_disposed) - throw new ObjectDisposedException (ObjectName); - - if (buffer == null) - throw new ArgumentNullException ("buffer"); - - if (offset < 0) { - var msg = "A negative value."; - - throw new ArgumentOutOfRangeException ("offset", msg); - } - - if (count < 0) { - var msg = "A negative value."; - - throw new ArgumentOutOfRangeException ("count", msg); - } - - var len = buffer.Length; - - if (offset + count > len) { - var msg = "The sum of offset and count is greater than the length of buffer."; - - throw new ArgumentException (msg); - } - - if (count == 0) - return _innerStream.BeginRead (buffer, offset, 0, callback, state); - - var nread = fillFromInitialBuffer (buffer, offset, count); - - if (nread != 0) { - var ares = new HttpStreamAsyncResult (callback, state); - - ares.Buffer = buffer; - ares.Offset = offset; - ares.Count = count; - ares.SyncRead = nread > 0 ? nread : 0; - - ares.Complete (); - - return ares; - } - - if (_bodyLeft > 0 && _bodyLeft < count) - count = (int) _bodyLeft; - - return _innerStream.BeginRead (buffer, offset, count, callback, state); - } - - public override IAsyncResult BeginWrite ( - byte[] buffer, - int offset, - int count, - AsyncCallback callback, - object state - ) - { - throw new NotSupportedException (); - } - - public override void Close () - { - _disposed = true; - } - - public override int EndRead (IAsyncResult asyncResult) - { - if (_disposed) - throw new ObjectDisposedException (ObjectName); - - if (asyncResult == null) - throw new ArgumentNullException ("asyncResult"); - - if (asyncResult is HttpStreamAsyncResult) { - var ares = (HttpStreamAsyncResult) asyncResult; - - if (!ares.IsCompleted) - ares.AsyncWaitHandle.WaitOne (); - - return ares.SyncRead; - } - - var nread = _innerStream.EndRead (asyncResult); - - if (nread > 0 && _bodyLeft > 0) - _bodyLeft -= nread; - - return nread; - } - - public override void EndWrite (IAsyncResult asyncResult) - { - throw new NotSupportedException (); - } - - public override void Flush () - { - } - - public override int Read (byte[] buffer, int offset, int count) - { - if (_disposed) - throw new ObjectDisposedException (ObjectName); - - if (buffer == null) - throw new ArgumentNullException ("buffer"); - - if (offset < 0) { - var msg = "A negative value."; - - throw new ArgumentOutOfRangeException ("offset", msg); - } - - if (count < 0) { - var msg = "A negative value."; - - throw new ArgumentOutOfRangeException ("count", msg); - } - - var len = buffer.Length; - - if (offset + count > len) { - var msg = "The sum of offset and count is greater than the length of buffer."; - - throw new ArgumentException (msg); - } - - if (count == 0) - return 0; - - var nread = fillFromInitialBuffer (buffer, offset, count); - - if (nread == -1) - return 0; - - if (nread > 0) - return nread; - - if (_bodyLeft > 0 && _bodyLeft < count) - count = (int) _bodyLeft; - - nread = _innerStream.Read (buffer, offset, count); - - if (nread > 0 && _bodyLeft > 0) - _bodyLeft -= nread; - - return nread; - } - - public override long Seek (long offset, SeekOrigin origin) - { - throw new NotSupportedException (); - } - - public override void SetLength (long value) - { - throw new NotSupportedException (); - } - - public override void Write (byte[] buffer, int offset, int count) - { - throw new NotSupportedException (); - } - - #endregion - } -} diff --git a/websocket-sharp/Net/ResponseStream.cs b/websocket-sharp/Net/ResponseStream.cs deleted file mode 100644 index 456d1e470..000000000 --- a/websocket-sharp/Net/ResponseStream.cs +++ /dev/null @@ -1,416 +0,0 @@ -#region License -/* - * ResponseStream.cs - * - * This code is derived from ResponseStream.cs (System.Net) of Mono - * (http://www.mono-project.com). - * - * The MIT License - * - * Copyright (c) 2005 Novell, Inc. (http://www.novell.com) - * Copyright (c) 2012-2023 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -#region Authors -/* - * Authors: - * - Gonzalo Paniagua Javier - */ -#endregion - -using System; -using System.IO; -using System.Text; - -namespace WebSocketSharp.Net -{ - internal class ResponseStream : Stream - { - #region Private Fields - - private MemoryStream _bodyBuffer; - private static readonly byte[] _crlf; - private bool _disposed; - private Stream _innerStream; - private static readonly byte[] _lastChunk; - private static readonly int _maxHeadersLength; - private HttpListenerResponse _response; - private bool _sendChunked; - private Action _write; - private Action _writeBody; - private Action _writeChunked; - - #endregion - - #region Static Constructor - - static ResponseStream () - { - _crlf = new byte[] { 13, 10 }; // "\r\n" - _lastChunk = new byte[] { 48, 13, 10, 13, 10 }; // "0\r\n\r\n" - _maxHeadersLength = 32768; - } - - #endregion - - #region Internal Constructors - - internal ResponseStream ( - Stream innerStream, - HttpListenerResponse response, - bool ignoreWriteExceptions - ) - { - _innerStream = innerStream; - _response = response; - - if (ignoreWriteExceptions) { - _write = writeWithoutThrowingException; - _writeChunked = writeChunkedWithoutThrowingException; - } - else { - _write = innerStream.Write; - _writeChunked = writeChunked; - } - - _bodyBuffer = new MemoryStream (); - } - - #endregion - - #region Internal Properties - - internal string ObjectName { - get { - return GetType ().ToString (); - } - } - - #endregion - - #region Public Properties - - public override bool CanRead { - get { - return false; - } - } - - public override bool CanSeek { - get { - return false; - } - } - - public override bool CanWrite { - get { - return !_disposed; - } - } - - public override long Length { - get { - throw new NotSupportedException (); - } - } - - public override long Position { - get { - throw new NotSupportedException (); - } - - set { - throw new NotSupportedException (); - } - } - - #endregion - - #region Private Methods - - private bool flush (bool closing) - { - if (!_response.HeadersSent) { - if (!flushHeaders ()) - return false; - - _response.HeadersSent = true; - - _sendChunked = _response.SendChunked; - _writeBody = _sendChunked ? _writeChunked : _write; - } - - flushBody (closing); - - return true; - } - - private void flushBody (bool closing) - { - using (_bodyBuffer) { - var len = _bodyBuffer.Length; - - if (len > Int32.MaxValue) { - _bodyBuffer.Position = 0; - - var buffLen = 1024; - var buff = new byte[buffLen]; - var nread = 0; - - while (true) { - nread = _bodyBuffer.Read (buff, 0, buffLen); - - if (nread <= 0) - break; - - _writeBody (buff, 0, nread); - } - } - else if (len > 0) { - var buff = _bodyBuffer.GetBuffer (); - - _writeBody (buff, 0, (int) len); - } - } - - if (!closing) { - _bodyBuffer = new MemoryStream (); - - return; - } - - if (_sendChunked) - _write (_lastChunk, 0, 5); - - _bodyBuffer = null; - } - - private bool flushHeaders () - { - if (!_response.SendChunked) { - if (_response.ContentLength64 != _bodyBuffer.Length) - return false; - } - - var headers = _response.FullHeaders; - - var stream = new MemoryStream (); - var enc = Encoding.UTF8; - - using (var writer = new StreamWriter (stream, enc, 256)) { - writer.Write (_response.StatusLine); - - var s = headers.ToStringMultiValue (true); - - writer.Write (s); - writer.Flush (); - - var start = enc.GetPreamble ().Length; - var len = stream.Length - start; - - if (len > _maxHeadersLength) - return false; - - var buff = stream.GetBuffer (); - - _write (buff, start, (int) len); - } - - _response.CloseConnection = headers["Connection"] == "close"; - - return true; - } - - private static byte[] getChunkSizeStringAsBytes (int size) - { - var fmt = "{0:x}\r\n"; - var s = String.Format (fmt, size); - - return Encoding.ASCII.GetBytes (s); - } - - private void writeChunked (byte[] buffer, int offset, int count) - { - var size = getChunkSizeStringAsBytes (count); - - _innerStream.Write (size, 0, size.Length); - _innerStream.Write (buffer, offset, count); - _innerStream.Write (_crlf, 0, 2); - } - - private void writeChunkedWithoutThrowingException ( - byte[] buffer, - int offset, - int count - ) - { - try { - writeChunked (buffer, offset, count); - } - catch { - } - } - - private void writeWithoutThrowingException ( - byte[] buffer, - int offset, - int count - ) - { - try { - _innerStream.Write (buffer, offset, count); - } - catch { - } - } - - #endregion - - #region Internal Methods - - internal void Close (bool force) - { - if (_disposed) - return; - - _disposed = true; - - if (!force) { - if (flush (true)) { - _response.Close (); - - _response = null; - _innerStream = null; - - return; - } - - _response.CloseConnection = true; - } - - if (_sendChunked) - _write (_lastChunk, 0, 5); - - _bodyBuffer.Dispose (); - _response.Abort (); - - _bodyBuffer = null; - _response = null; - _innerStream = null; - } - - internal void InternalWrite (byte[] buffer, int offset, int count) - { - _write (buffer, offset, count); - } - - #endregion - - #region Public Methods - - public override IAsyncResult BeginRead ( - byte[] buffer, - int offset, - int count, - AsyncCallback callback, - object state - ) - { - throw new NotSupportedException (); - } - - public override IAsyncResult BeginWrite ( - byte[] buffer, - int offset, - int count, - AsyncCallback callback, - object state - ) - { - if (_disposed) - throw new ObjectDisposedException (ObjectName); - - return _bodyBuffer.BeginWrite (buffer, offset, count, callback, state); - } - - public override void Close () - { - Close (false); - } - - protected override void Dispose (bool disposing) - { - Close (!disposing); - } - - public override int EndRead (IAsyncResult asyncResult) - { - throw new NotSupportedException (); - } - - public override void EndWrite (IAsyncResult asyncResult) - { - if (_disposed) - throw new ObjectDisposedException (ObjectName); - - _bodyBuffer.EndWrite (asyncResult); - } - - public override void Flush () - { - if (_disposed) - return; - - var sendChunked = _sendChunked || _response.SendChunked; - - if (!sendChunked) - return; - - flush (false); - } - - public override int Read (byte[] buffer, int offset, int count) - { - throw new NotSupportedException (); - } - - public override long Seek (long offset, SeekOrigin origin) - { - throw new NotSupportedException (); - } - - public override void SetLength (long value) - { - throw new NotSupportedException (); - } - - public override void Write (byte[] buffer, int offset, int count) - { - if (_disposed) - throw new ObjectDisposedException (ObjectName); - - _bodyBuffer.Write (buffer, offset, count); - } - - #endregion - } -} diff --git a/websocket-sharp/Net/ServerSslConfiguration.cs b/websocket-sharp/Net/ServerSslConfiguration.cs deleted file mode 100644 index b4de5d64c..000000000 --- a/websocket-sharp/Net/ServerSslConfiguration.cs +++ /dev/null @@ -1,239 +0,0 @@ -#region License -/* - * ServerSslConfiguration.cs - * - * The MIT License - * - * Copyright (c) 2014 liryna - * Copyright (c) 2014-2024 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -#region Authors -/* - * Authors: - * - Liryna - */ -#endregion - -using System; -using System.Net.Security; -using System.Security.Authentication; -using System.Security.Cryptography.X509Certificates; - -namespace WebSocketSharp.Net -{ - /// - /// Stores the parameters for instances used by - /// a server. - /// - public class ServerSslConfiguration - { - #region Private Fields - - private bool _checkCertRevocation; - private bool _clientCertRequired; - private RemoteCertificateValidationCallback _clientCertValidationCallback; - private SslProtocols _enabledSslProtocols; - private X509Certificate2 _serverCert; - - #endregion - - #region Public Constructors - - /// - /// Initializes a new instance of the - /// class. - /// - public ServerSslConfiguration () - { - _enabledSslProtocols = SslProtocols.None; - } - - /// - /// Initializes a new instance of the - /// class copying from the specified configuration. - /// - /// - /// A from which to copy. - /// - /// - /// is . - /// - public ServerSslConfiguration (ServerSslConfiguration configuration) - { - if (configuration == null) - throw new ArgumentNullException ("configuration"); - - _checkCertRevocation = configuration._checkCertRevocation; - _clientCertRequired = configuration._clientCertRequired; - _clientCertValidationCallback = configuration._clientCertValidationCallback; - _enabledSslProtocols = configuration._enabledSslProtocols; - _serverCert = configuration._serverCert; - } - - #endregion - - #region Public Properties - - /// - /// Gets or sets a value indicating whether the certificate revocation - /// list is checked during authentication. - /// - /// - /// - /// true if the certificate revocation list is checked during - /// authentication; otherwise, false. - /// - /// - /// The default value is false. - /// - /// - public bool CheckCertificateRevocation { - get { - return _checkCertRevocation; - } - - set { - _checkCertRevocation = value; - } - } - - /// - /// Gets or sets a value indicating whether each client is asked for - /// a certificate for authentication. - /// - /// - /// - /// true if each client is asked for a certificate for - /// authentication; otherwise, false. - /// - /// - /// The default value is false. - /// - /// - public bool ClientCertificateRequired { - get { - return _clientCertRequired; - } - - set { - _clientCertRequired = value; - } - } - - /// - /// Gets or sets the callback used to validate the certificate supplied by - /// each client. - /// - /// - /// The certificate is valid if the callback returns true. - /// - /// - /// - /// A delegate. - /// - /// - /// It represents the delegate called when the server validates - /// the certificate. - /// - /// - /// The default value invokes a method that only returns true. - /// - /// - public RemoteCertificateValidationCallback ClientCertificateValidationCallback { - get { - if (_clientCertValidationCallback == null) - _clientCertValidationCallback = defaultValidateClientCertificate; - - return _clientCertValidationCallback; - } - - set { - _clientCertValidationCallback = value; - } - } - - /// - /// Gets or sets the enabled versions of the SSL/TLS protocols. - /// - /// - /// - /// Any of the enum values. - /// - /// - /// It represents the enabled versions of the SSL/TLS protocols. - /// - /// - /// The default value is . - /// - /// - public SslProtocols EnabledSslProtocols { - get { - return _enabledSslProtocols; - } - - set { - _enabledSslProtocols = value; - } - } - - /// - /// Gets or sets the certificate used to authenticate the server. - /// - /// - /// - /// A that represents an X.509 certificate. - /// - /// - /// if not present. - /// - /// - /// The default value is . - /// - /// - public X509Certificate2 ServerCertificate { - get { - return _serverCert; - } - - set { - _serverCert = value; - } - } - - #endregion - - #region Private Methods - - private static bool defaultValidateClientCertificate ( - object sender, - X509Certificate certificate, - X509Chain chain, - SslPolicyErrors sslPolicyErrors - ) - { - return true; - } - - #endregion - } -} diff --git a/websocket-sharp/Net/WebHeaderCollection.cs b/websocket-sharp/Net/WebHeaderCollection.cs deleted file mode 100644 index 3f3ca74a9..000000000 --- a/websocket-sharp/Net/WebHeaderCollection.cs +++ /dev/null @@ -1,2006 +0,0 @@ -#region License -/* - * WebHeaderCollection.cs - * - * This code is derived from WebHeaderCollection.cs (System.Net) of Mono - * (http://www.mono-project.com). - * - * The MIT License - * - * Copyright (c) 2003 Ximian, Inc. (http://www.ximian.com) - * Copyright (c) 2007 Novell, Inc. (http://www.novell.com) - * Copyright (c) 2012-2026 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -#region Authors -/* - * Authors: - * - Lawrence Pit - * - Gonzalo Paniagua Javier - * - Miguel de Icaza - */ -#endregion - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.Runtime.InteropServices; -using System.Runtime.Serialization; -using System.Security.Permissions; -using System.Text; - -namespace WebSocketSharp.Net -{ - /// - /// Provides a collection of the HTTP headers associated with a request or - /// response. - /// - [Serializable] - [ComVisible (true)] - public class WebHeaderCollection : NameValueCollection, ISerializable - { - #region Private Fields - - private static readonly Dictionary _headers; - private bool _internallyUsed; - private HttpHeaderType _state; - - #endregion - - #region Static Constructor - - static WebHeaderCollection () - { - _headers = - new Dictionary ( - StringComparer.InvariantCultureIgnoreCase - ) - { - { - "Accept", - new HttpHeaderInfo ( - "Accept", - HttpHeaderType.Request - | HttpHeaderType.Restricted - | HttpHeaderType.MultiValue - ) - }, - { - "AcceptCharset", - new HttpHeaderInfo ( - "Accept-Charset", - HttpHeaderType.Request | HttpHeaderType.MultiValue - ) - }, - { - "AcceptEncoding", - new HttpHeaderInfo ( - "Accept-Encoding", - HttpHeaderType.Request | HttpHeaderType.MultiValue - ) - }, - { - "AcceptLanguage", - new HttpHeaderInfo ( - "Accept-Language", - HttpHeaderType.Request | HttpHeaderType.MultiValue - ) - }, - { - "AcceptRanges", - new HttpHeaderInfo ( - "Accept-Ranges", - HttpHeaderType.Response | HttpHeaderType.MultiValue - ) - }, - { - "Age", - new HttpHeaderInfo ( - "Age", - HttpHeaderType.Response - ) - }, - { - "Allow", - new HttpHeaderInfo ( - "Allow", - HttpHeaderType.Request - | HttpHeaderType.Response - | HttpHeaderType.MultiValue - ) - }, - { - "Authorization", - new HttpHeaderInfo ( - "Authorization", - HttpHeaderType.Request - | HttpHeaderType.Restricted - | HttpHeaderType.MultiValue - ) - }, - { - "CacheControl", - new HttpHeaderInfo ( - "Cache-Control", - HttpHeaderType.Request - | HttpHeaderType.Response - | HttpHeaderType.MultiValue - ) - }, - { - "Connection", - new HttpHeaderInfo ( - "Connection", - HttpHeaderType.Request - | HttpHeaderType.Response - | HttpHeaderType.Restricted - | HttpHeaderType.MultiValue - ) - }, - { - "ContentEncoding", - new HttpHeaderInfo ( - "Content-Encoding", - HttpHeaderType.Request - | HttpHeaderType.Response - | HttpHeaderType.MultiValue - ) - }, - { - "ContentLanguage", - new HttpHeaderInfo ( - "Content-Language", - HttpHeaderType.Request - | HttpHeaderType.Response - | HttpHeaderType.MultiValue - ) - }, - { - "ContentLength", - new HttpHeaderInfo ( - "Content-Length", - HttpHeaderType.Request - | HttpHeaderType.Response - | HttpHeaderType.Restricted - ) - }, - { - "ContentLocation", - new HttpHeaderInfo ( - "Content-Location", - HttpHeaderType.Request | HttpHeaderType.Response - ) - }, - { - "ContentMd5", - new HttpHeaderInfo ( - "Content-MD5", - HttpHeaderType.Request | HttpHeaderType.Response - ) - }, - { - "ContentRange", - new HttpHeaderInfo ( - "Content-Range", - HttpHeaderType.Request | HttpHeaderType.Response - ) - }, - { - "ContentType", - new HttpHeaderInfo ( - "Content-Type", - HttpHeaderType.Request - | HttpHeaderType.Response - | HttpHeaderType.Restricted - ) - }, - { - "Cookie", - new HttpHeaderInfo ( - "Cookie", - HttpHeaderType.Request | HttpHeaderType.Restricted - ) - }, - { - "Cookie2", - new HttpHeaderInfo ( - "Cookie2", - HttpHeaderType.Request | HttpHeaderType.Restricted - ) - }, - { - "Date", - new HttpHeaderInfo ( - "Date", - HttpHeaderType.Request - | HttpHeaderType.Response - | HttpHeaderType.Restricted - ) - }, - { - "Expect", - new HttpHeaderInfo ( - "Expect", - HttpHeaderType.Request - | HttpHeaderType.Restricted - | HttpHeaderType.MultiValue - ) - }, - { - "Expires", - new HttpHeaderInfo ( - "Expires", - HttpHeaderType.Request | HttpHeaderType.Response - ) - }, - { - "ETag", - new HttpHeaderInfo ( - "ETag", - HttpHeaderType.Response - ) - }, - { - "From", - new HttpHeaderInfo ( - "From", - HttpHeaderType.Request - ) - }, - { - "Host", - new HttpHeaderInfo ( - "Host", - HttpHeaderType.Request | HttpHeaderType.Restricted - ) - }, - { - "IfMatch", - new HttpHeaderInfo ( - "If-Match", - HttpHeaderType.Request | HttpHeaderType.MultiValue - ) - }, - { - "IfModifiedSince", - new HttpHeaderInfo ( - "If-Modified-Since", - HttpHeaderType.Request | HttpHeaderType.Restricted - ) - }, - { - "IfNoneMatch", - new HttpHeaderInfo ( - "If-None-Match", - HttpHeaderType.Request | HttpHeaderType.MultiValue - ) - }, - { - "IfRange", - new HttpHeaderInfo ( - "If-Range", - HttpHeaderType.Request - ) - }, - { - "IfUnmodifiedSince", - new HttpHeaderInfo ( - "If-Unmodified-Since", - HttpHeaderType.Request - ) - }, - { - "KeepAlive", - new HttpHeaderInfo ( - "Keep-Alive", - HttpHeaderType.Request - | HttpHeaderType.Response - | HttpHeaderType.MultiValue - ) - }, - { - "LastModified", - new HttpHeaderInfo ( - "Last-Modified", - HttpHeaderType.Request | HttpHeaderType.Response - ) - }, - { - "Location", - new HttpHeaderInfo ( - "Location", - HttpHeaderType.Response | HttpHeaderType.Restricted - ) - }, - { - "MaxForwards", - new HttpHeaderInfo ( - "Max-Forwards", - HttpHeaderType.Request - ) - }, - { - "Pragma", - new HttpHeaderInfo ( - "Pragma", - HttpHeaderType.Request | HttpHeaderType.Response - ) - }, - { - "ProxyAuthenticate", - new HttpHeaderInfo ( - "Proxy-Authenticate", - HttpHeaderType.Response - | HttpHeaderType.Restricted - | HttpHeaderType.MultiValue - ) - }, - { - "ProxyAuthorization", - new HttpHeaderInfo ( - "Proxy-Authorization", - HttpHeaderType.Request | HttpHeaderType.Restricted - ) - }, - { - "ProxyConnection", - new HttpHeaderInfo ( - "Proxy-Connection", - HttpHeaderType.Request - | HttpHeaderType.Response - | HttpHeaderType.Restricted - ) - }, - { - "Public", - new HttpHeaderInfo ( - "Public", - HttpHeaderType.Response | HttpHeaderType.MultiValue - ) - }, - { - "Range", - new HttpHeaderInfo ( - "Range", - HttpHeaderType.Request - | HttpHeaderType.Restricted - | HttpHeaderType.MultiValue - ) - }, - { - "Referer", - new HttpHeaderInfo ( - "Referer", - HttpHeaderType.Request | HttpHeaderType.Restricted - ) - }, - { - "RetryAfter", - new HttpHeaderInfo ( - "Retry-After", - HttpHeaderType.Response - ) - }, - { - "SecWebSocketAccept", - new HttpHeaderInfo ( - "Sec-WebSocket-Accept", - HttpHeaderType.Response | HttpHeaderType.Restricted - ) - }, - { - "SecWebSocketExtensions", - new HttpHeaderInfo ( - "Sec-WebSocket-Extensions", - HttpHeaderType.Request - | HttpHeaderType.Response - | HttpHeaderType.Restricted - | HttpHeaderType.MultiValueInRequest - ) - }, - { - "SecWebSocketKey", - new HttpHeaderInfo ( - "Sec-WebSocket-Key", - HttpHeaderType.Request | HttpHeaderType.Restricted - ) - }, - { - "SecWebSocketProtocol", - new HttpHeaderInfo ( - "Sec-WebSocket-Protocol", - HttpHeaderType.Request - | HttpHeaderType.Response - | HttpHeaderType.Restricted - | HttpHeaderType.MultiValueInRequest - ) - }, - { - "SecWebSocketVersion", - new HttpHeaderInfo ( - "Sec-WebSocket-Version", - HttpHeaderType.Request - | HttpHeaderType.Response - | HttpHeaderType.Restricted - | HttpHeaderType.MultiValueInResponse - ) - }, - { - "Server", - new HttpHeaderInfo ( - "Server", - HttpHeaderType.Response - ) - }, - { - "SetCookie", - new HttpHeaderInfo ( - "Set-Cookie", - HttpHeaderType.Response - | HttpHeaderType.Restricted - | HttpHeaderType.MultiValue - ) - }, - { - "SetCookie2", - new HttpHeaderInfo ( - "Set-Cookie2", - HttpHeaderType.Response - | HttpHeaderType.Restricted - | HttpHeaderType.MultiValue - ) - }, - { - "Te", - new HttpHeaderInfo ( - "TE", - HttpHeaderType.Request - ) - }, - { - "Trailer", - new HttpHeaderInfo ( - "Trailer", - HttpHeaderType.Request | HttpHeaderType.Response - ) - }, - { - "TransferEncoding", - new HttpHeaderInfo ( - "Transfer-Encoding", - HttpHeaderType.Request - | HttpHeaderType.Response - | HttpHeaderType.Restricted - | HttpHeaderType.MultiValue - ) - }, - { - "Translate", - new HttpHeaderInfo ( - "Translate", - HttpHeaderType.Request - ) - }, - { - "Upgrade", - new HttpHeaderInfo ( - "Upgrade", - HttpHeaderType.Request - | HttpHeaderType.Response - | HttpHeaderType.Restricted - | HttpHeaderType.MultiValue - ) - }, - { - "UserAgent", - new HttpHeaderInfo ( - "User-Agent", - HttpHeaderType.Request | HttpHeaderType.Restricted - ) - }, - { - "Vary", - new HttpHeaderInfo ( - "Vary", - HttpHeaderType.Response | HttpHeaderType.MultiValue - ) - }, - { - "Via", - new HttpHeaderInfo ( - "Via", - HttpHeaderType.Request - | HttpHeaderType.Response - | HttpHeaderType.MultiValue - ) - }, - { - "Warning", - new HttpHeaderInfo ( - "Warning", - HttpHeaderType.Request - | HttpHeaderType.Response - | HttpHeaderType.MultiValue - ) - }, - { - "WwwAuthenticate", - new HttpHeaderInfo ( - "WWW-Authenticate", - HttpHeaderType.Response - | HttpHeaderType.Restricted - | HttpHeaderType.MultiValue - ) - } - }; - } - - #endregion - - #region Internal Constructors - - internal WebHeaderCollection (HttpHeaderType state, bool internallyUsed) - { - _state = state; - _internallyUsed = internallyUsed; - } - - #endregion - - #region Protected Constructors - - /// - /// Initializes a new instance of the - /// class with the specified serialized data. - /// - /// - /// A that contains the serialized - /// object data. - /// - /// - /// A that specifies the source for - /// the deserialization. - /// - /// - /// An element with the specified name is not found in - /// . - /// - /// - /// is . - /// - protected WebHeaderCollection ( - SerializationInfo serializationInfo, - StreamingContext streamingContext - ) - { - if (serializationInfo == null) - throw new ArgumentNullException ("serializationInfo"); - - try { - _internallyUsed = serializationInfo.GetBoolean ("InternallyUsed"); - _state = (HttpHeaderType) serializationInfo.GetInt32 ("State"); - - var cnt = serializationInfo.GetInt32 ("Count"); - - for (var i = 0; i < cnt; i++) { - base.Add ( - serializationInfo.GetString (i.ToString ()), - serializationInfo.GetString ((cnt + i).ToString ()) - ); - } - } - catch (SerializationException ex) { - throw new ArgumentException (ex.Message, "serializationInfo", ex); - } - } - - #endregion - - #region Public Constructors - - /// - /// Initializes a new instance of the - /// class. - /// - public WebHeaderCollection () - { - } - - #endregion - - #region Internal Properties - - internal HttpHeaderType State { - get { - return _state; - } - } - - #endregion - - #region Public Properties - - /// - /// Gets all header names in the collection. - /// - /// - /// An array of that contains all header names in - /// the collection. - /// - public override string[] AllKeys { - get { - return base.AllKeys; - } - } - - /// - /// Gets the number of headers in the collection. - /// - /// - /// An that represents the number of headers in - /// the collection. - /// - public override int Count { - get { - return base.Count; - } - } - - /// - /// Gets or sets the specified request header. - /// - /// - /// - /// A that represents the value of the specified - /// request header. - /// - /// - /// The get operation returns if not found. - /// - /// - /// - /// - /// One of the enum values. - /// - /// - /// It specifies a request header to get or set. - /// - /// - /// - /// - /// The set operation throws this exception if - /// is a restricted header. - /// - /// - /// -or- - /// - /// - /// The set operation throws this exception if - /// contains an invalid character. - /// - /// - /// - /// The set operation throws this exception if the length of - /// is greater than 65,535 characters. - /// - /// - /// The set operation is not available if the current instance does not - /// allow the request header. - /// - public string this[HttpRequestHeader header] { - get { - var key = header.ToString (); - var name = getHeaderName (key); - - return Get (name); - } - - set { - Add (header, value); - } - } - - /// - /// Gets or sets the specified response header. - /// - /// - /// - /// A that represents the value of the specified - /// response header. - /// - /// - /// The get operation returns if not found. - /// - /// - /// - /// - /// One of the enum values. - /// - /// - /// It specifies a response header to get or set. - /// - /// - /// - /// - /// The set operation throws this exception if - /// is a restricted header. - /// - /// - /// -or- - /// - /// - /// The set operation throws this exception if - /// contains an invalid character. - /// - /// - /// - /// The set operation throws this exception if the length of - /// is greater than 65,535 characters. - /// - /// - /// The set operation is not available if the current instance does not - /// allow the response header. - /// - public string this[HttpResponseHeader header] { - get { - var key = header.ToString (); - var name = getHeaderName (key); - - return Get (name); - } - - set { - Add (header, value); - } - } - - /// - /// Gets a collection of header names in the collection. - /// - /// - /// A that contains - /// all header names in the collection. - /// - public override NameObjectCollectionBase.KeysCollection Keys { - get { - return base.Keys; - } - } - - #endregion - - #region Private Methods - - private void add (string name, string value, HttpHeaderType headerType) - { - base.Add (name, value); - - if (_state != HttpHeaderType.Unspecified) - return; - - if (headerType == HttpHeaderType.Unspecified) - return; - - _state = headerType; - } - - private void checkAllowed (HttpHeaderType headerType) - { - if (_state == HttpHeaderType.Unspecified) - return; - - if (headerType == HttpHeaderType.Unspecified) - return; - - if (headerType != _state) { - var msg = "The current instance does not allow that header type."; - - throw new InvalidOperationException (msg); - } - } - - private static string checkName (string name, string paramName) - { - if (name == null) - throw new ArgumentNullException (paramName); - - if (name.Length == 0) { - var msg = "An empty string."; - - throw new ArgumentException (msg, paramName); - } - - name = name.Trim (); - - if (name.Length == 0) { - var msg = "That name is a string of spaces."; - - throw new ArgumentException (msg, paramName); - } - - if (!name.IsToken ()) { - var msg = "That name contains an invalid character."; - - throw new ArgumentException (msg, paramName); - } - - return name; - } - - private void checkRestricted (string name, HttpHeaderType headerType) - { - if (_internallyUsed) - return; - - var res = headerType == HttpHeaderType.Response; - - if (isRestricted (name, res)) { - var msg = "That header is a restricted header."; - - throw new ArgumentException (msg); - } - } - - private static string checkValue (string value, string paramName) - { - if (value == null) - return String.Empty; - - value = value.Trim (); - - var len = value.Length; - - if (len == 0) - return value; - - if (len > 65535) { - var msg = "The length of that value is greater than 65,535 characters."; - - throw new ArgumentOutOfRangeException (paramName, msg); - } - - if (!value.IsText ()) { - var msg = "That value contains an invalid character."; - - throw new ArgumentException (msg, paramName); - } - - return value; - } - - private static HttpHeaderInfo getHeaderInfo (string name) - { - var compType = StringComparison.InvariantCultureIgnoreCase; - - foreach (var headerInfo in _headers.Values) { - if (headerInfo.HeaderName.Equals (name, compType)) - return headerInfo; - } - - return null; - } - - private static string getHeaderName (string key) - { - HttpHeaderInfo headerInfo; - - return _headers.TryGetValue (key, out headerInfo) - ? headerInfo.HeaderName - : null; - } - - private static HttpHeaderType getHeaderType (string name) - { - var headerInfo = getHeaderInfo (name); - - if (headerInfo == null) - return HttpHeaderType.Unspecified; - - if (headerInfo.IsRequest) { - return !headerInfo.IsResponse - ? HttpHeaderType.Request - : HttpHeaderType.Unspecified; - } - - return headerInfo.IsResponse - ? HttpHeaderType.Response - : HttpHeaderType.Unspecified; - } - - private static bool isMultiValue (string name, bool response) - { - var headerInfo = getHeaderInfo (name); - - return headerInfo != null && headerInfo.IsMultiValue (response); - } - - private static bool isRestricted (string name, bool response) - { - var headerInfo = getHeaderInfo (name); - - return headerInfo != null && headerInfo.IsRestricted (response); - } - - private void set (string name, string value, HttpHeaderType headerType) - { - base.Set (name, value); - - if (_state != HttpHeaderType.Unspecified) - return; - - if (headerType == HttpHeaderType.Unspecified) - return; - - _state = headerType; - } - - #endregion - - #region Internal Methods - - internal void InternalRemove (string name) - { - base.Remove (name); - } - - internal void InternalSet (string header, bool response) - { - var idx = header.IndexOf (':'); - - if (idx == -1) { - var msg = "It does not contain a colon character."; - - throw new ArgumentException (msg, "header"); - } - - if (idx == 0) { - var msg = "It does not include a header name."; - - throw new ArgumentException (msg, "header"); - } - - var name = header.Substring (0, idx); - var val = idx < header.Length - 1 - ? header.Substring (idx + 1) - : String.Empty; - - name = checkName (name, "header"); - val = checkValue (val, "header"); - - if (isMultiValue (name, response)) { - base.Add (name, val); - - return; - } - - base.Set (name, val); - } - - internal void InternalSet (string name, string value, bool response) - { - value = checkValue (value, "value"); - - if (isMultiValue (name, response)) { - base.Add (name, value); - - return; - } - - base.Set (name, value); - } - - internal string ToStringMultiValue (bool response) - { - var cnt = Count; - - if (cnt == 0) - return "\r\n"; - - var buff = new StringBuilder (); - - var fmt = "{0}: {1}\r\n"; - - for (var i = 0; i < cnt; i++) { - var name = GetKey (i); - - if (isMultiValue (name, response)) { - foreach (var val in GetValues (i)) - buff.AppendFormat (fmt, name, val); - - continue; - } - - buff.AppendFormat (fmt, name, Get (i)); - } - - buff.Append ("\r\n"); - - return buff.ToString (); - } - - #endregion - - #region Protected Methods - - /// - /// Adds a header to the collection without checking if that header is - /// on the restricted header list. - /// - /// - /// If the specified header is already present in the collection, - /// is added to the comma-separated - /// list of the existing values. - /// - /// - /// A that specifies the name of a header to add. - /// - /// - /// A that specifies the value of the header. - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// is a string of spaces. - /// - /// - /// -or- - /// - /// - /// contains an invalid character. - /// - /// - /// -or- - /// - /// - /// contains an invalid character. - /// - /// - /// - /// is . - /// - /// - /// The length of is greater than 65,535 - /// characters. - /// - /// - /// This method is not available if the current instance does not allow - /// that header type. - /// - protected void AddWithoutValidate (string headerName, string headerValue) - { - headerName = checkName (headerName, "headerName"); - - var headerType = getHeaderType (headerName); - - checkAllowed (headerType); - - headerValue = checkValue (headerValue, "headerValue"); - - add (headerName, headerValue, headerType); - } - - #endregion - - #region Public Methods - - /// - /// Adds the specified header to the collection. - /// - /// - /// If the specified header is already present in the collection, - /// the value part of is added to the - /// comma-separated list of the existing values. - /// - /// - /// A that specifies a header to add, - /// with the name and value separated by a colon character (':'). - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// does not contain a colon character. - /// - /// - /// -or- - /// - /// - /// does not include a header name. - /// - /// - /// -or- - /// - /// - /// The name part of is a string of spaces. - /// - /// - /// -or- - /// - /// - /// The name part of contains an invalid - /// character. - /// - /// - /// -or- - /// - /// - /// is a restricted header. - /// - /// - /// -or- - /// - /// - /// The value part of contains an invalid - /// character. - /// - /// - /// - /// is . - /// - /// - /// The length of the value part of is greater - /// than 65,535 characters. - /// - /// - /// This method is not available if the current instance does not allow - /// that header type. - /// - public void Add (string header) - { - if (header == null) - throw new ArgumentNullException ("header"); - - var len = header.Length; - - if (len == 0) { - var msg = "An empty string."; - - throw new ArgumentException (msg, "header"); - } - - var idx = header.IndexOf (':'); - - if (idx == -1) { - var msg = "It does not contain a colon character."; - - throw new ArgumentException (msg, "header"); - } - - if (idx == 0) { - var msg = "It does not include a header name."; - - throw new ArgumentException (msg, "header"); - } - - var name = header.Substring (0, idx); - var val = idx < len - 1 ? header.Substring (idx + 1) : String.Empty; - - name = checkName (name, "header"); - - var headerType = getHeaderType (name); - - checkAllowed (headerType); - - checkRestricted (name, headerType); - - val = checkValue (val, "header"); - - add (name, val, headerType); - } - - /// - /// Adds the specified request header with the specified value to - /// the collection. - /// - /// - /// If the specified request header is already present in the collection, - /// is added to the comma-separated list of the - /// existing values. - /// - /// - /// - /// One of the enum values. - /// - /// - /// It specifies a request header to add. - /// - /// - /// - /// A that specifies the value of the header. - /// - /// - /// - /// is a restricted header. - /// - /// - /// -or- - /// - /// - /// contains an invalid character. - /// - /// - /// - /// The length of is greater than 65,535 - /// characters. - /// - /// - /// This method is not available if the current instance does not allow - /// the request header. - /// - public void Add (HttpRequestHeader header, string value) - { - checkAllowed (HttpHeaderType.Request); - - var key = header.ToString (); - var name = getHeaderName (key); - - checkRestricted (name, HttpHeaderType.Request); - - value = checkValue (value, "value"); - - add (name, value, HttpHeaderType.Request); - } - - /// - /// Adds the specified response header with the specified value to - /// the collection. - /// - /// - /// If the specified response header is already present in the collection, - /// is added to the comma-separated list of the - /// existing values. - /// - /// - /// - /// One of the enum values. - /// - /// - /// It specifies a response header to add. - /// - /// - /// - /// A that specifies the value of the header. - /// - /// - /// - /// is a restricted header. - /// - /// - /// -or- - /// - /// - /// contains an invalid character. - /// - /// - /// - /// The length of is greater than 65,535 - /// characters. - /// - /// - /// This method is not available if the current instance does not allow - /// the response header. - /// - public void Add (HttpResponseHeader header, string value) - { - checkAllowed (HttpHeaderType.Response); - - var key = header.ToString (); - var name = getHeaderName (key); - - checkRestricted (name, HttpHeaderType.Response); - - value = checkValue (value, "value"); - - add (name, value, HttpHeaderType.Response); - } - - /// - /// Adds a header with the specified name and value to the collection. - /// - /// - /// If the specified header is already present in the collection, - /// is added to the comma-separated list of - /// the existing values. - /// - /// - /// A that specifies the name of a header to add. - /// - /// - /// A that specifies the value of the header. - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// is a string of spaces. - /// - /// - /// -or- - /// - /// - /// contains an invalid character. - /// - /// - /// -or- - /// - /// - /// is a restricted header name. - /// - /// - /// -or- - /// - /// - /// contains an invalid character. - /// - /// - /// - /// is . - /// - /// - /// The length of is greater than 65,535 - /// characters. - /// - /// - /// This method is not available if the current instance does not allow - /// that header type. - /// - public override void Add (string name, string value) - { - name = checkName (name, "name"); - - var headerType = getHeaderType (name); - - checkAllowed (headerType); - - checkRestricted (name, headerType); - - value = checkValue (value, "value"); - - add (name, value, headerType); - } - - /// - /// Removes all headers from the collection. - /// - public override void Clear () - { - base.Clear (); - - _state = HttpHeaderType.Unspecified; - } - - /// - /// Get the value of a header at the specified index from the collection. - /// - /// - /// A that holds the value of the specified header. - /// - /// - /// An that specifies the zero-based index of a header - /// to get. - /// - /// - /// is out of allowable range of indexes for - /// the collection. - /// - public override string Get (int index) - { - return base.Get (index); - } - - /// - /// Get the value of a header with the specified name from the collection. - /// - /// - /// - /// A that holds the value of the specified header. - /// - /// - /// if not found. - /// - /// - /// - /// A that specifies the name of a header to get. - /// - public override string Get (string name) - { - return base.Get (name); - } - - /// - /// Gets the enumerator used to iterate through the collection. - /// - /// - /// An instance used to iterate through - /// the collection. - /// - public override IEnumerator GetEnumerator () - { - return base.GetEnumerator (); - } - - /// - /// Get the name of a header at the specified index from the collection. - /// - /// - /// A that holds the name of the specified header. - /// - /// - /// An that specifies the zero-based index of a header - /// to get. - /// - /// - /// is out of allowable range of indexes for - /// the collection. - /// - public override string GetKey (int index) - { - return base.GetKey (index); - } - - /// - /// Get the values of a header at the specified index from the collection. - /// - /// - /// - /// An array of that holds the values of - /// the specified header. - /// - /// - /// if not present. - /// - /// - /// - /// An that specifies the zero-based index of a header - /// to get. - /// - /// - /// is out of allowable range of indexes for - /// the collection. - /// - public override string[] GetValues (int index) - { - var vals = base.GetValues (index); - - return vals != null && vals.Length > 0 ? vals : null; - } - - /// - /// Get the values of a header with the specified name from the collection. - /// - /// - /// - /// An array of that holds the values of - /// the specified header. - /// - /// - /// if not present. - /// - /// - /// - /// A that specifies the name of a header to get. - /// - public override string[] GetValues (string name) - { - var vals = base.GetValues (name); - - return vals != null && vals.Length > 0 ? vals : null; - } - - /// - /// Populates the specified instance with - /// the data needed to serialize the current instance. - /// - /// - /// A that holds the serialized object data. - /// - /// - /// A that specifies the destination for - /// the serialization. - /// - /// - /// is . - /// - [ - SecurityPermission ( - SecurityAction.LinkDemand, - Flags = SecurityPermissionFlag.SerializationFormatter - ) - ] - public override void GetObjectData ( - SerializationInfo serializationInfo, - StreamingContext streamingContext - ) - { - if (serializationInfo == null) - throw new ArgumentNullException ("serializationInfo"); - - serializationInfo.AddValue ("InternallyUsed", _internallyUsed); - serializationInfo.AddValue ("State", (int) _state); - - var cnt = Count; - - serializationInfo.AddValue ("Count", cnt); - - for (var i = 0; i < cnt; i++) { - serializationInfo.AddValue (i.ToString (), GetKey (i)); - serializationInfo.AddValue ((cnt + i).ToString (), Get (i)); - } - } - - /// - /// Determines whether the specified header can be set for the request. - /// - /// - /// true if the specified header cannot be set; otherwise, - /// false. - /// - /// - /// A that specifies the name of a header to test. - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// is a string of spaces. - /// - /// - /// -or- - /// - /// - /// contains an invalid character. - /// - /// - /// - /// is . - /// - public static bool IsRestricted (string headerName) - { - return IsRestricted (headerName, false); - } - - /// - /// Determines whether the specified header can be set for the request or - /// the response. - /// - /// - /// true if the specified header cannot be set; otherwise, - /// false. - /// - /// - /// A that specifies the name of a header to test. - /// - /// - /// A : true if the test is for the response; - /// otherwise, false. - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// is a string of spaces. - /// - /// - /// -or- - /// - /// - /// contains an invalid character. - /// - /// - /// - /// is . - /// - public static bool IsRestricted (string headerName, bool response) - { - headerName = checkName (headerName, "headerName"); - - return isRestricted (headerName, response); - } - - /// - /// Implements the interface and raises - /// the deserialization event when the deserialization is complete. - /// - /// - /// An instance that represents the source of - /// the deserialization event. - /// - public override void OnDeserialization (object sender) - { - } - - /// - /// Removes the specified request header from the collection. - /// - /// - /// - /// One of the enum values. - /// - /// - /// It specifies a request header to remove. - /// - /// - /// - /// is a restricted header. - /// - /// - /// This method is not available if the current instance does not allow - /// the request header. - /// - public void Remove (HttpRequestHeader header) - { - checkAllowed (HttpHeaderType.Request); - - var key = header.ToString (); - var name = getHeaderName (key); - - checkRestricted (name, HttpHeaderType.Request); - - base.Remove (name); - } - - /// - /// Removes the specified response header from the collection. - /// - /// - /// - /// One of the enum values. - /// - /// - /// It specifies a response header to remove. - /// - /// - /// - /// is a restricted header. - /// - /// - /// This method is not available if the current instance does not allow - /// the response header. - /// - public void Remove (HttpResponseHeader header) - { - checkAllowed (HttpHeaderType.Response); - - var key = header.ToString (); - var name = getHeaderName (key); - - checkRestricted (name, HttpHeaderType.Response); - - base.Remove (name); - } - - /// - /// Removes the specified header from the collection. - /// - /// - /// A that specifies the name of a header to remove. - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// is a string of spaces. - /// - /// - /// -or- - /// - /// - /// contains an invalid character. - /// - /// - /// -or- - /// - /// - /// is a restricted header name. - /// - /// - /// - /// is . - /// - /// - /// This method is not available if the current instance does not allow - /// that header type. - /// - public override void Remove (string name) - { - name = checkName (name, "name"); - - var headerType = getHeaderType (name); - - checkAllowed (headerType); - - checkRestricted (name, headerType); - - base.Remove (name); - } - - /// - /// Sets the specified request header to the specified value. - /// - /// - /// If the specified request header is already present in the collection, - /// replaces the existing value. - /// - /// - /// - /// One of the enum values. - /// - /// - /// It specifies a request header to set. - /// - /// - /// - /// A that specifies the value of the header. - /// - /// - /// - /// is a restricted header. - /// - /// - /// -or- - /// - /// - /// contains an invalid character. - /// - /// - /// - /// The length of is greater than 65,535 - /// characters. - /// - /// - /// This method is not available if the current instance does not allow - /// the request header. - /// - public void Set (HttpRequestHeader header, string value) - { - checkAllowed (HttpHeaderType.Request); - - var key = header.ToString (); - var name = getHeaderName (key); - - checkRestricted (name, HttpHeaderType.Request); - - value = checkValue (value, "value"); - - set (name, value, HttpHeaderType.Request); - } - - /// - /// Sets the specified response header to the specified value. - /// - /// - /// If the specified response header is already present in the collection, - /// replaces the existing value. - /// - /// - /// - /// One of the enum values. - /// - /// - /// It specifies a response header to set. - /// - /// - /// - /// A that specifies the value of the header. - /// - /// - /// - /// is a restricted header. - /// - /// - /// -or- - /// - /// - /// contains an invalid character. - /// - /// - /// - /// The length of is greater than 65,535 - /// characters. - /// - /// - /// This method is not available if the current instance does not allow - /// the response header. - /// - public void Set (HttpResponseHeader header, string value) - { - checkAllowed (HttpHeaderType.Response); - - var key = header.ToString (); - var name = getHeaderName (key); - - checkRestricted (name, HttpHeaderType.Response); - - value = checkValue (value, "value"); - - set (name, value, HttpHeaderType.Response); - } - - /// - /// Sets the specified header to the specified value. - /// - /// - /// If the specified header is already present in the collection, - /// replaces the existing value. - /// - /// - /// A that specifies the name of a header to set. - /// - /// - /// A that specifies the value of the header. - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// is a string of spaces. - /// - /// - /// -or- - /// - /// - /// contains an invalid character. - /// - /// - /// -or- - /// - /// - /// is a restricted header name. - /// - /// - /// -or- - /// - /// - /// contains an invalid character. - /// - /// - /// - /// is . - /// - /// - /// The length of is greater than 65,535 - /// characters. - /// - /// - /// This method is not available if the current instance does not allow - /// that header type. - /// - public override void Set (string name, string value) - { - name = checkName (name, "name"); - - var headerType = getHeaderType (name); - - checkAllowed (headerType); - - checkRestricted (name, headerType); - - value = checkValue (value, "value"); - - set (name, value, headerType); - } - - /// - /// Converts the current instance to an array of byte. - /// - /// - /// An array of converted from a string that represents - /// the current instance. - /// - public byte[] ToByteArray () - { - var s = ToString (); - - return Encoding.UTF8.GetBytes (s); - } - - /// - /// Returns a string that represents the current instance. - /// - /// - /// A that represents all headers in the collection. - /// - public override string ToString () - { - var cnt = Count; - - if (cnt == 0) - return "\r\n"; - - var buff = new StringBuilder (); - - var fmt = "{0}: {1}\r\n"; - - for (var i = 0; i < cnt; i++) - buff.AppendFormat (fmt, GetKey (i), Get (i)); - - buff.Append ("\r\n"); - - return buff.ToString (); - } - - #endregion - - #region Explicit Interface Implementations - - /// - /// Populates the specified instance with - /// the data needed to serialize the current instance. - /// - /// - /// A that holds the serialized object data. - /// - /// - /// A that specifies the destination for - /// the serialization. - /// - /// - /// is . - /// - [ - SecurityPermission ( - SecurityAction.LinkDemand, - Flags = SecurityPermissionFlag.SerializationFormatter, - SerializationFormatter = true - ) - ] - void ISerializable.GetObjectData ( - SerializationInfo serializationInfo, - StreamingContext streamingContext - ) - { - GetObjectData (serializationInfo, streamingContext); - } - - #endregion - } -} diff --git a/websocket-sharp/Net/WebSockets/HttpListenerWebSocketContext.cs b/websocket-sharp/Net/WebSockets/HttpListenerWebSocketContext.cs deleted file mode 100644 index 7e0358d1f..000000000 --- a/websocket-sharp/Net/WebSockets/HttpListenerWebSocketContext.cs +++ /dev/null @@ -1,406 +0,0 @@ -#region License -/* - * HttpListenerWebSocketContext.cs - * - * The MIT License - * - * Copyright (c) 2012-2025 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -using System; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.IO; -using System.Net.Sockets; -using System.Security.Principal; - -namespace WebSocketSharp.Net.WebSockets -{ - /// - /// Provides the access to the information in a WebSocket handshake request - /// to a instance. - /// - public class HttpListenerWebSocketContext : WebSocketContext - { - #region Private Fields - - private HttpListenerContext _context; - private WebSocket _websocket; - - #endregion - - #region Internal Constructors - - internal HttpListenerWebSocketContext ( - HttpListenerContext context, - string protocol - ) - { - _context = context; - _websocket = new WebSocket (this, protocol); - } - - #endregion - - #region Internal Properties - - internal Logger Log { - get { - return _context.Listener.Log; - } - } - - internal Socket Socket { - get { - return _context.Connection.Socket; - } - } - - internal Stream Stream { - get { - return _context.Connection.Stream; - } - } - - #endregion - - #region Public Properties - - /// - /// Gets the HTTP cookies included in the handshake request. - /// - /// - /// - /// A that contains - /// the cookies. - /// - /// - /// An empty collection if not included. - /// - /// - public override CookieCollection CookieCollection { - get { - return _context.Request.Cookies; - } - } - - /// - /// Gets the HTTP headers included in the handshake request. - /// - /// - /// A that contains the headers. - /// - public override NameValueCollection Headers { - get { - return _context.Request.Headers; - } - } - - /// - /// Gets the value of the Host header included in the handshake request. - /// - /// - /// - /// A that represents the server host name requested - /// by the client. - /// - /// - /// It includes the port number if provided. - /// - /// - public override string Host { - get { - return _context.Request.UserHostName; - } - } - - /// - /// Gets a value indicating whether the client is authenticated. - /// - /// - /// true if the client is authenticated; otherwise, false. - /// - public override bool IsAuthenticated { - get { - return _context.Request.IsAuthenticated; - } - } - - /// - /// Gets a value indicating whether the handshake request is sent from - /// the local computer. - /// - /// - /// true if the handshake request is sent from the same computer - /// as the server; otherwise, false. - /// - public override bool IsLocal { - get { - return _context.Request.IsLocal; - } - } - - /// - /// Gets a value indicating whether a secure connection is used to send - /// the handshake request. - /// - /// - /// true if the connection is secure; otherwise, false. - /// - public override bool IsSecureConnection { - get { - return _context.Request.IsSecureConnection; - } - } - - /// - /// Gets a value indicating whether the request is a WebSocket handshake - /// request. - /// - /// - /// true if the request is a WebSocket handshake request; otherwise, - /// false. - /// - public override bool IsWebSocketRequest { - get { - return _context.Request.IsWebSocketRequest; - } - } - - /// - /// Gets the value of the Origin header included in the handshake request. - /// - /// - /// - /// A that represents the value of the Origin header. - /// - /// - /// if not included. - /// - /// - public override string Origin { - get { - return _context.Request.Headers["Origin"]; - } - } - - /// - /// Gets the query string included in the handshake request. - /// - /// - /// - /// A that contains the query - /// parameters. - /// - /// - /// An empty collection if not included. - /// - /// - public override NameValueCollection QueryString { - get { - return _context.Request.QueryString; - } - } - - /// - /// Gets the URI requested by the client. - /// - /// - /// - /// A that represents the URI parsed from the request. - /// - /// - /// if the URI cannot be parsed. - /// - /// - public override Uri RequestUri { - get { - return _context.Request.Url; - } - } - - /// - /// Gets the value of the Sec-WebSocket-Key header included in - /// the handshake request. - /// - /// - /// - /// A that represents the value of - /// the Sec-WebSocket-Key header. - /// - /// - /// The value is used to prove that the server received - /// a valid WebSocket handshake request. - /// - /// - /// if not included. - /// - /// - public override string SecWebSocketKey { - get { - return _context.Request.Headers["Sec-WebSocket-Key"]; - } - } - - /// - /// Gets the names of the subprotocols from the Sec-WebSocket-Protocol - /// header included in the handshake request. - /// - /// - /// - /// An - /// instance. - /// - /// - /// It provides an enumerator which supports the iteration over - /// the collection of the names of the subprotocols. - /// - /// - public override IEnumerable SecWebSocketProtocols { - get { - var val = _context.Request.Headers["Sec-WebSocket-Protocol"]; - - if (val == null || val.Length == 0) - yield break; - - foreach (var elm in val.Split (',')) { - var protocol = elm.Trim (); - - if (protocol.Length == 0) - continue; - - yield return protocol; - } - } - } - - /// - /// Gets the value of the Sec-WebSocket-Version header included in - /// the handshake request. - /// - /// - /// - /// A that represents the WebSocket protocol - /// version specified by the client. - /// - /// - /// if not included. - /// - /// - public override string SecWebSocketVersion { - get { - return _context.Request.Headers["Sec-WebSocket-Version"]; - } - } - - /// - /// Gets the endpoint to which the handshake request is sent. - /// - /// - /// A that represents the server - /// IP address and port number. - /// - public override System.Net.IPEndPoint ServerEndPoint { - get { - return _context.Request.LocalEndPoint; - } - } - - /// - /// Gets the client information. - /// - /// - /// - /// A instance that represents identity, - /// authentication, and security roles for the client. - /// - /// - /// if the client is not authenticated. - /// - /// - public override IPrincipal User { - get { - return _context.User; - } - } - - /// - /// Gets the endpoint from which the handshake request is sent. - /// - /// - /// A that represents the client - /// IP address and port number. - /// - public override System.Net.IPEndPoint UserEndPoint { - get { - return _context.Request.RemoteEndPoint; - } - } - - /// - /// Gets the WebSocket interface used for two-way communication between - /// the client and server. - /// - /// - /// A that represents the interface. - /// - public override WebSocket WebSocket { - get { - return _websocket; - } - } - - #endregion - - #region Internal Methods - - internal void Close () - { - _context.Connection.Close (true); - } - - internal void Close (HttpStatusCode code) - { - _context.Response.StatusCode = (int) code; - - _context.Response.Close (); - } - - #endregion - - #region Public Methods - - /// - /// Returns a string that represents the current instance. - /// - /// - /// A that contains the request line and headers - /// included in the handshake request. - /// - public override string ToString () - { - return _context.Request.ToString (); - } - - #endregion - } -} diff --git a/websocket-sharp/Net/WebSockets/TcpListenerWebSocketContext.cs b/websocket-sharp/Net/WebSockets/TcpListenerWebSocketContext.cs deleted file mode 100644 index 3e8d12777..000000000 --- a/websocket-sharp/Net/WebSockets/TcpListenerWebSocketContext.cs +++ /dev/null @@ -1,501 +0,0 @@ -#region License -/* - * TcpListenerWebSocketContext.cs - * - * The MIT License - * - * Copyright (c) 2012-2025 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -#region Contributors -/* - * Contributors: - * - Liryna - */ -#endregion - -using System; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.IO; -using System.Net.Security; -using System.Net.Sockets; -using System.Security.Principal; -using System.Text; - -namespace WebSocketSharp.Net.WebSockets -{ - /// - /// Provides the access to the information in a WebSocket handshake request - /// to a instance. - /// - internal class TcpListenerWebSocketContext : WebSocketContext - { - #region Private Fields - - private bool _isSecureConnection; - private Logger _log; - private NameValueCollection _queryString; - private HttpRequest _request; - private Uri _requestUri; - private Stream _stream; - private TcpClient _tcpClient; - private IPrincipal _user; - private WebSocket _websocket; - - #endregion - - #region Internal Constructors - - internal TcpListenerWebSocketContext ( - TcpClient tcpClient, - string protocol, - bool secure, - ServerSslConfiguration sslConfig, - Logger log - ) - { - _tcpClient = tcpClient; - _log = log; - - var netStream = tcpClient.GetStream (); - - if (secure) { - var sslStream = new SslStream ( - netStream, - false, - sslConfig.ClientCertificateValidationCallback - ); - - sslStream.AuthenticateAsServer ( - sslConfig.ServerCertificate, - sslConfig.ClientCertificateRequired, - sslConfig.EnabledSslProtocols, - sslConfig.CheckCertificateRevocation - ); - - _isSecureConnection = true; - _stream = sslStream; - } - else { - _stream = netStream; - } - - _request = HttpRequest.ReadRequest (_stream, 90000); - _websocket = new WebSocket (this, protocol); - } - - #endregion - - #region Internal Properties - - internal Logger Log { - get { - return _log; - } - } - - internal Socket Socket { - get { - return _tcpClient.Client; - } - } - - internal Stream Stream { - get { - return _stream; - } - } - - #endregion - - #region Public Properties - - /// - /// Gets the HTTP cookies included in the handshake request. - /// - /// - /// - /// A that contains - /// the cookies. - /// - /// - /// An empty collection if not included. - /// - /// - public override CookieCollection CookieCollection { - get { - return _request.Cookies; - } - } - - /// - /// Gets the HTTP headers included in the handshake request. - /// - /// - /// A that contains the headers. - /// - public override NameValueCollection Headers { - get { - return _request.Headers; - } - } - - /// - /// Gets the value of the Host header included in the handshake request. - /// - /// - /// - /// A that represents the server host name requested - /// by the client. - /// - /// - /// It includes the port number if provided. - /// - /// - public override string Host { - get { - return _request.Headers["Host"]; - } - } - - /// - /// Gets a value indicating whether the client is authenticated. - /// - /// - /// true if the client is authenticated; otherwise, false. - /// - public override bool IsAuthenticated { - get { - return _user != null; - } - } - - /// - /// Gets a value indicating whether the handshake request is sent from - /// the local computer. - /// - /// - /// true if the handshake request is sent from the same computer - /// as the server; otherwise, false. - /// - public override bool IsLocal { - get { - return UserEndPoint.Address.IsLocal (); - } - } - - /// - /// Gets a value indicating whether a secure connection is used to send - /// the handshake request. - /// - /// - /// true if the connection is secure; otherwise, false. - /// - public override bool IsSecureConnection { - get { - return _isSecureConnection; - } - } - - /// - /// Gets a value indicating whether the request is a WebSocket handshake - /// request. - /// - /// - /// true if the request is a WebSocket handshake request; otherwise, - /// false. - /// - public override bool IsWebSocketRequest { - get { - return _request.IsWebSocketRequest; - } - } - - /// - /// Gets the value of the Origin header included in the handshake request. - /// - /// - /// - /// A that represents the value of the Origin header. - /// - /// - /// if not included. - /// - /// - public override string Origin { - get { - return _request.Headers["Origin"]; - } - } - - /// - /// Gets the query string included in the handshake request. - /// - /// - /// - /// A that contains the query - /// parameters. - /// - /// - /// An empty collection if not included. - /// - /// - public override NameValueCollection QueryString { - get { - if (_queryString == null) { - var uri = RequestUri; - var query = uri != null ? uri.Query : null; - - _queryString = QueryStringCollection.Parse (query, Encoding.UTF8); - } - - return _queryString; - } - } - - /// - /// Gets the URI requested by the client. - /// - /// - /// - /// A that represents the URI parsed from the request. - /// - /// - /// if the URI cannot be parsed. - /// - /// - public override Uri RequestUri { - get { - if (_requestUri == null) { - _requestUri = HttpUtility.CreateRequestUrl ( - _request.RequestTarget, - _request.Headers["Host"], - _request.IsWebSocketRequest, - _isSecureConnection - ); - } - - return _requestUri; - } - } - - /// - /// Gets the value of the Sec-WebSocket-Key header included in - /// the handshake request. - /// - /// - /// - /// A that represents the value of - /// the Sec-WebSocket-Key header. - /// - /// - /// The value is used to prove that the server received - /// a valid WebSocket handshake request. - /// - /// - /// if not included. - /// - /// - public override string SecWebSocketKey { - get { - return _request.Headers["Sec-WebSocket-Key"]; - } - } - - /// - /// Gets the names of the subprotocols from the Sec-WebSocket-Protocol - /// header included in the handshake request. - /// - /// - /// - /// An - /// instance. - /// - /// - /// It provides an enumerator which supports the iteration over - /// the collection of the names of the subprotocols. - /// - /// - public override IEnumerable SecWebSocketProtocols { - get { - var val = _request.Headers["Sec-WebSocket-Protocol"]; - - if (val == null || val.Length == 0) - yield break; - - foreach (var elm in val.Split (',')) { - var protocol = elm.Trim (); - - if (protocol.Length == 0) - continue; - - yield return protocol; - } - } - } - - /// - /// Gets the value of the Sec-WebSocket-Version header included in - /// the handshake request. - /// - /// - /// - /// A that represents the WebSocket protocol - /// version specified by the client. - /// - /// - /// if not included. - /// - /// - public override string SecWebSocketVersion { - get { - return _request.Headers["Sec-WebSocket-Version"]; - } - } - - /// - /// Gets the endpoint to which the handshake request is sent. - /// - /// - /// A that represents the server - /// IP address and port number. - /// - public override System.Net.IPEndPoint ServerEndPoint { - get { - return (System.Net.IPEndPoint) _tcpClient.Client.LocalEndPoint; - } - } - - /// - /// Gets the client information. - /// - /// - /// - /// A instance that represents identity, - /// authentication, and security roles for the client. - /// - /// - /// if the client is not authenticated. - /// - /// - public override IPrincipal User { - get { - return _user; - } - } - - /// - /// Gets the endpoint from which the handshake request is sent. - /// - /// - /// A that represents the client - /// IP address and port number. - /// - public override System.Net.IPEndPoint UserEndPoint { - get { - return (System.Net.IPEndPoint) _tcpClient.Client.RemoteEndPoint; - } - } - - /// - /// Gets the WebSocket interface used for two-way communication between - /// the client and server. - /// - /// - /// A that represents the interface. - /// - public override WebSocket WebSocket { - get { - return _websocket; - } - } - - #endregion - - #region Internal Methods - - internal void Close () - { - _stream.Close (); - _tcpClient.Close (); - } - - internal void Close (HttpStatusCode code) - { - HttpResponse.CreateCloseResponse (code).WriteTo (_stream); - - _stream.Close (); - _tcpClient.Close (); - } - - internal void SendAuthenticationChallenge (string challenge) - { - HttpResponse.CreateUnauthorizedResponse (challenge).WriteTo (_stream); - - _request = HttpRequest.ReadRequest (_stream, 15000); - } - - internal bool SetUser ( - AuthenticationSchemes scheme, - string realm, - Func credentialsFinder - ) - { - var user = HttpUtility.CreateUser ( - _request.Headers["Authorization"], - scheme, - realm, - _request.HttpMethod, - credentialsFinder - ); - - if (user == null) - return false; - - if (!user.Identity.IsAuthenticated) - return false; - - _user = user; - - return true; - } - - #endregion - - #region Public Methods - - /// - /// Returns a string that represents the current instance. - /// - /// - /// A that contains the request line and headers - /// included in the handshake request. - /// - public override string ToString () - { - return _request.ToString (); - } - - #endregion - } -} diff --git a/websocket-sharp/Net/WebSockets/WebSocketContext.cs b/websocket-sharp/Net/WebSockets/WebSocketContext.cs deleted file mode 100644 index 84841f5bd..000000000 --- a/websocket-sharp/Net/WebSockets/WebSocketContext.cs +++ /dev/null @@ -1,224 +0,0 @@ -#region License -/* - * WebSocketContext.cs - * - * The MIT License - * - * Copyright (c) 2012-2022 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -using System; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.Security.Principal; - -namespace WebSocketSharp.Net.WebSockets -{ - /// - /// Exposes the access to the information in a WebSocket handshake request. - /// - /// - /// This class is an abstract class. - /// - public abstract class WebSocketContext - { - #region Protected Constructors - - /// - /// Initializes a new instance of the class. - /// - protected WebSocketContext () - { - } - - #endregion - - #region Public Properties - - /// - /// Gets the HTTP cookies included in the handshake request. - /// - /// - /// A that contains - /// the cookies. - /// - public abstract CookieCollection CookieCollection { get; } - - /// - /// Gets the HTTP headers included in the handshake request. - /// - /// - /// A that contains the headers. - /// - public abstract NameValueCollection Headers { get; } - - /// - /// Gets the value of the Host header included in the handshake request. - /// - /// - /// A that represents the server host name requested - /// by the client. - /// - public abstract string Host { get; } - - /// - /// Gets a value indicating whether the client is authenticated. - /// - /// - /// true if the client is authenticated; otherwise, false. - /// - public abstract bool IsAuthenticated { get; } - - /// - /// Gets a value indicating whether the handshake request is sent from - /// the local computer. - /// - /// - /// true if the handshake request is sent from the same computer - /// as the server; otherwise, false. - /// - public abstract bool IsLocal { get; } - - /// - /// Gets a value indicating whether a secure connection is used to send - /// the handshake request. - /// - /// - /// true if the connection is secure; otherwise, false. - /// - public abstract bool IsSecureConnection { get; } - - /// - /// Gets a value indicating whether the request is a WebSocket handshake - /// request. - /// - /// - /// true if the request is a WebSocket handshake request; otherwise, - /// false. - /// - public abstract bool IsWebSocketRequest { get; } - - /// - /// Gets the value of the Origin header included in the handshake request. - /// - /// - /// A that represents the value of the Origin header. - /// - public abstract string Origin { get; } - - /// - /// Gets the query string included in the handshake request. - /// - /// - /// A that contains the query parameters. - /// - public abstract NameValueCollection QueryString { get; } - - /// - /// Gets the URI requested by the client. - /// - /// - /// A that represents the URI parsed from the request. - /// - public abstract Uri RequestUri { get; } - - /// - /// Gets the value of the Sec-WebSocket-Key header included in - /// the handshake request. - /// - /// - /// - /// A that represents the value of - /// the Sec-WebSocket-Key header. - /// - /// - /// The value is used to prove that the server received - /// a valid WebSocket handshake request. - /// - /// - public abstract string SecWebSocketKey { get; } - - /// - /// Gets the names of the subprotocols from the Sec-WebSocket-Protocol - /// header included in the handshake request. - /// - /// - /// - /// An - /// instance. - /// - /// - /// It provides an enumerator which supports the iteration over - /// the collection of the names of the subprotocols. - /// - /// - public abstract IEnumerable SecWebSocketProtocols { get; } - - /// - /// Gets the value of the Sec-WebSocket-Version header included in - /// the handshake request. - /// - /// - /// A that represents the WebSocket protocol - /// version specified by the client. - /// - public abstract string SecWebSocketVersion { get; } - - /// - /// Gets the endpoint to which the handshake request is sent. - /// - /// - /// A that represents the server - /// IP address and port number. - /// - public abstract System.Net.IPEndPoint ServerEndPoint { get; } - - /// - /// Gets the client information. - /// - /// - /// A instance that represents identity, - /// authentication, and security roles for the client. - /// - public abstract IPrincipal User { get; } - - /// - /// Gets the endpoint from which the handshake request is sent. - /// - /// - /// A that represents the client - /// IP address and port number. - /// - public abstract System.Net.IPEndPoint UserEndPoint { get; } - - /// - /// Gets the WebSocket interface used for two-way communication between - /// the client and server. - /// - /// - /// A that represents the interface. - /// - public abstract WebSocket WebSocket { get; } - - #endregion - } -} diff --git a/websocket-sharp/Opcode.cs b/websocket-sharp/Opcode.cs deleted file mode 100644 index 06da1b5e1..000000000 --- a/websocket-sharp/Opcode.cs +++ /dev/null @@ -1,68 +0,0 @@ -#region License -/* - * Opcode.cs - * - * The MIT License - * - * Copyright (c) 2012-2025 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -using System; - -namespace WebSocketSharp -{ - /// - /// Indicates the WebSocket frame type. - /// - /// - /// The values of this enumeration are defined in - /// - /// Section 5.2 of RFC 6455. - /// - internal enum Opcode - { - /// - /// Equivalent to numeric value 0. Indicates continuation frame. - /// - Cont = 0x0, - /// - /// Equivalent to numeric value 1. Indicates text frame. - /// - Text = 0x1, - /// - /// Equivalent to numeric value 2. Indicates binary frame. - /// - Binary = 0x2, - /// - /// Equivalent to numeric value 8. Indicates connection close frame. - /// - Close = 0x8, - /// - /// Equivalent to numeric value 9. Indicates ping frame. - /// - Ping = 0x9, - /// - /// Equivalent to numeric value 10. Indicates pong frame. - /// - Pong = 0xa - } -} diff --git a/websocket-sharp/PayloadData.cs b/websocket-sharp/PayloadData.cs deleted file mode 100644 index e01fcd27e..000000000 --- a/websocket-sharp/PayloadData.cs +++ /dev/null @@ -1,212 +0,0 @@ -#region License -/* - * PayloadData.cs - * - * The MIT License - * - * Copyright (c) 2012-2025 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -using System; -using System.Collections; -using System.Collections.Generic; - -namespace WebSocketSharp -{ - internal class PayloadData : IEnumerable - { - #region Private Fields - - private byte[] _data; - private static readonly byte[] _emptyBytes; - private long _extDataLength; - private long _length; - - #endregion - - #region Public Fields - - /// - /// Represents the empty payload data. - /// - public static readonly PayloadData Empty; - - /// - /// Represents the allowable max length of payload data. - /// - /// - /// - /// A is thrown when the length of - /// incoming payload data is greater than the value of this field. - /// - /// - /// If you would like to change the value of this field, it must be - /// a number between and - /// inclusive. - /// - /// - public static readonly ulong MaxLength; - - #endregion - - #region Static Constructor - - static PayloadData () - { - _emptyBytes = new byte[0]; - - Empty = new PayloadData (_emptyBytes, 0); - MaxLength = Int64.MaxValue; - } - - #endregion - - #region Internal Constructors - - internal PayloadData (byte[] data) - : this (data, data.LongLength) - { - } - - internal PayloadData (byte[] data, long length) - { - _data = data; - _length = length; - } - - internal PayloadData (ushort code, string reason) - { - _data = code.Append (reason); - _length = _data.LongLength; - } - - #endregion - - #region Internal Properties - - internal ushort Code { - get { - return _length >= 2 - ? _data.SubArray (0, 2).ToUInt16 (ByteOrder.Big) - : (ushort) 1005; - } - } - - internal long ExtensionDataLength { - get { - return _extDataLength; - } - - set { - _extDataLength = value; - } - } - - internal bool HasReservedCode { - get { - return _length >= 2 && Code.IsReservedStatusCode (); - } - } - - internal string Reason { - get { - if (_length <= 2) - return String.Empty; - - var bytes = _data.SubArray (2, _length - 2); - - string reason; - - return bytes.TryGetUTF8DecodedString (out reason) - ? reason - : String.Empty; - } - } - - #endregion - - #region Public Properties - - public byte[] ApplicationData { - get { - return _extDataLength > 0 - ? _data.SubArray (_extDataLength, _length - _extDataLength) - : _data; - } - } - - public byte[] ExtensionData { - get { - return _extDataLength > 0 - ? _data.SubArray (0, _extDataLength) - : _emptyBytes; - } - } - - public ulong Length { - get { - return (ulong) _length; - } - } - - #endregion - - #region Internal Methods - - internal void Mask (byte[] key) - { - for (long i = 0; i < _length; i++) - _data[i] = (byte) (_data[i] ^ key[i % 4]); - } - - #endregion - - #region Public Methods - - public IEnumerator GetEnumerator () - { - foreach (var b in _data) - yield return b; - } - - public byte[] ToArray () - { - return _data; - } - - public override string ToString () - { - return BitConverter.ToString (_data); - } - - #endregion - - #region Explicit Interface Implementations - - IEnumerator IEnumerable.GetEnumerator () - { - return GetEnumerator (); - } - - #endregion - } -} diff --git a/websocket-sharp/Rsv.cs b/websocket-sharp/Rsv.cs deleted file mode 100644 index c2a4dea7a..000000000 --- a/websocket-sharp/Rsv.cs +++ /dev/null @@ -1,53 +0,0 @@ -#region License -/* - * Rsv.cs - * - * The MIT License - * - * Copyright (c) 2012-2025 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -using System; - -namespace WebSocketSharp -{ - /// - /// Indicates whether each RSV (RSV1, RSV2, and RSV3) of a WebSocket - /// frame is non-zero. - /// - /// - /// The values of this enumeration are defined in - /// - /// Section 5.2 of RFC 6455. - /// - internal enum Rsv - { - /// - /// Equivalent to numeric value 0. Indicates zero. - /// - Off = 0x0, - /// - /// Equivalent to numeric value 1. Indicates non-zero. - /// - On = 0x1 - } -} diff --git a/websocket-sharp/Server/HttpRequestEventArgs.cs b/websocket-sharp/Server/HttpRequestEventArgs.cs deleted file mode 100644 index 45af3c1ee..000000000 --- a/websocket-sharp/Server/HttpRequestEventArgs.cs +++ /dev/null @@ -1,266 +0,0 @@ -#region License -/* - * HttpRequestEventArgs.cs - * - * The MIT License - * - * Copyright (c) 2012-2024 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -using System; -using System.IO; -using System.Security.Principal; -using System.Text; -using WebSocketSharp.Net; - -namespace WebSocketSharp.Server -{ - /// - /// Represents the event data for the HTTP request events of the - /// class. - /// - /// - /// - /// An HTTP request event occurs when the - /// instance receives an HTTP request. - /// - /// - /// You should access the property if you would - /// like to get the request data sent from a client. - /// - /// - /// And you should access the property if you - /// would like to get the response data to return to the client. - /// - /// - public class HttpRequestEventArgs : EventArgs - { - #region Private Fields - - private HttpListenerContext _context; - private string _docRootPath; - - #endregion - - #region Internal Constructors - - internal HttpRequestEventArgs ( - HttpListenerContext context, - string documentRootPath - ) - { - _context = context; - _docRootPath = documentRootPath; - } - - #endregion - - #region Public Properties - - /// - /// Gets the request data sent from a client. - /// - /// - /// A that provides the methods and - /// properties for the request data. - /// - public HttpListenerRequest Request { - get { - return _context.Request; - } - } - - /// - /// Gets the response data to return to the client. - /// - /// - /// A that provides the methods and - /// properties for the response data. - /// - public HttpListenerResponse Response { - get { - return _context.Response; - } - } - - /// - /// Gets the information for the client. - /// - /// - /// - /// A instance that represents identity, - /// authentication scheme, and security roles for the client. - /// - /// - /// if the client is not authenticated. - /// - /// - public IPrincipal User { - get { - return _context.User; - } - } - - #endregion - - #region Private Methods - - private string createFilePath (string childPath) - { - childPath = childPath.TrimStart ('/', '\\'); - - return new StringBuilder (_docRootPath, 32) - .AppendFormat ("/{0}", childPath) - .ToString () - .Replace ('\\', '/'); - } - - private static bool tryReadFile (string path, out byte[] contents) - { - contents = null; - - if (!File.Exists (path)) - return false; - - try { - contents = File.ReadAllBytes (path); - } - catch { - return false; - } - - return true; - } - - #endregion - - #region Public Methods - - /// - /// Reads the specified file from the document folder of the - /// class. - /// - /// - /// - /// An array of that receives the contents of - /// the file. - /// - /// - /// if the read has failed. - /// - /// - /// - /// A that specifies a virtual path to find - /// the file from the document folder. - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// contains "..". - /// - /// - /// - /// is . - /// - public byte[] ReadFile (string path) - { - if (path == null) - throw new ArgumentNullException ("path"); - - if (path.Length == 0) - throw new ArgumentException ("An empty string.", "path"); - - if (path.Contains ("..")) { - var msg = "It contains \"..\"."; - - throw new ArgumentException (msg, "path"); - } - - path = createFilePath (path); - byte[] contents; - - tryReadFile (path, out contents); - - return contents; - } - - /// - /// Tries to read the specified file from the document folder of - /// the class. - /// - /// - /// true if the try has succeeded; otherwise, false. - /// - /// - /// A that specifies a virtual path to find - /// the file from the document folder. - /// - /// - /// - /// When this method returns, an array of that - /// receives the contents of the file. - /// - /// - /// if the read has failed. - /// - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// contains "..". - /// - /// - /// - /// is . - /// - public bool TryReadFile (string path, out byte[] contents) - { - if (path == null) - throw new ArgumentNullException ("path"); - - if (path.Length == 0) - throw new ArgumentException ("An empty string.", "path"); - - if (path.Contains ("..")) { - var msg = "It contains \"..\"."; - - throw new ArgumentException (msg, "path"); - } - - path = createFilePath (path); - - return tryReadFile (path, out contents); - } - - #endregion - } -} diff --git a/websocket-sharp/Server/HttpServer.cs b/websocket-sharp/Server/HttpServer.cs deleted file mode 100644 index d95c162e4..000000000 --- a/websocket-sharp/Server/HttpServer.cs +++ /dev/null @@ -1,1334 +0,0 @@ -#region License -/* - * HttpServer.cs - * - * The MIT License - * - * Copyright (c) 2012-2024 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -#region Contributors -/* - * Contributors: - * - Juan Manuel Lallana - * - Liryna - * - Rohan Singh - */ -#endregion - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Net.Sockets; -using System.Security.Cryptography.X509Certificates; -using System.Security.Principal; -using System.Text; -using System.Threading; -using WebSocketSharp.Net; -using WebSocketSharp.Net.WebSockets; - -namespace WebSocketSharp.Server -{ - /// - /// Provides a simple HTTP server. - /// - /// - /// - /// The server supports HTTP/1.1 version request and response. - /// - /// - /// Also the server allows to accept WebSocket handshake requests. - /// - /// - /// This class can provide multiple WebSocket services. - /// - /// - public class HttpServer - { - #region Private Fields - - private System.Net.IPAddress _address; - private string _docRootPath; - private bool _isSecure; - private HttpListener _listener; - private Logger _log; - private int _port; - private Thread _receiveThread; - private WebSocketServiceManager _services; - private volatile ServerState _state; - private object _sync; - - #endregion - - #region Public Constructors - - /// - /// Initializes a new instance of the class. - /// - /// - /// The new instance listens for incoming requests on - /// and port 80. - /// - public HttpServer () - { - init ("*", System.Net.IPAddress.Any, 80, false); - } - - /// - /// Initializes a new instance of the class with - /// the specified port. - /// - /// - /// - /// The new instance listens for incoming requests on - /// and . - /// - /// - /// It provides secure connections if is 443. - /// - /// - /// - /// An that specifies the number of the port on which - /// to listen. - /// - /// - /// is less than 1 or greater than 65535. - /// - public HttpServer (int port) - : this (port, port == 443) - { - } - - /// - /// Initializes a new instance of the class with - /// the specified URL. - /// - /// - /// - /// The new instance listens for incoming requests on the IP address and - /// port of . - /// - /// - /// Either port 80 or 443 is used if includes - /// no port. Port 443 is used if the scheme of - /// is https; otherwise, port 80 is used. - /// - /// - /// The new instance provides secure connections if the scheme of - /// is https. - /// - /// - /// - /// A that specifies the HTTP URL of the server. - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// is invalid. - /// - /// - /// - /// is . - /// - public HttpServer (string url) - { - if (url == null) - throw new ArgumentNullException ("url"); - - if (url.Length == 0) - throw new ArgumentException ("An empty string.", "url"); - - Uri uri; - string msg; - - if (!tryCreateUri (url, out uri, out msg)) - throw new ArgumentException (msg, "url"); - - var host = uri.GetDnsSafeHost (true); - var addr = host.ToIPAddress (); - - if (addr == null) { - msg = "The host part could not be converted to an IP address."; - - throw new ArgumentException (msg, "url"); - } - - if (!addr.IsLocal ()) { - msg = "The IP address of the host is not a local IP address."; - - throw new ArgumentException (msg, "url"); - } - - init (host, addr, uri.Port, uri.Scheme == "https"); - } - - /// - /// Initializes a new instance of the class with - /// the specified port and boolean if secure or not. - /// - /// - /// The new instance listens for incoming requests on - /// and . - /// - /// - /// An that specifies the number of the port on which - /// to listen. - /// - /// - /// A : true if the new instance provides - /// secure connections; otherwise, false. - /// - /// - /// is less than 1 or greater than 65535. - /// - public HttpServer (int port, bool secure) - { - if (!port.IsPortNumber ()) { - var msg = "Less than 1 or greater than 65535."; - - throw new ArgumentOutOfRangeException ("port", msg); - } - - init ("*", System.Net.IPAddress.Any, port, secure); - } - - /// - /// Initializes a new instance of the class with - /// the specified IP address and port. - /// - /// - /// - /// The new instance listens for incoming requests on - /// and . - /// - /// - /// It provides secure connections if is 443. - /// - /// - /// - /// A that specifies the local IP - /// address on which to listen. - /// - /// - /// An that specifies the number of the port on which - /// to listen. - /// - /// - /// is not a local IP address. - /// - /// - /// is . - /// - /// - /// is less than 1 or greater than 65535. - /// - public HttpServer (System.Net.IPAddress address, int port) - : this (address, port, port == 443) - { - } - - /// - /// Initializes a new instance of the class with - /// the specified IP address, port, and boolean if secure or not. - /// - /// - /// The new instance listens for incoming requests on - /// and . - /// - /// - /// A that specifies the local IP - /// address on which to listen. - /// - /// - /// An that specifies the number of the port on which - /// to listen. - /// - /// - /// A : true if the new instance provides - /// secure connections; otherwise, false. - /// - /// - /// is not a local IP address. - /// - /// - /// is . - /// - /// - /// is less than 1 or greater than 65535. - /// - public HttpServer (System.Net.IPAddress address, int port, bool secure) - { - if (address == null) - throw new ArgumentNullException ("address"); - - if (!address.IsLocal ()) { - var msg = "Not a local IP address."; - - throw new ArgumentException (msg, "address"); - } - - if (!port.IsPortNumber ()) { - var msg = "Less than 1 or greater than 65535."; - - throw new ArgumentOutOfRangeException ("port", msg); - } - - init (address.ToString (true), address, port, secure); - } - - #endregion - - #region Public Properties - - /// - /// Gets the IP address of the server. - /// - /// - /// A that represents the local IP - /// address on which to listen for incoming requests. - /// - public System.Net.IPAddress Address { - get { - return _address; - } - } - - /// - /// Gets or sets the scheme used to authenticate the clients. - /// - /// - /// The set operation works if the current state of the server is - /// Ready or Stop. - /// - /// - /// - /// One of the - /// enum values. - /// - /// - /// It represents the scheme used to authenticate the clients. - /// - /// - /// The default value is - /// . - /// - /// - public AuthenticationSchemes AuthenticationSchemes { - get { - return _listener.AuthenticationSchemes; - } - - set { - lock (_sync) { - if (!canSet ()) - return; - - _listener.AuthenticationSchemes = value; - } - } - } - - /// - /// Gets or sets the path to the document folder of the server. - /// - /// - /// The set operation works if the current state of the server is - /// Ready or Stop. - /// - /// - /// - /// A that represents a path to the folder - /// from which to find the requested file. - /// - /// - /// / or \ is trimmed from the end of the value if present. - /// - /// - /// The default value is "./Public". - /// - /// - /// - /// - /// The value specified for a set operation is an empty string. - /// - /// - /// -or- - /// - /// - /// The value specified for a set operation is an absolute root. - /// - /// - /// -or- - /// - /// - /// The value specified for a set operation is an invalid path string. - /// - /// - /// - /// The value specified for a set operation is . - /// - public string DocumentRootPath { - get { - return _docRootPath; - } - - set { - if (value == null) - throw new ArgumentNullException ("value"); - - if (value.Length == 0) - throw new ArgumentException ("An empty string.", "value"); - - value = value.TrimSlashOrBackslashFromEnd (); - - if (value == "/") - throw new ArgumentException ("An absolute root.", "value"); - - if (value == "\\") - throw new ArgumentException ("An absolute root.", "value"); - - if (value.Length == 2 && value[1] == ':') - throw new ArgumentException ("An absolute root.", "value"); - - string full = null; - - try { - full = Path.GetFullPath (value); - } - catch (Exception ex) { - throw new ArgumentException ("An invalid path string.", "value", ex); - } - - if (full == "/") - throw new ArgumentException ("An absolute root.", "value"); - - full = full.TrimSlashOrBackslashFromEnd (); - - if (full.Length == 2 && full[1] == ':') - throw new ArgumentException ("An absolute root.", "value"); - - lock (_sync) { - if (!canSet ()) - return; - - _docRootPath = value; - } - } - } - - /// - /// Gets a value indicating whether the server has started. - /// - /// - /// true if the server has started; otherwise, false. - /// - public bool IsListening { - get { - return _state == ServerState.Start; - } - } - - /// - /// Gets a value indicating whether the server provides secure connections. - /// - /// - /// true if the server provides secure connections; otherwise, - /// false. - /// - public bool IsSecure { - get { - return _isSecure; - } - } - - /// - /// Gets or sets a value indicating whether the server cleans up - /// the inactive sessions periodically. - /// - /// - /// - /// true if the server cleans up the inactive sessions - /// every 60 seconds; otherwise, false. - /// - /// - /// The default value is false. - /// - /// - /// - /// The set operation is not available when the current state of - /// the server is neither Ready nor Stop. - /// - public bool KeepClean { - get { - return _services.KeepClean; - } - - set { - _services.KeepClean = value; - } - } - - /// - /// Gets the logging function for the server. - /// - /// - /// The default logging level is . - /// - /// - /// A that provides the logging function. - /// - public Logger Log { - get { - return _log; - } - } - - /// - /// Gets the port of the server. - /// - /// - /// An that represents the number of the port on which - /// to listen for incoming requests. - /// - public int Port { - get { - return _port; - } - } - - /// - /// Gets or sets the name of the realm associated with the server. - /// - /// - /// The set operation works if the current state of the server is - /// Ready or Stop. - /// - /// - /// - /// A that represents the name of the realm. - /// - /// - /// "SECRET AREA" is used as the name of the realm if the value is - /// or an empty string. - /// - /// - /// The default value is . - /// - /// - public string Realm { - get { - return _listener.Realm; - } - - set { - lock (_sync) { - if (!canSet ()) - return; - - _listener.Realm = value; - } - } - } - - /// - /// Gets or sets a value indicating whether the server is allowed to - /// be bound to an address that is already in use. - /// - /// - /// - /// You should set this property to true if you would like to - /// resolve to wait for socket in TIME_WAIT state. - /// - /// - /// The set operation works if the current state of the server is - /// Ready or Stop. - /// - /// - /// - /// - /// true if the server is allowed to be bound to an address - /// that is already in use; otherwise, false. - /// - /// - /// The default value is false. - /// - /// - public bool ReuseAddress { - get { - return _listener.ReuseAddress; - } - - set { - lock (_sync) { - if (!canSet ()) - return; - - _listener.ReuseAddress = value; - } - } - } - - /// - /// Gets the configuration for secure connection. - /// - /// - /// The configuration is used when the server attempts to start, - /// so it must be configured before the start method is called. - /// - /// - /// A that represents the - /// configuration used to provide secure connections. - /// - /// - /// The server does not provide secure connections. - /// - public ServerSslConfiguration SslConfiguration { - get { - if (!_isSecure) { - var msg = "The server does not provide secure connections."; - - throw new InvalidOperationException (msg); - } - - return _listener.SslConfiguration; - } - } - - /// - /// Gets or sets the delegate called to find the credentials for - /// an identity used to authenticate a client. - /// - /// - /// The set operation works if the current state of the server is - /// Ready or Stop. - /// - /// - /// - /// A - /// delegate. - /// - /// - /// It represents the delegate called when the server finds - /// the credentials used to authenticate a client. - /// - /// - /// It must return if the credentials - /// are not found. - /// - /// - /// if not necessary. - /// - /// - /// The default value is . - /// - /// - public Func UserCredentialsFinder { - get { - return _listener.UserCredentialsFinder; - } - - set { - lock (_sync) { - if (!canSet ()) - return; - - _listener.UserCredentialsFinder = value; - } - } - } - - /// - /// Gets or sets the time to wait for the response to the WebSocket - /// Ping or Close. - /// - /// - /// - /// A that represents the time to wait for - /// the response. - /// - /// - /// The default value is the same as 1 second. - /// - /// - /// - /// The value specified for a set operation is zero or less. - /// - /// - /// The set operation is not available when the current state of - /// the server is neither Ready nor Stop. - /// - public TimeSpan WaitTime { - get { - return _services.WaitTime; - } - - set { - _services.WaitTime = value; - } - } - - /// - /// Gets the management function for the WebSocket services provided by - /// the server. - /// - /// - /// A that manages the WebSocket - /// services provided by the server. - /// - public WebSocketServiceManager WebSocketServices { - get { - return _services; - } - } - - #endregion - - #region Public Events - - /// - /// Occurs when the server receives an HTTP CONNECT request. - /// - public event EventHandler OnConnect; - - /// - /// Occurs when the server receives an HTTP DELETE request. - /// - public event EventHandler OnDelete; - - /// - /// Occurs when the server receives an HTTP GET request. - /// - public event EventHandler OnGet; - - /// - /// Occurs when the server receives an HTTP HEAD request. - /// - public event EventHandler OnHead; - - /// - /// Occurs when the server receives an HTTP OPTIONS request. - /// - public event EventHandler OnOptions; - - /// - /// Occurs when the server receives an HTTP POST request. - /// - public event EventHandler OnPost; - - /// - /// Occurs when the server receives an HTTP PUT request. - /// - public event EventHandler OnPut; - - /// - /// Occurs when the server receives an HTTP TRACE request. - /// - public event EventHandler OnTrace; - - #endregion - - #region Private Methods - - private void abort () - { - lock (_sync) { - if (_state != ServerState.Start) - return; - - _state = ServerState.ShuttingDown; - } - - try { - _services.Stop (1006, String.Empty); - } - catch (Exception ex) { - _log.Fatal (ex.Message); - _log.Debug (ex.ToString ()); - } - - try { - _listener.Abort (); - } - catch (Exception ex) { - _log.Fatal (ex.Message); - _log.Debug (ex.ToString ()); - } - - _state = ServerState.Stop; - } - - private bool canSet () - { - return _state == ServerState.Ready || _state == ServerState.Stop; - } - - private bool checkCertificate (out string message) - { - message = null; - - var byUser = _listener.SslConfiguration.ServerCertificate != null; - - var path = _listener.CertificateFolderPath; - var withPort = EndPointListener.CertificateExists (_port, path); - - var either = byUser || withPort; - - if (!either) { - message = "There is no server certificate for secure connection."; - - return false; - } - - var both = byUser && withPort; - - if (both) { - var msg = "The server certificate associated with the port is used."; - - _log.Warn (msg); - } - - return true; - } - - private static HttpListener createListener ( - string hostname, - int port, - bool secure - ) - { - var ret = new HttpListener (); - - var fmt = "{0}://{1}:{2}/"; - var schm = secure ? "https" : "http"; - var pref = String.Format (fmt, schm, hostname, port); - - ret.Prefixes.Add (pref); - - return ret; - } - - private void init ( - string hostname, - System.Net.IPAddress address, - int port, - bool secure - ) - { - _address = address; - _port = port; - _isSecure = secure; - - _docRootPath = "./Public"; - _listener = createListener (hostname, port, secure); - _log = _listener.Log; - _services = new WebSocketServiceManager (_log); - _sync = new object (); - } - - private void processRequest (HttpListenerContext context) - { - var method = context.Request.HttpMethod; - var evt = method == "GET" - ? OnGet - : method == "HEAD" - ? OnHead - : method == "POST" - ? OnPost - : method == "PUT" - ? OnPut - : method == "DELETE" - ? OnDelete - : method == "CONNECT" - ? OnConnect - : method == "OPTIONS" - ? OnOptions - : method == "TRACE" - ? OnTrace - : null; - - if (evt == null) { - context.ErrorStatusCode = 501; - - context.SendError (); - - return; - } - - var e = new HttpRequestEventArgs (context, _docRootPath); - - evt (this, e); - - context.Response.Close (); - } - - private void processRequest (HttpListenerWebSocketContext context) - { - var uri = context.RequestUri; - - if (uri == null) { - context.Close (HttpStatusCode.BadRequest); - - return; - } - - var path = uri.AbsolutePath; - - if (path.IndexOfAny (new[] { '%', '+' }) > -1) - path = HttpUtility.UrlDecode (path, Encoding.UTF8); - - WebSocketServiceHost host; - - if (!_services.InternalTryGetServiceHost (path, out host)) { - context.Close (HttpStatusCode.NotImplemented); - - return; - } - - host.StartSession (context); - } - - private void receiveRequest () - { - while (true) { - HttpListenerContext ctx = null; - - try { - ctx = _listener.GetContext (); - - ThreadPool.QueueUserWorkItem ( - state => { - try { - if (ctx.Request.IsUpgradeRequest ("websocket")) { - processRequest (ctx.GetWebSocketContext (null)); - - return; - } - - processRequest (ctx); - } - catch (Exception ex) { - _log.Error (ex.Message); - _log.Debug (ex.ToString ()); - - ctx.Connection.Close (true); - } - } - ); - } - catch (HttpListenerException ex) { - if (_state == ServerState.ShuttingDown) - return; - - _log.Fatal (ex.Message); - _log.Debug (ex.ToString ()); - - break; - } - catch (InvalidOperationException ex) { - if (_state == ServerState.ShuttingDown) - return; - - _log.Fatal (ex.Message); - _log.Debug (ex.ToString ()); - - break; - } - catch (Exception ex) { - _log.Fatal (ex.Message); - _log.Debug (ex.ToString ()); - - if (ctx != null) - ctx.Connection.Close (true); - - if (_state == ServerState.ShuttingDown) - return; - - break; - } - } - - abort (); - } - - private void start () - { - lock (_sync) { - if (_state == ServerState.Start || _state == ServerState.ShuttingDown) - return; - - if (_isSecure) { - string msg; - - if (!checkCertificate (out msg)) - throw new InvalidOperationException (msg); - } - - _services.Start (); - - try { - startReceiving (); - } - catch { - _services.Stop (1011, String.Empty); - - throw; - } - - _state = ServerState.Start; - } - } - - private void startReceiving () - { - try { - _listener.Start (); - } - catch (Exception ex) { - var msg = "The underlying listener has failed to start."; - - throw new InvalidOperationException (msg, ex); - } - - var receiver = new ThreadStart (receiveRequest); - _receiveThread = new Thread (receiver); - _receiveThread.IsBackground = true; - - _receiveThread.Start (); - } - - private void stop (ushort code, string reason) - { - lock (_sync) { - if (_state != ServerState.Start) - return; - - _state = ServerState.ShuttingDown; - } - - try { - _services.Stop (code, reason); - } - catch (Exception ex) { - _log.Fatal (ex.Message); - _log.Debug (ex.ToString ()); - } - - try { - var timeout = 5000; - - stopReceiving (timeout); - } - catch (Exception ex) { - _log.Fatal (ex.Message); - _log.Debug (ex.ToString ()); - } - - _state = ServerState.Stop; - } - - private void stopReceiving (int millisecondsTimeout) - { - _listener.Stop (); - _receiveThread.Join (millisecondsTimeout); - } - - private static bool tryCreateUri ( - string uriString, - out Uri result, - out string message - ) - { - result = null; - message = null; - - var uri = uriString.ToUri (); - - if (uri == null) { - message = "An invalid URI string."; - - return false; - } - - if (!uri.IsAbsoluteUri) { - message = "A relative URI."; - - return false; - } - - var schm = uri.Scheme; - var isHttpSchm = schm == "http" || schm == "https"; - - if (!isHttpSchm) { - message = "The scheme part is not 'http' or 'https'."; - - return false; - } - - if (uri.PathAndQuery != "/") { - message = "It includes either or both path and query components."; - - return false; - } - - if (uri.Fragment.Length > 0) { - message = "It includes the fragment component."; - - return false; - } - - if (uri.Port == 0) { - message = "The port part is zero."; - - return false; - } - - result = uri; - - return true; - } - - #endregion - - #region Public Methods - - /// - /// Adds a WebSocket service with the specified behavior and path. - /// - /// - /// - /// A that specifies an absolute path to - /// the service to add. - /// - /// - /// / is trimmed from the end of the string if present. - /// - /// - /// - /// - /// The type of the behavior for the service. - /// - /// - /// It must inherit the class. - /// - /// - /// Also it must have a public parameterless constructor. - /// - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// is not an absolute path. - /// - /// - /// -or- - /// - /// - /// includes either or both - /// query and fragment components. - /// - /// - /// -or- - /// - /// - /// is already in use. - /// - /// - /// - /// is . - /// - public void AddWebSocketService (string path) - where TBehavior : WebSocketBehavior, new () - { - _services.AddService (path, null); - } - - /// - /// Adds a WebSocket service with the specified behavior, path, - /// and initializer. - /// - /// - /// - /// A that specifies an absolute path to - /// the service to add. - /// - /// - /// / is trimmed from the end of the string if present. - /// - /// - /// - /// - /// An delegate. - /// - /// - /// It specifies the delegate called when the service initializes - /// a new session instance. - /// - /// - /// if not necessary. - /// - /// - /// - /// - /// The type of the behavior for the service. - /// - /// - /// It must inherit the class. - /// - /// - /// Also it must have a public parameterless constructor. - /// - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// is not an absolute path. - /// - /// - /// -or- - /// - /// - /// includes either or both - /// query and fragment components. - /// - /// - /// -or- - /// - /// - /// is already in use. - /// - /// - /// - /// is . - /// - public void AddWebSocketService ( - string path, - Action initializer - ) - where TBehavior : WebSocketBehavior, new () - { - _services.AddService (path, initializer); - } - - /// - /// Removes a WebSocket service with the specified path. - /// - /// - /// The service is stopped with close status 1001 (going away) - /// if the current state of the service is Start. - /// - /// - /// true if the service is successfully found and removed; - /// otherwise, false. - /// - /// - /// - /// A that specifies an absolute path to - /// the service to remove. - /// - /// - /// / is trimmed from the end of the string if present. - /// - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// is not an absolute path. - /// - /// - /// -or- - /// - /// - /// includes either or both - /// query and fragment components. - /// - /// - /// - /// is . - /// - public bool RemoveWebSocketService (string path) - { - return _services.RemoveService (path); - } - - /// - /// Starts receiving incoming requests. - /// - /// - /// This method works if the current state of the server is Ready or Stop. - /// - /// - /// - /// There is no server certificate for secure connection. - /// - /// - /// -or- - /// - /// - /// The underlying has failed to start. - /// - /// - public void Start () - { - if (_state == ServerState.Start || _state == ServerState.ShuttingDown) - return; - - start (); - } - - /// - /// Stops receiving incoming requests. - /// - /// - /// This method works if the current state of the server is Start. - /// - public void Stop () - { - if (_state != ServerState.Start) - return; - - stop (1001, String.Empty); - } - - #endregion - } -} diff --git a/websocket-sharp/Server/IWebSocketSession.cs b/websocket-sharp/Server/IWebSocketSession.cs deleted file mode 100644 index 4ddc84aca..000000000 --- a/websocket-sharp/Server/IWebSocketSession.cs +++ /dev/null @@ -1,67 +0,0 @@ -#region License -/* - * IWebSocketSession.cs - * - * The MIT License - * - * Copyright (c) 2013-2022 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -using System; - -namespace WebSocketSharp.Server -{ - /// - /// Exposes the access to the information in a WebSocket session. - /// - public interface IWebSocketSession - { - #region Properties - - /// - /// Gets the unique ID of the session. - /// - /// - /// A that represents the unique ID of the session. - /// - string ID { get; } - - /// - /// Gets the time that the session has started. - /// - /// - /// A that represents the time that the session - /// has started. - /// - DateTime StartTime { get; } - - /// - /// Gets the WebSocket interface for the session. - /// - /// - /// A that represents the interface. - /// - WebSocket WebSocket { get; } - - #endregion - } -} diff --git a/websocket-sharp/Server/ServerState.cs b/websocket-sharp/Server/ServerState.cs deleted file mode 100644 index 2d7582920..000000000 --- a/websocket-sharp/Server/ServerState.cs +++ /dev/null @@ -1,40 +0,0 @@ -#region License -/* - * ServerState.cs - * - * The MIT License - * - * Copyright (c) 2013-2014 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -using System; - -namespace WebSocketSharp.Server -{ - internal enum ServerState - { - Ready, - Start, - ShuttingDown, - Stop - } -} diff --git a/websocket-sharp/Server/WebSocketBehavior.cs b/websocket-sharp/Server/WebSocketBehavior.cs deleted file mode 100644 index 8ecfa1bcf..000000000 --- a/websocket-sharp/Server/WebSocketBehavior.cs +++ /dev/null @@ -1,1622 +0,0 @@ -#region License -/* - * WebSocketBehavior.cs - * - * The MIT License - * - * Copyright (c) 2012-2026 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -using System; -using System.Collections.Specialized; -using System.IO; -using System.Security.Principal; -using WebSocketSharp.Net; -using WebSocketSharp.Net.WebSockets; - -namespace WebSocketSharp.Server -{ - /// - /// Exposes a set of methods and properties used to define the behavior of - /// a WebSocket service provided by the or - /// class. - /// - /// - /// This class is an abstract class. - /// - public abstract class WebSocketBehavior : IWebSocketSession - { - #region Private Fields - - private WebSocketContext _context; - private Action _cookiesResponder; - private bool _emitOnPing; - private Func _hostValidator; - private string _id; - private bool _ignoreExtensions; - private bool _noDelay; - private Func _originValidator; - private string _protocol; - private bool _registered; - private WebSocketSessionManager _sessions; - private DateTime _startTime; - private Action _userHeadersResponder; - private WebSocket _websocket; - - #endregion - - #region Protected Constructors - - /// - /// Initializes a new instance of the class. - /// - protected WebSocketBehavior () - { - _startTime = DateTime.MaxValue; - } - - #endregion - - #region Protected Properties - - /// - /// Gets the HTTP headers for a session. - /// - /// - /// A that contains the headers - /// included in the WebSocket handshake request. - /// - /// - /// The get operation is not available when the session has not started yet. - /// - protected NameValueCollection Headers { - get { - if (!_registered) { - var msg = "The session has not started yet."; - - throw new InvalidOperationException (msg); - } - - return _context.Headers; - } - } - - /// - /// Gets a value indicating whether the communication is possible for - /// a session. - /// - /// - /// true if the communication is possible; otherwise, false. - /// - /// - /// The get operation is not available when the session has not started yet. - /// - protected bool IsAlive { - get { - if (!_registered) { - var msg = "The session has not started yet."; - - throw new InvalidOperationException (msg); - } - - return _websocket.IsAlive; - } - } - - /// - /// Gets the query string for a session. - /// - /// - /// - /// A that contains the query - /// parameters included in the WebSocket handshake request. - /// - /// - /// An empty collection if not included. - /// - /// - /// - /// The get operation is not available when the session has not started yet. - /// - protected NameValueCollection QueryString { - get { - if (!_registered) { - var msg = "The session has not started yet."; - - throw new InvalidOperationException (msg); - } - - return _context.QueryString; - } - } - - /// - /// Gets the current state of the WebSocket interface for a session. - /// - /// - /// - /// One of the enum values. - /// - /// - /// It indicates the current state of the interface. - /// - /// - /// - /// The get operation is not available when the session has not started yet. - /// - protected WebSocketState ReadyState { - get { - if (!_registered) { - var msg = "The session has not started yet."; - - throw new InvalidOperationException (msg); - } - - return _websocket.ReadyState; - } - } - - /// - /// Gets the management function for the sessions in the service. - /// - /// - /// A that manages the sessions in - /// the service. - /// - /// - /// The get operation is not available when the session has not started yet. - /// - protected WebSocketSessionManager Sessions { - get { - if (!_registered) { - var msg = "The session has not started yet."; - - throw new InvalidOperationException (msg); - } - - return _sessions; - } - } - - /// - /// Gets the client information for a session. - /// - /// - /// - /// A instance that represents identity, - /// authentication, and security roles for the client. - /// - /// - /// if the client is not authenticated. - /// - /// - /// - /// The get operation is not available when the session has not started yet. - /// - protected IPrincipal User { - get { - if (!_registered) { - var msg = "The session has not started yet."; - - throw new InvalidOperationException (msg); - } - - return _context.User; - } - } - - /// - /// Gets the client endpoint for a session. - /// - /// - /// A that represents the client - /// IP address and port number. - /// - /// - /// The get operation is not available when the session has not started yet. - /// - protected System.Net.IPEndPoint UserEndPoint { - get { - if (!_registered) { - var msg = "The session has not started yet."; - - throw new InvalidOperationException (msg); - } - - return _context.UserEndPoint; - } - } - - #endregion - - #region Public Properties - - /// - /// Gets or sets the delegate used to respond to the HTTP cookies. - /// - /// - /// - /// A - /// delegate. - /// - /// - /// It represents the delegate called when the WebSocket interface - /// for a session respond to the handshake request. - /// - /// - /// 1st parameter passed to the delegate - /// contains the cookies included in the handshake request if any. - /// - /// - /// 2nd parameter passed to the delegate - /// holds the cookies to send to the client. - /// - /// - /// if not necessary. - /// - /// - /// The default value is . - /// - /// - /// - /// The set operation is not available when the session has already started. - /// - public Action CookiesResponder { - get { - return _cookiesResponder; - } - - set { - if (_registered) { - var msg = "The session has already started."; - - throw new InvalidOperationException (msg); - } - - _cookiesResponder = value; - } - } - - /// - /// Gets or sets a value indicating whether the WebSocket interface for - /// a session emits the message event when it receives a ping. - /// - /// - /// - /// true if the interface emits the message event when it receives - /// a ping; otherwise, false. - /// - /// - /// The default value is false. - /// - /// - /// - /// The set operation is not available when the session has already started. - /// - public bool EmitOnPing { - get { - return _emitOnPing; - } - - set { - if (_registered) { - var msg = "The session has already started."; - - throw new InvalidOperationException (msg); - } - - _emitOnPing = value; - } - } - - /// - /// Gets or sets the delegate used to validate the Host header. - /// - /// - /// - /// A delegate. - /// - /// - /// It represents the delegate called when the WebSocket interface - /// for a session validates the handshake request. - /// - /// - /// The parameter passed to the delegate is - /// the value of the Host header. - /// - /// - /// The method invoked by the delegate must return true - /// if the header value is valid. - /// - /// - /// if not necessary. - /// - /// - /// The default value is . - /// - /// - /// - /// The set operation is not available when the session has already started. - /// - public Func HostValidator { - get { - return _hostValidator; - } - - set { - if (_registered) { - var msg = "The session has already started."; - - throw new InvalidOperationException (msg); - } - - _hostValidator = value; - } - } - - /// - /// Gets the unique ID of a session. - /// - /// - /// A that represents the unique ID of the session. - /// - public string ID { - get { - if (_id == null) - _id = WebSocketSessionManager.CreateID (); - - return _id; - } - } - - /// - /// Gets or sets a value indicating whether the WebSocket interface for - /// a session ignores the Sec-WebSocket-Extensions header. - /// - /// - /// - /// true if the interface ignores the extensions requested - /// from the client; otherwise, false. - /// - /// - /// The default value is false. - /// - /// - /// - /// The set operation is not available when the session has already started. - /// - public bool IgnoreExtensions { - get { - return _ignoreExtensions; - } - - set { - if (_registered) { - var msg = "The session has already started."; - - throw new InvalidOperationException (msg); - } - - _ignoreExtensions = value; - } - } - - /// - /// Gets or sets a value indicating whether the underlying TCP socket of - /// the WebSocket interface for a session disables a delay when send or - /// receive buffer is not full. - /// - /// - /// - /// true if the delay is disabled; otherwise, false. - /// - /// - /// The default value is false. - /// - /// - /// - /// - /// The set operation is not available when the session has already started. - /// - public bool NoDelay { - get { - return _noDelay; - } - - set { - if (_registered) { - var msg = "The session has already started."; - - throw new InvalidOperationException (msg); - } - - _noDelay = value; - } - } - - /// - /// Gets or sets the delegate used to validate the Origin header. - /// - /// - /// - /// A delegate. - /// - /// - /// It represents the delegate called when the WebSocket interface - /// for a session validates the handshake request. - /// - /// - /// The parameter passed to the delegate is - /// the value of the Origin header or if - /// the header is not present. - /// - /// - /// The method invoked by the delegate must return true - /// if the header value is valid. - /// - /// - /// if not necessary. - /// - /// - /// The default value is . - /// - /// - /// - /// The set operation is not available when the session has already started. - /// - public Func OriginValidator { - get { - return _originValidator; - } - - set { - if (_registered) { - var msg = "The session has already started."; - - throw new InvalidOperationException (msg); - } - - _originValidator = value; - } - } - - /// - /// Gets or sets the name of the WebSocket subprotocol for a session. - /// - /// - /// - /// A that represents the name of the subprotocol. - /// - /// - /// The value specified for a set operation must be a token defined in - /// - /// RFC 2616. - /// - /// - /// The value is initialized if not requested. - /// - /// - /// The default value is an empty string. - /// - /// - /// - /// The value specified for a set operation is not a token. - /// - /// - /// The set operation is not available when the session has already started. - /// - public string Protocol { - get { - return _registered - ? _websocket.Protocol - : (_protocol ?? String.Empty); - } - - set { - if (_registered) { - var msg = "The session has already started."; - - throw new InvalidOperationException (msg); - } - - if (value.IsNullOrEmpty ()) { - _protocol = null; - - return; - } - - if (!value.IsToken ()) { - var msg = "Not a token."; - - throw new ArgumentException (msg, "value"); - } - - _protocol = value; - } - } - - /// - /// Gets the time that a session has started. - /// - /// - /// - /// A that represents the time that the session - /// has started. - /// - /// - /// when the session has not started yet. - /// - /// - public DateTime StartTime { - get { - return _startTime; - } - } - - /// - /// Gets or sets the delegate used to respond to the user headers. - /// - /// - /// - /// A - /// delegate. - /// - /// - /// It represents the delegate called when the WebSocket interface - /// for a session respond to the handshake request. - /// - /// - /// 1st parameter passed to the delegate - /// contains the HTTP headers included in the handshake request. - /// - /// - /// 2nd parameter passed to the delegate - /// holds the user headers to send to the client. - /// - /// - /// if not necessary. - /// - /// - /// The default value is . - /// - /// - /// - /// The set operation is not available when the session has already started. - /// - public Action UserHeadersResponder { - get { - return _userHeadersResponder; - } - - set { - if (_registered) { - var msg = "The session has already started."; - - throw new InvalidOperationException (msg); - } - - _userHeadersResponder = value; - } - } - - #endregion - - #region Private Methods - - private string checkHandshakeRequest (WebSocketContext context) - { - if (_hostValidator != null) { - if (!_hostValidator (context.Host)) { - var msg = "The Host header is invalid."; - - return msg; - } - } - - if (_originValidator != null) { - if (!_originValidator (context.Origin)) { - var msg = "The Origin header is non-existent or invalid."; - - return msg; - } - } - - return null; - } - - private bool executeBeforeClose () - { - if (!_registered) - return false; - - _sessions.Remove (_id); - - return true; - } - - private bool executeBeforeOpen () - { - _registered = _sessions.Add (this); - - if (!_registered) { - _websocket.Close (CloseStatusCode.Away); - - return false; - } - - _startTime = DateTime.Now; - - return true; - } - - private void onClose (object sender, CloseEventArgs e) - { - OnClose (e); - } - - private void onError (object sender, ErrorEventArgs e) - { - OnError (e); - } - - private void onMessage (object sender, MessageEventArgs e) - { - OnMessage (e); - } - - private void onOpen (object sender, EventArgs e) - { - OnOpen (); - } - - private void respondToHandshakeRequest (WebSocketContext context) - { - if (_cookiesResponder != null) { - var reqCookies = context.CookieCollection; - var resCookies = context.WebSocket.Cookies; - - _cookiesResponder (reqCookies, resCookies); - } - - if (_userHeadersResponder != null) { - var reqHeaders = context.Headers; - var userHeaders = context.WebSocket.UserHeaders; - - _userHeadersResponder (reqHeaders, userHeaders); - } - } - - #endregion - - #region Internal Methods - - internal void Start ( - WebSocketContext context, - WebSocketSessionManager sessions - ) - { - _context = context; - _sessions = sessions; - - _websocket = context.WebSocket; - - _websocket.CustomHandshakeRequestChecker = checkHandshakeRequest; - _websocket.CustomHandshakeRequestResponder = respondToHandshakeRequest; - - _websocket.ExecutorBeforeClose = executeBeforeClose; - _websocket.ExecutorBeforeOpen = executeBeforeOpen; - - if (_emitOnPing) - _websocket.EmitOnPing = true; - - if (_ignoreExtensions) - _websocket.IgnoreExtensions = true; - - if (_noDelay) - _websocket.NoDelay = true; - - if (_protocol != null) - _websocket.Protocol = _protocol; - - var waitTime = sessions.WaitTime; - - if (waitTime != _websocket.WaitTime) - _websocket.WaitTime = waitTime; - - _websocket.OnClose += onClose; - _websocket.OnError += onError; - _websocket.OnMessage += onMessage; - _websocket.OnOpen += onOpen; - - _websocket.Accept (); - } - - #endregion - - #region Protected Methods - - /// - /// Closes the WebSocket connection for a session. - /// - /// - /// This method does nothing when the current state of the WebSocket - /// interface is Closing or Closed. - /// - /// - /// This method is not available when the session has not started yet. - /// - protected void Close () - { - if (!_registered) { - var msg = "The session has not started yet."; - - throw new InvalidOperationException (msg); - } - - _websocket.Close (); - } - - /// - /// Closes the WebSocket connection for a session with the specified - /// status code and reason. - /// - /// - /// This method does nothing when the current state of the WebSocket - /// interface is Closing or Closed. - /// - /// - /// - /// A that specifies the status code indicating - /// the reason for the close. - /// - /// - /// The status codes are defined in - /// - /// Section 7.4 of RFC 6455. - /// - /// - /// - /// - /// A that specifies the reason for the close. - /// - /// - /// Its size must be 123 bytes or less in UTF-8. - /// - /// - /// - /// - /// is 1010 (mandatory extension). - /// - /// - /// -or- - /// - /// - /// is 1005 (no status) and - /// is specified. - /// - /// - /// -or- - /// - /// - /// could not be UTF-8-encoded. - /// - /// - /// - /// - /// is less than 1000 or greater than 4999. - /// - /// - /// -or- - /// - /// - /// The size of is greater than 123 bytes. - /// - /// - /// - /// This method is not available when the session has not started yet. - /// - protected void Close (ushort code, string reason) - { - if (!_registered) { - var msg = "The session has not started yet."; - - throw new InvalidOperationException (msg); - } - - _websocket.Close (code, reason); - } - - /// - /// Closes the WebSocket connection for a session with the specified - /// status code and reason. - /// - /// - /// This method does nothing when the current state of the WebSocket - /// interface is Closing or Closed. - /// - /// - /// - /// One of the enum values. - /// - /// - /// It specifies the status code indicating the reason for the close. - /// - /// - /// - /// - /// A that specifies the reason for the close. - /// - /// - /// Its size must be 123 bytes or less in UTF-8. - /// - /// - /// - /// - /// is an undefined enum value. - /// - /// - /// -or- - /// - /// - /// is . - /// - /// - /// -or- - /// - /// - /// is and - /// is specified. - /// - /// - /// -or- - /// - /// - /// could not be UTF-8-encoded. - /// - /// - /// - /// The size of is greater than 123 bytes. - /// - /// - /// This method is not available when the session has not started yet. - /// - protected void Close (CloseStatusCode code, string reason) - { - if (!_registered) { - var msg = "The session has not started yet."; - - throw new InvalidOperationException (msg); - } - - _websocket.Close (code, reason); - } - - /// - /// Closes the WebSocket connection for a session asynchronously. - /// - /// - /// - /// This method does not wait for the close to be complete. - /// - /// - /// This method does nothing when the current state of the WebSocket - /// interface is Closing or Closed. - /// - /// - /// - /// This method is not available when the session has not started yet. - /// - protected void CloseAsync () - { - if (!_registered) { - var msg = "The session has not started yet."; - - throw new InvalidOperationException (msg); - } - - _websocket.CloseAsync (); - } - - /// - /// Closes the WebSocket connection for a session asynchronously with - /// the specified status code and reason. - /// - /// - /// - /// This method does not wait for the close to be complete. - /// - /// - /// This method does nothing when the current state of the WebSocket - /// interface is Closing or Closed. - /// - /// - /// - /// - /// A that specifies the status code indicating - /// the reason for the close. - /// - /// - /// The status codes are defined in - /// - /// Section 7.4 of RFC 6455. - /// - /// - /// - /// - /// A that specifies the reason for the close. - /// - /// - /// Its size must be 123 bytes or less in UTF-8. - /// - /// - /// - /// - /// is 1010 (mandatory extension). - /// - /// - /// -or- - /// - /// - /// is 1005 (no status) and - /// is specified. - /// - /// - /// -or- - /// - /// - /// could not be UTF-8-encoded. - /// - /// - /// - /// - /// is less than 1000 or greater than 4999. - /// - /// - /// -or- - /// - /// - /// The size of is greater than 123 bytes. - /// - /// - /// - /// This method is not available when the session has not started yet. - /// - protected void CloseAsync (ushort code, string reason) - { - if (!_registered) { - var msg = "The session has not started yet."; - - throw new InvalidOperationException (msg); - } - - _websocket.CloseAsync (code, reason); - } - - /// - /// Closes the WebSocket connection for a session asynchronously with - /// the specified status code and reason. - /// - /// - /// - /// This method does not wait for the close to be complete. - /// - /// - /// This method does nothing when the current state of the WebSocket - /// interface is Closing or Closed. - /// - /// - /// - /// - /// One of the enum values. - /// - /// - /// It specifies the status code indicating the reason for the close. - /// - /// - /// - /// - /// A that specifies the reason for the close. - /// - /// - /// Its size must be 123 bytes or less in UTF-8. - /// - /// - /// - /// - /// is an undefined enum value. - /// - /// - /// -or- - /// - /// - /// is . - /// - /// - /// -or- - /// - /// - /// is and - /// is specified. - /// - /// - /// -or- - /// - /// - /// could not be UTF-8-encoded. - /// - /// - /// - /// The size of is greater than 123 bytes. - /// - /// - /// This method is not available when the session has not started yet. - /// - protected void CloseAsync (CloseStatusCode code, string reason) - { - if (!_registered) { - var msg = "The session has not started yet."; - - throw new InvalidOperationException (msg); - } - - _websocket.CloseAsync (code, reason); - } - - /// - /// Called when the WebSocket connection for a session has been closed. - /// - /// - /// A that represents the close event data - /// passed from the WebSocket interface. - /// - protected virtual void OnClose (CloseEventArgs e) - { - } - - /// - /// Called when the WebSocket interface for a session gets an error. - /// - /// - /// A that represents the error event data - /// passed from the interface. - /// - protected virtual void OnError (ErrorEventArgs e) - { - } - - /// - /// Called when the WebSocket interface for a session receives a message. - /// - /// - /// A that represents the message event data - /// passed from the interface. - /// - protected virtual void OnMessage (MessageEventArgs e) - { - } - - /// - /// Called when the WebSocket connection for a session has been established. - /// - protected virtual void OnOpen () - { - } - - /// - /// Sends a ping to the client for a session. - /// - /// - /// true if the send has successfully done and a pong has been - /// received within a time; otherwise, false. - /// - /// - /// This method is not available when the session has not started yet. - /// - protected bool Ping () - { - if (!_registered) { - var msg = "The session has not started yet."; - - throw new InvalidOperationException (msg); - } - - return _websocket.Ping (); - } - - /// - /// Sends a ping with the specified message to the client for a session. - /// - /// - /// true if the send has successfully done and a pong has been - /// received within a time; otherwise, false. - /// - /// - /// - /// A that specifies the message to send. - /// - /// - /// Its size must be 125 bytes or less in UTF-8. - /// - /// - /// - /// could not be UTF-8-encoded. - /// - /// - /// The size of is greater than 125 bytes. - /// - /// - /// This method is not available when the session has not started yet. - /// - protected bool Ping (string message) - { - if (!_registered) { - var msg = "The session has not started yet."; - - throw new InvalidOperationException (msg); - } - - return _websocket.Ping (message); - } - - /// - /// Sends the specified data to the client for a session. - /// - /// - /// An array of that specifies the binary data to send. - /// - /// - /// is . - /// - /// - /// - /// This method is not available when the session has not started yet. - /// - /// - /// -or- - /// - /// - /// This method is not available when the current state of the WebSocket - /// interface is not Open. - /// - /// - protected void Send (byte[] data) - { - if (!_registered) { - var msg = "The session has not started yet."; - - throw new InvalidOperationException (msg); - } - - _websocket.Send (data); - } - - /// - /// Sends the specified file to the client for a session. - /// - /// - /// - /// A that specifies the file to send. - /// - /// - /// The file is sent as the binary data. - /// - /// - /// - /// - /// The file does not exist. - /// - /// - /// -or- - /// - /// - /// The file could not be opened. - /// - /// - /// - /// is . - /// - /// - /// - /// This method is not available when the session has not started yet. - /// - /// - /// -or- - /// - /// - /// This method is not available when the current state of the WebSocket - /// interface is not Open. - /// - /// - protected void Send (FileInfo fileInfo) - { - if (!_registered) { - var msg = "The session has not started yet."; - - throw new InvalidOperationException (msg); - } - - _websocket.Send (fileInfo); - } - - /// - /// Sends the specified data to the client for a session. - /// - /// - /// A that specifies the text data to send. - /// - /// - /// could not be UTF-8-encoded. - /// - /// - /// is . - /// - /// - /// - /// This method is not available when the session has not started yet. - /// - /// - /// -or- - /// - /// - /// This method is not available when the current state of the WebSocket - /// interface is not Open. - /// - /// - protected void Send (string data) - { - if (!_registered) { - var msg = "The session has not started yet."; - - throw new InvalidOperationException (msg); - } - - _websocket.Send (data); - } - - /// - /// Sends the data from the specified stream instance to the client for - /// a session. - /// - /// - /// - /// 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. - /// - /// - /// - /// cannot be read. - /// - /// - /// -or- - /// - /// - /// is less than 1. - /// - /// - /// -or- - /// - /// - /// No data could be read from . - /// - /// - /// - /// is . - /// - /// - /// - /// This method is not available when the session has not started yet. - /// - /// - /// -or- - /// - /// - /// This method is not available when the current state of the WebSocket - /// interface is not Open. - /// - /// - protected void Send (Stream stream, int length) - { - if (!_registered) { - var msg = "The session has not started yet."; - - throw new InvalidOperationException (msg); - } - - _websocket.Send (stream, length); - } - - /// - /// Sends the specified data to the client for a session asynchronously. - /// - /// - /// This method does not wait for the send to be complete. - /// - /// - /// An array of that specifies the binary data to send. - /// - /// - /// - /// An delegate. - /// - /// - /// It specifies the delegate called when the send is complete. - /// - /// - /// The parameter passed to the delegate is true - /// if the send has successfully done; otherwise, false. - /// - /// - /// if not necessary. - /// - /// - /// - /// is . - /// - /// - /// - /// This method is not available when the session has not started yet. - /// - /// - /// -or- - /// - /// - /// This method is not available when the current state of the WebSocket - /// interface is not Open. - /// - /// - protected void SendAsync (byte[] data, Action completed) - { - if (!_registered) { - var msg = "The session has not started yet."; - - throw new InvalidOperationException (msg); - } - - _websocket.SendAsync (data, completed); - } - - /// - /// Sends the specified file to the client for a session asynchronously. - /// - /// - /// 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 delegate. - /// - /// - /// It specifies the delegate called when the send is complete. - /// - /// - /// The parameter passed to the delegate is true - /// if the send has successfully done; otherwise, false. - /// - /// - /// if not necessary. - /// - /// - /// - /// - /// The file does not exist. - /// - /// - /// -or- - /// - /// - /// The file could not be opened. - /// - /// - /// - /// is . - /// - /// - /// - /// This method is not available when the session has not started yet. - /// - /// - /// -or- - /// - /// - /// This method is not available when the current state of the WebSocket - /// interface is not Open. - /// - /// - protected void SendAsync (FileInfo fileInfo, Action completed) - { - if (!_registered) { - var msg = "The session has not started yet."; - - throw new InvalidOperationException (msg); - } - - _websocket.SendAsync (fileInfo, completed); - } - - /// - /// Sends the specified data to the client for a session asynchronously. - /// - /// - /// This method does not wait for the send to be complete. - /// - /// - /// A that specifies the text data to send. - /// - /// - /// - /// An delegate. - /// - /// - /// It specifies the delegate called when the send is complete. - /// - /// - /// The parameter passed to the delegate is true - /// if the send has successfully done; otherwise, false. - /// - /// - /// if not necessary. - /// - /// - /// - /// could not be UTF-8-encoded. - /// - /// - /// is . - /// - /// - /// - /// This method is not available when the session has not started yet. - /// - /// - /// -or- - /// - /// - /// This method is not available when the current state of the WebSocket - /// interface is not Open. - /// - /// - protected void SendAsync (string data, Action completed) - { - if (!_registered) { - var msg = "The session has not started yet."; - - throw new InvalidOperationException (msg); - } - - _websocket.SendAsync (data, completed); - } - - /// - /// Sends the data from the specified stream instance to the client for - /// a session asynchronously. - /// - /// - /// 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 delegate. - /// - /// - /// It specifies the delegate called when the send is complete. - /// - /// - /// The parameter passed to the delegate is true - /// if the send has successfully done; otherwise, false. - /// - /// - /// if not necessary. - /// - /// - /// - /// - /// cannot be read. - /// - /// - /// -or- - /// - /// - /// is less than 1. - /// - /// - /// -or- - /// - /// - /// No data could be read from . - /// - /// - /// - /// is . - /// - /// - /// - /// This method is not available when the session has not started yet. - /// - /// - /// -or- - /// - /// - /// This method is not available when the current state of the WebSocket - /// interface is not Open. - /// - /// - protected void SendAsync (Stream stream, int length, Action completed) - { - if (!_registered) { - var msg = "The session has not started yet."; - - throw new InvalidOperationException (msg); - } - - _websocket.SendAsync (stream, length, completed); - } - - #endregion - - #region Explicit Interface Implementations - - /// - /// Gets the WebSocket interface for a session. - /// - /// - /// - /// A that represents - /// the WebSocket interface. - /// - /// - /// when the session has not started yet. - /// - /// - WebSocket IWebSocketSession.WebSocket { - get { - return _websocket; - } - } - - #endregion - } -} diff --git a/websocket-sharp/Server/WebSocketServer.cs b/websocket-sharp/Server/WebSocketServer.cs deleted file mode 100644 index fc997ad83..000000000 --- a/websocket-sharp/Server/WebSocketServer.cs +++ /dev/null @@ -1,1185 +0,0 @@ -#region License -/* - * WebSocketServer.cs - * - * The MIT License - * - * Copyright (c) 2012-2024 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -#region Contributors -/* - * Contributors: - * - Juan Manuel Lallana - * - Jonas Hovgaard - * - Liryna - * - Rohan Singh - */ -#endregion - -using System; -using System.Collections.Generic; -using System.Net.Sockets; -using System.Security.Cryptography.X509Certificates; -using System.Security.Principal; -using System.Text; -using System.Threading; -using WebSocketSharp.Net; -using WebSocketSharp.Net.WebSockets; - -namespace WebSocketSharp.Server -{ - /// - /// Provides a WebSocket protocol server. - /// - /// - /// This class can provide multiple WebSocket services. - /// - public class WebSocketServer - { - #region Private Fields - - private System.Net.IPAddress _address; - private AuthenticationSchemes _authSchemes; - private static readonly string _defaultRealm; - private string _hostname; - private bool _isDnsStyle; - private bool _isSecure; - private TcpListener _listener; - private Logger _log; - private int _port; - private string _realm; - private string _realmInUse; - private Thread _receiveThread; - private bool _reuseAddress; - private WebSocketServiceManager _services; - private ServerSslConfiguration _sslConfig; - private ServerSslConfiguration _sslConfigInUse; - private volatile ServerState _state; - private object _sync; - private Func _userCredFinder; - - #endregion - - #region Static Constructor - - static WebSocketServer () - { - _defaultRealm = "SECRET AREA"; - } - - #endregion - - #region Public Constructors - - /// - /// Initializes a new instance of the class. - /// - /// - /// The new instance listens for incoming handshake requests on - /// and port 80. - /// - public WebSocketServer () - { - var addr = System.Net.IPAddress.Any; - - init (addr.ToString (), addr, 80, false); - } - - /// - /// Initializes a new instance of the class - /// with the specified port. - /// - /// - /// - /// The new instance listens for incoming handshake requests on - /// and . - /// - /// - /// It provides secure connections if is 443. - /// - /// - /// - /// An that specifies the number of the port on which - /// to listen. - /// - /// - /// is less than 1 or greater than 65535. - /// - public WebSocketServer (int port) - : this (port, port == 443) - { - } - - /// - /// Initializes a new instance of the class - /// with the specified URL. - /// - /// - /// - /// The new instance listens for incoming handshake requests on - /// the IP address and port of . - /// - /// - /// Either port 80 or 443 is used if includes - /// no port. Port 443 is used if the scheme of - /// is wss; otherwise, port 80 is used. - /// - /// - /// The new instance provides secure connections if the scheme of - /// is wss. - /// - /// - /// - /// A that specifies the WebSocket URL of the server. - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// is invalid. - /// - /// - /// - /// is . - /// - public WebSocketServer (string url) - { - if (url == null) - throw new ArgumentNullException ("url"); - - if (url.Length == 0) - throw new ArgumentException ("An empty string.", "url"); - - Uri uri; - string msg; - - if (!tryCreateUri (url, out uri, out msg)) - throw new ArgumentException (msg, "url"); - - var host = uri.DnsSafeHost; - var addr = host.ToIPAddress (); - - if (addr == null) { - msg = "The host part could not be converted to an IP address."; - - throw new ArgumentException (msg, "url"); - } - - if (!addr.IsLocal ()) { - msg = "The IP address of the host is not a local IP address."; - - throw new ArgumentException (msg, "url"); - } - - init (host, addr, uri.Port, uri.Scheme == "wss"); - } - - /// - /// Initializes a new instance of the class - /// with the specified port and boolean if secure or not. - /// - /// - /// The new instance listens for incoming handshake requests on - /// and . - /// - /// - /// An that specifies the number of the port on which - /// to listen. - /// - /// - /// A : true if the new instance provides - /// secure connections; otherwise, false. - /// - /// - /// is less than 1 or greater than 65535. - /// - public WebSocketServer (int port, bool secure) - { - if (!port.IsPortNumber ()) { - var msg = "Less than 1 or greater than 65535."; - - throw new ArgumentOutOfRangeException ("port", msg); - } - - var addr = System.Net.IPAddress.Any; - - init (addr.ToString (), addr, port, secure); - } - - /// - /// Initializes a new instance of the class - /// with the specified IP address and port. - /// - /// - /// - /// The new instance listens for incoming handshake requests on - /// and . - /// - /// - /// It provides secure connections if is 443. - /// - /// - /// - /// A that specifies the local IP - /// address on which to listen. - /// - /// - /// An that specifies the number of the port on which - /// to listen. - /// - /// - /// is not a local IP address. - /// - /// - /// is . - /// - /// - /// is less than 1 or greater than 65535. - /// - public WebSocketServer (System.Net.IPAddress address, int port) - : this (address, port, port == 443) - { - } - - /// - /// Initializes a new instance of the class - /// with the specified IP address, port, and boolean if secure or not. - /// - /// - /// The new instance listens for incoming handshake requests on - /// and . - /// - /// - /// A that specifies the local IP - /// address on which to listen. - /// - /// - /// An that specifies the number of the port on which - /// to listen. - /// - /// - /// A : true if the new instance provides - /// secure connections; otherwise, false. - /// - /// - /// is not a local IP address. - /// - /// - /// is . - /// - /// - /// is less than 1 or greater than 65535. - /// - public WebSocketServer (System.Net.IPAddress address, int port, bool secure) - { - if (address == null) - throw new ArgumentNullException ("address"); - - if (!address.IsLocal ()) { - var msg = "Not a local IP address."; - - throw new ArgumentException (msg, "address"); - } - - if (!port.IsPortNumber ()) { - var msg = "Less than 1 or greater than 65535."; - - throw new ArgumentOutOfRangeException ("port", msg); - } - - init (address.ToString (), address, port, secure); - } - - #endregion - - #region Public Properties - - /// - /// Gets the IP address of the server. - /// - /// - /// A that represents the local IP - /// address on which to listen for incoming handshake requests. - /// - public System.Net.IPAddress Address { - get { - return _address; - } - } - - /// - /// Gets or sets the scheme used to authenticate the clients. - /// - /// - /// The set operation works if the current state of the server is - /// Ready or Stop. - /// - /// - /// - /// One of the - /// enum values. - /// - /// - /// It represents the scheme used to authenticate the clients. - /// - /// - /// The default value is - /// . - /// - /// - public AuthenticationSchemes AuthenticationSchemes { - get { - return _authSchemes; - } - - set { - lock (_sync) { - if (!canSet ()) - return; - - _authSchemes = value; - } - } - } - - /// - /// Gets a value indicating whether the server has started. - /// - /// - /// true if the server has started; otherwise, false. - /// - public bool IsListening { - get { - return _state == ServerState.Start; - } - } - - /// - /// Gets a value indicating whether the server provides secure connections. - /// - /// - /// true if the server provides secure connections; otherwise, - /// false. - /// - public bool IsSecure { - get { - return _isSecure; - } - } - - /// - /// Gets or sets a value indicating whether the server cleans up - /// the inactive sessions periodically. - /// - /// - /// - /// true if the server cleans up the inactive sessions - /// every 60 seconds; otherwise, false. - /// - /// - /// The default value is false. - /// - /// - /// - /// The set operation is not available when the current state of - /// the server is neither Ready nor Stop. - /// - public bool KeepClean { - get { - return _services.KeepClean; - } - - set { - _services.KeepClean = value; - } - } - - /// - /// Gets the logging function for the server. - /// - /// - /// The default logging level is . - /// - /// - /// A that provides the logging function. - /// - public Logger Log { - get { - return _log; - } - } - - /// - /// Gets the port of the server. - /// - /// - /// An that represents the number of the port on which - /// to listen for incoming handshake requests. - /// - public int Port { - get { - return _port; - } - } - - /// - /// Gets or sets the name of the realm associated with the server. - /// - /// - /// The set operation works if the current state of the server is - /// Ready or Stop. - /// - /// - /// - /// A that represents the name of the realm. - /// - /// - /// "SECRET AREA" is used as the name of the realm if the value is - /// or an empty string. - /// - /// - /// The default value is . - /// - /// - public string Realm { - get { - return _realm; - } - - set { - lock (_sync) { - if (!canSet ()) - return; - - _realm = value; - } - } - } - - /// - /// Gets or sets a value indicating whether the server is allowed to - /// be bound to an address that is already in use. - /// - /// - /// - /// You should set this property to true if you would like to - /// resolve to wait for socket in TIME_WAIT state. - /// - /// - /// The set operation works if the current state of the server is - /// Ready or Stop. - /// - /// - /// - /// - /// true if the server is allowed to be bound to an address - /// that is already in use; otherwise, false. - /// - /// - /// The default value is false. - /// - /// - public bool ReuseAddress { - get { - return _reuseAddress; - } - - set { - lock (_sync) { - if (!canSet ()) - return; - - _reuseAddress = value; - } - } - } - - /// - /// Gets the configuration for secure connection. - /// - /// - /// The configuration is used when the server attempts to start, - /// so it must be configured before the start method is called. - /// - /// - /// A that represents the - /// configuration used to provide secure connections. - /// - /// - /// The server does not provide secure connections. - /// - public ServerSslConfiguration SslConfiguration { - get { - if (!_isSecure) { - var msg = "The server does not provide secure connections."; - - throw new InvalidOperationException (msg); - } - - return getSslConfiguration (); - } - } - - /// - /// Gets or sets the delegate called to find the credentials for - /// an identity used to authenticate a client. - /// - /// - /// The set operation works if the current state of the server is - /// Ready or Stop. - /// - /// - /// - /// A - /// delegate. - /// - /// - /// It represents the delegate called when the server finds - /// the credentials used to authenticate a client. - /// - /// - /// It must return if the credentials - /// are not found. - /// - /// - /// if not necessary. - /// - /// - /// The default value is . - /// - /// - public Func UserCredentialsFinder { - get { - return _userCredFinder; - } - - set { - lock (_sync) { - if (!canSet ()) - return; - - _userCredFinder = value; - } - } - } - - /// - /// Gets or sets the time to wait for the response to the WebSocket - /// Ping or Close. - /// - /// - /// - /// A that represents the time to wait for - /// the response. - /// - /// - /// The default value is the same as 1 second. - /// - /// - /// - /// The value specified for a set operation is zero or less. - /// - /// - /// The set operation is not available when the current state of - /// the server is neither Ready nor Stop. - /// - public TimeSpan WaitTime { - get { - return _services.WaitTime; - } - - set { - _services.WaitTime = value; - } - } - - /// - /// Gets the management function for the WebSocket services provided by - /// the server. - /// - /// - /// A that manages the WebSocket - /// services provided by the server. - /// - public WebSocketServiceManager WebSocketServices { - get { - return _services; - } - } - - #endregion - - #region Private Methods - - private void abort () - { - lock (_sync) { - if (_state != ServerState.Start) - return; - - _state = ServerState.ShuttingDown; - } - - try { - _listener.Stop (); - } - catch (Exception ex) { - _log.Fatal (ex.Message); - _log.Debug (ex.ToString ()); - } - - try { - _services.Stop (1006, String.Empty); - } - catch (Exception ex) { - _log.Fatal (ex.Message); - _log.Debug (ex.ToString ()); - } - - _state = ServerState.Stop; - } - - private bool authenticateClient (TcpListenerWebSocketContext context) - { - if (_authSchemes == AuthenticationSchemes.Anonymous) - return true; - - if (_authSchemes == AuthenticationSchemes.None) - return false; - - var chal = new AuthenticationChallenge (_authSchemes, _realmInUse) - .ToString (); - - var retry = -1; - Func auth = null; - auth = - () => { - retry++; - - if (retry > 99) - return false; - - if (context.SetUser (_authSchemes, _realmInUse, _userCredFinder)) - return true; - - context.SendAuthenticationChallenge (chal); - - return auth (); - }; - - return auth (); - } - - private bool canSet () - { - return _state == ServerState.Ready || _state == ServerState.Stop; - } - - private bool checkHostNameForRequest (string name) - { - return !_isDnsStyle - || Uri.CheckHostName (name) != UriHostNameType.Dns - || name == _hostname; - } - - private string getRealm () - { - var realm = _realm; - - return realm != null && realm.Length > 0 ? realm : _defaultRealm; - } - - private ServerSslConfiguration getSslConfiguration () - { - if (_sslConfig == null) - _sslConfig = new ServerSslConfiguration (); - - return _sslConfig; - } - - private void init ( - string hostname, - System.Net.IPAddress address, - int port, - bool secure - ) - { - _hostname = hostname; - _address = address; - _port = port; - _isSecure = secure; - - _authSchemes = AuthenticationSchemes.Anonymous; - _isDnsStyle = Uri.CheckHostName (hostname) == UriHostNameType.Dns; - _listener = new TcpListener (address, port); - _log = new Logger (); - _services = new WebSocketServiceManager (_log); - _sync = new object (); - } - - private void processRequest (TcpListenerWebSocketContext context) - { - if (!authenticateClient (context)) { - context.Close (HttpStatusCode.Forbidden); - - return; - } - - var uri = context.RequestUri; - - if (uri == null) { - context.Close (HttpStatusCode.BadRequest); - - return; - } - - var name = uri.DnsSafeHost; - - if (!checkHostNameForRequest (name)) { - context.Close (HttpStatusCode.NotFound); - - return; - } - - var path = uri.AbsolutePath; - - if (path.IndexOfAny (new[] { '%', '+' }) > -1) - path = HttpUtility.UrlDecode (path, Encoding.UTF8); - - WebSocketServiceHost host; - - if (!_services.InternalTryGetServiceHost (path, out host)) { - context.Close (HttpStatusCode.NotImplemented); - - return; - } - - host.StartSession (context); - } - - private void receiveRequest () - { - while (true) { - TcpClient cl = null; - - try { - cl = _listener.AcceptTcpClient (); - - ThreadPool.QueueUserWorkItem ( - state => { - try { - var ctx = new TcpListenerWebSocketContext ( - cl, - null, - _isSecure, - _sslConfigInUse, - _log - ); - - processRequest (ctx); - } - catch (Exception ex) { - _log.Error (ex.Message); - _log.Debug (ex.ToString ()); - - cl.Close (); - } - } - ); - } - catch (SocketException ex) { - if (_state == ServerState.ShuttingDown) - return; - - _log.Fatal (ex.Message); - _log.Debug (ex.ToString ()); - - break; - } - catch (InvalidOperationException ex) { - if (_state == ServerState.ShuttingDown) - return; - - _log.Fatal (ex.Message); - _log.Debug (ex.ToString ()); - - break; - } - catch (Exception ex) { - _log.Fatal (ex.Message); - _log.Debug (ex.ToString ()); - - if (cl != null) - cl.Close (); - - if (_state == ServerState.ShuttingDown) - return; - - break; - } - } - - abort (); - } - - private void start () - { - lock (_sync) { - if (_state == ServerState.Start || _state == ServerState.ShuttingDown) - return; - - if (_isSecure) { - var src = getSslConfiguration (); - var conf = new ServerSslConfiguration (src); - - if (conf.ServerCertificate == null) { - var msg = "There is no server certificate for secure connection."; - - throw new InvalidOperationException (msg); - } - - _sslConfigInUse = conf; - } - - _realmInUse = getRealm (); - - _services.Start (); - - try { - startReceiving (); - } - catch { - _services.Stop (1011, String.Empty); - - throw; - } - - _state = ServerState.Start; - } - } - - private void startReceiving () - { - if (_reuseAddress) { - _listener.Server.SetSocketOption ( - SocketOptionLevel.Socket, - SocketOptionName.ReuseAddress, - true - ); - } - - try { - _listener.Start (); - } - catch (Exception ex) { - var msg = "The underlying listener has failed to start."; - - throw new InvalidOperationException (msg, ex); - } - - var receiver = new ThreadStart (receiveRequest); - _receiveThread = new Thread (receiver); - _receiveThread.IsBackground = true; - - _receiveThread.Start (); - } - - private void stop (ushort code, string reason) - { - lock (_sync) { - if (_state != ServerState.Start) - return; - - _state = ServerState.ShuttingDown; - } - - try { - var timeout = 5000; - - stopReceiving (timeout); - } - catch (Exception ex) { - _log.Fatal (ex.Message); - _log.Debug (ex.ToString ()); - } - - try { - _services.Stop (code, reason); - } - catch (Exception ex) { - _log.Fatal (ex.Message); - _log.Debug (ex.ToString ()); - } - - _state = ServerState.Stop; - } - - private void stopReceiving (int millisecondsTimeout) - { - _listener.Stop (); - _receiveThread.Join (millisecondsTimeout); - } - - private static bool tryCreateUri ( - string uriString, - out Uri result, - out string message - ) - { - if (!uriString.TryCreateWebSocketUri (out result, out message)) - return false; - - if (result.PathAndQuery != "/") { - result = null; - message = "It includes either or both path and query components."; - - return false; - } - - return true; - } - - #endregion - - #region Public Methods - - /// - /// Adds a WebSocket service with the specified behavior and path. - /// - /// - /// - /// A that specifies an absolute path to - /// the service to add. - /// - /// - /// / is trimmed from the end of the string if present. - /// - /// - /// - /// - /// The type of the behavior for the service. - /// - /// - /// It must inherit the class. - /// - /// - /// Also it must have a public parameterless constructor. - /// - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// is not an absolute path. - /// - /// - /// -or- - /// - /// - /// includes either or both - /// query and fragment components. - /// - /// - /// -or- - /// - /// - /// is already in use. - /// - /// - /// - /// is . - /// - public void AddWebSocketService (string path) - where TBehavior : WebSocketBehavior, new () - { - _services.AddService (path, null); - } - - /// - /// Adds a WebSocket service with the specified behavior, path, - /// and initializer. - /// - /// - /// - /// A that specifies an absolute path to - /// the service to add. - /// - /// - /// / is trimmed from the end of the string if present. - /// - /// - /// - /// - /// An delegate. - /// - /// - /// It specifies the delegate called when the service initializes - /// a new session instance. - /// - /// - /// if not necessary. - /// - /// - /// - /// - /// The type of the behavior for the service. - /// - /// - /// It must inherit the class. - /// - /// - /// Also it must have a public parameterless constructor. - /// - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// is not an absolute path. - /// - /// - /// -or- - /// - /// - /// includes either or both - /// query and fragment components. - /// - /// - /// -or- - /// - /// - /// is already in use. - /// - /// - /// - /// is . - /// - public void AddWebSocketService ( - string path, - Action initializer - ) - where TBehavior : WebSocketBehavior, new () - { - _services.AddService (path, initializer); - } - - /// - /// Removes a WebSocket service with the specified path. - /// - /// - /// The service is stopped with close status 1001 (going away) - /// if the current state of the service is Start. - /// - /// - /// true if the service is successfully found and removed; - /// otherwise, false. - /// - /// - /// - /// A that specifies an absolute path to - /// the service to remove. - /// - /// - /// / is trimmed from the end of the string if present. - /// - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// is not an absolute path. - /// - /// - /// -or- - /// - /// - /// includes either or both - /// query and fragment components. - /// - /// - /// - /// is . - /// - public bool RemoveWebSocketService (string path) - { - return _services.RemoveService (path); - } - - /// - /// Starts receiving incoming handshake requests. - /// - /// - /// This method works if the current state of the server is Ready or Stop. - /// - /// - /// - /// There is no server certificate for secure connection. - /// - /// - /// -or- - /// - /// - /// The underlying has failed to start. - /// - /// - public void Start () - { - if (_state == ServerState.Start || _state == ServerState.ShuttingDown) - return; - - start (); - } - - /// - /// Stops receiving incoming handshake requests. - /// - /// - /// This method works if the current state of the server is Start. - /// - public void Stop () - { - if (_state != ServerState.Start) - return; - - stop (1001, String.Empty); - } - - #endregion - } -} diff --git a/websocket-sharp/Server/WebSocketServiceHost.cs b/websocket-sharp/Server/WebSocketServiceHost.cs deleted file mode 100644 index 863c39742..000000000 --- a/websocket-sharp/Server/WebSocketServiceHost.cs +++ /dev/null @@ -1,227 +0,0 @@ -#region License -/* - * WebSocketServiceHost.cs - * - * The MIT License - * - * Copyright (c) 2012-2026 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -#region Contributors -/* - * Contributors: - * - Juan Manuel Lallana - */ -#endregion - -using System; -using WebSocketSharp.Net.WebSockets; - -namespace WebSocketSharp.Server -{ - /// - /// Exposes the methods and properties used to access the information in - /// a WebSocket service provided by the or - /// class. - /// - /// - /// This class is an abstract class. - /// - public abstract class WebSocketServiceHost - { - #region Private Fields - - private Logger _log; - private string _path; - private WebSocketSessionManager _sessions; - - #endregion - - #region Protected Constructors - - /// - /// Initializes a new instance of the - /// class with the specified path and logging function. - /// - /// - /// A that specifies the absolute path to - /// the service. - /// - /// - /// A that specifies the logging function for - /// the service. - /// - protected WebSocketServiceHost (string path, Logger log) - { - _path = path; - _log = log; - - _sessions = new WebSocketSessionManager (log); - } - - #endregion - - #region Internal Properties - - internal ServerState State { - get { - return _sessions.State; - } - } - - #endregion - - #region Protected Properties - - /// - /// Gets the logging function for the service. - /// - /// - /// A that provides the logging function. - /// - protected Logger Log { - get { - return _log; - } - } - - #endregion - - #region Public Properties - - /// - /// Gets or sets a value indicating whether the service cleans up - /// the inactive sessions periodically. - /// - /// - /// true if the service cleans up the inactive sessions every - /// 60 seconds; otherwise, false. - /// - /// - /// The set operation is not available when the current state of - /// the service is neither Ready nor Stop. - /// - public bool KeepClean { - get { - return _sessions.KeepClean; - } - - set { - _sessions.KeepClean = value; - } - } - - /// - /// Gets the path to the service. - /// - /// - /// A that represents the absolute path to - /// the service. - /// - public string Path { - get { - return _path; - } - } - - /// - /// Gets the management function for the sessions in the service. - /// - /// - /// A that manages the sessions in - /// the service. - /// - public WebSocketSessionManager Sessions { - get { - return _sessions; - } - } - - /// - /// Gets the type of the behavior of the service. - /// - /// - /// A that represents the type of the behavior of - /// the service. - /// - public abstract Type BehaviorType { get; } - - /// - /// Gets or sets the time to wait for the response to the WebSocket - /// Ping or Close. - /// - /// - /// A that represents the time to wait for - /// the response. - /// - /// - /// The value specified for a set operation is zero or less. - /// - /// - /// The set operation is not available when the current state of - /// the service is neither Ready nor Stop. - /// - public TimeSpan WaitTime { - get { - return _sessions.WaitTime; - } - - set { - _sessions.WaitTime = value; - } - } - - #endregion - - #region Internal Methods - - internal void Start () - { - _sessions.Start (); - } - - internal void StartSession (WebSocketContext context) - { - CreateSession ().Start (context, _sessions); - } - - internal void Stop (ushort code, string reason) - { - _sessions.Stop (code, reason); - } - - #endregion - - #region Protected Methods - - /// - /// Creates a new session for the service. - /// - /// - /// A instance that represents - /// the new session. - /// - protected abstract WebSocketBehavior CreateSession (); - - #endregion - } -} diff --git a/websocket-sharp/Server/WebSocketServiceHost`1.cs b/websocket-sharp/Server/WebSocketServiceHost`1.cs deleted file mode 100644 index 8aac424e3..000000000 --- a/websocket-sharp/Server/WebSocketServiceHost`1.cs +++ /dev/null @@ -1,95 +0,0 @@ -#region License -/* - * WebSocketServiceHost`1.cs - * - * The MIT License - * - * Copyright (c) 2015-2024 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -using System; - -namespace WebSocketSharp.Server -{ - internal class WebSocketServiceHost : WebSocketServiceHost - where TBehavior : WebSocketBehavior, new () - { - #region Private Fields - - private Func _creator; - - #endregion - - #region Internal Constructors - - internal WebSocketServiceHost ( - string path, - Action initializer, - Logger log - ) - : base (path, log) - { - _creator = createSessionCreator (initializer); - } - - #endregion - - #region Public Properties - - public override Type BehaviorType { - get { - return typeof (TBehavior); - } - } - - #endregion - - #region Private Methods - - private static Func createSessionCreator ( - Action initializer - ) - { - if (initializer == null) - return () => new TBehavior (); - - return () => { - var ret = new TBehavior (); - - initializer (ret); - - return ret; - }; - } - - #endregion - - #region Protected Methods - - protected override WebSocketBehavior CreateSession () - { - return _creator (); - } - - #endregion - } -} diff --git a/websocket-sharp/Server/WebSocketServiceManager.cs b/websocket-sharp/Server/WebSocketServiceManager.cs deleted file mode 100644 index 7cfea951f..000000000 --- a/websocket-sharp/Server/WebSocketServiceManager.cs +++ /dev/null @@ -1,617 +0,0 @@ -#region License -/* - * WebSocketServiceManager.cs - * - * The MIT License - * - * Copyright (c) 2012-2024 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -using System; -using System.Collections; -using System.Collections.Generic; - -namespace WebSocketSharp.Server -{ - /// - /// Provides the management function for the WebSocket services. - /// - /// - /// This class manages the WebSocket services provided by the - /// or class. - /// - public class WebSocketServiceManager - { - #region Private Fields - - private Dictionary _hosts; - private volatile bool _keepClean; - private Logger _log; - private volatile ServerState _state; - private object _sync; - private TimeSpan _waitTime; - - #endregion - - #region Internal Constructors - - internal WebSocketServiceManager (Logger log) - { - _log = log; - - _hosts = new Dictionary (); - _state = ServerState.Ready; - _sync = ((ICollection) _hosts).SyncRoot; - _waitTime = TimeSpan.FromSeconds (1); - } - - #endregion - - #region Public Properties - - /// - /// Gets the number of the WebSocket services. - /// - /// - /// An that represents the number of the services. - /// - public int Count { - get { - lock (_sync) - return _hosts.Count; - } - } - - /// - /// Gets the service host instances for the WebSocket services. - /// - /// - /// - /// An - /// instance. - /// - /// - /// It provides an enumerator which supports the iteration over - /// the collection of the service host instances. - /// - /// - public IEnumerable Hosts { - get { - lock (_sync) - return _hosts.Values.ToList (); - } - } - - /// - /// Gets the service host instance for a WebSocket service with - /// the specified path. - /// - /// - /// - /// A instance that represents - /// the service host instance. - /// - /// - /// It provides the function to access the information in the service. - /// - /// - /// if not found. - /// - /// - /// - /// - /// A that specifies an absolute path to - /// the service to get. - /// - /// - /// / is trimmed from the end of the string if present. - /// - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// is not an absolute path. - /// - /// - /// -or- - /// - /// - /// includes either or both - /// query and fragment components. - /// - /// - /// - /// is . - /// - public WebSocketServiceHost this[string path] { - get { - if (path == null) - throw new ArgumentNullException ("path"); - - if (path.Length == 0) - throw new ArgumentException ("An empty string.", "path"); - - if (path[0] != '/') { - var msg = "Not an absolute path."; - - throw new ArgumentException (msg, "path"); - } - - if (path.IndexOfAny (new[] { '?', '#' }) > -1) { - var msg = "It includes either or both query and fragment components."; - - throw new ArgumentException (msg, "path"); - } - - WebSocketServiceHost host; - - InternalTryGetServiceHost (path, out host); - - return host; - } - } - - /// - /// Gets or sets a value indicating whether the inactive sessions in - /// the WebSocket services are cleaned up periodically. - /// - /// - /// - /// true if the inactive sessions are cleaned up every 60 - /// seconds; otherwise, false. - /// - /// - /// The default value is false. - /// - /// - /// - /// The set operation is not available when the current state of - /// the server is neither Ready nor Stop. - /// - public bool KeepClean { - get { - return _keepClean; - } - - set { - lock (_sync) { - var canSet = _state == ServerState.Ready - || _state == ServerState.Stop; - - if (!canSet) { - var msg = "The current state of the server is neither Ready nor Stop."; - - throw new InvalidOperationException (msg); - } - - foreach (var host in _hosts.Values) - host.KeepClean = value; - - _keepClean = value; - } - } - } - - /// - /// Gets the paths for the WebSocket services. - /// - /// - /// - /// An - /// instance. - /// - /// - /// It provides an enumerator which supports the iteration over - /// the collection of the paths. - /// - /// - public IEnumerable Paths { - get { - lock (_sync) - return _hosts.Keys.ToList (); - } - } - - /// - /// Gets or sets the time to wait for the response to the WebSocket - /// Ping or Close. - /// - /// - /// - /// A that represents the time to wait for - /// the response. - /// - /// - /// The default value is the same as 1 second. - /// - /// - /// - /// The value specified for a set operation is zero or less. - /// - /// - /// The set operation is not available when the current state of - /// the server is neither Ready nor Stop. - /// - public TimeSpan WaitTime { - get { - return _waitTime; - } - - set { - if (value <= TimeSpan.Zero) { - var msg = "Zero or less."; - - throw new ArgumentOutOfRangeException ("value", msg); - } - - lock (_sync) { - var canSet = _state == ServerState.Ready - || _state == ServerState.Stop; - - if (!canSet) { - var msg = "The current state of the server is neither Ready nor Stop."; - - throw new InvalidOperationException (msg); - } - - foreach (var host in _hosts.Values) - host.WaitTime = value; - - _waitTime = value; - } - } - } - - #endregion - - #region Internal Methods - - internal bool InternalTryGetServiceHost ( - string path, - out WebSocketServiceHost host - ) - { - path = path.TrimSlashFromEnd (); - - lock (_sync) - return _hosts.TryGetValue (path, out host); - } - - internal void Start () - { - lock (_sync) { - foreach (var host in _hosts.Values) - host.Start (); - - _state = ServerState.Start; - } - } - - internal void Stop (ushort code, string reason) - { - lock (_sync) { - _state = ServerState.ShuttingDown; - - foreach (var host in _hosts.Values) - host.Stop (code, reason); - - _state = ServerState.Stop; - } - } - - #endregion - - #region Public Methods - - /// - /// Adds a WebSocket service with the specified behavior, path, - /// and initializer. - /// - /// - /// - /// A that specifies an absolute path to - /// the service to add. - /// - /// - /// / is trimmed from the end of the string if present. - /// - /// - /// - /// - /// An delegate. - /// - /// - /// It specifies the delegate called when the service initializes - /// a new session instance. - /// - /// - /// if not necessary. - /// - /// - /// - /// - /// The type of the behavior for the service. - /// - /// - /// It must inherit the class. - /// - /// - /// Also it must have a public parameterless constructor. - /// - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// is not an absolute path. - /// - /// - /// -or- - /// - /// - /// includes either or both - /// query and fragment components. - /// - /// - /// -or- - /// - /// - /// is already in use. - /// - /// - /// - /// is . - /// - public void AddService ( - string path, - Action initializer - ) - where TBehavior : WebSocketBehavior, new () - { - if (path == null) - throw new ArgumentNullException ("path"); - - if (path.Length == 0) - throw new ArgumentException ("An empty string.", "path"); - - if (path[0] != '/') { - var msg = "Not an absolute path."; - - throw new ArgumentException (msg, "path"); - } - - if (path.IndexOfAny (new[] { '?', '#' }) > -1) { - var msg = "It includes either or both query and fragment components."; - - throw new ArgumentException (msg, "path"); - } - - path = path.TrimSlashFromEnd (); - - lock (_sync) { - WebSocketServiceHost host; - - if (_hosts.TryGetValue (path, out host)) { - var msg = "It is already in use."; - - throw new ArgumentException (msg, "path"); - } - - host = new WebSocketServiceHost (path, initializer, _log); - - if (_keepClean) - host.KeepClean = true; - - if (_waitTime != host.WaitTime) - host.WaitTime = _waitTime; - - if (_state == ServerState.Start) - host.Start (); - - _hosts.Add (path, host); - } - } - - /// - /// Removes all WebSocket services managed by the manager. - /// - /// - /// Each service is stopped with close status 1001 (going away) - /// if the current state of the service is Start. - /// - public void Clear () - { - List hosts = null; - - lock (_sync) { - hosts = _hosts.Values.ToList (); - - _hosts.Clear (); - } - - foreach (var host in hosts) { - if (host.State == ServerState.Start) - host.Stop (1001, String.Empty); - } - } - - /// - /// Removes a WebSocket service with the specified path. - /// - /// - /// The service is stopped with close status 1001 (going away) - /// if the current state of the service is Start. - /// - /// - /// true if the service is successfully found and removed; - /// otherwise, false. - /// - /// - /// - /// A that specifies an absolute path to - /// the service to remove. - /// - /// - /// / is trimmed from the end of the string if present. - /// - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// is not an absolute path. - /// - /// - /// -or- - /// - /// - /// includes either or both - /// query and fragment components. - /// - /// - /// - /// is . - /// - public bool RemoveService (string path) - { - if (path == null) - throw new ArgumentNullException ("path"); - - if (path.Length == 0) - throw new ArgumentException ("An empty string.", "path"); - - if (path[0] != '/') { - var msg = "Not an absolute path."; - - throw new ArgumentException (msg, "path"); - } - - if (path.IndexOfAny (new[] { '?', '#' }) > -1) { - var msg = "It includes either or both query and fragment components."; - - throw new ArgumentException (msg, "path"); - } - - path = path.TrimSlashFromEnd (); - WebSocketServiceHost host; - - lock (_sync) { - if (!_hosts.TryGetValue (path, out host)) - return false; - - _hosts.Remove (path); - } - - if (host.State == ServerState.Start) - host.Stop (1001, String.Empty); - - return true; - } - - /// - /// Tries to get the service host instance for a WebSocket service with - /// the specified path. - /// - /// - /// true if the try has succeeded; otherwise, false. - /// - /// - /// - /// A that specifies an absolute path to - /// the service to get. - /// - /// - /// / is trimmed from the end of the string if present. - /// - /// - /// - /// - /// When this method returns, a - /// instance that receives the service host instance. - /// - /// - /// It provides the function to access the information in the service. - /// - /// - /// if not found. - /// - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// is not an absolute path. - /// - /// - /// -or- - /// - /// - /// includes either or both - /// query and fragment components. - /// - /// - /// - /// is . - /// - public bool TryGetServiceHost (string path, out WebSocketServiceHost host) - { - if (path == null) - throw new ArgumentNullException ("path"); - - if (path.Length == 0) - throw new ArgumentException ("An empty string.", "path"); - - if (path[0] != '/') { - var msg = "Not an absolute path."; - - throw new ArgumentException (msg, "path"); - } - - if (path.IndexOfAny (new[] { '?', '#' }) > -1) { - var msg = "It includes either or both query and fragment components."; - - throw new ArgumentException (msg, "path"); - } - - return InternalTryGetServiceHost (path, out host); - } - - #endregion - } -} diff --git a/websocket-sharp/Server/WebSocketSessionManager.cs b/websocket-sharp/Server/WebSocketSessionManager.cs deleted file mode 100644 index 67910650c..000000000 --- a/websocket-sharp/Server/WebSocketSessionManager.cs +++ /dev/null @@ -1,1660 +0,0 @@ -#region License -/* - * WebSocketSessionManager.cs - * - * The MIT License - * - * Copyright (c) 2012-2025 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -using System; -using System.Collections; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading; -using System.Timers; - -namespace WebSocketSharp.Server -{ - /// - /// Provides the management function for the sessions in a WebSocket service. - /// - /// - /// This class manages the sessions in a WebSocket service provided by the - /// or class. - /// - public class WebSocketSessionManager - { - #region Private Fields - - private object _forSweep; - private volatile bool _keepClean; - private Logger _log; - private static readonly byte[] _rawEmptyPingFrame; - private Dictionary _sessions; - private volatile ServerState _state; - private volatile bool _sweeping; - private System.Timers.Timer _sweepTimer; - private object _sync; - private TimeSpan _waitTime; - - #endregion - - #region Static Constructor - - static WebSocketSessionManager () - { - _rawEmptyPingFrame = WebSocketFrame.CreatePingFrame (false).ToArray (); - } - - #endregion - - #region Internal Constructors - - internal WebSocketSessionManager (Logger log) - { - _log = log; - - _forSweep = new object (); - _sessions = new Dictionary (); - _state = ServerState.Ready; - _sync = ((ICollection) _sessions).SyncRoot; - _waitTime = TimeSpan.FromSeconds (1); - - setSweepTimer (60000); - } - - #endregion - - #region Internal Properties - - internal ServerState State { - get { - return _state; - } - } - - #endregion - - #region Public Properties - - /// - /// Gets the IDs for the active sessions in the WebSocket service. - /// - /// - /// - /// An - /// instance. - /// - /// - /// It provides an enumerator which supports the iteration over - /// the collection of the IDs for the active sessions. - /// - /// - public IEnumerable ActiveIDs { - get { - foreach (var res in broadping (_rawEmptyPingFrame)) { - if (res.Value) - yield return res.Key; - } - } - } - - /// - /// Gets the number of the sessions in the WebSocket service. - /// - /// - /// An that represents the number of the sessions. - /// - public int Count { - get { - lock (_sync) - return _sessions.Count; - } - } - - /// - /// Gets the IDs for the sessions in the WebSocket service. - /// - /// - /// - /// An - /// instance. - /// - /// - /// It provides an enumerator which supports the iteration over - /// the collection of the IDs for the sessions. - /// - /// - public IEnumerable IDs { - get { - if (_state != ServerState.Start) - return Enumerable.Empty (); - - lock (_sync) { - if (_state != ServerState.Start) - return Enumerable.Empty (); - - return _sessions.Keys.ToList (); - } - } - } - - /// - /// Gets the IDs for the inactive sessions in the WebSocket service. - /// - /// - /// - /// An - /// instance. - /// - /// - /// It provides an enumerator which supports the iteration over - /// the collection of the IDs for the inactive sessions. - /// - /// - public IEnumerable InactiveIDs { - get { - foreach (var res in broadping (_rawEmptyPingFrame)) { - if (!res.Value) - yield return res.Key; - } - } - } - - /// - /// Gets the specified session instance. - /// - /// - /// - /// A instance that represents - /// the session instance. - /// - /// - /// It provides the access to the information in the session. - /// - /// - /// if not found. - /// - /// - /// - /// A that specifies the ID of the session to get. - /// - /// - /// is an empty string. - /// - /// - /// is . - /// - public IWebSocketSession this[string id] { - get { - if (id == null) - throw new ArgumentNullException ("id"); - - if (id.Length == 0) - throw new ArgumentException ("An empty string.", "id"); - - IWebSocketSession session; - - tryGetSession (id, out session); - - return session; - } - } - - /// - /// Gets or sets a value indicating whether the inactive sessions in - /// the WebSocket service are cleaned up periodically. - /// - /// - /// true if the inactive sessions are cleaned up every 60 seconds; - /// otherwise, false. - /// - /// - /// The set operation is not available when the current state of - /// the service is neither Ready nor Stop. - /// - public bool KeepClean { - get { - return _keepClean; - } - - set { - lock (_sync) { - if (!canSet ()) { - var msg = "The current state of the service is neither Ready nor Stop."; - - throw new InvalidOperationException (msg); - } - - _keepClean = value; - } - } - } - - /// - /// Gets the session instances in the WebSocket service. - /// - /// - /// - /// An - /// instance. - /// - /// - /// It provides an enumerator which supports the iteration over - /// the collection of the session instances. - /// - /// - public IEnumerable Sessions { - get { - if (_state != ServerState.Start) - return Enumerable.Empty (); - - lock (_sync) { - if (_state != ServerState.Start) - return Enumerable.Empty (); - - return _sessions.Values.ToList (); - } - } - } - - /// - /// Gets or sets the time to wait for the response to the WebSocket - /// Ping or Close. - /// - /// - /// A that represents the time to wait for - /// the response. - /// - /// - /// The value specified for a set operation is zero or less. - /// - /// - /// The set operation is not available when the current state of - /// the service is neither Ready nor Stop. - /// - public TimeSpan WaitTime { - get { - return _waitTime; - } - - set { - if (value <= TimeSpan.Zero) { - var msg = "Zero or less."; - - throw new ArgumentOutOfRangeException ("value", msg); - } - - lock (_sync) { - if (!canSet ()) { - var msg = "The current state of the service is neither Ready nor Stop."; - - throw new InvalidOperationException (msg); - } - - _waitTime = value; - } - } - } - - #endregion - - #region Private Methods - - private void broadcast (Opcode opcode, byte[] data, Action completed) - { - var cache = new Dictionary (); - - try { - foreach (var session in Sessions) { - if (_state != ServerState.Start) { - _log.Error ("The send is cancelled."); - - break; - } - - session.WebSocket.Send (opcode, data, cache); - } - - if (completed != null) - completed (); - } - catch (Exception ex) { - _log.Error (ex.Message); - _log.Debug (ex.ToString ()); - } - finally { - cache.Clear (); - } - } - - private void broadcast ( - Opcode opcode, - Stream sourceStream, - Action completed - ) - { - var cache = new Dictionary (); - - try { - foreach (var session in Sessions) { - if (_state != ServerState.Start) { - _log.Error ("The send is cancelled."); - - break; - } - - session.WebSocket.Send (opcode, sourceStream, cache); - } - - if (completed != null) - completed (); - } - catch (Exception ex) { - _log.Error (ex.Message); - _log.Debug (ex.ToString ()); - } - finally { - foreach (var cached in cache.Values) - cached.Dispose (); - - cache.Clear (); - } - } - - private void broadcastAsync (Opcode opcode, byte[] data, Action completed) - { - ThreadPool.QueueUserWorkItem ( - state => broadcast (opcode, data, completed) - ); - } - - private void broadcastAsync ( - Opcode opcode, - Stream sourceStream, - Action completed - ) - { - ThreadPool.QueueUserWorkItem ( - state => broadcast (opcode, sourceStream, completed) - ); - } - - private Dictionary broadping (byte[] rawFrame) - { - var ret = new Dictionary (); - - foreach (var session in Sessions) { - if (_state != ServerState.Start) { - ret.Clear (); - - break; - } - - var res = session.WebSocket.Ping (rawFrame); - - ret.Add (session.ID, res); - } - - return ret; - } - - private bool canSet () - { - return _state == ServerState.Ready || _state == ServerState.Stop; - } - - private void setSweepTimer (double interval) - { - _sweepTimer = new System.Timers.Timer (interval); - _sweepTimer.Elapsed += (sender, e) => Sweep (); - } - - private void stop (PayloadData payloadData, bool send) - { - var rawFrame = send - ? WebSocketFrame - .CreateCloseFrame (payloadData, false) - .ToArray () - : null; - - lock (_sync) { - _state = ServerState.ShuttingDown; - _sweepTimer.Enabled = false; - - foreach (var session in _sessions.Values.ToList ()) - session.WebSocket.Close (payloadData, rawFrame); - - _state = ServerState.Stop; - } - } - - private bool tryGetSession (string id, out IWebSocketSession session) - { - session = null; - - if (_state != ServerState.Start) - return false; - - lock (_sync) { - if (_state != ServerState.Start) - return false; - - return _sessions.TryGetValue (id, out session); - } - } - - #endregion - - #region Internal Methods - - internal bool Add (IWebSocketSession session) - { - lock (_sync) { - if (_state != ServerState.Start) - return false; - - _sessions.Add (session.ID, session); - - return true; - } - } - - internal static string CreateID () - { - return Guid.NewGuid ().ToString ("N"); - } - - internal bool Remove (string id) - { - lock (_sync) - return _sessions.Remove (id); - } - - internal void Start () - { - lock (_sync) { - _sweepTimer.Enabled = _keepClean; - _state = ServerState.Start; - } - } - - internal void Stop (ushort code, string reason) - { - if (code == 1005) { - stop (PayloadData.Empty, true); - - return; - } - - var payloadData = new PayloadData (code, reason); - var send = !code.IsReservedStatusCode (); - - stop (payloadData, send); - } - - #endregion - - #region Public Methods - - /// - /// Sends the specified data to every client in the WebSocket service. - /// - /// - /// An array of that specifies the binary data to send. - /// - /// - /// is . - /// - /// - /// This method is not available when the current state of the service is - /// not Start. - /// - public void Broadcast (byte[] data) - { - if (_state != ServerState.Start) { - var msg = "The current state of the service is not Start."; - - throw new InvalidOperationException (msg); - } - - if (data == null) - throw new ArgumentNullException ("data"); - - if (data.LongLength <= WebSocket.FragmentLength) - broadcast (Opcode.Binary, data, null); - else - broadcast (Opcode.Binary, new MemoryStream (data), null); - } - - /// - /// Sends the specified data to every client in the WebSocket service. - /// - /// - /// A that specifies the text data to send. - /// - /// - /// could not be UTF-8-encoded. - /// - /// - /// is . - /// - /// - /// This method is not available when the current state of the service is - /// not Start. - /// - public void Broadcast (string data) - { - if (_state != ServerState.Start) { - var msg = "The current state of the service is not Start."; - - 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"); - } - - if (bytes.LongLength <= WebSocket.FragmentLength) - broadcast (Opcode.Text, bytes, null); - else - broadcast (Opcode.Text, new MemoryStream (bytes), null); - } - - /// - /// Sends the data from the specified stream instance to every client in - /// the WebSocket service. - /// - /// - /// - /// 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. - /// - /// - /// - /// cannot be read. - /// - /// - /// -or- - /// - /// - /// is less than 1. - /// - /// - /// -or- - /// - /// - /// No data could be read from . - /// - /// - /// - /// is . - /// - /// - /// This method is not available when the current state of the service is - /// not Start. - /// - public void Broadcast (Stream stream, int length) - { - if (_state != ServerState.Start) { - var msg = "The current state of the service is not Start."; - - 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) { - var fmt = "Only {0} byte(s) of data could be read from the stream."; - var msg = String.Format (fmt, len); - - _log.Warn (msg); - } - - if (len <= WebSocket.FragmentLength) - broadcast (Opcode.Binary, bytes, null); - else - broadcast (Opcode.Binary, new MemoryStream (bytes), null); - } - - /// - /// Sends the specified data to every client in the WebSocket service - /// asynchronously. - /// - /// - /// This method does not wait for the send to be complete. - /// - /// - /// An array of that specifies the binary data to send. - /// - /// - /// - /// An delegate. - /// - /// - /// It specifies the delegate called when the send is complete. - /// - /// - /// if not necessary. - /// - /// - /// - /// is . - /// - /// - /// This method is not available when the current state of the service is - /// not Start. - /// - public void BroadcastAsync (byte[] data, Action completed) - { - if (_state != ServerState.Start) { - var msg = "The current state of the service is not Start."; - - throw new InvalidOperationException (msg); - } - - if (data == null) - throw new ArgumentNullException ("data"); - - if (data.LongLength <= WebSocket.FragmentLength) - broadcastAsync (Opcode.Binary, data, completed); - else - broadcastAsync (Opcode.Binary, new MemoryStream (data), completed); - } - - /// - /// Sends the specified data to every client in the WebSocket service - /// asynchronously. - /// - /// - /// This method does not wait for the send to be complete. - /// - /// - /// A that specifies the text data to send. - /// - /// - /// - /// An delegate. - /// - /// - /// It specifies the delegate called when the send is complete. - /// - /// - /// if not necessary. - /// - /// - /// - /// could not be UTF-8-encoded. - /// - /// - /// is . - /// - /// - /// This method is not available when the current state of the service is - /// not Start. - /// - public void BroadcastAsync (string data, Action completed) - { - if (_state != ServerState.Start) { - var msg = "The current state of the service is not Start."; - - 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"); - } - - if (bytes.LongLength <= WebSocket.FragmentLength) - broadcastAsync (Opcode.Text, bytes, completed); - else - broadcastAsync (Opcode.Text, new MemoryStream (bytes), completed); - } - - /// - /// Sends the data from the specified stream instance to every client in - /// the WebSocket service asynchronously. - /// - /// - /// 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 delegate. - /// - /// - /// It specifies the delegate called when the send is complete. - /// - /// - /// if not necessary. - /// - /// - /// - /// - /// cannot be read. - /// - /// - /// -or- - /// - /// - /// is less than 1. - /// - /// - /// -or- - /// - /// - /// No data could be read from . - /// - /// - /// - /// is . - /// - /// - /// This method is not available when the current state of the service is - /// not Start. - /// - public void BroadcastAsync (Stream stream, int length, Action completed) - { - if (_state != ServerState.Start) { - var msg = "The current state of the service is not Start."; - - 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) { - var fmt = "Only {0} byte(s) of data could be read from the stream."; - var msg = String.Format (fmt, len); - - _log.Warn (msg); - } - - if (len <= WebSocket.FragmentLength) - broadcastAsync (Opcode.Binary, bytes, completed); - else - broadcastAsync (Opcode.Binary, new MemoryStream (bytes), completed); - } - - /// - /// Closes the specified session. - /// - /// - /// A that specifies the ID of the session to close. - /// - /// - /// is an empty string. - /// - /// - /// is . - /// - /// - /// This method is not available if the session could not be found. - /// - public void CloseSession (string id) - { - IWebSocketSession session; - - if (!TryGetSession (id, out session)) { - var msg = "The session could not be found."; - - throw new InvalidOperationException (msg); - } - - session.WebSocket.Close (); - } - - /// - /// Closes the specified session with the specified status code and reason. - /// - /// - /// A that specifies the ID of the session to close. - /// - /// - /// - /// A that specifies the status code indicating - /// the reason for the close. - /// - /// - /// The status codes are defined in - /// - /// Section 7.4 of RFC 6455. - /// - /// - /// - /// - /// A that specifies the reason for the close. - /// - /// - /// Its size must be 123 bytes or less in UTF-8. - /// - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// is 1010 (mandatory extension). - /// - /// - /// -or- - /// - /// - /// is 1005 (no status) and - /// is specified. - /// - /// - /// -or- - /// - /// - /// could not be UTF-8-encoded. - /// - /// - /// - /// is . - /// - /// - /// - /// is less than 1000 or greater than 4999. - /// - /// - /// -or- - /// - /// - /// The size of is greater than 123 bytes. - /// - /// - /// - /// This method is not available if the session could not be found. - /// - public void CloseSession (string id, ushort code, string reason) - { - IWebSocketSession session; - - if (!TryGetSession (id, out session)) { - var msg = "The session could not be found."; - - throw new InvalidOperationException (msg); - } - - session.WebSocket.Close (code, reason); - } - - /// - /// Closes the specified session with the specified status code and reason. - /// - /// - /// A that specifies the ID of the session to close. - /// - /// - /// - /// One of the enum values. - /// - /// - /// It specifies the status code indicating the reason for the close. - /// - /// - /// - /// - /// A that specifies the reason for the close. - /// - /// - /// Its size must be 123 bytes or less in UTF-8. - /// - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// is an undefined enum value. - /// - /// - /// -or- - /// - /// - /// is . - /// - /// - /// -or- - /// - /// - /// is and - /// is specified. - /// - /// - /// -or- - /// - /// - /// could not be UTF-8-encoded. - /// - /// - /// - /// is . - /// - /// - /// The size of is greater than 123 bytes. - /// - /// - /// This method is not available if the session could not be found. - /// - public void CloseSession (string id, CloseStatusCode code, string reason) - { - IWebSocketSession session; - - if (!TryGetSession (id, out session)) { - var msg = "The session could not be found."; - - throw new InvalidOperationException (msg); - } - - session.WebSocket.Close (code, reason); - } - - /// - /// Sends a ping to the client for the specified session. - /// - /// - /// true if the send has successfully done and a pong has been - /// received within a time; otherwise, false. - /// - /// - /// A that specifies the ID of the session. - /// - /// - /// is an empty string. - /// - /// - /// is . - /// - /// - /// This method is not available if the session could not be found. - /// - public bool PingTo (string id) - { - IWebSocketSession session; - - if (!TryGetSession (id, out session)) { - var msg = "The session could not be found."; - - throw new InvalidOperationException (msg); - } - - return session.WebSocket.Ping (); - } - - /// - /// Sends a ping with the specified message to the client for - /// the specified session. - /// - /// - /// true if the send has successfully done and a pong has been - /// received within a time; otherwise, false. - /// - /// - /// - /// A that specifies the message to send. - /// - /// - /// Its size must be 125 bytes or less in UTF-8. - /// - /// - /// - /// A that specifies the ID of the session. - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// could not be UTF-8-encoded. - /// - /// - /// - /// is . - /// - /// - /// The size of is greater than 125 bytes. - /// - /// - /// This method is not available if the session could not be found. - /// - public bool PingTo (string message, string id) - { - IWebSocketSession session; - - if (!TryGetSession (id, out session)) { - var msg = "The session could not be found."; - - throw new InvalidOperationException (msg); - } - - return session.WebSocket.Ping (message); - } - - /// - /// Sends the specified data to the client for the specified session. - /// - /// - /// An array of that specifies the binary data to send. - /// - /// - /// A that specifies the ID of the session. - /// - /// - /// is an empty string. - /// - /// - /// - /// is . - /// - /// - /// -or- - /// - /// - /// is . - /// - /// - /// - /// - /// This method is not available if the session could not be found. - /// - /// - /// -or- - /// - /// - /// This method is not available when the current state of the WebSocket - /// interface is not Open. - /// - /// - public void SendTo (byte[] data, string id) - { - IWebSocketSession session; - - if (!TryGetSession (id, out session)) { - var msg = "The session could not be found."; - - throw new InvalidOperationException (msg); - } - - session.WebSocket.Send (data); - } - - /// - /// Sends the specified data to the client for the specified session. - /// - /// - /// A that specifies the text data to send. - /// - /// - /// A that specifies the ID of the session. - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// could not be UTF-8-encoded. - /// - /// - /// - /// - /// is . - /// - /// - /// -or- - /// - /// - /// is . - /// - /// - /// - /// - /// This method is not available if the session could not be found. - /// - /// - /// -or- - /// - /// - /// This method is not available when the current state of the WebSocket - /// interface is not Open. - /// - /// - public void SendTo (string data, string id) - { - IWebSocketSession session; - - if (!TryGetSession (id, out session)) { - var msg = "The session could not be found."; - - throw new InvalidOperationException (msg); - } - - session.WebSocket.Send (data); - } - - /// - /// Sends the data from the specified stream instance to the client for - /// the specified session. - /// - /// - /// - /// 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. - /// - /// - /// A that specifies the ID of the session. - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// cannot be read. - /// - /// - /// -or- - /// - /// - /// is less than 1. - /// - /// - /// -or- - /// - /// - /// No data could be read from . - /// - /// - /// - /// - /// is . - /// - /// - /// -or- - /// - /// - /// is . - /// - /// - /// - /// - /// This method is not available if the session could not be found. - /// - /// - /// -or- - /// - /// - /// This method is not available when the current state of the WebSocket - /// interface is not Open. - /// - /// - public void SendTo (Stream stream, int length, string id) - { - IWebSocketSession session; - - if (!TryGetSession (id, out session)) { - var msg = "The session could not be found."; - - throw new InvalidOperationException (msg); - } - - session.WebSocket.Send (stream, length); - } - - /// - /// Sends the specified data to the client for the specified session - /// asynchronously. - /// - /// - /// This method does not wait for the send to be complete. - /// - /// - /// An array of that specifies the binary data to send. - /// - /// - /// A that specifies the ID of the session. - /// - /// - /// - /// An delegate. - /// - /// - /// It specifies the delegate called when the send is complete. - /// - /// - /// The parameter passed to the delegate is - /// true if the send has successfully done; otherwise, - /// false. - /// - /// - /// if not necessary. - /// - /// - /// - /// is an empty string. - /// - /// - /// - /// is . - /// - /// - /// -or- - /// - /// - /// is . - /// - /// - /// - /// - /// This method is not available if the session could not be found. - /// - /// - /// -or- - /// - /// - /// This method is not available when the current state of the WebSocket - /// interface is not Open. - /// - /// - public void SendToAsync (byte[] data, string id, Action completed) - { - IWebSocketSession session; - - if (!TryGetSession (id, out session)) { - var msg = "The session could not be found."; - - throw new InvalidOperationException (msg); - } - - session.WebSocket.SendAsync (data, completed); - } - - /// - /// Sends the specified data to the client for the specified session - /// asynchronously. - /// - /// - /// This method does not wait for the send to be complete. - /// - /// - /// A that specifies the text data to send. - /// - /// - /// A that specifies the ID of the session. - /// - /// - /// - /// An delegate. - /// - /// - /// It specifies the delegate called when the send is complete. - /// - /// - /// The parameter passed to the delegate is - /// true if the send has successfully done; otherwise, - /// false. - /// - /// - /// if not necessary. - /// - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// could not be UTF-8-encoded. - /// - /// - /// - /// - /// is . - /// - /// - /// -or- - /// - /// - /// is . - /// - /// - /// - /// - /// This method is not available if the session could not be found. - /// - /// - /// -or- - /// - /// - /// This method is not available when the current state of the WebSocket - /// interface is not Open. - /// - /// - public void SendToAsync (string data, string id, Action completed) - { - IWebSocketSession session; - - if (!TryGetSession (id, out session)) { - var msg = "The session could not be found."; - - throw new InvalidOperationException (msg); - } - - session.WebSocket.SendAsync (data, completed); - } - - /// - /// Sends the data from the specified stream instance to the client for - /// the specified session asynchronously. - /// - /// - /// 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. - /// - /// - /// A that specifies the ID of the session. - /// - /// - /// - /// An delegate. - /// - /// - /// It specifies the delegate called when the send is complete. - /// - /// - /// The parameter passed to the delegate is - /// true if the send has successfully done; otherwise, - /// false. - /// - /// - /// if not necessary. - /// - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// cannot be read. - /// - /// - /// -or- - /// - /// - /// is less than 1. - /// - /// - /// -or- - /// - /// - /// No data could be read from . - /// - /// - /// - /// - /// is . - /// - /// - /// -or- - /// - /// - /// is . - /// - /// - /// - /// - /// This method is not available if the session could not be found. - /// - /// - /// -or- - /// - /// - /// This method is not available when the current state of the WebSocket - /// interface is not Open. - /// - /// - public void SendToAsync ( - Stream stream, - int length, - string id, - Action completed - ) - { - IWebSocketSession session; - - if (!TryGetSession (id, out session)) { - var msg = "The session could not be found."; - - throw new InvalidOperationException (msg); - } - - session.WebSocket.SendAsync (stream, length, completed); - } - - /// - /// Cleans up the inactive sessions in the WebSocket service. - /// - public void Sweep () - { - if (_sweeping) { - _log.Trace ("The sweep process is already in progress."); - - return; - } - - lock (_forSweep) { - if (_sweeping) { - _log.Trace ("The sweep process is already in progress."); - - return; - } - - _sweeping = true; - } - - foreach (var id in InactiveIDs) { - if (_state != ServerState.Start) - break; - - lock (_sync) { - if (_state != ServerState.Start) - break; - - IWebSocketSession session; - - if (!_sessions.TryGetValue (id, out session)) - continue; - - var state = session.WebSocket.ReadyState; - - if (state == WebSocketState.Open) { - session.WebSocket.Close (CloseStatusCode.Abnormal); - - continue; - } - - if (state == WebSocketState.Closing) - continue; - - _sessions.Remove (id); - } - } - - lock (_forSweep) - _sweeping = false; - } - - /// - /// Tries to get the specified session instance. - /// - /// - /// true if the get operation succeeds; otherwise, false. - /// - /// - /// A that specifies the ID of the session to get. - /// - /// - /// - /// When this method returns, a instance - /// that holds the session instance. - /// - /// - /// It provides the access to the information in the session. - /// - /// - /// if not found. - /// - /// - /// - /// is an empty string. - /// - /// - /// is . - /// - public bool TryGetSession (string id, out IWebSocketSession session) - { - if (id == null) - throw new ArgumentNullException ("id"); - - if (id.Length == 0) - throw new ArgumentException ("An empty string.", "id"); - - return tryGetSession (id, out session); - } - - #endregion - } -} diff --git a/websocket-sharp/WebSocket.cs b/websocket-sharp/WebSocket.cs deleted file mode 100644 index 3ba985774..000000000 --- a/websocket-sharp/WebSocket.cs +++ /dev/null @@ -1,4576 +0,0 @@ -#region License -/* - * WebSocket.cs - * - * This code is derived from WebSocket.java - * (http://github.com/adamac/Java-WebSocket-client). - * - * The MIT License - * - * Copyright (c) 2009 Adam MacBeth - * Copyright (c) 2010-2026 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -#region Contributors -/* - * Contributors: - * - Frank Razenberg - * - David Wood - * - Liryna - */ -#endregion - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.Diagnostics; -using System.IO; -using System.Net.Security; -using System.Net.Sockets; -using System.Security.Cryptography; -using System.Text; -using System.Threading; -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 static readonly byte[] _emptyBytes; - private bool _enableRedirection; - private Func _executorBeforeClose; - private Func _executorBeforeOpen; - private string _extensions; - 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 Action _handshakeRequestResponder; - private CookieCollection _handshakeResponseCookies; - private NameValueCollection _handshakeResponseHeaders; - private bool _hasExtension; - private bool _hasProtocol; - private bool _ignoreExtensions; - private bool _inContinuation; - private volatile bool _inMessage; - private volatile Logger _log; - private static readonly int _maxRetryCountForConnect; - private Action _message; - private Queue _messageEventQueue; - private bool _noDelay; - private uint _nonceCount; - private string _origin; - private ManualResetEvent _pongReceived; - private bool _preAuth; - private string _protocol; - private string[] _protocols; - private NetworkCredential _proxyCredentials; - private Uri _proxyUri; - private volatile WebSocketState _readyState; - private ManualResetEvent _receivingExited; - private int _retryCountForConnect; - private bool _secure; - private Socket _socket; - private ClientSslConfiguration _sslConfig; - private Stream _stream; - private TcpClient _tcpClient; - private Uri _uri; - private WebHeaderCollection _userHeaders; - private const string _version = "13"; - private TimeSpan _waitTime; - - #endregion - - #region Internal Fields - - /// - /// Represents the length used to determine whether the data should - /// be fragmented in sending. - /// - /// - /// - /// The data will be fragmented if its 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 () - { - _emptyBytes = new byte[0]; - _maxRetryCountForConnect = 10; - - FragmentLength = 1016; - RandomNumber = new RNGCryptoServiceProvider (); - } - - #endregion - - #region Internal Constructors - - // As server - internal WebSocket (HttpListenerWebSocketContext context, string protocol) - { - _context = context; - _protocol = protocol; - - _closeContext = context.Close; - _log = context.Log; - _message = messages; - _secure = context.IsSecureConnection; - _socket = context.Socket; - _stream = context.Stream; - _waitTime = TimeSpan.FromSeconds (1); - - init (); - } - - // As server - internal WebSocket (TcpListenerWebSocketContext context, string protocol) - { - _context = context; - _protocol = protocol; - - _closeContext = context.Close; - _log = context.Log; - _message = messages; - _secure = context.IsSecureConnection; - _socket = context.Socket; - _stream = context.Stream; - _waitTime = TimeSpan.FromSeconds (1); - - init (); - } - - #endregion - - #region Public Constructors - - /// - /// Initializes a new instance of the class with - /// the specified URL and optionally subprotocols. - /// - /// - /// - /// 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 an empty string. - /// - /// - /// -or- - /// - /// - /// is an invalid WebSocket URL string. - /// - /// - /// -or- - /// - /// - /// contains a value that is not a token. - /// - /// - /// -or- - /// - /// - /// contains a value twice. - /// - /// - /// - /// is . - /// - 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; - _hasProtocol = true; - } - - _base64Key = CreateBase64Key (); - _client = true; - _log = new Logger (); - _message = messagec; - _retryCountForConnect = -1; - _secure = _uri.Scheme == "wss"; - _waitTime = TimeSpan.FromSeconds (5); - - init (); - } - - #endregion - - #region Internal Properties - - internal CookieCollection Cookies { - get { - if (_cookies == null) - _cookies = new CookieCollection (); - - return _cookies; - } - } - - // As server - internal Func CustomHandshakeRequestChecker { - get { - return _handshakeRequestChecker; - } - - set { - _handshakeRequestChecker = value; - } - } - - // As server - internal Action CustomHandshakeRequestResponder { - get { - return _handshakeRequestResponder; - } - - set { - _handshakeRequestResponder = value; - } - } - - // As server - internal Func ExecutorBeforeClose { - get { - return _executorBeforeClose; - } - - set { - _executorBeforeClose = value; - } - } - - // As server - internal Func ExecutorBeforeOpen { - get { - return _executorBeforeOpen; - } - - set { - _executorBeforeOpen = value; - } - } - - // As server - internal bool IgnoreExtensions { - get { - return _ignoreExtensions; - } - - set { - _ignoreExtensions = value; - } - } - - internal WebHeaderCollection UserHeaders { - get { - if (_userHeaders == null) { - var state = _client - ? HttpHeaderType.Request - : HttpHeaderType.Response; - - _userHeaders = new WebHeaderCollection (state, false); - } - - return _userHeaders; - } - } - - #endregion - - #region Public Properties - - /// - /// Gets or sets the compression method used to compress a message. - /// - /// - /// - /// One of the enum values. - /// - /// - /// It indicates the compression method used to compress a message. - /// - /// - /// The default value is . - /// - /// - /// - /// - /// The set operation is not available if the interface is not for - /// the client. - /// - /// - /// -or- - /// - /// - /// The set operation is not available when the current state of - /// the interface is neither New nor Closed. - /// - /// - public CompressionMethod Compression { - get { - return _compression; - } - - set { - if (!_client) { - var msg = "The interface is not for the client."; - - throw new InvalidOperationException (msg); - } - - lock (_forState) { - if (!canSet ()) { - var msg = "The current state of the interface is neither New nor Closed."; - - throw new InvalidOperationException (msg); - } - - _compression = 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; - } - } - - /// - /// Gets or sets a value indicating whether the interface emits - /// the message event when it receives a ping. - /// - /// - /// - /// true if the interface emits the message event when - /// it receives a ping; otherwise, false. - /// - /// - /// The default value is false. - /// - /// - /// - /// The set operation is not available when the current state of - /// the interface is neither New nor Closed. - /// - public bool EmitOnPing { - get { - return _emitOnPing; - } - - set { - lock (_forState) { - if (!canSet ()) { - var msg = "The current state of the interface is neither New nor Closed."; - - throw new InvalidOperationException (msg); - } - - _emitOnPing = value; - } - } - } - - /// - /// Gets or sets a value indicating whether the URL redirection for - /// the handshake request is allowed. - /// - /// - /// - /// true if the interface allows the URL redirection for - /// the handshake request; otherwise, false. - /// - /// - /// The default value is false. - /// - /// - /// - /// - /// The set operation is not available if the interface is not for - /// the client. - /// - /// - /// -or- - /// - /// - /// The set operation is not available when the current state of - /// the interface is neither New nor Closed. - /// - /// - public bool EnableRedirection { - get { - return _enableRedirection; - } - - set { - if (!_client) { - var msg = "The interface is not for the client."; - - throw new InvalidOperationException (msg); - } - - lock (_forState) { - if (!canSet ()) { - var msg = "The current state of the interface is neither New nor Closed."; - - throw new InvalidOperationException (msg); - } - - _enableRedirection = value; - } - } - } - - /// - /// Gets the extensions selected by the server. - /// - /// - /// - /// A that represents a list of the extensions - /// negotiated between the client and server. - /// - /// - /// An empty string if not specified or selected. - /// - /// - public string Extensions { - get { - return _extensions ?? String.Empty; - } - } - - /// - /// Gets the HTTP cookies included in the handshake response. - /// - /// - /// - /// A that contains the cookies - /// included in the handshake response if any. - /// - /// - /// if the interface could not receive - /// the handshake response. - /// - /// - /// - /// - /// The get operation is not available if the interface is not for - /// the client. - /// - /// - /// -or- - /// - /// - /// The get operation is not available when the current state of - /// the interface is New or Connecting. - /// - /// - public CookieCollection HandshakeResponseCookies { - get { - if (!_client) { - var msg = "The interface is not for the client."; - - throw new InvalidOperationException (msg); - } - - lock (_forState) { - var canGet = _readyState > WebSocketState.Connecting; - - if (!canGet) { - var msg = "The current state of the interface is New or Connecting."; - - throw new InvalidOperationException (msg); - } - - return _handshakeResponseCookies; - } - } - } - - /// - /// Gets the HTTP headers included in the handshake response. - /// - /// - /// - /// A that contains the headers - /// included in the handshake response. - /// - /// - /// if the interface could not receive - /// the handshake response. - /// - /// - /// - /// - /// The get operation is not available if the interface is not for - /// the client. - /// - /// - /// -or- - /// - /// - /// The get operation is not available when the current state of - /// the interface is New or Connecting. - /// - /// - public NameValueCollection HandshakeResponseHeaders { - get { - if (!_client) { - var msg = "The interface is not for the client."; - - throw new InvalidOperationException (msg); - } - - lock (_forState) { - var canGet = _readyState > WebSocketState.Connecting; - - if (!canGet) { - var msg = "The current state of the interface is New or Connecting."; - - throw new InvalidOperationException (msg); - } - - return _handshakeResponseHeaders; - } - } - } - - /// - /// Gets a value indicating whether the communication is possible. - /// - /// - /// true if the communication is possible; otherwise, false. - /// - public bool IsAlive { - get { - return ping (_emptyBytes); - } - } - - /// - /// Gets a value indicating whether the connection is secure. - /// - /// - /// true if the connection is secure; otherwise, false. - /// - public bool IsSecure { - get { - return _secure; - } - } - - /// - /// Gets the logging function. - /// - /// - /// The default logging level is . - /// - /// - /// A that provides the logging function. - /// - /// - /// The get operation is not available if the interface is not for - /// the client. - /// - public Logger Log { - get { - if (!_client) { - var msg = "The interface is not for the client."; - - throw new InvalidOperationException (msg); - } - - return _log; - } - - internal set { - _log = value; - } - } - - /// - /// Gets or sets a value indicating whether the underlying TCP socket - /// disables a delay when send or receive buffer is not full. - /// - /// - /// - /// true if the delay is disabled; otherwise, false. - /// - /// - /// The default value is false. - /// - /// - /// - /// - /// The set operation is not available when the current state of - /// the interface is neither New nor Closed. - /// - public bool NoDelay { - get { - return _noDelay; - } - - set { - lock (_forState) { - if (!canSet ()) { - var msg = "The current state of the interface is neither New nor Closed."; - - throw new InvalidOperationException (msg); - } - - _noDelay = value; - } - } - } - - /// - /// 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. - /// - /// - /// The interface sends the Origin header if this property has any. - /// - /// - /// - /// - /// A that represents the value of the Origin - /// header to send. - /// - /// - /// The syntax is <scheme>://<host>[:<port>]. - /// - /// - /// The default value is . - /// - /// - /// - /// - /// 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. - /// - /// - /// - /// - /// The set operation is not available if the interface is not for - /// the client. - /// - /// - /// -or- - /// - /// - /// The set operation is not available when the current state of - /// the interface is neither New nor Closed. - /// - /// - public string Origin { - get { - return _origin; - } - - set { - if (!_client) { - var msg = "The interface is not for the client."; - - throw new InvalidOperationException (msg); - } - - var hasValue = !value.IsNullOrEmpty (); - - if (hasValue) { - Uri uri; - - if (!Uri.TryCreate (value, UriKind.Absolute, out uri)) { - var msg = "Not an absolute URI string."; - - throw new ArgumentException (msg, "value"); - } - - if (uri.Segments.Length > 1) { - var msg = "It includes the path segments."; - - throw new ArgumentException (msg, "value"); - } - } - - lock (_forState) { - if (!canSet ()) { - var msg = "The current state of the interface is neither New nor Closed."; - - throw new InvalidOperationException (msg); - } - - _origin = hasValue ? value.TrimEnd ('/') : 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; - } - - internal set { - _protocol = value; - } - } - - /// - /// Gets the current state of the interface. - /// - /// - /// - /// One of the enum values. - /// - /// - /// It indicates the current state of the interface. - /// - /// - /// The default value is . - /// - /// - public WebSocketState ReadyState { - get { - return _readyState; - } - } - - /// - /// Gets the configuration for secure connection. - /// - /// - /// The configuration is used when the interface 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. - /// - /// - /// - /// The get operation is not available if the interface is not for - /// the client. - /// - /// - /// -or- - /// - /// - /// The get operation is not available if the interface does not use - /// a secure connection. - /// - /// - public ClientSslConfiguration SslConfiguration { - get { - if (!_client) { - var msg = "The interface is not for the client."; - - throw new InvalidOperationException (msg); - } - - if (!_secure) { - var msg = "The interface does not use a secure connection."; - - throw new InvalidOperationException (msg); - } - - return getSslConfiguration (); - } - } - - /// - /// Gets the URL to which to connect. - /// - /// - /// - /// A that represents the URL to which to connect. - /// - /// - /// Also it represents the URL requested by the client if the interface - /// is for the server. - /// - /// - public Uri Url { - get { - return _client ? _uri : _context.RequestUri; - } - } - - /// - /// Gets or sets the time to wait for the response to the ping or close. - /// - /// - /// - /// A that represents the time to wait for - /// the response. - /// - /// - /// The default value is the same as 5 seconds if the interface is - /// for the client. - /// - /// - /// - /// The value specified for a set operation is zero or less. - /// - /// - /// The set operation is not available when the current state of - /// the interface is neither New nor Closed. - /// - public TimeSpan WaitTime { - get { - return _waitTime; - } - - set { - if (value <= TimeSpan.Zero) { - var msg = "Zero or less."; - - throw new ArgumentOutOfRangeException ("value", msg); - } - - lock (_forState) { - if (!canSet ()) { - var msg = "The current state of the interface is neither New nor Closed."; - - throw new InvalidOperationException (msg); - } - - _waitTime = value; - } - } - } - - #endregion - - #region Public Events - - /// - /// Occurs when the connection has been closed. - /// - public event EventHandler OnClose; - - /// - /// Occurs when the interface gets an error. - /// - public event EventHandler OnError; - - /// - /// Occurs when the interface receives a message. - /// - public event EventHandler OnMessage; - - /// - /// Occurs when the connection has been established. - /// - public event EventHandler OnOpen; - - #endregion - - #region Private Methods - - private void abort (string reason, Exception exception) - { - var code = exception is WebSocketException - ? ((WebSocketException) exception).Code - : (ushort) 1006; - - abort (code, reason); - } - - private void abort (ushort code, string reason) - { - var data = new PayloadData (code, reason); - - close (data, false, false); - } - - // As server - private bool accept () - { - lock (_forState) { - if (_readyState == WebSocketState.Open) { - _log.Trace ("The connection has already been established."); - - return false; - } - - if (_readyState == WebSocketState.Closing) { - _log.Error ("The close process is in progress."); - - error ("An error has occurred before accepting.", null); - - return false; - } - - if (_readyState == WebSocketState.Closed) { - _log.Error ("The connection has been closed."); - - error ("An error has occurred before accepting.", null); - - return false; - } - - _readyState = WebSocketState.Connecting; - - var accepted = false; - - try { - accepted = acceptHandshake (); - } - catch (Exception ex) { - _log.Fatal (ex.Message); - _log.Debug (ex.ToString ()); - - abort (1011, "An exception has occurred while accepting."); - } - - if (!accepted) - return false; - - _readyState = WebSocketState.Open; - - return true; - } - } - - // As server - private bool acceptHandshake () - { - string msg; - - if (!checkHandshakeRequest (_context, out msg)) { - _log.Error (msg); - _log.Debug (_context.ToString ()); - - refuseHandshake (1002, "A handshake error has occurred."); - - return false; - } - - if (!customCheckHandshakeRequest (_context, out msg)) { - _log.Error (msg); - _log.Debug (_context.ToString ()); - - refuseHandshake (1002, "A handshake error has occurred."); - - return false; - } - - _base64Key = _context.Headers["Sec-WebSocket-Key"]; - - if (_protocol != null) { - var matched = _context - .SecWebSocketProtocols - .Contains (p => p == _protocol); - - if (!matched) - _protocol = null; - } - - if (!_ignoreExtensions) { - var val = _context.Headers["Sec-WebSocket-Extensions"]; - - processSecWebSocketExtensionsClientHeader (val); - } - - customRespondToHandshakeRequest (_context); - - if (_noDelay) - _socket.NoDelay = true; - - createHandshakeResponse ().WriteTo (_stream); - - return true; - } - - private bool canSet () - { - return _readyState == WebSocketState.New - || _readyState == WebSocketState.Closed; - } - - // As server - private bool checkHandshakeRequest ( - WebSocketContext context, - out string message - ) - { - message = null; - - if (!context.IsWebSocketRequest) { - message = "Not a WebSocket handshake request."; - - return false; - } - - var headers = context.Headers; - - var key = headers["Sec-WebSocket-Key"]; - - if (key == null) { - message = "The Sec-WebSocket-Key header is non-existent."; - - return false; - } - - if (key.Length == 0) { - message = "The Sec-WebSocket-Key header is invalid."; - - return false; - } - - var ver = headers["Sec-WebSocket-Version"]; - - if (ver == null) { - message = "The Sec-WebSocket-Version header is non-existent."; - - return false; - } - - if (ver != _version) { - message = "The Sec-WebSocket-Version header is invalid."; - - return false; - } - - var subps = headers["Sec-WebSocket-Protocol"]; - - if (subps != null) { - if (subps.Length == 0) { - message = "The Sec-WebSocket-Protocol header is invalid."; - - return false; - } - } - - if (!_ignoreExtensions) { - var exts = headers["Sec-WebSocket-Extensions"]; - - if (exts != null) { - if (exts.Length == 0) { - message = "The Sec-WebSocket-Extensions header is invalid."; - - return false; - } - } - } - - return true; - } - - // As client - private bool checkHandshakeResponse ( - HttpResponse response, - out string message - ) - { - message = null; - - if (response.IsRedirect) { - message = "The redirection is indicated."; - - return false; - } - - if (response.IsUnauthorized) { - message = "The authentication is required."; - - return false; - } - - if (!response.IsWebSocketResponse) { - message = "Not a WebSocket handshake response."; - - return false; - } - - var headers = response.Headers; - - var key = headers["Sec-WebSocket-Accept"]; - - if (key == null) { - message = "The Sec-WebSocket-Accept header is non-existent."; - - return false; - } - - if (key != CreateResponseKey (_base64Key)) { - message = "The Sec-WebSocket-Accept header is invalid."; - - return false; - } - - var ver = headers["Sec-WebSocket-Version"]; - - if (ver != null) { - if (ver != _version) { - message = "The Sec-WebSocket-Version header is invalid."; - - return false; - } - } - - var subp = headers["Sec-WebSocket-Protocol"]; - - if (subp == null) { - if (_hasProtocol) { - message = "The Sec-WebSocket-Protocol header is non-existent."; - - return false; - } - } - else { - var isValid = _hasProtocol - && subp.Length > 0 - && _protocols.Contains (p => p == subp); - - if (!isValid) { - message = "The Sec-WebSocket-Protocol header is invalid."; - - return false; - } - } - - var exts = headers["Sec-WebSocket-Extensions"]; - - if (exts != null) { - if (!validateSecWebSocketExtensionsServerHeader (exts)) { - message = "The Sec-WebSocket-Extensions header is invalid."; - - return false; - } - } - - return true; - } - - private static bool checkProtocols (string[] protocols, out string message) - { - message = null; - - Func cond = p => p.IsNullOrEmpty () || !p.IsToken (); - - if (protocols.Contains (cond)) { - message = "It contains a value that is not a token."; - - return false; - } - - if (protocols.ContainsTwice ()) { - message = "It contains a value twice."; - - return false; - } - - return true; - } - - // As client - private bool checkProxyConnectResponse ( - HttpResponse response, - out string message - ) - { - message = null; - - if (response.IsProxyAuthenticationRequired) { - message = "The proxy authentication is required."; - - return false; - } - - if (!response.IsSuccess) { - message = "The proxy has failed a connection to the requested URL."; - - return false; - } - - return true; - } - - private bool checkReceivedFrame (WebSocketFrame frame, out string message) - { - message = null; - - if (frame.IsMasked) { - if (_client) { - message = "A frame from the server is masked."; - - return false; - } - } - else { - if (!_client) { - message = "A frame from a client is not masked."; - - return false; - } - } - - if (frame.IsCompressed) { - if (_compression == CompressionMethod.None) { - message = "A frame is compressed without any agreement for it."; - - return false; - } - - if (!frame.IsData) { - message = "A non data frame is compressed."; - - return false; - } - } - - if (frame.IsData) { - if (_inContinuation) { - message = "A data frame was received while receiving continuation frames."; - - return false; - } - } - - if (frame.IsControl) { - if (frame.Fin == Fin.More) { - message = "A control frame is fragmented."; - - return false; - } - - if (frame.PayloadLength > 125) { - message = "The payload length of a control frame is greater than 125."; - - 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; - } - - private void close (ushort code, string reason) - { - if (_readyState == WebSocketState.Closing) { - _log.Trace ("The close process is already in progress."); - - return; - } - - if (_readyState == WebSocketState.Closed) { - _log.Trace ("The connection has already been closed."); - - return; - } - - if (code == 1005) { - close (PayloadData.Empty, true, false); - - return; - } - - var data = new PayloadData (code, reason); - var send = !code.IsReservedStatusCode (); - - close (data, send, false); - } - - private void close (PayloadData payloadData, bool send, bool received) - { - lock (_forState) { - if (_readyState == WebSocketState.Closing) { - _log.Trace ("The close process is already in progress."); - - return; - } - - if (_readyState == WebSocketState.Closed) { - _log.Trace ("The connection has already been closed."); - - return; - } - - send = send && _readyState == WebSocketState.Open; - - _readyState = WebSocketState.Closing; - } - - _log.Trace ("Begin closing the connection."); - - var res = closeHandshake (payloadData, send, received); - - releaseResources (); - - _log.Trace ("End closing the connection."); - - _readyState = WebSocketState.Closed; - - var canEmit = executeBeforeClose (); - - if (!canEmit) - return; - - var e = new CloseEventArgs (payloadData, res); - - try { - OnClose.Emit (this, e); - } - catch (Exception ex) { - _log.Error (ex.Message); - _log.Debug (ex.ToString ()); - } - } - - private void closeAsync (ushort code, string reason) - { - if (_readyState == WebSocketState.Closing) { - _log.Trace ("The close process is already in progress."); - - return; - } - - if (_readyState == WebSocketState.Closed) { - _log.Trace ("The connection has already been closed."); - - return; - } - - if (code == 1005) { - closeAsync (PayloadData.Empty, true, false); - - return; - } - - var data = new PayloadData (code, reason); - var send = !code.IsReservedStatusCode (); - - closeAsync (data, send, false); - } - - private void closeAsync (PayloadData payloadData, bool send, bool received) - { - Action closer = close; - - closer.BeginInvoke ( - payloadData, - send, - received, - ar => closer.EndInvoke (ar), - null - ); - } - - private bool closeHandshake ( - PayloadData payloadData, - bool send, - bool received - ) - { - var sent = false; - - if (send) { - var frame = WebSocketFrame.CreateCloseFrame (payloadData, _client); - var bytes = frame.ToArray (); - - sent = sendBytes (bytes); - - if (_client) - frame.Unmask (); - } - - var wait = !received && sent && _receivingExited != null; - - if (wait) - received = _receivingExited.WaitOne (_waitTime); - - var ret = sent && received; - - var msg = String.Format ( - "The closing was clean? {0} (sent: {1} received: {2})", - ret, - sent, - received - ); - - _log.Debug (msg); - - return ret; - } - - // As client - private bool connect () - { - if (_readyState == WebSocketState.Connecting) { - _log.Trace ("The connect process is in progress."); - - return false; - } - - lock (_forState) { - if (_readyState == WebSocketState.Open) { - _log.Trace ("The connection has already been established."); - - return false; - } - - if (_readyState == WebSocketState.Closing) { - _log.Error ("The close process is in progress."); - - error ("An error has occurred before connecting.", null); - - return false; - } - - if (_retryCountForConnect >= _maxRetryCountForConnect) { - _log.Error ("An opportunity for reconnecting has been lost."); - - error ("An error has occurred before connecting.", null); - - return false; - } - - if (_readyState == WebSocketState.Closed) - initr (); - - _retryCountForConnect++; - - _readyState = WebSocketState.Connecting; - - var done = false; - - try { - done = doHandshake (); - } - catch (Exception ex) { - _log.Fatal (ex.Message); - _log.Debug (ex.ToString ()); - - abort ("An exception has occurred while connecting.", ex); - } - - if (!done) - return false; - - _retryCountForConnect = -1; - - _readyState = WebSocketState.Open; - - return true; - } - } - - // As client - private AuthenticationResponse createAuthenticationResponse () - { - if (_credentials == null) - return null; - - if (_authChallenge == null) - return _preAuth ? new AuthenticationResponse (_credentials) : null; - - var ret = new AuthenticationResponse ( - _authChallenge, - _credentials, - _nonceCount - ); - - _nonceCount = ret.NonceCount; - - return ret; - } - - // As client - private string createExtensions () - { - var buff = new StringBuilder (80); - - if (_compression != CompressionMethod.None) { - var str = _compression.ToExtensionString ( - "server_no_context_takeover", - "client_no_context_takeover" - ); - - buff.AppendFormat ("{0}, ", str); - } - - var len = buff.Length; - - if (len <= 2) - return null; - - buff.Length = len - 2; - - return buff.ToString (); - } - - // As server - private HttpResponse createHandshakeFailureResponse () - { - var ret = HttpResponse.CreateCloseResponse (HttpStatusCode.BadRequest); - - ret.Headers["Sec-WebSocket-Version"] = _version; - - return ret; - } - - // As client - private HttpRequest createHandshakeRequest () - { - var ret = HttpRequest.CreateWebSocketHandshakeRequest (_uri); - - var headers = ret.Headers; - - headers["Sec-WebSocket-Key"] = _base64Key; - headers["Sec-WebSocket-Version"] = _version; - - if (!_origin.IsNullOrEmpty ()) - headers["Origin"] = _origin; - - if (_hasProtocol) - headers["Sec-WebSocket-Protocol"] = _protocols.ToString (", "); - - var exts = createExtensions (); - - _hasExtension = exts != null; - - if (_hasExtension) - headers["Sec-WebSocket-Extensions"] = exts; - - var ares = createAuthenticationResponse (); - - if (ares != null) - headers["Authorization"] = ares.ToString (); - - var hasUserHeader = _userHeaders != null && _userHeaders.Count > 0; - - if (hasUserHeader) - headers.Add (_userHeaders); - - var hasCookie = _cookies != null && _cookies.Count > 0; - - if (hasCookie) - ret.SetCookies (_cookies); - - return ret; - } - - // As server - private HttpResponse createHandshakeResponse () - { - var ret = HttpResponse.CreateWebSocketHandshakeResponse (); - - var headers = ret.Headers; - - headers["Sec-WebSocket-Accept"] = CreateResponseKey (_base64Key); - - if (_protocol != null) - headers["Sec-WebSocket-Protocol"] = _protocol; - - if (_extensions != null) - headers["Sec-WebSocket-Extensions"] = _extensions; - - var hasUserHeader = _userHeaders != null && _userHeaders.Count > 0; - - if (hasUserHeader) - headers.Add (_userHeaders); - - var hasCookie = _cookies != null && _cookies.Count > 0; - - if (hasCookie) - ret.SetCookies (_cookies); - - return ret; - } - - // As client - private TcpClient createTcpClient (string hostname, int port) - { - var ret = new TcpClient (hostname, port); - - if (_noDelay) - ret.NoDelay = true; - - return ret; - } - - // As server - private bool customCheckHandshakeRequest ( - WebSocketContext context, - out string message - ) - { - message = null; - - if (_handshakeRequestChecker == null) - return true; - - message = _handshakeRequestChecker (context); - - return message == null; - } - - // As server - private void customRespondToHandshakeRequest (WebSocketContext context) - { - if (_handshakeRequestResponder == null) - return; - - _handshakeRequestResponder (context); - } - - private MessageEventArgs dequeueFromMessageEventQueue () - { - lock (_forMessageEventQueue) { - return _messageEventQueue.Count > 0 - ? _messageEventQueue.Dequeue () - : null; - } - } - - // As client - private bool doHandshake () - { - setClientStream (); - - var res = sendHandshakeRequest (); - - _log.Debug (res.ToString ()); - - _handshakeResponseHeaders = res.Headers; - _handshakeResponseCookies = res.Cookies; - - string msg; - - if (!checkHandshakeResponse (res, out msg)) { - _log.Error (msg); - - abort (1002, "A handshake error has occurred."); - - return false; - } - - if (_hasProtocol) - _protocol = _handshakeResponseHeaders["Sec-WebSocket-Protocol"]; - - if (_hasExtension) { - var exts = _handshakeResponseHeaders["Sec-WebSocket-Extensions"]; - - if (exts != null) - _extensions = exts; - else - _compression = CompressionMethod.None; - } - - if (_handshakeResponseCookies.Count > 0) - Cookies.SetOrRemove (_handshakeResponseCookies); - - return true; - } - - private void enqueueToMessageEventQueue (MessageEventArgs e) - { - lock (_forMessageEventQueue) - _messageEventQueue.Enqueue (e); - } - - private void error (string message, Exception exception) - { - var e = new ErrorEventArgs (message, exception); - - try { - OnError.Emit (this, e); - } - catch (Exception ex) { - _log.Error (ex.Message); - _log.Debug (ex.ToString ()); - } - } - - private bool executeBeforeClose () - { - return _executorBeforeClose != null ? _executorBeforeClose () : true; - } - - private bool executeBeforeOpen () - { - return _executorBeforeOpen != null ? _executorBeforeOpen () : true; - } - - private ClientSslConfiguration getSslConfiguration () - { - if (_sslConfig == null) - _sslConfig = new ClientSslConfiguration (_uri.DnsSafeHost); - - return _sslConfig; - } - - private void init () - { - _compression = CompressionMethod.None; - _forPing = new object (); - _forSend = new object (); - _forState = new object (); - _messageEventQueue = new Queue (); - _forMessageEventQueue = ((ICollection) _messageEventQueue).SyncRoot; - _readyState = WebSocketState.New; - } - - // As client - private void initr () - { - _handshakeResponseCookies = null; - _handshakeResponseHeaders = null; - } - - private void message () - { - MessageEventArgs e = null; - - lock (_forMessageEventQueue) { - if (_inMessage) - return; - - if (_messageEventQueue.Count == 0) - return; - - if (_readyState != WebSocketState.Open) - return; - - e = _messageEventQueue.Dequeue (); - - _inMessage = true; - } - - _message (e); - } - - private void messagec (MessageEventArgs e) - { - do { - try { - OnMessage.Emit (this, e); - } - catch (Exception ex) { - _log.Error (ex.Message); - _log.Debug (ex.ToString ()); - - error ("An exception has occurred during an OnMessage event.", ex); - } - - lock (_forMessageEventQueue) { - if (_messageEventQueue.Count == 0) { - _inMessage = false; - - break; - } - - if (_readyState != WebSocketState.Open) { - _inMessage = false; - - break; - } - - e = _messageEventQueue.Dequeue (); - } - } - while (true); - } - - private void messages (MessageEventArgs e) - { - try { - OnMessage.Emit (this, e); - } - catch (Exception ex) { - _log.Error (ex.Message); - _log.Debug (ex.ToString ()); - - error ("An exception has occurred during an OnMessage event.", ex); - } - - lock (_forMessageEventQueue) { - if (_messageEventQueue.Count == 0) { - _inMessage = false; - - return; - } - - if (_readyState != WebSocketState.Open) { - _inMessage = false; - - return; - } - - e = _messageEventQueue.Dequeue (); - } - - ThreadPool.QueueUserWorkItem (state => messages (e)); - } - - private void open () - { - _inMessage = true; - - startReceiving (); - - var canEmit = executeBeforeOpen (); - - try { - if (canEmit) - OnOpen.Emit (this, EventArgs.Empty); - } - catch (Exception ex) { - _log.Error (ex.Message); - _log.Debug (ex.ToString ()); - - error ("An exception has occurred during the OnOpen event.", ex); - } - - MessageEventArgs e = null; - - lock (_forMessageEventQueue) { - if (_messageEventQueue.Count == 0) { - _inMessage = false; - - return; - } - - if (_readyState != WebSocketState.Open) { - _inMessage = false; - - return; - } - - e = _messageEventQueue.Dequeue (); - } - - _message.BeginInvoke (e, ar => _message.EndInvoke (ar), null); - } - - private bool ping (byte[] data) - { - if (_readyState != WebSocketState.Open) - return false; - - var received = _pongReceived; - - if (received == null) - return false; - - lock (_forPing) { - try { - received.Reset (); - - var sent = send (Fin.Final, Opcode.Ping, data, false); - - if (!sent) - return false; - - return received.WaitOne (_waitTime); - } - catch (ObjectDisposedException) { - return false; - } - } - } - - private bool processCloseFrame (WebSocketFrame frame) - { - var data = frame.PayloadData; - var send = !data.HasReservedCode; - - close (data, send, true); - - return false; - } - - private bool processDataFrame (WebSocketFrame frame) - { - var e = frame.IsCompressed - ? new MessageEventArgs ( - frame.Opcode, - frame.PayloadData.ApplicationData.Decompress (_compression) - ) - : new MessageEventArgs (frame); - - enqueueToMessageEventQueue (e); - - return true; - } - - private bool processFragmentFrame (WebSocketFrame frame) - { - if (!_inContinuation) { - if (frame.IsContinuation) - return true; - - _fragmentsOpcode = frame.Opcode; - _fragmentsCompressed = frame.IsCompressed; - _fragmentsBuffer = new MemoryStream (); - _inContinuation = true; - } - - _fragmentsBuffer.WriteBytes (frame.PayloadData.ApplicationData, 1024); - - if (frame.IsFinal) { - using (_fragmentsBuffer) { - var data = _fragmentsCompressed - ? _fragmentsBuffer.DecompressToArray (_compression) - : _fragmentsBuffer.ToArray (); - - var e = new MessageEventArgs (_fragmentsOpcode, data); - - enqueueToMessageEventQueue (e); - } - - _fragmentsBuffer = null; - _inContinuation = false; - } - - return true; - } - - private bool processPingFrame (WebSocketFrame frame) - { - _log.Trace ("A ping was received."); - - var pong = WebSocketFrame.CreatePongFrame (frame.PayloadData, _client); - - lock (_forState) { - if (_readyState != WebSocketState.Open) { - _log.Trace ("A pong to this ping cannot be sent."); - - return true; - } - - var bytes = pong.ToArray (); - var sent = sendBytes (bytes); - - if (!sent) - return false; - } - - _log.Trace ("A pong to this ping has been sent."); - - if (_emitOnPing) { - if (_client) - pong.Unmask (); - - var e = new MessageEventArgs (frame); - - enqueueToMessageEventQueue (e); - } - - return true; - } - - private bool processPongFrame (WebSocketFrame frame) - { - _log.Trace ("A pong was received."); - - try { - _pongReceived.Set (); - } - catch (NullReferenceException) { - return false; - } - catch (ObjectDisposedException) { - return false; - } - - _log.Trace ("It has been signaled."); - - return true; - } - - private bool processReceivedFrame (WebSocketFrame frame) - { - string msg; - - if (!checkReceivedFrame (frame, out msg)) { - _log.Error (msg); - _log.Debug (frame.ToString (false)); - - abort (1002, "An error has occurred while receiving."); - - return false; - } - - 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); - } - - // As server - private void processSecWebSocketExtensionsClientHeader (string value) - { - if (value == null) - return; - - var buff = new StringBuilder (80); - - var compRequested = false; - - foreach (var elm in value.SplitHeaderValue (',')) { - var ext = elm.Trim (); - - if (ext.Length == 0) - continue; - - if (!compRequested) { - if (ext.IsCompressionExtension (CompressionMethod.Deflate)) { - _compression = CompressionMethod.Deflate; - - var str = _compression.ToExtensionString ( - "client_no_context_takeover", - "server_no_context_takeover" - ); - - buff.AppendFormat ("{0}, ", str); - - compRequested = true; - } - } - } - - var len = buff.Length; - - if (len <= 2) - return; - - buff.Length = len - 2; - - _extensions = buff.ToString (); - } - - private bool processUnsupportedFrame (WebSocketFrame frame) - { - _log.Fatal ("An unsupported frame was received."); - _log.Debug (frame.ToString (false)); - - abort (1003, "There is no way to handle it."); - - return false; - } - - // As server - private void refuseHandshake (ushort code, string reason) - { - createHandshakeFailureResponse ().WriteTo (_stream); - - abort (code, reason); - } - - // As client - private void releaseClientResources () - { - if (_stream != null) { - _stream.Dispose (); - - _stream = null; - } - - if (_tcpClient != null) { - _tcpClient.Close (); - - _tcpClient = null; - } - } - - 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; - } - } - - private void releaseResources () - { - if (_client) - releaseClientResources (); - else - releaseServerResources (); - - releaseCommonResources (); - } - - // As server - private void releaseServerResources () - { - if (_closeContext != null) { - _closeContext (); - - _closeContext = null; - } - - _stream = null; - _context = null; - } - - private bool send (byte[] rawFrame) - { - lock (_forState) { - if (_readyState != WebSocketState.Open) { - _log.Error ("The current state of the interface is not Open."); - - return false; - } - - return sendBytes (rawFrame); - } - } - - private bool send (Opcode opcode, Stream sourceStream) - { - lock (_forSend) { - var dataStream = sourceStream; - var compressed = false; - var sent = false; - - try { - if (_compression != CompressionMethod.None) { - dataStream = sourceStream.Compress (_compression); - compressed = true; - } - - sent = send (opcode, dataStream, compressed); - - if (!sent) - error ("A send has failed.", null); - } - catch (Exception ex) { - _log.Error (ex.Message); - _log.Debug (ex.ToString ()); - - error ("An exception has occurred during a send.", ex); - } - finally { - if (compressed) - dataStream.Dispose (); - - sourceStream.Dispose (); - } - - return sent; - } - } - - private bool send (Opcode opcode, Stream dataStream, bool compressed) - { - var len = dataStream.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 dataStream.Read (buff, 0, rem) == rem - && send (Fin.Final, opcode, buff, compressed); - } - - if (quo == 1 && rem == 0) { - buff = new byte[FragmentLength]; - - return dataStream.Read (buff, 0, FragmentLength) == FragmentLength - && send (Fin.Final, opcode, buff, compressed); - } - - /* Send fragments */ - - // Begin - - buff = new byte[FragmentLength]; - - var sent = dataStream.Read (buff, 0, FragmentLength) == FragmentLength - && send (Fin.More, opcode, buff, compressed); - - if (!sent) - return false; - - // Continue - - var n = rem == 0 ? quo - 2 : quo - 1; - - for (long i = 0; i < n; i++) { - sent = dataStream.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 dataStream.Read (buff, 0, rem) == rem - && send (Fin.Final, Opcode.Cont, buff, false); - } - - private bool send (Fin fin, Opcode opcode, byte[] data, bool compressed) - { - var frame = new WebSocketFrame (fin, opcode, data, compressed, _client); - var rawFrame = frame.ToArray (); - - return send (rawFrame); - } - - private void sendAsync ( - Opcode opcode, - Stream sourceStream, - Action completed - ) - { - Func sender = send; - - sender.BeginInvoke ( - opcode, - sourceStream, - ar => { - try { - var sent = sender.EndInvoke (ar); - - if (completed != null) - completed (sent); - } - catch (Exception ex) { - _log.Error (ex.Message); - _log.Debug (ex.ToString ()); - - error ( - "An exception has occurred during the callback for an async send.", - ex - ); - } - }, - null - ); - } - - private bool sendBytes (byte[] bytes) - { - try { - _stream.Write (bytes, 0, bytes.Length); - } - catch (Exception ex) { - _log.Error (ex.Message); - _log.Debug (ex.ToString ()); - - return false; - } - - return true; - } - - // As client - private HttpResponse sendHandshakeRequest () - { - var req = createHandshakeRequest (); - - _log.Debug (req.ToString ()); - - var timeout = 90000; - var res = req.GetResponse (_stream, timeout); - - if (res.IsUnauthorized) { - var val = res.Headers["WWW-Authenticate"]; - - if (val.IsNullOrEmpty ()) { - _log.Debug ("No authentication challenge is specified."); - - return res; - } - - var achal = AuthenticationChallenge.Parse (val); - - if (achal == null) { - _log.Debug ("An invalid authentication challenge is specified."); - - return res; - } - - _authChallenge = achal; - - if (_credentials == null) - return res; - - var ares = new AuthenticationResponse ( - _authChallenge, - _credentials, - _nonceCount - ); - - _nonceCount = ares.NonceCount; - - req.Headers["Authorization"] = ares.ToString (); - - if (res.CloseConnection) { - releaseClientResources (); - setClientStream (); - } - - _log.Debug (req.ToString ()); - - timeout = 15000; - res = req.GetResponse (_stream, timeout); - } - - if (res.IsRedirect) { - if (!_enableRedirection) - return res; - - var val = res.Headers["Location"]; - - if (val.IsNullOrEmpty ()) { - _log.Debug ("No URL to redirect is located."); - - return res; - } - - Uri uri; - string msg; - - if (!val.TryCreateWebSocketUri (out uri, out msg)) { - _log.Debug ("An invalid URL to redirect is located."); - - return res; - } - - releaseClientResources (); - - _uri = uri; - _secure = uri.Scheme == "wss"; - - setClientStream (); - - return sendHandshakeRequest (); - } - - return res; - } - - // As client - private HttpResponse sendProxyConnectRequest () - { - var req = HttpRequest.CreateConnectRequest (_uri); - - var timeout = 90000; - var res = req.GetResponse (_stream, timeout); - - if (res.IsProxyAuthenticationRequired) { - if (_proxyCredentials == null) - return res; - - var val = res.Headers["Proxy-Authenticate"]; - - if (val.IsNullOrEmpty ()) { - _log.Debug ("No proxy authentication challenge is specified."); - - return res; - } - - var achal = AuthenticationChallenge.Parse (val); - - if (achal == null) { - _log.Debug ("An invalid proxy authentication challenge is specified."); - - return res; - } - - var ares = new AuthenticationResponse (achal, _proxyCredentials, 0); - - req.Headers["Proxy-Authorization"] = ares.ToString (); - - if (res.CloseConnection) { - releaseClientResources (); - - _tcpClient = createTcpClient (_proxyUri.DnsSafeHost, _proxyUri.Port); - _stream = _tcpClient.GetStream (); - } - - timeout = 15000; - res = req.GetResponse (_stream, timeout); - } - - return res; - } - - // As client - private void setClientStream () - { - if (_proxyUri != null) { - _tcpClient = createTcpClient (_proxyUri.DnsSafeHost, _proxyUri.Port); - _stream = _tcpClient.GetStream (); - - var res = sendProxyConnectRequest (); - - string msg; - - if (!checkProxyConnectResponse (res, out msg)) - throw new WebSocketException (msg); - } - else { - _tcpClient = createTcpClient (_uri.DnsSafeHost, _uri.Port); - _stream = _tcpClient.GetStream (); - } - - if (_secure) { - var conf = getSslConfiguration (); - var host = conf.TargetHost; - - if (host != _uri.DnsSafeHost) { - var msg = "An invalid host name is specified."; - - throw new WebSocketException ( - CloseStatusCode.TlsHandshakeFailure, - msg - ); - } - - 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 - ); - } - } - } - - 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 => { - var doNext = processReceivedFrame (frame) - && _readyState != WebSocketState.Closed; - - if (!doNext) { - var exited = _receivingExited; - - if (exited != null) - exited.Set (); - - return; - } - - receive (); - - if (_inMessage) - return; - - message (); - }, - ex => { - _log.Fatal (ex.Message); - _log.Debug (ex.ToString ()); - - abort ("An exception has occurred while receiving.", ex); - } - ); - - receive (); - } - - // As client - private bool validateSecWebSocketExtensionsServerHeader (string value) - { - if (!_hasExtension) - return false; - - if (value.Length == 0) - return false; - - var compRequested = _compression != CompressionMethod.None; - - foreach (var elm in value.SplitHeaderValue (',')) { - var ext = elm.Trim (); - - if (compRequested && ext.IsCompressionExtension (_compression)) { - var param1 = "server_no_context_takeover"; - var param2 = "client_no_context_takeover"; - - if (!ext.Contains (param1)) { - // The server did not send back "server_no_context_takeover". - - return false; - } - - var name = _compression.ToExtensionString (); - - var isInvalid = ext.SplitHeaderValue (';').Contains ( - t => { - t = t.Trim (); - - var isValid = t == name - || t == param1 - || t == param2; - - return !isValid; - } - ); - - if (isInvalid) - return false; - - compRequested = false; - } - else { - return false; - } - } - - return true; - } - - #endregion - - #region Internal Methods - - // As server - internal void Accept () - { - var accepted = accept (); - - if (!accepted) - return; - - open (); - } - - // As server - internal void AcceptAsync () - { - Func acceptor = accept; - - acceptor.BeginInvoke ( - ar => { - var accepted = acceptor.EndInvoke (ar); - - if (!accepted) - return; - - open (); - }, - null - ); - } - - // As server - internal void Close (PayloadData payloadData, byte[] rawFrame) - { - lock (_forState) { - if (_readyState == WebSocketState.Closing) { - _log.Trace ("The close process is already in progress."); - - return; - } - - if (_readyState == WebSocketState.Closed) { - _log.Trace ("The connection has already been closed."); - - return; - } - - _readyState = WebSocketState.Closing; - } - - _log.Trace ("Begin closing the connection."); - - var sent = rawFrame != null && sendBytes (rawFrame); - var received = sent && _receivingExited != null - ? _receivingExited.WaitOne (_waitTime) - : false; - - var res = sent && received; - - var msg = String.Format ( - "The closing was clean? {0} (sent: {1} received: {2})", - res, - sent, - received - ); - - _log.Debug (msg); - - releaseServerResources (); - releaseCommonResources (); - - _log.Trace ("End closing the connection."); - - _readyState = WebSocketState.Closed; - - var canEmit = executeBeforeClose (); - - if (!canEmit) - return; - - var e = new CloseEventArgs (payloadData, res); - - try { - OnClose.Emit (this, e); - } - catch (Exception ex) { - _log.Error (ex.Message); - _log.Debug (ex.ToString ()); - } - } - - // As client - internal static string CreateBase64Key () - { - var key = new byte[16]; - - RandomNumber.GetBytes (key); - - return Convert.ToBase64String (key); - } - - internal static string CreateResponseKey (string base64Key) - { - SHA1 sha1 = new SHA1CryptoServiceProvider (); - - var src = base64Key + _guid; - var bytes = src.GetUTF8EncodedBytes (); - var key = sha1.ComputeHash (bytes); - - return Convert.ToBase64String (key); - } - - // As server - internal bool Ping (byte[] rawFrame) - { - if (_readyState != WebSocketState.Open) - return false; - - var received = _pongReceived; - - if (received == null) - return false; - - lock (_forPing) { - try { - received.Reset (); - - var sent = send (rawFrame); - - if (!sent) - return false; - - return received.WaitOne (_waitTime); - } - catch (ObjectDisposedException) { - return false; - } - } - } - - // As server - internal void Send ( - Opcode opcode, - byte[] data, - Dictionary cache - ) - { - lock (_forSend) { - 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); - } - - send (found); - } - } - - // As server - internal void Send ( - Opcode opcode, - Stream sourceStream, - Dictionary cache - ) - { - lock (_forSend) { - Stream found; - - if (!cache.TryGetValue (_compression, out found)) { - found = sourceStream.Compress (_compression); - - cache.Add (_compression, found); - } - else { - found.Position = 0; - } - - send (opcode, found, _compression != CompressionMethod.None); - } - } - - #endregion - - #region Public Methods - - /// - /// Closes the connection. - /// - /// - /// This method does nothing if the current state of the interface is - /// Closing or Closed. - /// - public void Close () - { - close (1005, String.Empty); - } - - /// - /// Closes the connection with the specified status code. - /// - /// - /// This method does nothing if the current state of the interface is - /// Closing or Closed. - /// - /// - /// - /// A that specifies the status code indicating - /// the reason for the close. - /// - /// - /// The status codes are defined in - /// - /// Section 7.4 of RFC 6455. - /// - /// - /// - /// - /// is 1011 (server error). - /// It cannot be used by a client. - /// - /// - /// -or- - /// - /// - /// is 1010 (mandatory extension). - /// It cannot be used by a server. - /// - /// - /// - /// is less than 1000 or greater than 4999. - /// - public void Close (ushort code) - { - Close (code, String.Empty); - } - - /// - /// Closes the connection with the specified status code. - /// - /// - /// This method does nothing if the current state of the interface is - /// Closing or Closed. - /// - /// - /// - /// One of the enum values. - /// - /// - /// It specifies the status code indicating the reason for the close. - /// - /// - /// - /// - /// is an undefined enum value. - /// - /// - /// -or- - /// - /// - /// is . - /// It cannot be used by a client. - /// - /// - /// -or- - /// - /// - /// is . - /// It cannot be used by a server. - /// - /// - public void Close (CloseStatusCode code) - { - Close (code, String.Empty); - } - - /// - /// Closes the connection with the specified status code and reason. - /// - /// - /// This method does nothing if the current state of the interface is - /// Closing or Closed. - /// - /// - /// - /// A that specifies the status code indicating - /// the reason for the close. - /// - /// - /// The status codes are defined in - /// - /// Section 7.4 of RFC 6455. - /// - /// - /// - /// - /// A that specifies the reason for the close. - /// - /// - /// Its size must be 123 bytes or less in UTF-8. - /// - /// - /// - /// - /// is 1011 (server error). - /// It cannot be used by a client. - /// - /// - /// -or- - /// - /// - /// is 1010 (mandatory extension). - /// It cannot be used by a server. - /// - /// - /// -or- - /// - /// - /// is 1005 (no status) and - /// is specified. - /// - /// - /// -or- - /// - /// - /// could not be UTF-8-encoded. - /// - /// - /// - /// - /// is less than 1000 or greater than 4999. - /// - /// - /// -or- - /// - /// - /// The size of is greater than 123 bytes. - /// - /// - 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) { - if (code == 1011) { - var msg = "1011 cannot be used."; - - throw new ArgumentException (msg, "code"); - } - } - else { - if (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); - } - - /// - /// Closes the connection with the specified status code and reason. - /// - /// - /// This method does nothing if the current state of the interface is - /// Closing or Closed. - /// - /// - /// - /// One of the enum values. - /// - /// - /// It specifies the status code indicating the reason for the close. - /// - /// - /// - /// - /// A that specifies the reason for the close. - /// - /// - /// Its size must be 123 bytes or less in UTF-8. - /// - /// - /// - /// - /// is an undefined enum value. - /// - /// - /// -or- - /// - /// - /// is . - /// It cannot be used by a client. - /// - /// - /// -or- - /// - /// - /// is . - /// It cannot be used by a server. - /// - /// - /// -or- - /// - /// - /// is and - /// is specified. - /// - /// - /// -or- - /// - /// - /// could not be UTF-8-encoded. - /// - /// - /// - /// The size of is greater than 123 bytes. - /// - public void Close (CloseStatusCode code, string reason) - { - if (!code.IsDefined ()) { - var msg = "An undefined enum value."; - - throw new ArgumentException (msg, "code"); - } - - if (_client) { - if (code == CloseStatusCode.ServerError) { - var msg = "ServerError cannot be used."; - - throw new ArgumentException (msg, "code"); - } - } - else { - if (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 interface is - /// Closing or Closed. - /// - /// - public void CloseAsync () - { - closeAsync (1005, String.Empty); - } - - /// - /// Closes the connection asynchronously with the specified status code. - /// - /// - /// - /// This method does not wait for the close to be complete. - /// - /// - /// This method does nothing if the current state of the interface is - /// Closing or Closed. - /// - /// - /// - /// - /// A that specifies the status code indicating - /// the reason for the close. - /// - /// - /// The status codes are defined in - /// - /// Section 7.4 of RFC 6455. - /// - /// - /// - /// - /// is 1011 (server error). - /// It cannot be used by a client. - /// - /// - /// -or- - /// - /// - /// is 1010 (mandatory extension). - /// It cannot be used by a server. - /// - /// - /// - /// is less than 1000 or greater than 4999. - /// - public void CloseAsync (ushort code) - { - CloseAsync (code, String.Empty); - } - - /// - /// Closes the connection asynchronously with the specified status code. - /// - /// - /// - /// This method does not wait for the close to be complete. - /// - /// - /// This method does nothing if the current state of the interface is - /// Closing or Closed. - /// - /// - /// - /// - /// One of the enum values. - /// - /// - /// It specifies the status code indicating the reason for the close. - /// - /// - /// - /// - /// is an undefined enum value. - /// - /// - /// -or- - /// - /// - /// is . - /// It cannot be used by a client. - /// - /// - /// -or- - /// - /// - /// is . - /// It cannot be used by a server. - /// - /// - public void CloseAsync (CloseStatusCode code) - { - CloseAsync (code, String.Empty); - } - - /// - /// Closes the connection asynchronously with the specified status code and - /// reason. - /// - /// - /// - /// This method does not wait for the close to be complete. - /// - /// - /// This method does nothing if the current state of the interface is - /// Closing or Closed. - /// - /// - /// - /// - /// A that specifies the status code indicating - /// the reason for the close. - /// - /// - /// The status codes are defined in - /// - /// Section 7.4 of RFC 6455. - /// - /// - /// - /// - /// A that specifies the reason for the close. - /// - /// - /// Its size must be 123 bytes or less in UTF-8. - /// - /// - /// - /// - /// is 1011 (server error). - /// It cannot be used by a client. - /// - /// - /// -or- - /// - /// - /// is 1010 (mandatory extension). - /// It cannot be used by a server. - /// - /// - /// -or- - /// - /// - /// is 1005 (no status) and - /// is specified. - /// - /// - /// -or- - /// - /// - /// could not be UTF-8-encoded. - /// - /// - /// - /// - /// is less than 1000 or greater than 4999. - /// - /// - /// -or- - /// - /// - /// The size of is greater than 123 bytes. - /// - /// - 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) { - if (code == 1011) { - var msg = "1011 cannot be used."; - - throw new ArgumentException (msg, "code"); - } - } - else { - if (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 status code and - /// reason. - /// - /// - /// - /// This method does not wait for the close to be complete. - /// - /// - /// This method does nothing if the current state of the interface is - /// Closing or Closed. - /// - /// - /// - /// - /// One of the enum values. - /// - /// - /// It specifies the status code indicating the reason for the close. - /// - /// - /// - /// - /// A that specifies the reason for the close. - /// - /// - /// Its size must be 123 bytes or less in UTF-8. - /// - /// - /// - /// - /// is an undefined enum value. - /// - /// - /// -or- - /// - /// - /// is . - /// It cannot be used by a client. - /// - /// - /// -or- - /// - /// - /// is . - /// It cannot be used by a server. - /// - /// - /// -or- - /// - /// - /// is and - /// is specified. - /// - /// - /// -or- - /// - /// - /// could not be UTF-8-encoded. - /// - /// - /// - /// The size of is greater than 123 bytes. - /// - public void CloseAsync (CloseStatusCode code, string reason) - { - if (!code.IsDefined ()) { - var msg = "An undefined enum value."; - - throw new ArgumentException (msg, "code"); - } - - if (_client) { - if (code == CloseStatusCode.ServerError) { - var msg = "ServerError cannot be used."; - - throw new ArgumentException (msg, "code"); - } - } - else { - if (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); - } - - /// - /// Establishes a connection. - /// - /// - /// This method does nothing when the current state of the interface is - /// Connecting or Open. - /// - /// - /// - /// This method is not available if the interface is not for the client. - /// - /// - /// -or- - /// - /// - /// This method is not available if reconnection attempts have failed. - /// - /// - public void Connect () - { - if (!_client) { - var msg = "The interface is not for the client."; - - throw new InvalidOperationException (msg); - } - - if (_retryCountForConnect >= _maxRetryCountForConnect) { - var msg = "Reconnection attempts have failed."; - - throw new InvalidOperationException (msg); - } - - var connected = connect (); - - if (!connected) - return; - - open (); - } - - /// - /// Establishes a connection asynchronously. - /// - /// - /// - /// This method does not wait for the connect process to be complete. - /// - /// - /// This method does nothing when the current state of the interface is - /// Connecting or Open. - /// - /// - /// - /// - /// This method is not available if the interface is not for the client. - /// - /// - /// -or- - /// - /// - /// This method is not available if reconnection attempts have failed. - /// - /// - public void ConnectAsync () - { - if (!_client) { - var msg = "The interface is not for the client."; - - throw new InvalidOperationException (msg); - } - - if (_retryCountForConnect >= _maxRetryCountForConnect) { - var msg = "Reconnection attempts have failed."; - - throw new InvalidOperationException (msg); - } - - Func connector = connect; - - connector.BeginInvoke ( - ar => { - var connected = connector.EndInvoke (ar); - - if (!connected) - return; - - open (); - }, - null - ); - } - - /// - /// Sends a ping to the remote endpoint. - /// - /// - /// true if the send has successfully done and a pong has been - /// received within a time; otherwise, false. - /// - public bool Ping () - { - return ping (_emptyBytes); - } - - /// - /// Sends a ping with the specified message to the remote endpoint. - /// - /// - /// true if the send has successfully done and a pong has been - /// received within a time; otherwise, false. - /// - /// - /// - /// A that specifies the message to send. - /// - /// - /// Its 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"); - } - - if (bytes.Length > 125) { - var msg = "Its size is greater than 125 bytes."; - - throw new ArgumentOutOfRangeException ("message", msg); - } - - return ping (bytes); - } - - /// - /// Sends the specified data to the remote endpoint. - /// - /// - /// An array of that specifies the binary data to send. - /// - /// - /// is . - /// - /// - /// This method is not available when the current state of the interface - /// is not Open. - /// - public void Send (byte[] data) - { - if (_readyState != WebSocketState.Open) { - var msg = "The current state of the interface is not Open."; - - throw new InvalidOperationException (msg); - } - - if (data == null) - throw new ArgumentNullException ("data"); - - send (Opcode.Binary, new MemoryStream (data)); - } - - /// - /// Sends the specified file to the remote endpoint. - /// - /// - /// - /// A that specifies the file to send. - /// - /// - /// The file is sent as the binary data. - /// - /// - /// - /// - /// The file does not exist. - /// - /// - /// -or- - /// - /// - /// The file could not be opened. - /// - /// - /// - /// is . - /// - /// - /// This method is not available when the current state of the interface - /// is not Open. - /// - public void Send (FileInfo fileInfo) - { - if (_readyState != WebSocketState.Open) { - var msg = "The current state of the interface 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); - } - - /// - /// Sends the specified data to the remote endpoint. - /// - /// - /// A that specifies the text data to send. - /// - /// - /// could not be UTF-8-encoded. - /// - /// - /// is . - /// - /// - /// This method is not available when the current state of the interface - /// is not Open. - /// - public void Send (string data) - { - if (_readyState != WebSocketState.Open) { - var msg = "The current state of the interface 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"); - } - - send (Opcode.Text, new MemoryStream (bytes)); - } - - /// - /// Sends the data from the specified stream instance to the remote endpoint. - /// - /// - /// - /// 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. - /// - /// - /// - /// cannot be read. - /// - /// - /// -or- - /// - /// - /// is less than 1. - /// - /// - /// -or- - /// - /// - /// No data could be read from . - /// - /// - /// - /// is . - /// - /// - /// This method is not available when the current state of the interface - /// is not Open. - /// - public void Send (Stream stream, int length) - { - if (_readyState != WebSocketState.Open) { - var msg = "The current state of the interface 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) { - var fmt = "Only {0} byte(s) of data could be read from the stream."; - var msg = String.Format (fmt, len); - - _log.Warn (msg); - } - - send (Opcode.Binary, new MemoryStream (bytes)); - } - - /// - /// Sends the specified data to the remote endpoint asynchronously. - /// - /// - /// This method does not wait for the send to be complete. - /// - /// - /// An array of that specifies the binary data to send. - /// - /// - /// - /// An delegate. - /// - /// - /// It specifies the delegate called when the send is complete. - /// - /// - /// The parameter passed to the delegate is true - /// if the send has successfully done; otherwise, false. - /// - /// - /// if not necessary. - /// - /// - /// - /// is . - /// - /// - /// This method is not available when the current state of the interface - /// is not Open. - /// - public void SendAsync (byte[] data, Action completed) - { - if (_readyState != WebSocketState.Open) { - var msg = "The current state of the interface is not Open."; - - throw new InvalidOperationException (msg); - } - - if (data == null) - throw new ArgumentNullException ("data"); - - sendAsync (Opcode.Binary, new MemoryStream (data), completed); - } - - /// - /// Sends the specified file to the remote endpoint asynchronously. - /// - /// - /// 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 delegate. - /// - /// - /// It specifies the delegate called when the send is complete. - /// - /// - /// The parameter passed to the delegate is true - /// if the send has successfully done; otherwise, false. - /// - /// - /// if not necessary. - /// - /// - /// - /// - /// The file does not exist. - /// - /// - /// -or- - /// - /// - /// The file could not be opened. - /// - /// - /// - /// is . - /// - /// - /// This method is not available when the current state of the interface - /// is not Open. - /// - public void SendAsync (FileInfo fileInfo, Action completed) - { - if (_readyState != WebSocketState.Open) { - var msg = "The current state of the interface 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); - } - - /// - /// Sends the specified data to the remote endpoint asynchronously. - /// - /// - /// This method does not wait for the send to be complete. - /// - /// - /// A that specifies the text data to send. - /// - /// - /// - /// An delegate. - /// - /// - /// It specifies the delegate called when the send is complete. - /// - /// - /// The parameter passed to the delegate is true - /// if the send has successfully done; otherwise, false. - /// - /// - /// if not necessary. - /// - /// - /// - /// could not be UTF-8-encoded. - /// - /// - /// is . - /// - /// - /// This method is not available when the current state of the interface - /// is not Open. - /// - public void SendAsync (string data, Action completed) - { - if (_readyState != WebSocketState.Open) { - var msg = "The current state of the interface 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"); - } - - sendAsync (Opcode.Text, new MemoryStream (bytes), completed); - } - - /// - /// Sends the data from the specified stream instance to the remote - /// endpoint asynchronously. - /// - /// - /// 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 delegate. - /// - /// - /// It specifies the delegate called when the send is complete. - /// - /// - /// The parameter passed to the delegate is true - /// if the send has successfully done; otherwise, false. - /// - /// - /// if not necessary. - /// - /// - /// - /// - /// cannot be read. - /// - /// - /// -or- - /// - /// - /// is less than 1. - /// - /// - /// -or- - /// - /// - /// No data could be read from . - /// - /// - /// - /// is . - /// - /// - /// This method is not available when the current state of the interface - /// is not Open. - /// - public void SendAsync (Stream stream, int length, Action completed) - { - if (_readyState != WebSocketState.Open) { - var msg = "The current state of the interface 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) { - var fmt = "Only {0} byte(s) of data could be read from the stream."; - var msg = String.Format (fmt, len); - - _log.Warn (msg); - } - - sendAsync (Opcode.Binary, new MemoryStream (bytes), completed); - } - - /// - /// Sets an HTTP cookie to send with the handshake request or response. - /// - /// - /// A that specifies the cookie to send. - /// - /// - /// is . - /// - /// - /// This method is not available when the current state of the interface - /// is neither New nor Closed. - /// - public void SetCookie (Cookie cookie) - { - if (cookie == null) - throw new ArgumentNullException ("cookie"); - - lock (_forState) { - if (!canSet ()) { - var msg = "The current state of the interface is neither New nor Closed."; - - throw new InvalidOperationException (msg); - } - - Cookies.SetOrRemove (cookie); - } - } - - /// - /// Sets the credentials for the HTTP authentication (Basic/Digest). - /// - /// - /// - /// A that specifies the username associated - /// with the credentials. - /// - /// - /// or an empty string if the credentials need - /// to be initialized. - /// - /// - /// - /// - /// A that specifies the password for the - /// username associated with the credentials. - /// - /// - /// or an empty string if not necessary. - /// - /// - /// - /// A : true if the interface sends the - /// credentials for the Basic authentication in advance with - /// the first handshake request; otherwise, false. - /// - /// - /// - /// contains an invalid character. - /// - /// - /// -or- - /// - /// - /// contains an invalid character. - /// - /// - /// - /// - /// This method is not available if the interface is not for the client. - /// - /// - /// -or- - /// - /// - /// This method is not available when the current state of the interface - /// is neither New nor Closed. - /// - /// - public void SetCredentials (string username, string password, bool preAuth) - { - if (!_client) { - var msg = "The interface is not for the client."; - - throw new InvalidOperationException (msg); - } - - if (!username.IsNullOrEmpty ()) { - if (username.Contains (':') || !username.IsText ()) { - var msg = "It contains an invalid character."; - - throw new ArgumentException (msg, "username"); - } - } - - if (!password.IsNullOrEmpty ()) { - if (!password.IsText ()) { - var msg = "It contains an invalid character."; - - throw new ArgumentException (msg, "password"); - } - } - - lock (_forState) { - if (!canSet ()) { - var msg = "The current state of the interface is neither New nor Closed."; - - throw new InvalidOperationException (msg); - } - - if (username.IsNullOrEmpty ()) { - _credentials = null; - _preAuth = false; - - return; - } - - _credentials = new NetworkCredential ( - username, - password, - _uri.PathAndQuery - ); - - _preAuth = preAuth; - } - } - - /// - /// Sets the URL of the HTTP proxy server through which to connect and - /// the credentials for the HTTP proxy authentication (Basic/Digest). - /// - /// - /// - /// A that specifies the URL of the proxy - /// server through which to connect. - /// - /// - /// The syntax is http://<host>[:<port>]. - /// - /// - /// or an empty string if the URL and - /// the credentials need to be initialized. - /// - /// - /// - /// - /// A that specifies the username associated - /// with the credentials. - /// - /// - /// or an empty string if the credentials - /// are not necessary. - /// - /// - /// - /// - /// A that specifies the password for the - /// username associated with the credentials. - /// - /// - /// or an empty string if not necessary. - /// - /// - /// - /// - /// 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. - /// - /// - /// - /// - /// This method is not available if the interface is not for the client. - /// - /// - /// -or- - /// - /// - /// This method is not available when the current state of the interface - /// is neither New nor Closed. - /// - /// - public void SetProxy (string url, string username, string password) - { - if (!_client) { - var msg = "The interface is not for the client."; - - throw new InvalidOperationException (msg); - } - - Uri uri = null; - - if (!url.IsNullOrEmpty ()) { - if (!Uri.TryCreate (url, UriKind.Absolute, out uri)) { - var msg = "Not an absolute URI string."; - - throw new ArgumentException (msg, "url"); - } - - if (uri.Scheme != "http") { - var msg = "The scheme part is not http."; - - throw new ArgumentException (msg, "url"); - } - - if (uri.Segments.Length > 1) { - var msg = "It includes the path segments."; - - throw new ArgumentException (msg, "url"); - } - } - - if (!username.IsNullOrEmpty ()) { - if (username.Contains (':') || !username.IsText ()) { - var msg = "It contains an invalid character."; - - throw new ArgumentException (msg, "username"); - } - } - - if (!password.IsNullOrEmpty ()) { - if (!password.IsText ()) { - var msg = "It contains an invalid character."; - - throw new ArgumentException (msg, "password"); - } - } - - lock (_forState) { - if (!canSet ()) { - var msg = "The current state of the interface is neither New nor Closed."; - - throw new InvalidOperationException (msg); - } - - if (url.IsNullOrEmpty ()) { - _proxyUri = null; - _proxyCredentials = null; - - return; - } - - _proxyUri = uri; - - if (username.IsNullOrEmpty ()) { - _proxyCredentials = null; - - return; - } - - var domain = String.Format ("{0}:{1}", _uri.DnsSafeHost, _uri.Port); - - _proxyCredentials = new NetworkCredential (username, password, domain); - } - } - - /// - /// Sets a user header to send with the handshake request or response. - /// - /// - /// A that specifies the name of the header to set. - /// - /// - /// A that specifies the value of the header to set. - /// - /// - /// - /// is an empty string. - /// - /// - /// -or- - /// - /// - /// is a string of spaces. - /// - /// - /// -or- - /// - /// - /// contains an invalid character. - /// - /// - /// -or- - /// - /// - /// contains an invalid character. - /// - /// - /// -or- - /// - /// - /// is a restricted header name. - /// - /// - /// - /// is . - /// - /// - /// The length of is greater than 65,535 - /// characters. - /// - /// - /// - /// This method is not available when the current state of the interface - /// is neither New nor Closed. - /// - /// - /// -or- - /// - /// - /// This method is not available if the interface does not allow - /// that header type. - /// - /// - public void SetUserHeader (string name, string value) - { - lock (_forState) { - if (!canSet ()) { - var msg = "The current state of the interface is neither New nor Closed."; - - throw new InvalidOperationException (msg); - } - - UserHeaders.Set (name, value); - } - } - - #endregion - - #region Explicit Interface Implementations - - /// - /// Closes the connection and releases all associated resources. - /// - /// - /// - /// This method closes the connection with close status 1001 (going away). - /// - /// - /// This method does nothing if the current state of the interface is - /// Closing or Closed. - /// - /// - void IDisposable.Dispose () - { - close (1001, String.Empty); - } - - #endregion - } -} diff --git a/websocket-sharp/WebSocketException.cs b/websocket-sharp/WebSocketException.cs deleted file mode 100644 index 12bfc48f8..000000000 --- a/websocket-sharp/WebSocketException.cs +++ /dev/null @@ -1,129 +0,0 @@ -#region License -/* - * WebSocketException.cs - * - * The MIT License - * - * Copyright (c) 2012-2024 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -using System; - -namespace WebSocketSharp -{ - /// - /// The exception that is thrown when a fatal error occurs in - /// the WebSocket communication. - /// - public class WebSocketException : Exception - { - #region Private Fields - - private ushort _code; - - #endregion - - #region Private Constructors - - private WebSocketException ( - ushort code, - string message, - Exception innerException - ) - : base (message ?? code.GetErrorMessage (), innerException) - { - _code = code; - } - - #endregion - - #region Internal Constructors - - internal WebSocketException () - : this (CloseStatusCode.Abnormal, null, null) - { - } - - internal WebSocketException (Exception innerException) - : this (CloseStatusCode.Abnormal, null, innerException) - { - } - - internal WebSocketException (string message) - : this (CloseStatusCode.Abnormal, message, null) - { - } - - internal WebSocketException (CloseStatusCode code) - : this (code, null, null) - { - } - - internal WebSocketException (string message, Exception innerException) - : this (CloseStatusCode.Abnormal, message, innerException) - { - } - - internal WebSocketException (CloseStatusCode code, Exception innerException) - : this (code, null, innerException) - { - } - - internal WebSocketException (CloseStatusCode code, string message) - : this (code, message, null) - { - } - - internal WebSocketException ( - CloseStatusCode code, - string message, - Exception innerException - ) - : this ((ushort) code, message, innerException) - { - } - - #endregion - - #region Public Properties - - /// - /// Gets the status code indicating the cause of the exception. - /// - /// - /// - /// A that represents the status code indicating - /// the cause of the exception. - /// - /// - /// It is one of the status codes for the WebSocket connection close. - /// - /// - public ushort Code { - get { - return _code; - } - } - - #endregion - } -} diff --git a/websocket-sharp/WebSocketFrame.cs b/websocket-sharp/WebSocketFrame.cs deleted file mode 100644 index 9ce51b945..000000000 --- a/websocket-sharp/WebSocketFrame.cs +++ /dev/null @@ -1,916 +0,0 @@ -#region License -/* - * WebSocketFrame.cs - * - * The MIT License - * - * Copyright (c) 2012-2025 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -#region Contributors -/* - * Contributors: - * - Chris Swiedler - */ -#endregion - -using System; -using System.Collections; -using System.Collections.Generic; -using System.IO; -using System.Text; - -namespace WebSocketSharp -{ - internal class WebSocketFrame : IEnumerable - { - #region Private Fields - - private static readonly int _defaultHeaderLength; - private static readonly int _defaultMaskingKeyLength; - private static readonly byte[] _emptyBytes; - private byte[] _extPayloadLength; - private Fin _fin; - private Mask _mask; - private byte[] _maskingKey; - private Opcode _opcode; - private PayloadData _payloadData; - private int _payloadLength; - private Rsv _rsv1; - private Rsv _rsv2; - private Rsv _rsv3; - - #endregion - - #region Static Constructor - - static WebSocketFrame () - { - _defaultHeaderLength = 2; - _defaultMaskingKeyLength = 4; - _emptyBytes = new byte[0]; - } - - #endregion - - #region Private Constructors - - private WebSocketFrame () - { - } - - #endregion - - #region Internal Constructors - - internal WebSocketFrame ( - Fin fin, - Opcode opcode, - byte[] data, - bool compressed, - bool mask - ) - : this (fin, opcode, new PayloadData (data), compressed, mask) - { - } - - internal WebSocketFrame ( - Fin fin, - Opcode opcode, - PayloadData payloadData, - bool compressed, - bool mask - ) - { - _fin = fin; - _opcode = opcode; - - _rsv1 = compressed ? Rsv.On : Rsv.Off; - _rsv2 = Rsv.Off; - _rsv3 = Rsv.Off; - - var len = payloadData.Length; - - if (len < 126) { - _payloadLength = (int) len; - _extPayloadLength = _emptyBytes; - } - else if (len < 0x010000) { - _payloadLength = 126; - _extPayloadLength = ((ushort) len).ToByteArray (ByteOrder.Big); - } - else { - _payloadLength = 127; - _extPayloadLength = len.ToByteArray (ByteOrder.Big); - } - - if (mask) { - _mask = Mask.On; - _maskingKey = createMaskingKey (); - - payloadData.Mask (_maskingKey); - } - else { - _mask = Mask.Off; - _maskingKey = _emptyBytes; - } - - _payloadData = payloadData; - } - - #endregion - - #region Internal Properties - - internal ulong ExactPayloadLength { - get { - return _payloadLength < 126 - ? (ulong) _payloadLength - : _payloadLength == 126 - ? _extPayloadLength.ToUInt16 (ByteOrder.Big) - : _extPayloadLength.ToUInt64 (ByteOrder.Big); - } - } - - internal int ExtendedPayloadLengthWidth { - get { - return _payloadLength < 126 - ? 0 - : _payloadLength == 126 - ? 2 - : 8; - } - } - - #endregion - - #region Public Properties - - public byte[] ExtendedPayloadLength { - get { - return _extPayloadLength; - } - } - - public Fin Fin { - get { - return _fin; - } - } - - public bool IsBinary { - get { - return _opcode == Opcode.Binary; - } - } - - public bool IsClose { - get { - return _opcode == Opcode.Close; - } - } - - public bool IsCompressed { - get { - return _rsv1 == Rsv.On; - } - } - - public bool IsContinuation { - get { - return _opcode == Opcode.Cont; - } - } - - public bool IsControl { - get { - return _opcode >= Opcode.Close; - } - } - - public bool IsData { - get { - return _opcode == Opcode.Text || _opcode == Opcode.Binary; - } - } - - public bool IsFinal { - get { - return _fin == Fin.Final; - } - } - - public bool IsFragment { - get { - return _fin == Fin.More || _opcode == Opcode.Cont; - } - } - - public bool IsMasked { - get { - return _mask == Mask.On; - } - } - - public bool IsPing { - get { - return _opcode == Opcode.Ping; - } - } - - public bool IsPong { - get { - return _opcode == Opcode.Pong; - } - } - - public bool IsText { - get { - return _opcode == Opcode.Text; - } - } - - public ulong Length { - get { - return (ulong) ( - _defaultHeaderLength - + _extPayloadLength.Length - + _maskingKey.Length - ) - + _payloadData.Length; - } - } - - public Mask Mask { - get { - return _mask; - } - } - - public byte[] MaskingKey { - get { - return _maskingKey; - } - } - - public Opcode Opcode { - get { - return _opcode; - } - } - - public PayloadData PayloadData { - get { - return _payloadData; - } - } - - public int PayloadLength { - get { - return _payloadLength; - } - } - - public Rsv Rsv1 { - get { - return _rsv1; - } - } - - public Rsv Rsv2 { - get { - return _rsv2; - } - } - - public Rsv Rsv3 { - get { - return _rsv3; - } - } - - #endregion - - #region Private Methods - - private static byte[] createMaskingKey () - { - var key = new byte[_defaultMaskingKeyLength]; - - WebSocket.RandomNumber.GetBytes (key); - - return key; - } - - private static WebSocketFrame processHeader (byte[] header) - { - if (header.Length != _defaultHeaderLength) { - var msg = "The header part of a frame could not be read."; - - throw new WebSocketException (msg); - } - - // FIN - var fin = (header[0] & 0x80) == 0x80 ? Fin.Final : Fin.More; - - // RSV1 - var rsv1 = (header[0] & 0x40) == 0x40 ? Rsv.On : Rsv.Off; - - // RSV2 - var rsv2 = (header[0] & 0x20) == 0x20 ? Rsv.On : Rsv.Off; - - // RSV3 - var rsv3 = (header[0] & 0x10) == 0x10 ? Rsv.On : Rsv.Off; - - // Opcode - var opcode = header[0] & 0x0f; - - // MASK - var mask = (header[1] & 0x80) == 0x80 ? Mask.On : Mask.Off; - - // Payload Length - var payloadLen = header[1] & 0x7f; - - if (!opcode.IsSupportedOpcode ()) { - var msg = "The opcode of a frame is not supported."; - - throw new WebSocketException (CloseStatusCode.UnsupportedData, msg); - } - - var frame = new WebSocketFrame (); - - frame._fin = fin; - frame._rsv1 = rsv1; - frame._rsv2 = rsv2; - frame._rsv3 = rsv3; - frame._opcode = (Opcode) opcode; - frame._mask = mask; - frame._payloadLength = payloadLen; - - return frame; - } - - private static WebSocketFrame readExtendedPayloadLength ( - Stream stream, - WebSocketFrame frame - ) - { - var len = frame.ExtendedPayloadLengthWidth; - - if (len == 0) { - frame._extPayloadLength = _emptyBytes; - - return frame; - } - - var bytes = stream.ReadBytes (len); - - if (bytes.Length != len) { - var msg = "The extended payload length of a frame could not be read."; - - throw new WebSocketException (msg); - } - - frame._extPayloadLength = bytes; - - return frame; - } - - private static void readExtendedPayloadLengthAsync ( - Stream stream, - WebSocketFrame frame, - Action completed, - Action error - ) - { - var len = frame.ExtendedPayloadLengthWidth; - - if (len == 0) { - frame._extPayloadLength = _emptyBytes; - - completed (frame); - - return; - } - - stream.ReadBytesAsync ( - len, - bytes => { - if (bytes.Length != len) { - var msg = "The extended payload length of a frame could not be read."; - - throw new WebSocketException (msg); - } - - frame._extPayloadLength = bytes; - - completed (frame); - }, - error - ); - } - - private static WebSocketFrame readHeader (Stream stream) - { - var bytes = stream.ReadBytes (_defaultHeaderLength); - - return processHeader (bytes); - } - - private static void readHeaderAsync ( - Stream stream, - Action completed, - Action error - ) - { - stream.ReadBytesAsync ( - _defaultHeaderLength, - bytes => { - var frame = processHeader (bytes); - - completed (frame); - }, - error - ); - } - - private static WebSocketFrame readMaskingKey ( - Stream stream, - WebSocketFrame frame - ) - { - if (!frame.IsMasked) { - frame._maskingKey = _emptyBytes; - - return frame; - } - - var bytes = stream.ReadBytes (_defaultMaskingKeyLength); - - if (bytes.Length != _defaultMaskingKeyLength) { - var msg = "The masking key of a frame could not be read."; - - throw new WebSocketException (msg); - } - - frame._maskingKey = bytes; - - return frame; - } - - private static void readMaskingKeyAsync ( - Stream stream, - WebSocketFrame frame, - Action completed, - Action error - ) - { - if (!frame.IsMasked) { - frame._maskingKey = _emptyBytes; - - completed (frame); - - return; - } - - stream.ReadBytesAsync ( - _defaultMaskingKeyLength, - bytes => { - if (bytes.Length != _defaultMaskingKeyLength) { - var msg = "The masking key of a frame could not be read."; - - throw new WebSocketException (msg); - } - - frame._maskingKey = bytes; - - completed (frame); - }, - error - ); - } - - private static WebSocketFrame readPayloadData ( - Stream stream, - WebSocketFrame frame - ) - { - var exactPayloadLen = frame.ExactPayloadLength; - - if (exactPayloadLen > PayloadData.MaxLength) { - var msg = "The payload data of a frame is too big."; - - throw new WebSocketException (CloseStatusCode.TooBig, msg); - } - - if (exactPayloadLen == 0) { - frame._payloadData = PayloadData.Empty; - - return frame; - } - - var len = (long) exactPayloadLen; - var bytes = frame._payloadLength > 126 - ? stream.ReadBytes (len, 1024) - : stream.ReadBytes ((int) len); - - if (bytes.LongLength != len) { - var msg = "The payload data of a frame could not be read."; - - throw new WebSocketException (msg); - } - - frame._payloadData = new PayloadData (bytes, len); - - return frame; - } - - private static void readPayloadDataAsync ( - Stream stream, - WebSocketFrame frame, - Action completed, - Action error - ) - { - var exactPayloadLen = frame.ExactPayloadLength; - - if (exactPayloadLen > PayloadData.MaxLength) { - var msg = "The payload data of a frame is too big."; - - throw new WebSocketException (CloseStatusCode.TooBig, msg); - } - - if (exactPayloadLen == 0) { - frame._payloadData = PayloadData.Empty; - - completed (frame); - - return; - } - - var len = (long) exactPayloadLen; - - Action comp = - bytes => { - if (bytes.LongLength != len) { - var msg = "The payload data of a frame could not be read."; - - throw new WebSocketException (msg); - } - - frame._payloadData = new PayloadData (bytes, len); - - completed (frame); - }; - - if (frame._payloadLength > 126) { - stream.ReadBytesAsync (len, 1024, comp, error); - - return; - } - - stream.ReadBytesAsync ((int) len, comp, error); - } - - private string toDumpString () - { - var len = Length; - var cnt = (long) (len / 4); - var rem = (int) (len % 4); - - string spFmt; - string cntFmt; - - if (cnt < 10000) { - spFmt = "{0,4}"; - cntFmt = "{0,4}"; - } - else if (cnt < 0x010000) { - spFmt = "{0,4}"; - cntFmt = "{0,4:X}"; - } - else if (cnt < 0x0100000000) { - spFmt = "{0,8}"; - cntFmt = "{0,8:X}"; - } - else { - spFmt = "{0,16}"; - cntFmt = "{0,16:X}"; - } - - var baseFmt = @"{0} 01234567 89ABCDEF 01234567 89ABCDEF -{0}+--------+--------+--------+--------+ -"; - var headerFmt = String.Format (baseFmt, spFmt); - - baseFmt = "{0}|{{1,8}} {{2,8}} {{3,8}} {{4,8}}|\n"; - var lineFmt = String.Format (baseFmt, cntFmt); - - baseFmt = "{0}+--------+--------+--------+--------+"; - var footerFmt = String.Format (baseFmt, spFmt); - - var buff = new StringBuilder (64); - - Func> lineWriter = - () => { - long lineCnt = 0; - - return (arg1, arg2, arg3, arg4) => { - buff.AppendFormat ( - lineFmt, - ++lineCnt, - arg1, - arg2, - arg3, - arg4 - ); - }; - }; - - var writeLine = lineWriter (); - var bytes = ToArray (); - - buff.AppendFormat (headerFmt, String.Empty); - - for (long i = 0; i <= cnt; i++) { - var j = i * 4; - - if (i < cnt) { - var arg1 = Convert.ToString (bytes[j], 2).PadLeft (8, '0'); - var arg2 = Convert.ToString (bytes[j + 1], 2).PadLeft (8, '0'); - var arg3 = Convert.ToString (bytes[j + 2], 2).PadLeft (8, '0'); - var arg4 = Convert.ToString (bytes[j + 3], 2).PadLeft (8, '0'); - - writeLine (arg1, arg2, arg3, arg4); - - continue; - } - - if (rem > 0) { - var arg1 = Convert.ToString (bytes[j], 2).PadLeft (8, '0'); - var arg2 = rem >= 2 - ? Convert.ToString (bytes[j + 1], 2).PadLeft (8, '0') - : String.Empty; - - var arg3 = rem == 3 - ? Convert.ToString (bytes[j + 2], 2).PadLeft (8, '0') - : String.Empty; - - writeLine (arg1, arg2, arg3, String.Empty); - } - } - - buff.AppendFormat (footerFmt, String.Empty); - - return buff.ToString (); - } - - private string toString () - { - var extPayloadLen = _payloadLength >= 126 - ? ExactPayloadLength.ToString () - : String.Empty; - - var maskingKey = _mask == Mask.On - ? BitConverter.ToString (_maskingKey) - : String.Empty; - - var payloadData = _payloadLength >= 126 - ? "***" - : _payloadLength > 0 - ? _payloadData.ToString () - : String.Empty; - - var fmt = @" FIN: {0} - RSV1: {1} - RSV2: {2} - RSV3: {3} - Opcode: {4} - MASK: {5} - Payload Length: {6} -Extended Payload Length: {7} - Masking Key: {8} - Payload Data: {9}"; - - return String.Format ( - fmt, - _fin, - _rsv1, - _rsv2, - _rsv3, - _opcode, - _mask, - _payloadLength, - extPayloadLen, - maskingKey, - payloadData - ); - } - - #endregion - - #region Internal Methods - - internal static WebSocketFrame CreateCloseFrame ( - PayloadData payloadData, - bool mask - ) - { - return new WebSocketFrame ( - Fin.Final, - Opcode.Close, - payloadData, - false, - mask - ); - } - - internal static WebSocketFrame CreatePingFrame (bool mask) - { - return new WebSocketFrame ( - Fin.Final, - Opcode.Ping, - PayloadData.Empty, - false, - mask - ); - } - - internal static WebSocketFrame CreatePingFrame (byte[] data, bool mask) - { - return new WebSocketFrame ( - Fin.Final, - Opcode.Ping, - new PayloadData (data), - false, - mask - ); - } - - internal static WebSocketFrame CreatePongFrame ( - PayloadData payloadData, - bool mask - ) - { - return new WebSocketFrame ( - Fin.Final, - Opcode.Pong, - payloadData, - false, - mask - ); - } - - internal static WebSocketFrame ReadFrame (Stream stream, bool unmask) - { - var frame = readHeader (stream); - - readExtendedPayloadLength (stream, frame); - readMaskingKey (stream, frame); - readPayloadData (stream, frame); - - if (unmask) - frame.Unmask (); - - return frame; - } - - internal static void ReadFrameAsync ( - Stream stream, - bool unmask, - Action completed, - Action error - ) - { - readHeaderAsync ( - stream, - frame => - readExtendedPayloadLengthAsync ( - stream, - frame, - frame1 => - readMaskingKeyAsync ( - stream, - frame1, - frame2 => - readPayloadDataAsync ( - stream, - frame2, - frame3 => { - if (unmask) - frame3.Unmask (); - - completed (frame3); - }, - error - ), - error - ), - error - ), - error - ); - } - - internal string ToString (bool dump) - { - return dump ? toDumpString () : toString (); - } - - internal void Unmask () - { - if (_mask == Mask.Off) - return; - - _payloadData.Mask (_maskingKey); - - _maskingKey = _emptyBytes; - _mask = Mask.Off; - } - - #endregion - - #region Public Methods - - public IEnumerator GetEnumerator () - { - foreach (var b in ToArray ()) - yield return b; - } - - public byte[] ToArray () - { - using (var buff = new MemoryStream ()) { - var header = (int) _fin; - - header = (header << 1) + (int) _rsv1; - header = (header << 1) + (int) _rsv2; - header = (header << 1) + (int) _rsv3; - header = (header << 4) + (int) _opcode; - header = (header << 1) + (int) _mask; - header = (header << 7) + _payloadLength; - - var headerAsUInt16 = (ushort) header; - var headerAsBytes = headerAsUInt16.ToByteArray (ByteOrder.Big); - - buff.Write (headerAsBytes, 0, _defaultHeaderLength); - - if (_payloadLength >= 126) - buff.Write (_extPayloadLength, 0, _extPayloadLength.Length); - - if (_mask == Mask.On) - buff.Write (_maskingKey, 0, _defaultMaskingKeyLength); - - if (_payloadLength > 0) { - var bytes = _payloadData.ToArray (); - - if (_payloadLength > 126) - buff.WriteBytes (bytes, 1024); - else - buff.Write (bytes, 0, bytes.Length); - } - - buff.Close (); - - return buff.ToArray (); - } - } - - public override string ToString () - { - var val = ToArray (); - - return BitConverter.ToString (val); - } - - #endregion - - #region Explicit Interface Implementations - - IEnumerator IEnumerable.GetEnumerator () - { - return GetEnumerator (); - } - - #endregion - } -} diff --git a/websocket-sharp/WebSocketState.cs b/websocket-sharp/WebSocketState.cs deleted file mode 100644 index fa1704970..000000000 --- a/websocket-sharp/WebSocketState.cs +++ /dev/null @@ -1,64 +0,0 @@ -#region License -/* - * WebSocketState.cs - * - * The MIT License - * - * Copyright (c) 2010-2022 sta.blockhead - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#endregion - -using System; - -namespace WebSocketSharp -{ - /// - /// Indicates the state of the WebSocket interface. - /// - public enum WebSocketState : ushort - { - /// - /// Equivalent to numeric value 0. Indicates that a new interface has - /// been created. - /// - New = 0, - /// - /// Equivalent to numeric value 1. Indicates that the connect process is - /// in progress. - /// - Connecting = 1, - /// - /// Equivalent to numeric value 2. Indicates that the connection has - /// been established and the communication is possible. - /// - Open = 2, - /// - /// Equivalent to numeric value 3. Indicates that the close process is - /// in progress. - /// - Closing = 3, - /// - /// Equivalent to numeric value 4. Indicates that the connection has - /// been closed or could not be established. - /// - Closed = 4 - } -} 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 deleted file mode 100644 index 9dac290aa..000000000 --- a/websocket-sharp/websocket-sharp.csproj +++ /dev/null @@ -1,148 +0,0 @@ - - - - Debug - AnyCPU - 9.0.21022 - 2.0 - {B357BAC7-529E-4D81-A0D2-71041B19C8DE} - Library - WebSocketSharp - websocket-sharp - v3.5 - true - websocket-sharp.snk - - - 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 a2546f385..000000000 Binary files a/websocket-sharp/websocket-sharp.snk and /dev/null differ diff --git a/websocket-sharp_icon.png b/websocket-sharp_icon.png deleted file mode 100644 index a07c30eb3..000000000 Binary files a/websocket-sharp_icon.png and /dev/null differ diff --git a/websocket-sharp_logo.png b/websocket-sharp_logo.png deleted file mode 100644 index b6055e143..000000000 Binary files a/websocket-sharp_logo.png and /dev/null differ