├── websocket-sharp ├── doc │ ├── .gitignore │ └── doc.sh ├── websocket-sharp.snk ├── AssemblyInfo.cs ├── Fin.cs ├── Rsv.cs ├── Mask.cs ├── Net │ ├── HttpHeaderType.cs │ ├── HttpVersion.cs │ ├── Security │ │ └── SslStream.cs │ ├── HttpHeaderInfo.cs │ ├── CookieException.cs │ ├── ChunkStream.cs │ ├── HttpStatusCode.cs │ ├── CookieCollection.cs │ └── Cookie.cs ├── ByteOrder.cs ├── CompressionMethod.cs ├── LogLevel.cs ├── WebSocketState.cs ├── Opcode.cs ├── ErrorEventArgs.cs ├── WebSocketException.cs ├── websocket-sharp.csproj ├── HandshakeBase.cs ├── CloseEventArgs.cs ├── MessageEventArgs.cs ├── LogData.cs ├── PayloadData.cs ├── HandshakeResponse.cs ├── CloseStatusCode.cs ├── HandshakeRequest.cs ├── WsStream.cs ├── Logger.cs └── WsFrame.cs ├── websocket-sharp.png ├── .gitignore ├── EchoClient ├── Properties │ └── AssemblyInfo.cs ├── Program.cs └── EchoClient.csproj ├── LICENSE.txt ├── websocket-sharp.sln └── README.md /websocket-sharp/doc/.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore MonoDevelop build results. 2 | 3 | html 4 | mdoc 5 | -------------------------------------------------------------------------------- /websocket-sharp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KLab/websocket-unitymobile/HEAD/websocket-sharp.png -------------------------------------------------------------------------------- /websocket-sharp/websocket-sharp.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KLab/websocket-unitymobile/HEAD/websocket-sharp/websocket-sharp.snk -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore build results and temporary files. 2 | 3 | Backup* 4 | _UpgradeReport_Files 5 | bin 6 | obj 7 | 8 | *.mdb 9 | *.pdb 10 | *.pidb 11 | *.suo 12 | *.user 13 | *.userprefs 14 | UpgradeLog*.* 15 | -------------------------------------------------------------------------------- /websocket-sharp/doc/doc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # @(#) doc.sh ver.0.0.2 2013.01.24 4 | # 5 | # Usage: 6 | # doc.sh 7 | # 8 | # Description: 9 | # Creating documentation for websocket-sharp. 10 | # 11 | ########################################################################### 12 | 13 | SRC_DIR="../bin/Release_Ubuntu" 14 | XML="${SRC_DIR}/websocket-sharp.xml" 15 | DLL="${SRC_DIR}/websocket-sharp.dll" 16 | 17 | DOC_DIR="." 18 | MDOC_DIR="${DOC_DIR}/mdoc" 19 | HTML_DIR="${DOC_DIR}/html" 20 | 21 | createDir() { 22 | if [ ! -d $1 ]; then 23 | mkdir -p $1 24 | fi 25 | } 26 | 27 | set -e 28 | createDir ${MDOC_DIR} 29 | createDir ${HTML_DIR} 30 | mdoc update --delete -fno-assembly-versions -i ${XML} -o ${MDOC_DIR}/ ${DLL} 31 | mdoc export-html -o ${HTML_DIR}/ ${MDOC_DIR}/ 32 | -------------------------------------------------------------------------------- /EchoClient/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | 4 | // Information about this assembly is defined by the following attributes. 5 | // Change them to the values specific to your project. 6 | 7 | [assembly: AssemblyTitle ("EchoClient")] 8 | [assembly: AssemblyDescription ("")] 9 | [assembly: AssemblyConfiguration ("")] 10 | [assembly: AssemblyCompany ("")] 11 | [assembly: AssemblyProduct ("")] 12 | [assembly: AssemblyCopyright ("inada-n")] 13 | [assembly: AssemblyTrademark ("")] 14 | [assembly: AssemblyCulture ("")] 15 | 16 | // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". 17 | // The form "{Major}.{Minor}.*" will automatically update the build and revision, 18 | // and "{Major}.{Minor}.{Build}.*" will update just the revision. 19 | 20 | [assembly: AssemblyVersion ("1.0.*")] 21 | 22 | // The following attributes are used to specify the signing key for the assembly, 23 | // if desired. See the Mono documentation for more information about signing. 24 | 25 | //[assembly: AssemblyDelaySign(false)] 26 | //[assembly: AssemblyKeyFile("")] 27 | 28 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2010-2014 sta.blockhead 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /websocket-sharp/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | 4 | // Information about this assembly is defined by the following attributes. 5 | // Change them to the values specific to your project. 6 | 7 | [assembly: AssemblyTitle("websocket-sharp")] 8 | [assembly: AssemblyDescription("A C# implementation of the WebSocket protocol client and server")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("websocket-sharp.dll")] 12 | [assembly: AssemblyCopyright("sta.blockhead")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". 17 | // The form "{Major}.{Minor}.*" will automatically update the build and revision, 18 | // and "{Major}.{Minor}.{Build}.*" will update just the revision. 19 | 20 | [assembly: AssemblyVersion("1.0.2.*")] 21 | 22 | // The following attributes are used to specify the signing key for the assembly, 23 | // if desired. See the Mono documentation for more information about signing. 24 | 25 | //[assembly: AssemblyDelaySign(false)] 26 | //[assembly: AssemblyKeyFile("")] 27 | -------------------------------------------------------------------------------- /EchoClient/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using WebSocketSharp; 3 | 4 | namespace EchoClient 5 | { 6 | class MainClass 7 | { 8 | public static void Main (string[] args) 9 | { 10 | if (args.Length != 1) { 11 | Console.WriteLine ("Usage: EchoClient.exe ws://echo.websocket.org"); 12 | return; 13 | } 14 | 15 | WebSocket ws = new WebSocket(args[0]); 16 | ws.OnOpen = (Object sender, EventArgs e) => { 17 | Console.WriteLine ("Connected"); 18 | Console.Write("-> "); 19 | }; 20 | ws.OnMessage = (Object sender, MessageEventArgs e) => { 21 | Console.WriteLine ("<- " + e.Data); 22 | Console.Write("-> "); 23 | }; 24 | ws.OnError = (Object sender, ErrorEventArgs e) => { 25 | Console.WriteLine ("ERROR: " + e.Message); 26 | Console.Write("-> "); 27 | }; 28 | ws.OnClose = (Object sender, CloseEventArgs e) => { 29 | Console.WriteLine ("Closed " + e.Code + e.Reason + e.WasClean); 30 | }; 31 | 32 | ws.Connect (); 33 | 34 | while (true) { 35 | string line = Console.ReadLine (); 36 | ws.Send (line.TrimEnd(new char[] {'\r', '\n'})); 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /websocket-sharp/Fin.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | /* 3 | * Fin.cs 4 | * 5 | * The MIT License 6 | * 7 | * Copyright (c) 2012-2013 sta.blockhead 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | */ 27 | #endregion 28 | 29 | using System; 30 | 31 | namespace WebSocketSharp { 32 | 33 | internal enum Fin : byte 34 | { 35 | MORE = 0x0, 36 | FINAL = 0x1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /websocket-sharp/Rsv.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | /* 3 | * Rsv.cs 4 | * 5 | * The MIT License 6 | * 7 | * Copyright (c) 2012-2013 sta.blockhead 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | */ 27 | #endregion 28 | 29 | using System; 30 | 31 | namespace WebSocketSharp { 32 | 33 | internal enum Rsv : byte 34 | { 35 | OFF = 0x0, 36 | ON = 0x1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /websocket-sharp/Mask.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | /* 3 | * Mask.cs 4 | * 5 | * The MIT License 6 | * 7 | * Copyright (c) 2012-2013 sta.blockhead 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | */ 27 | #endregion 28 | 29 | using System; 30 | 31 | namespace WebSocketSharp { 32 | 33 | internal enum Mask : byte 34 | { 35 | UNMASK = 0x0, 36 | MASK = 0x1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /websocket-sharp/Net/HttpHeaderType.cs: -------------------------------------------------------------------------------- 1 | // 2 | // HttpHeaderType.cs 3 | // 4 | // Authors: 5 | // sta (sta.blockhead@gmail.com) 6 | // 7 | // Copyright (c) 2013 sta.blockhead 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining 10 | // a copy of this software and associated documentation files (the 11 | // "Software"), to deal in the Software without restriction, including 12 | // without limitation the rights to use, copy, modify, merge, publish, 13 | // distribute, sublicense, and/or sell copies of the Software, and to 14 | // permit persons to whom the Software is furnished to do so, subject to 15 | // the following conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be 18 | // included in all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 24 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 25 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 26 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 27 | // 28 | 29 | using System; 30 | 31 | namespace WebSocketSharp.Net { 32 | 33 | [Flags] 34 | enum HttpHeaderType 35 | { 36 | Unspecified = 0, 37 | Request = 1, 38 | Response = 1 << 1, 39 | Restricted = 1 << 2, 40 | MultiValue = 1 << 3, 41 | MultiValueInRequest = 1 << 4, 42 | MultiValueInResponse = 1 << 5 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /websocket-sharp/ByteOrder.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | /* 3 | * ByteOrder.cs 4 | * 5 | * The MIT License 6 | * 7 | * Copyright (c) 2012-2013 sta.blockhead 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | */ 27 | #endregion 28 | 29 | using System; 30 | 31 | namespace WebSocketSharp { 32 | 33 | /// 34 | /// Contains the values that indicate whether the byte order is a Little-endian or Big-endian. 35 | /// 36 | public enum ByteOrder : byte 37 | { 38 | /// 39 | /// Indicates a Little-endian. 40 | /// 41 | LITTLE, 42 | /// 43 | /// Indicates a Big-endian. 44 | /// 45 | BIG 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /websocket-sharp/Net/HttpVersion.cs: -------------------------------------------------------------------------------- 1 | // 2 | // HttpVersion.cs 3 | // Copied from System.Net.HttpVersion.cs 4 | // 5 | // Author: 6 | // Lawrence Pit (loz@cable.a2000.nl) 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining 9 | // a copy of this software and associated documentation files (the 10 | // "Software"), to deal in the Software without restriction, including 11 | // without limitation the rights to use, copy, modify, merge, publish, 12 | // distribute, sublicense, and/or sell copies of the Software, and to 13 | // permit persons to whom the Software is furnished to do so, subject to 14 | // the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be 17 | // included in all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 23 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 24 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 25 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | // 27 | 28 | using System; 29 | 30 | namespace WebSocketSharp.Net { 31 | 32 | /// 33 | /// Provides the HTTP version numbers. 34 | /// 35 | public class HttpVersion { 36 | 37 | /// 38 | /// Provides a instance for HTTP 1.0. 39 | /// 40 | public static readonly Version Version10 = new Version (1, 0); 41 | 42 | /// 43 | /// Provides a instance for HTTP 1.1. 44 | /// 45 | public static readonly Version Version11 = new Version (1, 1); 46 | 47 | /// 48 | /// Initializes a new instance of the class. 49 | /// 50 | public HttpVersion () {} 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /EchoClient/EchoClient.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | AnyCPU 6 | 9.0.21022 7 | 2.0 8 | {3C35488A-1B9D-4E76-A1E4-BED3062E5517} 9 | Exe 10 | EchoClient 11 | EchoClient 12 | v3.5 13 | 14 | 15 | true 16 | full 17 | false 18 | bin\Debug 19 | DEBUG; 20 | prompt 21 | 4 22 | true 23 | 24 | 25 | full 26 | true 27 | bin\Release 28 | prompt 29 | 4 30 | true 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | {B357BAC7-529E-4D81-A0D2-71041B19C8DE} 43 | websocket-sharp 44 | 45 | 46 | -------------------------------------------------------------------------------- /websocket-sharp/CompressionMethod.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | /* 3 | * CompressionMethod.cs 4 | * 5 | * The MIT License 6 | * 7 | * Copyright (c) 2013 sta.blockhead 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | */ 27 | #endregion 28 | 29 | using System; 30 | 31 | namespace WebSocketSharp { 32 | 33 | /// 34 | /// Contains the values of the compression methods used to compress the payload data of the WebSocket Data frame. 35 | /// 36 | /// 37 | /// The CompressionMethod enumeration contains the values of the compression methods defined in 38 | /// Compression Extensions for WebSocket. 39 | /// 40 | public enum CompressionMethod : byte 41 | { 42 | /// 43 | /// Indicates non compression. 44 | /// 45 | NONE, 46 | /// 47 | /// Indicates using DEFLATE. 48 | /// 49 | DEFLATE 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /websocket-sharp/LogLevel.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | /* 3 | * LogLevel.cs 4 | * 5 | * The MIT License 6 | * 7 | * Copyright (c) 2013 sta.blockhead 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | */ 27 | #endregion 28 | 29 | using System; 30 | 31 | namespace WebSocketSharp 32 | { 33 | /// 34 | /// Contains the values of the logging level. 35 | /// 36 | public enum LogLevel 37 | { 38 | /// 39 | /// Indicates the bottom logging level. 40 | /// 41 | TRACE, 42 | /// 43 | /// Indicates the 2nd logging level from the bottom. 44 | /// 45 | DEBUG, 46 | /// 47 | /// Indicates the 3rd logging level from the bottom. 48 | /// 49 | INFO, 50 | /// 51 | /// Indicates the 3rd logging level from the top. 52 | /// 53 | WARN, 54 | /// 55 | /// Indicates the 2nd logging level from the top. 56 | /// 57 | ERROR, 58 | /// 59 | /// Indicates the top logging level. 60 | /// 61 | FATAL 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /websocket-sharp/WebSocketState.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | /* 3 | * WebSocketState.cs 4 | * 5 | * The MIT License 6 | * 7 | * Copyright (c) 2010-2013 sta.blockhead 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | */ 27 | #endregion 28 | 29 | using System; 30 | 31 | namespace WebSocketSharp { 32 | 33 | /// 34 | /// Contains the values of the state of the WebSocket connection. 35 | /// 36 | /// 37 | /// The WebSocketState enumeration contains the values of the state of the WebSocket connection defined in 38 | /// The WebSocket API. 39 | /// 40 | public enum WebSocketState : ushort 41 | { 42 | /// 43 | /// Equivalent to numeric value 0. Indicates that the connection has not yet been established. 44 | /// 45 | CONNECTING = 0, 46 | /// 47 | /// Equivalent to numeric value 1. Indicates that the connection is established and the communication 48 | /// is possible. 49 | /// 50 | OPEN = 1, 51 | /// 52 | /// Equivalent to numeric value 2. Indicates that the connection is going through the closing handshake, 53 | /// or the WebSocket.Close method has been invoked. 54 | /// 55 | CLOSING = 2, 56 | /// 57 | /// Equivalent to numeric value 3. Indicates that the connection has been closed or could not be opened. 58 | /// 59 | CLOSED = 3 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /websocket-sharp/Opcode.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | /* 3 | * Opcode.cs 4 | * 5 | * The MIT License 6 | * 7 | * Copyright (c) 2012-2013 sta.blockhead 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | */ 27 | #endregion 28 | 29 | using System; 30 | 31 | namespace WebSocketSharp { 32 | 33 | /// 34 | /// Contains the values of the opcodes that denotes the frame type of the WebSocket frame. 35 | /// 36 | /// 37 | /// The Opcode enumeration contains the values of the opcodes defined in 38 | /// RFC 6455 for the WebSocket protocol. 39 | /// 40 | public enum Opcode : byte 41 | { 42 | /// 43 | /// Equivalent to numeric value 0. Indicates a continuation frame. 44 | /// 45 | CONT = 0x0, 46 | /// 47 | /// Equivalent to numeric value 1. Indicates a text frame. 48 | /// 49 | TEXT = 0x1, 50 | /// 51 | /// Equivalent to numeric value 2. Indicates a binary frame. 52 | /// 53 | BINARY = 0x2, 54 | /// 55 | /// Equivalent to numeric value 8. Indicates a connection close frame. 56 | /// 57 | CLOSE = 0x8, 58 | /// 59 | /// Equivalent to numeric value 9. Indicates a ping frame. 60 | /// 61 | PING = 0x9, 62 | /// 63 | /// Equivalent to numeric value 10. Indicates a pong frame. 64 | /// 65 | PONG = 0xa 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /websocket-sharp/Net/Security/SslStream.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | /* 3 | * SslStream.cs 4 | * 5 | * The MIT License 6 | * 7 | * Copyright (c) 2012-2013 sta.blockhead 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | */ 27 | #endregion 28 | 29 | using System; 30 | using System.Net.Security; 31 | using System.Net.Sockets; 32 | 33 | namespace WebSocketSharp.Net.Security { 34 | 35 | internal class SslStream : System.Net.Security.SslStream 36 | { 37 | #region Public Constructors 38 | 39 | public SslStream(NetworkStream innerStream) 40 | : base(innerStream) 41 | { 42 | } 43 | 44 | public SslStream(NetworkStream innerStream, bool leaveInnerStreamOpen) 45 | : base(innerStream, leaveInnerStreamOpen) 46 | { 47 | } 48 | 49 | public SslStream( 50 | NetworkStream innerStream, 51 | bool leaveInnerStreamOpen, 52 | RemoteCertificateValidationCallback userCertificateValidationCallback 53 | ) : base(innerStream, leaveInnerStreamOpen, userCertificateValidationCallback) 54 | { 55 | } 56 | 57 | public SslStream( 58 | NetworkStream innerStream, 59 | bool leaveInnerStreamOpen, 60 | RemoteCertificateValidationCallback userCertificateValidationCallback, 61 | LocalCertificateSelectionCallback userCertificateSelectionCallback 62 | ) : base( 63 | innerStream, 64 | leaveInnerStreamOpen, 65 | userCertificateValidationCallback, 66 | userCertificateSelectionCallback 67 | ) 68 | { 69 | } 70 | 71 | #endregion 72 | 73 | #region Public Properties 74 | 75 | public bool DataAvailable { 76 | get { 77 | return ((NetworkStream)InnerStream).DataAvailable; 78 | } 79 | } 80 | 81 | #endregion 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /websocket-sharp/Net/HttpHeaderInfo.cs: -------------------------------------------------------------------------------- 1 | // 2 | // HttpHeaderInfo.cs 3 | // 4 | // Authors: 5 | // sta (sta.blockhead@gmail.com) 6 | // 7 | // Copyright (c) 2013 sta.blockhead 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining 10 | // a copy of this software and associated documentation files (the 11 | // "Software"), to deal in the Software without restriction, including 12 | // without limitation the rights to use, copy, modify, merge, publish, 13 | // distribute, sublicense, and/or sell copies of the Software, and to 14 | // permit persons to whom the Software is furnished to do so, subject to 15 | // the following conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be 18 | // included in all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 24 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 25 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 26 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 27 | // 28 | 29 | using System; 30 | 31 | namespace WebSocketSharp.Net { 32 | 33 | class HttpHeaderInfo { 34 | 35 | #region Constructor 36 | 37 | public HttpHeaderInfo () 38 | { 39 | } 40 | 41 | #endregion 42 | 43 | #region Properties 44 | 45 | public bool IsMultiValueInRequest { 46 | get { 47 | return (Type & HttpHeaderType.MultiValueInRequest) == HttpHeaderType.MultiValueInRequest; 48 | } 49 | } 50 | 51 | public bool IsMultiValueInResponse { 52 | get { 53 | return (Type & HttpHeaderType.MultiValueInResponse) == HttpHeaderType.MultiValueInResponse; 54 | } 55 | } 56 | 57 | public bool IsRequest { 58 | get { 59 | return (Type & HttpHeaderType.Request) == HttpHeaderType.Request; 60 | } 61 | } 62 | 63 | public bool IsResponse { 64 | get { 65 | return (Type & HttpHeaderType.Response) == HttpHeaderType.Response; 66 | } 67 | } 68 | 69 | public string Name { get; set; } 70 | 71 | public HttpHeaderType Type { get; set; } 72 | 73 | #endregion 74 | 75 | #region Methods 76 | 77 | public bool IsMultiValue (bool response) 78 | { 79 | return (Type & HttpHeaderType.MultiValue) != HttpHeaderType.MultiValue 80 | ? response 81 | ? IsMultiValueInResponse 82 | : IsMultiValueInRequest 83 | : response 84 | ? IsResponse 85 | : IsRequest; 86 | } 87 | 88 | public bool IsRestricted (bool response) 89 | { 90 | return (Type & HttpHeaderType.Restricted) != HttpHeaderType.Restricted 91 | ? false 92 | : response 93 | ? IsResponse 94 | : IsRequest; 95 | } 96 | 97 | #endregion 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /websocket-sharp/ErrorEventArgs.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | /* 3 | * ErrorEventArgs.cs 4 | * 5 | * The MIT License 6 | * 7 | * Copyright (c) 2012-2013 sta.blockhead 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | */ 27 | #endregion 28 | 29 | using System; 30 | 31 | namespace WebSocketSharp 32 | { 33 | /// 34 | /// Contains the event data associated with a event. 35 | /// 36 | /// 37 | /// A event occurs when the gets an error. 38 | /// If you would like to get the error message, you should access the 39 | /// property. 40 | /// 41 | public class ErrorEventArgs : EventArgs 42 | { 43 | #region Private Fields 44 | 45 | private string _message; 46 | private Exception _exception; 47 | 48 | #endregion 49 | 50 | #region Internal Constructors 51 | 52 | internal ErrorEventArgs (string message) 53 | : this (message, null) 54 | { 55 | } 56 | 57 | internal ErrorEventArgs (string message, Exception exception) 58 | { 59 | _message = message; 60 | _exception = exception; 61 | } 62 | 63 | #endregion 64 | 65 | #region Public Properties 66 | 67 | /// 68 | /// Gets the error message. 69 | /// 70 | /// 71 | /// A that represents the error message. 72 | /// 73 | public string Message { 74 | get { 75 | return _message; 76 | } 77 | } 78 | 79 | /// 80 | /// Gets the exception that caused the error. 81 | /// 82 | /// A instance that represents the cause of the error, 83 | /// or if the error isn't due to an exception. 84 | /// 85 | public Exception Exception { 86 | get { 87 | return _exception; 88 | } 89 | } 90 | 91 | #endregion 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /websocket-sharp/WebSocketException.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | /* 3 | * WebSocketException.cs 4 | * 5 | * The MIT License 6 | * 7 | * Copyright (c) 2012-2014 sta.blockhead 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | */ 27 | #endregion 28 | 29 | using System; 30 | 31 | namespace WebSocketSharp 32 | { 33 | /// 34 | /// Represents the exception that occurred when attempting to perform an 35 | /// operation on the WebSocket connection. 36 | /// 37 | public class WebSocketException : Exception 38 | { 39 | #region Internal Constructors 40 | 41 | internal WebSocketException () 42 | : this (CloseStatusCode.ABNORMAL, null, null) 43 | { 44 | } 45 | 46 | internal WebSocketException (CloseStatusCode code) 47 | : this (code, null, null) 48 | { 49 | } 50 | 51 | internal WebSocketException (string reason) 52 | : this (CloseStatusCode.ABNORMAL, reason, null) 53 | { 54 | } 55 | 56 | internal WebSocketException (string reason, Exception innerException) 57 | : this (CloseStatusCode.ABNORMAL, reason, innerException) 58 | { 59 | } 60 | 61 | internal WebSocketException (CloseStatusCode code, string reason) 62 | : this (code, reason, null) 63 | { 64 | } 65 | 66 | internal WebSocketException ( 67 | CloseStatusCode code, string reason, Exception innerException) 68 | : base (reason ?? code.GetMessage (), innerException) 69 | { 70 | Code = code; 71 | } 72 | 73 | #endregion 74 | 75 | #region Public Properties 76 | 77 | /// 78 | /// Gets the associated with the 79 | /// . 80 | /// 81 | /// 82 | /// One of the values, indicates the cause of 83 | /// the . 84 | /// 85 | public CloseStatusCode Code { 86 | get; private set; 87 | } 88 | 89 | #endregion 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /websocket-sharp/websocket-sharp.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | AnyCPU 6 | 9.0.21022 7 | 2.0 8 | {B357BAC7-529E-4D81-A0D2-71041B19C8DE} 9 | Library 10 | WebSocketSharp 11 | websocket-sharp 12 | v3.5 13 | True 14 | websocket-sharp.snk 15 | 16 | 17 | True 18 | full 19 | False 20 | bin\Debug 21 | DEBUG 22 | prompt 23 | 4 24 | False 25 | 26 | 27 | none 28 | False 29 | bin\Release 30 | prompt 31 | 4 32 | False 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /websocket-sharp/HandshakeBase.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | /* 3 | * HandshakeBase.cs 4 | * 5 | * The MIT License 6 | * 7 | * Copyright (c) 2012-2014 sta.blockhead 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | */ 27 | #endregion 28 | 29 | using System; 30 | using System.Collections.Specialized; 31 | using System.Text; 32 | using WebSocketSharp.Net; 33 | 34 | namespace WebSocketSharp 35 | { 36 | internal abstract class HandshakeBase 37 | { 38 | #region Private Fields 39 | 40 | private byte [] _entity; 41 | private NameValueCollection _headers; 42 | private Version _version; 43 | 44 | #endregion 45 | 46 | #region Protected Const Fields 47 | 48 | protected const string CrLf = "\r\n"; 49 | 50 | #endregion 51 | 52 | #region Protected Constructors 53 | 54 | protected HandshakeBase () 55 | { 56 | } 57 | 58 | #endregion 59 | 60 | #region Internal Properties 61 | 62 | internal byte [] EntityBodyData { 63 | get { 64 | return _entity; 65 | } 66 | 67 | set { 68 | _entity = value; 69 | } 70 | } 71 | 72 | #endregion 73 | 74 | #region Public Properties 75 | 76 | public string EntityBody { 77 | get { 78 | return _entity != null && _entity.LongLength > 0 79 | ? getEncoding (_headers ["Content-Type"]).GetString (_entity) 80 | : String.Empty; 81 | } 82 | } 83 | 84 | public NameValueCollection Headers { 85 | get { 86 | return _headers ?? (_headers = new NameValueCollection ()); 87 | } 88 | 89 | protected set { 90 | _headers = value; 91 | } 92 | } 93 | 94 | public Version ProtocolVersion { 95 | get { 96 | return _version ?? (_version = HttpVersion.Version11); 97 | } 98 | 99 | protected set { 100 | _version = value; 101 | } 102 | } 103 | 104 | #endregion 105 | 106 | #region Private Methods 107 | 108 | private static Encoding getEncoding (string contentType) 109 | { 110 | if (contentType == null || contentType.Length == 0) 111 | return Encoding.UTF8; 112 | 113 | var i = contentType.IndexOf ("charset=", StringComparison.Ordinal); 114 | if (i == -1) 115 | return Encoding.UTF8; 116 | 117 | var charset = contentType.Substring (i + 8); 118 | i = charset.IndexOf (';'); 119 | if (i != -1) 120 | charset = charset.Substring (0, i); 121 | 122 | return Encoding.GetEncoding (charset); 123 | } 124 | 125 | #endregion 126 | 127 | #region Public Methods 128 | 129 | public byte [] ToByteArray () 130 | { 131 | return Encoding.UTF8.GetBytes (ToString ()); 132 | } 133 | 134 | #endregion 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /websocket-sharp/CloseEventArgs.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | /* 3 | * CloseEventArgs.cs 4 | * 5 | * The MIT License 6 | * 7 | * Copyright (c) 2012-2014 sta.blockhead 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | */ 27 | #endregion 28 | 29 | using System; 30 | using System.Text; 31 | 32 | namespace WebSocketSharp 33 | { 34 | /// 35 | /// Contains the event data associated with a 36 | /// event. 37 | /// 38 | /// 39 | /// A event occurs when the WebSocket connection 40 | /// has been closed. If you want to get the reason for closure, you access the 41 | /// or property. 42 | /// 43 | public class CloseEventArgs : EventArgs 44 | { 45 | #region Private Fields 46 | 47 | private bool _clean; 48 | private ushort _code; 49 | private string _reason; 50 | 51 | #endregion 52 | 53 | #region Internal Constructors 54 | 55 | internal CloseEventArgs (PayloadData payload) 56 | { 57 | var data = payload.ApplicationData; 58 | _code = getCodeFrom (data); 59 | _reason = getReasonFrom (data); 60 | _clean = false; 61 | } 62 | 63 | #endregion 64 | 65 | #region Public Properties 66 | 67 | /// 68 | /// Gets the status code for closure. 69 | /// 70 | /// 71 | /// A that indicates the status code for closure if any. 72 | /// 73 | public ushort Code { 74 | get { 75 | return _code; 76 | } 77 | } 78 | 79 | /// 80 | /// Gets the reason for closure. 81 | /// 82 | /// 83 | /// A that represents the reason for closure if any. 84 | /// 85 | public string Reason { 86 | get { 87 | return _reason; 88 | } 89 | } 90 | 91 | /// 92 | /// Gets a value indicating whether the WebSocket connection has been closed 93 | /// cleanly. 94 | /// 95 | /// 96 | /// true if the WebSocket connection has been closed cleanly; 97 | /// otherwise, false. 98 | /// 99 | public bool WasClean { 100 | get { 101 | return _clean; 102 | } 103 | 104 | internal set { 105 | _clean = value; 106 | } 107 | } 108 | 109 | #endregion 110 | 111 | #region Private Methods 112 | 113 | private static ushort getCodeFrom (byte [] data) 114 | { 115 | return data.Length > 1 116 | ? data.SubArray (0, 2).ToUInt16 (ByteOrder.BIG) 117 | : (ushort) CloseStatusCode.NO_STATUS_CODE; 118 | } 119 | 120 | private static string getReasonFrom (byte [] data) 121 | { 122 | var len = data.Length; 123 | return len > 2 124 | ? Encoding.UTF8.GetString (data.SubArray (2, len - 2)) 125 | : String.Empty; 126 | } 127 | 128 | #endregion 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /websocket-sharp/MessageEventArgs.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | /* 3 | * MessageEventArgs.cs 4 | * 5 | * The MIT License 6 | * 7 | * Copyright (c) 2012-2013 sta.blockhead 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | */ 27 | #endregion 28 | 29 | using System; 30 | using System.Text; 31 | 32 | namespace WebSocketSharp 33 | { 34 | /// 35 | /// Contains the event data associated with a event. 36 | /// 37 | /// 38 | /// A event occurs when the receives 39 | /// a text or binary data frame. 40 | /// If you want to get the received data, you access the or 41 | /// property. 42 | /// 43 | public class MessageEventArgs : EventArgs 44 | { 45 | #region Private Fields 46 | 47 | private string _data; 48 | private Opcode _opcode; 49 | private byte[] _rawData; 50 | 51 | #endregion 52 | 53 | #region Internal Constructors 54 | 55 | internal MessageEventArgs (Opcode opcode, byte[] data) 56 | { 57 | if ((ulong) data.LongLength > PayloadData.MaxLength) 58 | throw new WebSocketException (CloseStatusCode.TOO_BIG); 59 | 60 | _opcode = opcode; 61 | _rawData = data; 62 | } 63 | 64 | internal MessageEventArgs (Opcode opcode, PayloadData payload) 65 | { 66 | _opcode = opcode; 67 | _rawData = payload.ApplicationData; 68 | } 69 | 70 | #endregion 71 | 72 | #region Public Properties 73 | 74 | /// 75 | /// Gets the received data as a . 76 | /// 77 | /// 78 | /// A that contains the received data. 79 | /// 80 | public string Data { 81 | get { 82 | if (_data == null) 83 | { 84 | _data = convertToString (_opcode, _rawData); 85 | } 86 | return _data; 87 | } 88 | } 89 | 90 | /// 91 | /// Gets the received data as an array of . 92 | /// 93 | /// 94 | /// An array of that contains the received data. 95 | /// 96 | public byte [] RawData { 97 | get { 98 | return _rawData; 99 | } 100 | } 101 | 102 | /// 103 | /// Gets the type of the received data. 104 | /// 105 | /// 106 | /// One of the values, indicates the type of the received data. 107 | /// 108 | public Opcode Type { 109 | get { 110 | return _opcode; 111 | } 112 | } 113 | 114 | #endregion 115 | 116 | #region Private Methods 117 | 118 | private static string convertToString (Opcode opcode, byte [] data) 119 | { 120 | return data.LongLength == 0 121 | ? String.Empty 122 | : opcode == Opcode.TEXT 123 | ? Encoding.UTF8.GetString (data) 124 | : opcode.ToString (); 125 | } 126 | 127 | #endregion 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /websocket-sharp/Net/CookieException.cs: -------------------------------------------------------------------------------- 1 | // 2 | // CookieException.cs 3 | // Copied from System.Net.CookieException.cs 4 | // 5 | // Author: 6 | // Lawrence Pit (loz@cable.a2000.nl) 7 | // 8 | // Copyright (c) 2012-2013 sta.blockhead (sta.blockhead@gmail.com) 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining 11 | // a copy of this software and associated documentation files (the 12 | // "Software"), to deal in the Software without restriction, including 13 | // without limitation the rights to use, copy, modify, merge, publish, 14 | // distribute, sublicense, and/or sell copies of the Software, and to 15 | // permit persons to whom the Software is furnished to do so, subject to 16 | // the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be 19 | // included in all copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 22 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 24 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 25 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 26 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 27 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 28 | // 29 | 30 | using System; 31 | using System.Globalization; 32 | using System.Runtime.Serialization; 33 | using System.Security.Permissions; 34 | 35 | namespace WebSocketSharp.Net { 36 | 37 | /// 38 | /// The exception that is thrown when a gets an error. 39 | /// 40 | [Serializable] 41 | public class CookieException : FormatException, ISerializable 42 | { 43 | #region Internal Constructors 44 | 45 | internal CookieException (string message) 46 | : base (message) 47 | { 48 | } 49 | 50 | internal CookieException (string message, Exception innerException) 51 | : base (message, innerException) 52 | { 53 | } 54 | 55 | #endregion 56 | 57 | #region Protected Constructor 58 | 59 | /// 60 | /// Initializes a new instance of the class 61 | /// with the specified and . 62 | /// 63 | /// 64 | /// A that holds the serialized object data. 65 | /// 66 | /// 67 | /// A that contains the contextual information about the source or destination. 68 | /// 69 | protected CookieException (SerializationInfo serializationInfo, StreamingContext streamingContext) 70 | : base (serializationInfo, streamingContext) 71 | { 72 | } 73 | 74 | #endregion 75 | 76 | #region Public Constructor 77 | 78 | /// 79 | /// Initializes a new instance of the class. 80 | /// 81 | public CookieException () 82 | : base () 83 | { 84 | } 85 | 86 | #endregion 87 | 88 | #region Explicit Interface Implementation 89 | 90 | /// 91 | /// Populates the specified with the data needed to serialize the . 92 | /// 93 | /// 94 | /// A that holds the serialized object data. 95 | /// 96 | /// 97 | /// A that specifies the destination for the serialization. 98 | /// 99 | [SecurityPermission (SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter, SerializationFormatter = true)] 100 | void ISerializable.GetObjectData (SerializationInfo serializationInfo, StreamingContext streamingContext) 101 | { 102 | base.GetObjectData (serializationInfo, streamingContext); 103 | } 104 | 105 | #endregion 106 | 107 | #region Public Method 108 | 109 | /// 110 | /// Populates the specified with the data needed to serialize the . 111 | /// 112 | /// 113 | /// A that holds the serialized object data. 114 | /// 115 | /// 116 | /// A that specifies the destination for the serialization. 117 | /// 118 | [SecurityPermission (SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)] 119 | public override void GetObjectData (SerializationInfo serializationInfo, StreamingContext streamingContext) 120 | { 121 | base.GetObjectData (serializationInfo, streamingContext); 122 | } 123 | 124 | #endregion 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /websocket-sharp/LogData.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | /* 3 | * LogData.cs 4 | * 5 | * The MIT License 6 | * 7 | * Copyright (c) 2013 sta.blockhead 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | */ 27 | #endregion 28 | 29 | using System; 30 | using System.Diagnostics; 31 | using System.Text; 32 | 33 | namespace WebSocketSharp 34 | { 35 | /// 36 | /// Represents the log data used by the class. 37 | /// 38 | public class LogData 39 | { 40 | #region Private Fields 41 | 42 | private StackFrame _caller; 43 | private DateTime _date; 44 | private LogLevel _level; 45 | private string _message; 46 | 47 | #endregion 48 | 49 | #region Internal Constructors 50 | 51 | internal LogData (LogLevel level, StackFrame caller, string message) 52 | { 53 | _level = level; 54 | _caller = caller; 55 | _message = message; 56 | _date = DateTime.Now; 57 | } 58 | 59 | #endregion 60 | 61 | #region Public Properties 62 | 63 | /// 64 | /// Gets the information of the logging method caller. 65 | /// 66 | /// 67 | /// A that contains the information of a logging method caller. 68 | /// 69 | public StackFrame Caller { 70 | get { 71 | return _caller; 72 | } 73 | } 74 | 75 | /// 76 | /// Gets the date and time when the log data was created. 77 | /// 78 | /// 79 | /// A that contains the date and time when the log data was created. 80 | /// 81 | public DateTime Date { 82 | get { 83 | return _date; 84 | } 85 | } 86 | 87 | /// 88 | /// Gets the logging level associated with the log data. 89 | /// 90 | /// 91 | /// One of the values that indicates the logging level 92 | /// associated with the log data. 93 | /// 94 | public LogLevel Level { 95 | get { 96 | return _level; 97 | } 98 | } 99 | 100 | /// 101 | /// Gets the message of the log data. 102 | /// 103 | /// 104 | /// A that contains the message of a log data. 105 | /// 106 | public string Message { 107 | get { 108 | return _message; 109 | } 110 | } 111 | 112 | #endregion 113 | 114 | #region Public Methods 115 | 116 | /// 117 | /// Returns a that represents the current . 118 | /// 119 | /// 120 | /// A that represents the current . 121 | /// 122 | public override string ToString () 123 | { 124 | var header = String.Format ("{0}|{1,-5}|", _date, _level); 125 | var method = _caller.GetMethod (); 126 | var type = method.DeclaringType; 127 | #if DEBUG 128 | var lineNum = _caller.GetFileLineNumber (); 129 | var headerAndCaller = String.Format ("{0}{1}.{2}:{3}|", header, type.Name, method.Name, lineNum); 130 | #else 131 | var headerAndCaller = String.Format ("{0}{1}.{2}|", header, type.Name, method.Name); 132 | #endif 133 | 134 | var messages = _message.Replace ("\r\n", "\n").TrimEnd ('\n').Split ('\n'); 135 | if (messages.Length <= 1) 136 | return String.Format ("{0}{1}", headerAndCaller, _message); 137 | 138 | var output = new StringBuilder (String.Format ("{0}{1}\n", headerAndCaller, messages [0]), 64); 139 | var space = header.Length; 140 | var format = String.Format ("{{0,{0}}}{{1}}\n", space); 141 | for (var i = 1; i < messages.Length; i++) 142 | output.AppendFormat (format, "", messages [i]); 143 | 144 | output.Length--; 145 | return output.ToString (); 146 | } 147 | 148 | #endregion 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /websocket-sharp/PayloadData.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | /* 3 | * PayloadData.cs 4 | * 5 | * The MIT License 6 | * 7 | * Copyright (c) 2012-2013 sta.blockhead 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | */ 27 | #endregion 28 | 29 | using System; 30 | using System.Collections; 31 | using System.Collections.Generic; 32 | using System.IO; 33 | using System.Linq; 34 | using System.Text; 35 | 36 | namespace WebSocketSharp 37 | { 38 | internal class PayloadData : IEnumerable 39 | { 40 | #region Public Const Fields 41 | 42 | public const ulong MaxLength = long.MaxValue; 43 | 44 | #endregion 45 | 46 | #region Public Constructors 47 | 48 | public PayloadData () 49 | : this (new byte []{}) 50 | { 51 | } 52 | 53 | public PayloadData (byte [] appData) 54 | : this (new byte []{}, appData) 55 | { 56 | } 57 | 58 | public PayloadData (string appData) 59 | : this (Encoding.UTF8.GetBytes (appData)) 60 | { 61 | } 62 | 63 | public PayloadData (byte [] appData, bool masked) 64 | : this (new byte []{}, appData, masked) 65 | { 66 | } 67 | 68 | public PayloadData (byte [] extData, byte [] appData) 69 | : this (extData, appData, false) 70 | { 71 | } 72 | 73 | public PayloadData (byte [] extData, byte [] appData, bool masked) 74 | { 75 | if ((ulong) extData.LongLength + (ulong) appData.LongLength > MaxLength) 76 | throw new ArgumentOutOfRangeException ( 77 | "The length of 'extData' plus 'appData' must be less than MaxLength."); 78 | 79 | ExtensionData = extData; 80 | ApplicationData = appData; 81 | IsMasked = masked; 82 | } 83 | 84 | #endregion 85 | 86 | #region Internal Properties 87 | 88 | internal bool ContainsReservedCloseStatusCode { 89 | get { 90 | return ApplicationData.Length > 1 91 | ? ApplicationData.SubArray (0, 2).ToUInt16 (ByteOrder.BIG).IsReserved () 92 | : false; 93 | } 94 | } 95 | 96 | internal bool IsMasked { 97 | get; private set; 98 | } 99 | 100 | #endregion 101 | 102 | #region Public Properties 103 | 104 | public byte [] ExtensionData { 105 | get; private set; 106 | } 107 | 108 | public byte [] ApplicationData { 109 | get; private set; 110 | } 111 | 112 | public ulong Length { 113 | get { 114 | return (ulong) (ExtensionData.LongLength + ApplicationData.LongLength); 115 | } 116 | } 117 | 118 | #endregion 119 | 120 | #region Private Methods 121 | 122 | private static void mask (byte [] src, byte [] key) 123 | { 124 | for (long i = 0; i < src.LongLength; i++) 125 | src [i] = (byte) (src [i] ^ key [i % 4]); 126 | } 127 | 128 | #endregion 129 | 130 | #region Internal Methods 131 | 132 | #endregion 133 | 134 | #region Public Methods 135 | 136 | public IEnumerator GetEnumerator () 137 | { 138 | foreach (byte b in ExtensionData) 139 | yield return b; 140 | 141 | foreach (byte b in ApplicationData) 142 | yield return b; 143 | } 144 | 145 | public void Mask (byte [] maskingKey) 146 | { 147 | if (ExtensionData.LongLength > 0) 148 | mask (ExtensionData, maskingKey); 149 | 150 | if (ApplicationData.LongLength > 0) 151 | mask (ApplicationData, maskingKey); 152 | 153 | IsMasked = !IsMasked; 154 | } 155 | 156 | public byte [] ToByteArray () 157 | { 158 | return ExtensionData.LongLength > 0 159 | ? this.ToArray () 160 | : ApplicationData; 161 | } 162 | 163 | public override string ToString () 164 | { 165 | return BitConverter.ToString (ToByteArray ()); 166 | } 167 | 168 | #endregion 169 | 170 | #region Explicitly Implemented Interface Members 171 | 172 | IEnumerator IEnumerable.GetEnumerator () 173 | { 174 | return GetEnumerator (); 175 | } 176 | 177 | #endregion 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /websocket-sharp/HandshakeResponse.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | /* 3 | * HandshakeResponse.cs 4 | * 5 | * The MIT License 6 | * 7 | * Copyright (c) 2012-2014 sta.blockhead 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | */ 27 | #endregion 28 | 29 | using System; 30 | using System.Collections.Specialized; 31 | using System.Text; 32 | using WebSocketSharp.Net; 33 | 34 | namespace WebSocketSharp 35 | { 36 | internal class HandshakeResponse : HandshakeBase 37 | { 38 | #region Private Fields 39 | 40 | private string _code; 41 | private string _reason; 42 | 43 | #endregion 44 | 45 | #region Private Constructors 46 | 47 | private HandshakeResponse () 48 | { 49 | } 50 | 51 | #endregion 52 | 53 | #region Public Constructors 54 | 55 | public HandshakeResponse (HttpStatusCode code) 56 | { 57 | _code = ((int) code).ToString (); 58 | _reason = code.GetDescription (); 59 | 60 | var headers = Headers; 61 | headers ["Server"] = "websocket-sharp/1.0"; 62 | if (code == HttpStatusCode.SwitchingProtocols) { 63 | headers ["Upgrade"] = "websocket"; 64 | headers ["Connection"] = "Upgrade"; 65 | } 66 | } 67 | 68 | #endregion 69 | 70 | #region Public Properties 71 | public CookieCollection Cookies { 72 | get { 73 | return Headers.GetCookies (true); 74 | } 75 | } 76 | 77 | public bool IsUnauthorized { 78 | get { 79 | return _code == "401"; 80 | } 81 | } 82 | 83 | public bool IsWebSocketResponse { 84 | get { 85 | var headers = Headers; 86 | return ProtocolVersion >= HttpVersion.Version11 && 87 | _code == "101" && 88 | headers.Contains ("Upgrade", "websocket") && 89 | headers.Contains ("Connection", "Upgrade"); 90 | } 91 | } 92 | 93 | public string Reason { 94 | get { 95 | return _reason; 96 | } 97 | 98 | private set { 99 | _reason = value; 100 | } 101 | } 102 | 103 | public string StatusCode { 104 | get { 105 | return _code; 106 | } 107 | 108 | private set { 109 | _code = value; 110 | } 111 | } 112 | 113 | #endregion 114 | 115 | #region Public Methods 116 | 117 | public static HandshakeResponse CreateCloseResponse (HttpStatusCode code) 118 | { 119 | var res = new HandshakeResponse (code); 120 | res.Headers ["Connection"] = "close"; 121 | 122 | return res; 123 | } 124 | 125 | public static HandshakeResponse Parse (string [] headerParts) 126 | { 127 | var statusLine = headerParts [0].Split (new char [] { ' ' }, 3); 128 | if (statusLine.Length != 3) 129 | throw new ArgumentException ("Invalid status line: " + headerParts [0]); 130 | 131 | var headers = new WebHeaderCollection (); 132 | for (int i = 1; i < headerParts.Length; i++) 133 | headers.SetInternal (headerParts [i], true); 134 | 135 | return new HandshakeResponse { 136 | Headers = headers, 137 | ProtocolVersion = new Version (statusLine [0].Substring (5)), 138 | Reason = statusLine [2], 139 | StatusCode = statusLine [1] 140 | }; 141 | } 142 | 143 | public void SetCookies (CookieCollection cookies) 144 | { 145 | if (cookies == null || cookies.Count == 0) 146 | return; 147 | 148 | var headers = Headers; 149 | foreach (var cookie in cookies.Sorted) 150 | headers.Add ("Set-Cookie", cookie.ToResponseString ()); 151 | } 152 | 153 | public override string ToString () 154 | { 155 | var buffer = new StringBuilder (64); 156 | buffer.AppendFormat ( 157 | "HTTP/{0} {1} {2}{3}", ProtocolVersion, _code, _reason, CrLf); 158 | 159 | var headers = Headers; 160 | foreach (var key in headers.AllKeys) 161 | buffer.AppendFormat ("{0}: {1}{2}", key, headers [key], CrLf); 162 | 163 | buffer.Append (CrLf); 164 | 165 | var entity = EntityBody; 166 | if (entity.Length > 0) 167 | buffer.Append (entity); 168 | 169 | return buffer.ToString (); 170 | } 171 | 172 | #endregion 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /websocket-sharp/CloseStatusCode.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | /* 3 | * CloseStatusCode.cs 4 | * 5 | * The MIT License 6 | * 7 | * Copyright (c) 2012-2014 sta.blockhead 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | */ 27 | #endregion 28 | 29 | using System; 30 | 31 | namespace WebSocketSharp 32 | { 33 | /// 34 | /// Contains the values of the status codes for the WebSocket connection 35 | /// closure. 36 | /// 37 | /// 38 | /// 39 | /// The CloseStatusCode enumeration contains the values of the status codes 40 | /// for the WebSocket connection closure defined in 41 | /// RFC 6455 42 | /// for the WebSocket protocol. 43 | /// 44 | /// 45 | /// "Reserved value" must not be set as a status code in a close control frame 46 | /// by an endpoint. It's designated for use in applications expecting a status 47 | /// code to indicate that the connection was closed due to a system grounds. 48 | /// 49 | /// 50 | public enum CloseStatusCode : ushort 51 | { 52 | /// 53 | /// Equivalent to close status 1000. 54 | /// Indicates a normal closure. 55 | /// 56 | NORMAL = 1000, 57 | /// 58 | /// Equivalent to close status 1001. 59 | /// Indicates that an endpoint is "going away". 60 | /// 61 | AWAY = 1001, 62 | /// 63 | /// Equivalent to close status 1002. 64 | /// Indicates that an endpoint is terminating the connection due to a protocol 65 | /// error. 66 | /// 67 | PROTOCOL_ERROR = 1002, 68 | /// 69 | /// Equivalent to close status 1003. 70 | /// Indicates that an endpoint is terminating the connection because it has 71 | /// received a type of data it cannot accept. 72 | /// 73 | INCORRECT_DATA = 1003, 74 | /// 75 | /// Equivalent to close status 1004. 76 | /// Still undefined. Reserved value. 77 | /// 78 | UNDEFINED = 1004, 79 | /// 80 | /// Equivalent to close status 1005. 81 | /// Indicates that no status code was actually present. Reserved value. 82 | /// 83 | NO_STATUS_CODE = 1005, 84 | /// 85 | /// Equivalent to close status 1006. 86 | /// Indicates that the connection was closed abnormally. Reserved value. 87 | /// 88 | ABNORMAL = 1006, 89 | /// 90 | /// Equivalent to close status 1007. 91 | /// Indicates that an endpoint is terminating the connection because it has 92 | /// received the data within a message that wasn't consistent with the type of 93 | /// the message. 94 | /// 95 | INCONSISTENT_DATA = 1007, 96 | /// 97 | /// Equivalent to close status 1008. 98 | /// Indicates that an endpoint is terminating the connection because it has 99 | /// received a message that violates its policy. 100 | /// 101 | POLICY_VIOLATION = 1008, 102 | /// 103 | /// Equivalent to close status 1009. 104 | /// Indicates that an endpoint is terminating the connection because it has 105 | /// received a message that is too big to process. 106 | /// 107 | TOO_BIG = 1009, 108 | /// 109 | /// Equivalent to close status 1010. 110 | /// Indicates that an endpoint (client) is terminating the connection because 111 | /// it has expected the server to negotiate one or more extension, but the 112 | /// server didn't return them in the response message of the WebSocket 113 | /// handshake. 114 | /// 115 | IGNORE_EXTENSION = 1010, 116 | /// 117 | /// Equivalent to close status 1011. 118 | /// Indicates that the server is terminating the connection because it has 119 | /// encountered an unexpected condition that prevented it from fulfilling the 120 | /// request. 121 | /// 122 | SERVER_ERROR = 1011, 123 | /// 124 | /// Equivalent to close status 1015. 125 | /// Indicates that the connection was closed due to a failure to perform a TLS 126 | /// handshake. Reserved value. 127 | /// 128 | TLS_HANDSHAKE_FAILURE = 1015 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /websocket-sharp/HandshakeRequest.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | /* 3 | * HandshakeRequest.cs 4 | * 5 | * The MIT License 6 | * 7 | * Copyright (c) 2012-2014 sta.blockhead 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | */ 27 | #endregion 28 | 29 | using System; 30 | using System.Collections.Specialized; 31 | using System.Linq; 32 | using System.Text; 33 | using WebSocketSharp.Net; 34 | 35 | namespace WebSocketSharp 36 | { 37 | internal class HandshakeRequest : HandshakeBase 38 | { 39 | #region Private Fields 40 | 41 | private string _method; 42 | private NameValueCollection _queryString; 43 | private string _rawUrl; 44 | private Uri _uri; 45 | 46 | #endregion 47 | 48 | #region Private Constructors 49 | 50 | private HandshakeRequest () 51 | { 52 | } 53 | 54 | #endregion 55 | 56 | #region Public Constructors 57 | 58 | public HandshakeRequest (string uriString) 59 | { 60 | _uri = uriString.ToUri (); 61 | _rawUrl = _uri.IsAbsoluteUri ? _uri.PathAndQuery : uriString; 62 | _method = "GET"; 63 | 64 | var headers = Headers; 65 | headers ["User-Agent"] = "websocket-sharp/1.0"; 66 | headers ["Upgrade"] = "websocket"; 67 | headers ["Connection"] = "Upgrade"; 68 | } 69 | 70 | #endregion 71 | 72 | #region Public Properties 73 | 74 | public CookieCollection Cookies { 75 | get { 76 | return Headers.GetCookies (false); 77 | } 78 | } 79 | 80 | public string HttpMethod { 81 | get { 82 | return _method; 83 | } 84 | 85 | private set { 86 | _method = value; 87 | } 88 | } 89 | 90 | public bool IsWebSocketRequest { 91 | get { 92 | var headers = Headers; 93 | return _method == "GET" && 94 | ProtocolVersion >= HttpVersion.Version11 && 95 | headers.Contains ("Upgrade", "websocket") && 96 | headers.Contains ("Connection", "Upgrade"); 97 | } 98 | } 99 | 100 | public NameValueCollection QueryString { 101 | get { 102 | if (_queryString == null) { 103 | _queryString = new NameValueCollection (); 104 | 105 | var i = RawUrl.IndexOf ('?'); 106 | if (i > 0) { 107 | var query = RawUrl.Substring (i + 1); 108 | var components = query.Split ('&'); 109 | foreach (var c in components) { 110 | var nv = c.GetNameAndValue ("="); 111 | if (nv.Key != null) { 112 | var name = nv.Key.UrlDecode (); 113 | var val = nv.Value.UrlDecode (); 114 | _queryString.Add (name, val); 115 | } 116 | } 117 | } 118 | } 119 | 120 | return _queryString; 121 | } 122 | } 123 | 124 | public string RawUrl { 125 | get { 126 | return _rawUrl; 127 | } 128 | 129 | private set { 130 | _rawUrl = value; 131 | } 132 | } 133 | 134 | public Uri RequestUri { 135 | get { 136 | return _uri; 137 | } 138 | 139 | private set { 140 | _uri = value; 141 | } 142 | } 143 | 144 | #endregion 145 | 146 | #region Public Methods 147 | 148 | public static HandshakeRequest Parse (string [] headerParts) 149 | { 150 | var requestLine = headerParts [0].Split (new char [] { ' ' }, 3); 151 | if (requestLine.Length != 3) 152 | throw new ArgumentException ("Invalid request line: " + headerParts [0]); 153 | 154 | var headers = new WebHeaderCollection (); 155 | for (int i = 1; i < headerParts.Length; i++) 156 | headers.SetInternal (headerParts [i], false); 157 | 158 | return new HandshakeRequest { 159 | Headers = headers, 160 | HttpMethod = requestLine [0], 161 | ProtocolVersion = new Version (requestLine [2].Substring (5)), 162 | RawUrl = requestLine [1], 163 | RequestUri = requestLine [1].ToUri () 164 | }; 165 | } 166 | 167 | public void SetCookies (CookieCollection cookies) 168 | { 169 | if (cookies == null || cookies.Count == 0) 170 | return; 171 | 172 | var sorted = cookies.Sorted.ToArray (); 173 | var header = new StringBuilder (sorted [0].ToString (), 64); 174 | for (int i = 1; i < sorted.Length; i++) 175 | if (!sorted [i].Expired) 176 | header.AppendFormat ("; {0}", sorted [i].ToString ()); 177 | 178 | Headers ["Cookie"] = header.ToString (); 179 | } 180 | 181 | public override string ToString () 182 | { 183 | var buffer = new StringBuilder (64); 184 | buffer.AppendFormat ( 185 | "{0} {1} HTTP/{2}{3}", _method, _rawUrl, ProtocolVersion, CrLf); 186 | 187 | var headers = Headers; 188 | foreach (var key in headers.AllKeys) 189 | buffer.AppendFormat ("{0}: {1}{2}", key, headers [key], CrLf); 190 | 191 | buffer.Append (CrLf); 192 | 193 | var entity = EntityBody; 194 | if (entity.Length > 0) 195 | buffer.Append (entity); 196 | 197 | return buffer.ToString (); 198 | } 199 | 200 | #endregion 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /websocket-sharp.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 10.00 3 | # Visual Studio 2008 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "websocket-sharp", "websocket-sharp\websocket-sharp.csproj", "{B357BAC7-529E-4D81-A0D2-71041B19C8DE}" 5 | EndProject 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EchoClient", "EchoClient\EchoClient.csproj", "{3C35488A-1B9D-4E76-A1E4-BED3062E5517}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {3C35488A-1B9D-4E76-A1E4-BED3062E5517}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {3C35488A-1B9D-4E76-A1E4-BED3062E5517}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {3C35488A-1B9D-4E76-A1E4-BED3062E5517}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {3C35488A-1B9D-4E76-A1E4-BED3062E5517}.Release|Any CPU.Build.0 = Release|Any CPU 18 | {B357BAC7-529E-4D81-A0D2-71041B19C8DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {B357BAC7-529E-4D81-A0D2-71041B19C8DE}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {B357BAC7-529E-4D81-A0D2-71041B19C8DE}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {B357BAC7-529E-4D81-A0D2-71041B19C8DE}.Release|Any CPU.Build.0 = Release|Any CPU 22 | EndGlobalSection 23 | GlobalSection(MonoDevelopProperties) = preSolution 24 | Policies = $0 25 | $0.TextStylePolicy = $1 26 | $1.IndentWidth = 2 27 | $1.NoTabsAfterNonTabs = True 28 | $1.EolMarker = Unix 29 | $1.inheritsSet = VisualStudio 30 | $1.inheritsScope = text/plain 31 | $1.scope = text/x-csharp 32 | $0.CSharpFormattingPolicy = $2 33 | $2.inheritsSet = Mono 34 | $2.inheritsScope = text/x-csharp 35 | $2.scope = text/x-csharp 36 | $0.DotNetNamingPolicy = $3 37 | $3.DirectoryNamespaceAssociation = None 38 | $3.ResourceNamePolicy = FileFormatDefault 39 | $0.StandardHeader = $4 40 | $4.Text = 41 | $4.IncludeInNewFiles = True 42 | $0.NameConventionPolicy = $5 43 | $5.Rules = $6 44 | $6.NamingRule = $7 45 | $7.Name = Namespaces 46 | $7.AffectedEntity = Namespace 47 | $7.VisibilityMask = VisibilityMask 48 | $7.NamingStyle = PascalCase 49 | $7.IncludeInstanceMembers = True 50 | $7.IncludeStaticEntities = True 51 | $6.NamingRule = $8 52 | $8.Name = Types 53 | $8.AffectedEntity = Class, Struct, Enum, Delegate 54 | $8.VisibilityMask = Public 55 | $8.NamingStyle = PascalCase 56 | $8.IncludeInstanceMembers = True 57 | $8.IncludeStaticEntities = True 58 | $6.NamingRule = $9 59 | $9.Name = Interfaces 60 | $9.RequiredPrefixes = $10 61 | $10.String = I 62 | $9.AffectedEntity = Interface 63 | $9.VisibilityMask = Public 64 | $9.NamingStyle = PascalCase 65 | $9.IncludeInstanceMembers = True 66 | $9.IncludeStaticEntities = True 67 | $6.NamingRule = $11 68 | $11.Name = Attributes 69 | $11.RequiredSuffixes = $12 70 | $12.String = Attribute 71 | $11.AffectedEntity = CustomAttributes 72 | $11.VisibilityMask = Public 73 | $11.NamingStyle = PascalCase 74 | $11.IncludeInstanceMembers = True 75 | $11.IncludeStaticEntities = True 76 | $6.NamingRule = $13 77 | $13.Name = Event Arguments 78 | $13.RequiredSuffixes = $14 79 | $14.String = EventArgs 80 | $13.AffectedEntity = CustomEventArgs 81 | $13.VisibilityMask = Public 82 | $13.NamingStyle = PascalCase 83 | $13.IncludeInstanceMembers = True 84 | $13.IncludeStaticEntities = True 85 | $6.NamingRule = $15 86 | $15.Name = Exceptions 87 | $15.RequiredSuffixes = $16 88 | $16.String = Exception 89 | $15.AffectedEntity = CustomExceptions 90 | $15.VisibilityMask = VisibilityMask 91 | $15.NamingStyle = PascalCase 92 | $15.IncludeInstanceMembers = True 93 | $15.IncludeStaticEntities = True 94 | $6.NamingRule = $17 95 | $17.Name = Methods 96 | $17.AffectedEntity = Methods 97 | $17.VisibilityMask = Protected, Public 98 | $17.NamingStyle = PascalCase 99 | $17.IncludeInstanceMembers = True 100 | $17.IncludeStaticEntities = True 101 | $6.NamingRule = $18 102 | $18.Name = Static Readonly Fields 103 | $18.AffectedEntity = ReadonlyField 104 | $18.VisibilityMask = Protected, Public 105 | $18.NamingStyle = PascalCase 106 | $18.IncludeInstanceMembers = False 107 | $18.IncludeStaticEntities = True 108 | $6.NamingRule = $19 109 | $19.Name = Fields 110 | $19.AffectedEntity = Field 111 | $19.VisibilityMask = Protected, Public 112 | $19.NamingStyle = PascalCase 113 | $19.IncludeInstanceMembers = True 114 | $19.IncludeStaticEntities = True 115 | $6.NamingRule = $20 116 | $20.Name = ReadOnly Fields 117 | $20.AffectedEntity = ReadonlyField 118 | $20.VisibilityMask = Protected, Public 119 | $20.NamingStyle = PascalCase 120 | $20.IncludeInstanceMembers = True 121 | $20.IncludeStaticEntities = False 122 | $6.NamingRule = $21 123 | $21.Name = Constant Fields 124 | $21.AffectedEntity = ConstantField 125 | $21.VisibilityMask = Protected, Public 126 | $21.NamingStyle = PascalCase 127 | $21.IncludeInstanceMembers = True 128 | $21.IncludeStaticEntities = True 129 | $6.NamingRule = $22 130 | $22.Name = Properties 131 | $22.AffectedEntity = Property 132 | $22.VisibilityMask = Protected, Public 133 | $22.NamingStyle = PascalCase 134 | $22.IncludeInstanceMembers = True 135 | $22.IncludeStaticEntities = True 136 | $6.NamingRule = $23 137 | $23.Name = Events 138 | $23.AffectedEntity = Event 139 | $23.VisibilityMask = Protected, Public 140 | $23.NamingStyle = PascalCase 141 | $23.IncludeInstanceMembers = True 142 | $23.IncludeStaticEntities = True 143 | $6.NamingRule = $24 144 | $24.Name = Enum Members 145 | $24.AffectedEntity = EnumMember 146 | $24.VisibilityMask = VisibilityMask 147 | $24.NamingStyle = PascalCase 148 | $24.IncludeInstanceMembers = True 149 | $24.IncludeStaticEntities = True 150 | $6.NamingRule = $25 151 | $25.Name = Parameters 152 | $25.AffectedEntity = Parameter 153 | $25.VisibilityMask = VisibilityMask 154 | $25.NamingStyle = CamelCase 155 | $25.IncludeInstanceMembers = True 156 | $25.IncludeStaticEntities = True 157 | $6.NamingRule = $26 158 | $26.Name = Type Parameters 159 | $26.RequiredPrefixes = $27 160 | $27.String = T 161 | $26.AffectedEntity = TypeParameter 162 | $26.VisibilityMask = VisibilityMask 163 | $26.NamingStyle = PascalCase 164 | $26.IncludeInstanceMembers = True 165 | $26.IncludeStaticEntities = True 166 | $0.VersionControlPolicy = $28 167 | $28.inheritsSet = Mono 168 | $0.ChangeLogPolicy = $29 169 | $29.UpdateMode = None 170 | $29.MessageStyle = $30 171 | $30.LineAlign = 0 172 | $29.inheritsSet = Mono 173 | EndGlobalSection 174 | EndGlobal 175 | -------------------------------------------------------------------------------- /websocket-sharp/WsStream.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | /* 3 | * WsStream.cs 4 | * 5 | * The MIT License 6 | * 7 | * Copyright (c) 2010-2014 sta.blockhead 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | */ 27 | #endregion 28 | 29 | using System; 30 | using System.Collections.Generic; 31 | using System.IO; 32 | using System.Net.Sockets; 33 | using System.Security.Cryptography.X509Certificates; 34 | using System.Text; 35 | using System.Threading; 36 | using WebSocketSharp.Net.Security; 37 | 38 | namespace WebSocketSharp 39 | { 40 | internal class WsStream : IDisposable 41 | { 42 | #region Private Const Fields 43 | 44 | private const int _handshakeHeadersLimitLen = 8192; 45 | 46 | #endregion 47 | 48 | #region Private Fields 49 | 50 | private object _forWrite; 51 | private Stream _innerStream; 52 | private bool _secure; 53 | 54 | #endregion 55 | 56 | #region Private Constructors 57 | 58 | private WsStream (Stream innerStream, bool secure) 59 | { 60 | _innerStream = innerStream; 61 | _secure = secure; 62 | _forWrite = new object (); 63 | } 64 | 65 | #endregion 66 | 67 | #region Internal Constructors 68 | 69 | internal WsStream (NetworkStream innerStream) 70 | : this (innerStream, false) 71 | { 72 | } 73 | 74 | internal WsStream (SslStream innerStream) 75 | : this (innerStream, true) 76 | { 77 | } 78 | 79 | #endregion 80 | 81 | #region Public Properties 82 | 83 | public bool DataAvailable { 84 | get { 85 | return _secure 86 | ? ((SslStream) _innerStream).DataAvailable 87 | : ((NetworkStream) _innerStream).DataAvailable; 88 | } 89 | } 90 | 91 | public bool IsSecure { 92 | get { 93 | return _secure; 94 | } 95 | } 96 | 97 | #endregion 98 | 99 | #region Private Methods 100 | 101 | private static byte [] readHandshakeEntityBody (Stream stream, string length) 102 | { 103 | var len = Int64.Parse (length); 104 | return len > 1024 105 | ? stream.ReadBytes (len, 1024) 106 | : stream.ReadBytes ((int) len); 107 | } 108 | 109 | private static string [] readHandshakeHeaders (Stream stream) 110 | { 111 | var buffer = new List (); 112 | var count = 0; 113 | Action add = i => { 114 | buffer.Add ((byte) i); 115 | count++; 116 | }; 117 | 118 | var read = false; 119 | while (count < _handshakeHeadersLimitLen) { 120 | if (stream.ReadByte ().EqualsWith ('\r', add) && 121 | stream.ReadByte ().EqualsWith ('\n', add) && 122 | stream.ReadByte ().EqualsWith ('\r', add) && 123 | stream.ReadByte ().EqualsWith ('\n', add)) { 124 | read = true; 125 | break; 126 | } 127 | } 128 | 129 | if (!read) 130 | throw new WebSocketException ( 131 | "The header part of a handshake is greater than the limit length."); 132 | 133 | var crlf = "\r\n"; 134 | return Encoding.UTF8.GetString (buffer.ToArray ()) 135 | .Replace (crlf + " ", " ") 136 | .Replace (crlf + "\t", " ") 137 | .Split (new string [] { crlf }, StringSplitOptions.RemoveEmptyEntries); 138 | } 139 | 140 | #endregion 141 | 142 | #region Internal Methods 143 | 144 | internal static WsStream CreateClientStream ( 145 | TcpClient client, 146 | bool secure, 147 | string host, 148 | System.Net.Security.RemoteCertificateValidationCallback validationCallback) 149 | { 150 | var netStream = client.GetStream (); 151 | if (secure) { 152 | if (validationCallback == null) 153 | validationCallback = (sender, certificate, chain, sslPolicyErrors) => true; 154 | 155 | var sslStream = new SslStream (netStream, false, validationCallback); 156 | sslStream.AuthenticateAsClient (host); 157 | 158 | return new WsStream (sslStream); 159 | } 160 | 161 | return new WsStream (netStream); 162 | } 163 | 164 | internal static WsStream CreateServerStream ( 165 | TcpClient client, X509Certificate cert, bool secure) 166 | { 167 | var netStream = client.GetStream (); 168 | if (secure) { 169 | var sslStream = new SslStream (netStream, false); 170 | sslStream.AuthenticateAsServer (cert); 171 | 172 | return new WsStream (sslStream); 173 | } 174 | 175 | return new WsStream (netStream); 176 | } 177 | 178 | internal T ReadHandshake ( 179 | Func parser, int millisecondsTimeout) 180 | where T : HandshakeBase 181 | { 182 | var timeout = false; 183 | var timer = new Timer ( 184 | state => { 185 | timeout = true; 186 | _innerStream.Close (); 187 | }, 188 | null, 189 | millisecondsTimeout, 190 | -1); 191 | 192 | T handshake = null; 193 | Exception exception = null; 194 | try { 195 | handshake = parser (readHandshakeHeaders (_innerStream)); 196 | var contentLen = handshake.Headers ["Content-Length"]; 197 | if (contentLen != null && contentLen.Length > 0) 198 | handshake.EntityBodyData = readHandshakeEntityBody ( 199 | _innerStream, contentLen); 200 | } 201 | catch (Exception ex) { 202 | exception = ex; 203 | } 204 | finally { 205 | timer.Change (-1, -1); 206 | timer.Dispose (); 207 | } 208 | 209 | var reason = timeout 210 | ? "A timeout has occurred while receiving a handshake." 211 | : exception != null 212 | ? "An exception has occurred while receiving a handshake." 213 | : null; 214 | 215 | if (reason != null) 216 | throw new WebSocketException (reason, exception); 217 | 218 | return handshake; 219 | } 220 | 221 | internal bool Write (byte [] data) 222 | { 223 | lock (_forWrite) { 224 | try { 225 | _innerStream.Write (data, 0, data.Length); 226 | return true; 227 | } 228 | catch { 229 | return false; 230 | } 231 | } 232 | } 233 | 234 | #endregion 235 | 236 | #region Public Methods 237 | 238 | public void Close () 239 | { 240 | _innerStream.Close (); 241 | } 242 | 243 | public void Dispose () 244 | { 245 | _innerStream.Dispose (); 246 | } 247 | 248 | public WsFrame ReadFrame () 249 | { 250 | return WsFrame.Parse (_innerStream, true); 251 | } 252 | 253 | public void ReadFrameAsync ( 254 | Action completed, Action error) 255 | { 256 | WsFrame.ParseAsync (_innerStream, true, completed, error); 257 | } 258 | 259 | public HandshakeRequest ReadHandshakeRequest () 260 | { 261 | return ReadHandshake (HandshakeRequest.Parse, 90000); 262 | } 263 | 264 | public HandshakeResponse ReadHandshakeResponse () 265 | { 266 | return ReadHandshake (HandshakeResponse.Parse, 90000); 267 | } 268 | 269 | public bool WriteFrame (WsFrame frame) 270 | { 271 | return Write (frame.ToByteArray ()); 272 | } 273 | 274 | public bool WriteHandshake (HandshakeBase handshake) 275 | { 276 | return Write (handshake.ToByteArray ()); 277 | } 278 | 279 | #endregion 280 | } 281 | } 282 | -------------------------------------------------------------------------------- /websocket-sharp/Net/ChunkStream.cs: -------------------------------------------------------------------------------- 1 | // 2 | // ChunkStream.cs 3 | // Copied from System.Net.ChunkStream.cs 4 | // 5 | // Authors: 6 | // Gonzalo Paniagua Javier (gonzalo@ximian.com) 7 | // 8 | // (C) 2003 Ximian, Inc (http://www.ximian.com) 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining 11 | // a copy of this software and associated documentation files (the 12 | // "Software"), to deal in the Software without restriction, including 13 | // without limitation the rights to use, copy, modify, merge, publish, 14 | // distribute, sublicense, and/or sell copies of the Software, and to 15 | // permit persons to whom the Software is furnished to do so, subject to 16 | // the following conditions: 17 | // 18 | // The above copyright notice and this permission notice shall be 19 | // included in all copies or substantial portions of the Software. 20 | // 21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 22 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 24 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 25 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 26 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 27 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 28 | // 29 | 30 | using System; 31 | using System.Collections.Generic; 32 | using System.Globalization; 33 | using System.IO; 34 | using System.Net; 35 | using System.Text; 36 | 37 | namespace WebSocketSharp.Net { 38 | 39 | class ChunkStream { 40 | 41 | enum State { 42 | 43 | None, 44 | Body, 45 | BodyFinished, 46 | Trailer 47 | } 48 | 49 | class Chunk { 50 | 51 | public byte [] Bytes; 52 | public int Offset; 53 | 54 | public Chunk (byte [] chunk) 55 | { 56 | this.Bytes = chunk; 57 | } 58 | 59 | public int Read (byte [] buffer, int offset, int size) 60 | { 61 | int nread = (size > Bytes.Length - Offset) ? Bytes.Length - Offset : size; 62 | Buffer.BlockCopy (Bytes, Offset, buffer, offset, nread); 63 | Offset += nread; 64 | return nread; 65 | } 66 | } 67 | 68 | #region Private Fields 69 | 70 | int chunkRead; 71 | List chunks; 72 | int chunkSize; 73 | bool gotit; 74 | StringBuilder saved; 75 | bool sawCR; 76 | State state; 77 | int trailerState; 78 | 79 | #endregion 80 | 81 | #region Internal Fields 82 | 83 | internal WebHeaderCollection headers; 84 | 85 | #endregion 86 | 87 | #region Constructors 88 | 89 | public ChunkStream (byte [] buffer, int offset, int size, WebHeaderCollection headers) 90 | : this (headers) 91 | { 92 | Write (buffer, offset, size); 93 | } 94 | 95 | public ChunkStream (WebHeaderCollection headers) 96 | { 97 | this.headers = headers; 98 | saved = new StringBuilder (); 99 | chunks = new List (); 100 | chunkSize = -1; 101 | } 102 | 103 | #endregion 104 | 105 | #region Properties 106 | 107 | public int ChunkLeft { 108 | get { return chunkSize - chunkRead; } 109 | } 110 | 111 | public bool WantMore { 112 | get { return (chunkRead != chunkSize || chunkSize != 0 || state != State.None); } 113 | } 114 | 115 | #endregion 116 | 117 | #region Private Methods 118 | 119 | State GetChunkSize (byte [] buffer, ref int offset, int size) 120 | { 121 | char c = '\0'; 122 | while (offset < size) { 123 | c = (char) buffer [offset++]; 124 | if (c == '\r') { 125 | if (sawCR) 126 | ThrowProtocolViolation ("2 CR found."); 127 | 128 | sawCR = true; 129 | continue; 130 | } 131 | 132 | if (sawCR && c == '\n') 133 | break; 134 | 135 | if (c == ' ') 136 | gotit = true; 137 | 138 | if (!gotit) 139 | saved.Append (c); 140 | 141 | if (saved.Length > 20) 142 | ThrowProtocolViolation ("Chunk size too long."); 143 | } 144 | 145 | if (!sawCR || c != '\n') { 146 | if (offset < size) 147 | ThrowProtocolViolation ("Missing \\n."); 148 | 149 | try { 150 | if (saved.Length > 0) { 151 | chunkSize = Int32.Parse (RemoveChunkExtension (saved.ToString ()), NumberStyles.HexNumber); 152 | } 153 | } catch (Exception) { 154 | ThrowProtocolViolation ("Cannot parse chunk size."); 155 | } 156 | 157 | return State.None; 158 | } 159 | 160 | chunkRead = 0; 161 | try { 162 | chunkSize = Int32.Parse (RemoveChunkExtension (saved.ToString ()), NumberStyles.HexNumber); 163 | } catch (Exception) { 164 | ThrowProtocolViolation ("Cannot parse chunk size."); 165 | } 166 | 167 | if (chunkSize == 0) { 168 | trailerState = 2; 169 | return State.Trailer; 170 | } 171 | 172 | return State.Body; 173 | } 174 | 175 | void InternalWrite (byte [] buffer, ref int offset, int size) 176 | { 177 | if (state == State.None) { 178 | state = GetChunkSize (buffer, ref offset, size); 179 | if (state == State.None) 180 | return; 181 | 182 | saved.Length = 0; 183 | sawCR = false; 184 | gotit = false; 185 | } 186 | 187 | if (state == State.Body && offset < size) { 188 | state = ReadBody (buffer, ref offset, size); 189 | if (state == State.Body) 190 | return; 191 | } 192 | 193 | if (state == State.BodyFinished && offset < size) { 194 | state = ReadCRLF (buffer, ref offset, size); 195 | if (state == State.BodyFinished) 196 | return; 197 | 198 | sawCR = false; 199 | } 200 | 201 | if (state == State.Trailer && offset < size) { 202 | state = ReadTrailer (buffer, ref offset, size); 203 | if (state == State.Trailer) 204 | return; 205 | 206 | saved.Length = 0; 207 | sawCR = false; 208 | gotit = false; 209 | } 210 | 211 | if (offset < size) 212 | InternalWrite (buffer, ref offset, size); 213 | } 214 | 215 | State ReadBody (byte [] buffer, ref int offset, int size) 216 | { 217 | if (chunkSize == 0) 218 | return State.BodyFinished; 219 | 220 | int diff = size - offset; 221 | if (diff + chunkRead > chunkSize) 222 | diff = chunkSize - chunkRead; 223 | 224 | byte [] chunk = new byte [diff]; 225 | Buffer.BlockCopy (buffer, offset, chunk, 0, diff); 226 | chunks.Add (new Chunk (chunk)); 227 | offset += diff; 228 | chunkRead += diff; 229 | return (chunkRead == chunkSize) ? State.BodyFinished : State.Body; 230 | } 231 | 232 | State ReadCRLF (byte [] buffer, ref int offset, int size) 233 | { 234 | if (!sawCR) { 235 | if ((char) buffer [offset++] != '\r') 236 | ThrowProtocolViolation ("Expecting \\r."); 237 | 238 | sawCR = true; 239 | if (offset == size) 240 | return State.BodyFinished; 241 | } 242 | 243 | if (sawCR && (char) buffer [offset++] != '\n') 244 | ThrowProtocolViolation ("Expecting \\n."); 245 | 246 | return State.None; 247 | } 248 | 249 | int ReadFromChunks (byte [] buffer, int offset, int size) 250 | { 251 | int count = chunks.Count; 252 | int nread = 0; 253 | for (int i = 0; i < count; i++) { 254 | var chunk = chunks [i]; 255 | if (chunk == null) 256 | continue; 257 | 258 | if (chunk.Offset == chunk.Bytes.Length) { 259 | chunks [i] = null; 260 | continue; 261 | } 262 | 263 | nread += chunk.Read (buffer, offset + nread, size - nread); 264 | if (nread == size) 265 | break; 266 | } 267 | 268 | return nread; 269 | } 270 | 271 | State ReadTrailer (byte [] buffer, ref int offset, int size) 272 | { 273 | char c = '\0'; 274 | 275 | // short path 276 | if (trailerState == 2 && (char) buffer [offset] == '\r' && saved.Length == 0) { 277 | offset++; 278 | if (offset < size && (char) buffer [offset] == '\n') { 279 | offset++; 280 | return State.None; 281 | } 282 | 283 | offset--; 284 | } 285 | 286 | int st = trailerState; 287 | string stString = "\r\n\r"; 288 | while (offset < size && st < 4) { 289 | c = (char) buffer [offset++]; 290 | if ((st == 0 || st == 2) && c == '\r') { 291 | st++; 292 | continue; 293 | } 294 | 295 | if ((st == 1 || st == 3) && c == '\n') { 296 | st++; 297 | continue; 298 | } 299 | 300 | if (st > 0) { 301 | saved.Append (stString.Substring (0, saved.Length == 0? st-2: st)); 302 | st = 0; 303 | if (saved.Length > 4196) 304 | ThrowProtocolViolation ("Error reading trailer (too long)."); 305 | } 306 | } 307 | 308 | if (st < 4) { 309 | trailerState = st; 310 | if (offset < size) 311 | ThrowProtocolViolation ("Error reading trailer."); 312 | 313 | return State.Trailer; 314 | } 315 | 316 | var reader = new StringReader (saved.ToString ()); 317 | string line; 318 | while ((line = reader.ReadLine ()) != null && line != "") 319 | headers.Add (line); 320 | 321 | return State.None; 322 | } 323 | 324 | static string RemoveChunkExtension (string input) 325 | { 326 | int idx = input.IndexOf (';'); 327 | if (idx == -1) 328 | return input; 329 | 330 | return input.Substring (0, idx); 331 | } 332 | 333 | static void ThrowProtocolViolation (string message) 334 | { 335 | var we = new WebException (message, null, WebExceptionStatus.ServerProtocolViolation, null); 336 | throw we; 337 | } 338 | 339 | #endregion 340 | 341 | #region Public Methods 342 | 343 | public int Read (byte [] buffer, int offset, int size) 344 | { 345 | return ReadFromChunks (buffer, offset, size); 346 | } 347 | 348 | public void ResetBuffer () 349 | { 350 | chunkSize = -1; 351 | chunkRead = 0; 352 | chunks.Clear (); 353 | } 354 | 355 | public void Write (byte [] buffer, int offset, int size) 356 | { 357 | InternalWrite (buffer, ref offset, size); 358 | } 359 | 360 | public void WriteAndReadBack (byte [] buffer, int offset, int size, ref int read) 361 | { 362 | if (offset + read > 0) 363 | Write (buffer, offset, offset + read); 364 | 365 | read = Read (buffer, offset, size); 366 | } 367 | 368 | #endregion 369 | } 370 | } 371 | -------------------------------------------------------------------------------- /websocket-sharp/Logger.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | /* 3 | * Logger.cs 4 | * 5 | * The MIT License 6 | * 7 | * Copyright (c) 2013 sta.blockhead 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | */ 27 | #endregion 28 | 29 | using System; 30 | using System.Diagnostics; 31 | 32 | 33 | namespace WebSocketSharp 34 | { 35 | /// 36 | /// Provides the simple logging functions. 37 | /// 38 | /// 39 | /// 40 | /// The Logger class provides some methods that output the logs associated with the each 41 | /// values. 42 | /// If the value associated with a log is less than the , 43 | /// the log can not be outputted. 44 | /// 45 | /// 46 | /// The default output action used by the output methods outputs the log data to the standard output stream. 47 | /// 48 | /// 49 | /// If you want to run custom output action, you can replace the current output action with 50 | /// your output action by using the method. 51 | /// 52 | /// 53 | public class Logger 54 | { 55 | #region Private Fields 56 | 57 | private volatile LogLevel _level; 58 | private Action _output; 59 | private object _sync; 60 | 61 | #endregion 62 | 63 | #region Public Constructors 64 | 65 | /// 66 | /// Initializes a new instance of the class. 67 | /// 68 | /// 69 | /// This constructor initializes the current logging level with the . 70 | /// 71 | public Logger () 72 | : this (LogLevel.ERROR, null) 73 | { 74 | } 75 | 76 | /// 77 | /// Initializes a new instance of the class 78 | /// with the specified logging . 79 | /// 80 | /// 81 | /// One of the values to initialize. 82 | /// 83 | public Logger (LogLevel level) 84 | : this (level, null) 85 | { 86 | } 87 | 88 | /// 89 | /// Initializes a new instance of the class 90 | /// with the specified logging , path to the log 91 | /// and action. 92 | /// 93 | /// 94 | /// One of the values to initialize. 95 | /// 96 | /// 97 | /// A that contains a path to the log file to initialize. 98 | /// 99 | /// 100 | /// An Action<LogData> delegate that references the method(s) to initialize. 101 | /// 102 | public Logger (LogLevel level, Action output) 103 | { 104 | _level = level; 105 | _output = output ?? defaultOutput; 106 | _sync = new object (); 107 | } 108 | 109 | #endregion 110 | 111 | #region Public Properties 112 | 113 | /// 114 | /// Gets or sets the current logging level. 115 | /// 116 | /// 117 | /// A log associated with a less than the current logging level can not be outputted. 118 | /// 119 | /// 120 | /// One of the values that indicates the current logging level. 121 | /// 122 | public LogLevel Level { 123 | get { 124 | return _level; 125 | } 126 | 127 | set { 128 | _level = value; 129 | Warn (String.Format ("The current logging level has been changed to {0}.", _level)); 130 | } 131 | } 132 | 133 | #endregion 134 | 135 | #region Private Methods 136 | 137 | private static void defaultOutput (LogData data) 138 | { 139 | var log = data.ToString (); 140 | Console.WriteLine (log); 141 | } 142 | 143 | private void output (string message, LogLevel level) 144 | { 145 | if (level < _level || message == null || message.Length == 0) 146 | return; 147 | 148 | lock (_sync) 149 | { 150 | LogData data = null; 151 | try { 152 | data = new LogData (level, new StackFrame (2, true), message); 153 | _output (data); 154 | } 155 | catch (Exception ex) { 156 | data = new LogData (LogLevel.FATAL, new StackFrame (0, true), ex.Message); 157 | Console.WriteLine (data.ToString ()); 158 | } 159 | } 160 | } 161 | 162 | #endregion 163 | 164 | #region Public Methods 165 | 166 | /// 167 | /// Outputs the specified as a log with the . 168 | /// 169 | /// 170 | /// If the current logging level is greater than the , 171 | /// this method does not output as a log. 172 | /// 173 | /// 174 | /// A that contains a message to output as a log. 175 | /// 176 | public void Debug (string message) 177 | { 178 | output (message, LogLevel.DEBUG); 179 | } 180 | 181 | /// 182 | /// Outputs the specified as a log with the . 183 | /// 184 | /// 185 | /// If the current logging level is greater than the , 186 | /// this method does not output as a log. 187 | /// 188 | /// 189 | /// A that contains a message to output as a log. 190 | /// 191 | public void Error (string message) 192 | { 193 | output (message, LogLevel.ERROR); 194 | } 195 | 196 | /// 197 | /// Outputs the specified as a log with the . 198 | /// 199 | /// 200 | /// If the current logging level is greater than the , 201 | /// this method does not output as a log. 202 | /// 203 | /// 204 | /// A that contains a message to output as a log. 205 | /// 206 | public void Fatal (string message) 207 | { 208 | output (message, LogLevel.FATAL); 209 | } 210 | 211 | /// 212 | /// Outputs the specified as a log with the . 213 | /// 214 | /// 215 | /// If the current logging level is greater than the , 216 | /// this method does not output as a log. 217 | /// 218 | /// 219 | /// A that contains a message to output as a log. 220 | /// 221 | public void Info (string message) 222 | { 223 | output (message, LogLevel.INFO); 224 | } 225 | 226 | /// 227 | /// Replaces the current output action with the specified action. 228 | /// 229 | /// 230 | /// If is , 231 | /// this method replaces the current output action with the default output action. 232 | /// 233 | /// 234 | /// An Action<LogData> delegate that references the method(s) to set. 235 | /// 236 | public void SetOutput (Action output) 237 | { 238 | lock (_sync) 239 | { 240 | _output = output ?? defaultOutput; 241 | Warn ("The current output action has been replaced."); 242 | } 243 | } 244 | 245 | /// 246 | /// Outputs the specified as a log with the . 247 | /// 248 | /// 249 | /// If the current logging level is greater than the , 250 | /// this method does not output as a log. 251 | /// 252 | /// 253 | /// A that contains a message to output as a log. 254 | /// 255 | public void Trace (string message) 256 | { 257 | output (message, LogLevel.TRACE); 258 | } 259 | 260 | /// 261 | /// Outputs the specified as a log with the . 262 | /// 263 | /// 264 | /// If the current logging level is greater than the , 265 | /// this method does not output as a log. 266 | /// 267 | /// 268 | /// A that contains a message to output as a log. 269 | /// 270 | public void Warn (string message) 271 | { 272 | output (message, LogLevel.WARN); 273 | } 274 | 275 | #endregion 276 | } 277 | } 278 | -------------------------------------------------------------------------------- /websocket-sharp/Net/HttpStatusCode.cs: -------------------------------------------------------------------------------- 1 | // 2 | // HttpStatusCode.cs 3 | // Copied from System.Net.HttpStatusCode.cs 4 | // 5 | // This code was automatically generated from 6 | // ECMA CLI XML Library Specification. 7 | // Generator: libgen.xsl [1.0; (C) Sergey Chaban (serge@wildwestsoftware.com)] 8 | // Created: Wed, 5 Sep 2001 06:32:05 UTC 9 | // Source file: AllTypes.xml 10 | // URL: http://msdn.microsoft.com/net/ecma/AllTypes.xml 11 | // 12 | // Copyright (C) 2001 Ximian, Inc. (http://www.ximian.com) 13 | // 14 | // Permission is hereby granted, free of charge, to any person obtaining 15 | // a copy of this software and associated documentation files (the 16 | // "Software"), to deal in the Software without restriction, including 17 | // without limitation the rights to use, copy, modify, merge, publish, 18 | // distribute, sublicense, and/or sell copies of the Software, and to 19 | // permit persons to whom the Software is furnished to do so, subject to 20 | // the following conditions: 21 | // 22 | // The above copyright notice and this permission notice shall be 23 | // included in all copies or substantial portions of the Software. 24 | // 25 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 26 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 27 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 28 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 29 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 30 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 31 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 32 | // 33 | 34 | namespace WebSocketSharp.Net { 35 | 36 | /// 37 | /// Contains the values of the HTTP status codes. 38 | /// 39 | /// 40 | /// The HttpStatusCode enumeration contains the values of the HTTP status codes defined in 41 | /// RFC 2616 for HTTP 1.1. 42 | /// 43 | public enum HttpStatusCode { 44 | 45 | /// 46 | /// Equivalent to status code 100. Indicates that the client should continue with its request. 47 | /// 48 | Continue = 100, 49 | 50 | /// 51 | /// Equivalent to status code 101. Indicates that the server is switching the HTTP version or protocol on the connection. 52 | /// 53 | SwitchingProtocols = 101, 54 | 55 | /// 56 | /// Equivalent to status code 200. Indicates that the client's request has succeeded. 57 | /// 58 | OK = 200, 59 | 60 | /// 61 | /// Equivalent to status code 201. Indicates that the client's request has been fulfilled 62 | /// and resulted in a new resource being created. 63 | /// 64 | Created = 201, 65 | 66 | /// 67 | /// Equivalent to status code 202. Indicates that the client's request has been accepted for processing, 68 | /// but the processing has not been completed. 69 | /// 70 | Accepted = 202, 71 | 72 | /// 73 | /// Equivalent to status code 203. Indicates that the returned metainformation is from a local or a third-party copy instead of the origin server. 74 | /// 75 | NonAuthoritativeInformation = 203, 76 | 77 | /// 78 | /// Equivalent to status code 204. Indicates that the server has fulfilled the client's request 79 | /// but does not need to return an entity-body. 80 | /// 81 | NoContent = 204, 82 | 83 | /// 84 | /// Equivalent to status code 205. Indicates that the server has fulfilled the client's request 85 | /// and the user agent should reset the document view which caused the request to be sent. 86 | /// 87 | ResetContent = 205, 88 | 89 | /// 90 | /// Equivalent to status code 206. Indicates that the server has fulfilled the partial GET request for the resource. 91 | /// 92 | PartialContent = 206, 93 | 94 | /// 95 | /// 96 | /// Equivalent to status code 300. Indicates that the requested resource corresponds to 97 | /// any one of multiple representations. 98 | /// 99 | /// 100 | /// MultipleChoices is a synonym for Ambiguous. 101 | /// 102 | /// 103 | MultipleChoices = 300, 104 | 105 | /// 106 | /// 107 | /// Equivalent to status code 300. Indicates that the requested resource corresponds to 108 | /// any one of multiple representations. 109 | /// 110 | /// 111 | /// Ambiguous is a synonym for MultipleChoices. 112 | /// 113 | /// 114 | Ambiguous = 300, 115 | 116 | /// 117 | /// 118 | /// Equivalent to status code 301. Indicates that the requested resource has been assigned a new permanent URI 119 | /// and any future references to this resource should use one of the returned URIs. 120 | /// 121 | /// 122 | /// MovedPermanently is a synonym for Moved. 123 | /// 124 | /// 125 | MovedPermanently = 301, 126 | 127 | /// 128 | /// 129 | /// Equivalent to status code 301. Indicates that the requested resource has been assigned a new permanent URI 130 | /// and any future references to this resource should use one of the returned URIs. 131 | /// 132 | /// 133 | /// Moved is a synonym for MovedPermanently. 134 | /// 135 | /// 136 | Moved = 301, 137 | 138 | /// 139 | /// 140 | /// Equivalent to status code 302. Indicates that the requested resource is located temporarily 141 | /// under a different URI. 142 | /// 143 | /// 144 | /// Found is a synonym for Redirect. 145 | /// 146 | /// 147 | Found = 302, 148 | 149 | /// 150 | /// 151 | /// Equivalent to status code 302. Indicates that the requested resource is located temporarily 152 | /// under a different URI. 153 | /// 154 | /// 155 | /// Redirect is a synonym for Found. 156 | /// 157 | /// 158 | Redirect = 302, 159 | 160 | /// 161 | /// 162 | /// Equivalent to status code 303. Indicates that the response to the request can be found 163 | /// under a different URI and should be retrieved using a GET method on that resource. 164 | /// 165 | /// 166 | /// SeeOther is a synonym for RedirectMethod. 167 | /// 168 | /// 169 | SeeOther = 303, 170 | 171 | /// 172 | /// 173 | /// Equivalent to status code 303. Indicates that the response to the request can be found 174 | /// under a different URI and should be retrieved using a GET method on that resource. 175 | /// 176 | /// 177 | /// RedirectMethod is a synonym for SeeOther. 178 | /// 179 | /// 180 | RedirectMethod = 303, 181 | 182 | /// 183 | /// Equivalent to status code 304. Indicates that the client has performed a conditional GET request 184 | /// and access is allowed, but the document has not been modified. 185 | /// 186 | NotModified = 304, 187 | 188 | /// 189 | /// Equivalent to status code 305. Indicates that the requested resource must be accessed 190 | /// through the proxy given by the Location field. 191 | /// 192 | UseProxy = 305, 193 | 194 | /// 195 | /// Equivalent to status code 306. This code was used in a previous version of the specification, 196 | /// is no longer used, and is reserved for future use. 197 | /// 198 | Unused = 306, 199 | 200 | /// 201 | /// 202 | /// Equivalent to status code 307. Indicates that the requested resource is located temporarily 203 | /// under a different URI. 204 | /// 205 | /// 206 | /// TemporaryRedirect is a synonym for RedirectKeepVerb. 207 | /// 208 | /// 209 | TemporaryRedirect = 307, 210 | 211 | /// 212 | /// 213 | /// Equivalent to status code 307. Indicates that the requested resource is located temporarily 214 | /// under a different URI. 215 | /// 216 | /// 217 | /// RedirectKeepVerb is a synonym for TemporaryRedirect. 218 | /// 219 | /// 220 | RedirectKeepVerb = 307, 221 | 222 | /// 223 | /// Equivalent to status code 400. Indicates that the client's request could not be understood 224 | /// by the server due to malformed syntax. 225 | /// 226 | BadRequest = 400, 227 | 228 | /// 229 | /// Equivalent to status code 401. Indicates that the client's request requires user authentication. 230 | /// 231 | Unauthorized = 401, 232 | 233 | /// 234 | /// Equivalent to status code 402. This code is reserved for future use. 235 | /// 236 | PaymentRequired = 402, 237 | 238 | /// 239 | /// Equivalent to status code 403. Indicates that the server understood the client's request 240 | /// but is refusing to fulfill it. 241 | /// 242 | Forbidden = 403, 243 | 244 | /// 245 | /// Equivalent to status code 404. Indicates that the server has not found anything 246 | /// matching the request URI. 247 | /// 248 | NotFound = 404, 249 | 250 | /// 251 | /// Equivalent to status code 405. Indicates that the method specified in the request line 252 | /// is not allowed for the resource identified by the request URI. 253 | /// 254 | MethodNotAllowed = 405, 255 | 256 | /// 257 | /// Equivalent to status code 406. Indicates that the server does not have the appropriate resource 258 | /// to respond to the accept headers in the client's request. 259 | /// 260 | NotAcceptable = 406, 261 | 262 | /// 263 | /// Equivalent to status code 407. Indicates that the client must first authenticate itself with the proxy. 264 | /// 265 | ProxyAuthenticationRequired = 407, 266 | 267 | /// 268 | /// Equivalent to status code 408. Indicates that the client did not produce a request 269 | /// within the time that the server was prepared to wait. 270 | /// 271 | RequestTimeout = 408, 272 | 273 | /// 274 | /// Equivalent to status code 409. Indicates that the client's request could not be completed 275 | /// due to a conflict on the server. 276 | /// 277 | Conflict = 409, 278 | 279 | /// 280 | /// Equivalent to status code 410. Indicates that the requested resource is no longer available 281 | /// at the server and no forwarding address is known. 282 | /// 283 | Gone = 410, 284 | 285 | /// 286 | /// Equivalent to status code 411. Indicates that the server refuses to accept the client's request 287 | /// without a defined Content-Length. 288 | /// 289 | LengthRequired = 411, 290 | 291 | /// 292 | /// Equivalent to status code 412. Indicates that the precondition given in one or more of the request header fields 293 | /// evaluated to false when it was tested on the server. 294 | /// 295 | PreconditionFailed = 412, 296 | 297 | /// 298 | /// Equivalent to status code 413. Indicates that the client's request entity is larger 299 | /// than the server is willing or able to process. 300 | /// 301 | RequestEntityTooLarge = 413, 302 | 303 | /// 304 | /// Equivalent to status code 414. Indicates that the request URI is longer 305 | /// than the server is willing to interpret. 306 | /// 307 | RequestUriTooLong = 414, 308 | 309 | /// 310 | /// Equivalent to status code 415. Indicates that the entity of the client's request is in a format 311 | /// not supported by the requested resource for the requested method. 312 | /// 313 | UnsupportedMediaType = 415, 314 | 315 | /// 316 | /// Equivalent to status code 416. Indicates that none of the range specifier values in a Range request header field 317 | /// overlap the current extent of the selected resource. 318 | /// 319 | RequestedRangeNotSatisfiable = 416, 320 | 321 | /// 322 | /// Equivalent to status code 417. Indicates that the expectation given in an Expect request header field 323 | /// could not be met by the server. 324 | /// 325 | ExpectationFailed = 417, 326 | 327 | /// 328 | /// Equivalent to status code 500. Indicates that the server encountered an unexpected condition 329 | /// which prevented it from fulfilling the client's request. 330 | /// 331 | InternalServerError = 500, 332 | 333 | /// 334 | /// Equivalent to status code 501. Indicates that the server does not support the functionality 335 | /// required to fulfill the client's request. 336 | /// 337 | NotImplemented = 501, 338 | 339 | /// 340 | /// Equivalent to status code 502. Indicates that a gateway or proxy server received an invalid response 341 | /// from the upstream server. 342 | /// 343 | BadGateway = 502, 344 | 345 | /// 346 | /// Equivalent to status code 503. Indicates that the server is currently unable to handle the client's request 347 | /// due to a temporary overloading or maintenance of the server. 348 | /// 349 | ServiceUnavailable = 503, 350 | 351 | /// 352 | /// Equivalent to status code 504. Indicates that a gateway or proxy server did not receive a timely response 353 | /// from the upstream server or some other auxiliary server. 354 | /// 355 | GatewayTimeout = 504, 356 | 357 | /// 358 | /// Equivalent to status code 505. Indicates that the server does not support the HTTP version 359 | /// used in the client's request. 360 | /// 361 | HttpVersionNotSupported = 505, 362 | } 363 | } 364 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Logo](websocket-sharp.png) 2 | 3 | ## Welcome to websocket-sharp! ## 4 | 5 | **websocket-sharp** supports the followings: 6 | 7 | - **[WebSocket Client](#websocket-client)** and **[Server](#websocket-server)** 8 | - **[RFC 6455](#supported-websocket-specifications)** 9 | - **[Per-message Compression](#per-message-compression)** extension 10 | - **[Secure Connection](#secure-connection)** 11 | - **[HTTP Authentication](#http-authentication)** 12 | - .NET **3.5** or later (includes compatible) 13 | 14 | ## Branches ## 15 | 16 | - **[master]** for production releases. 17 | - **[hybi-00]** for older [draft-ietf-hybi-thewebsocketprotocol-00]. No longer maintained. 18 | - **[draft75]** for even more old [draft-hixie-thewebsocketprotocol-75]. No longer maintained. 19 | 20 | ## Build ## 21 | 22 | websocket-sharp is built as a single assembly, **websocket-sharp.dll**. 23 | 24 | websocket-sharp is developed with **[MonoDevelop]**. So the simple way to build is to open **websocket-sharp.sln** and run build for the websocket-sharp project with any of the build configurations (e.g. Debug) in the MonoDevelop. 25 | 26 | ## Install ## 27 | 28 | ### Self Build ### 29 | 30 | You should add **websocket-sharp.dll** (e.g. /path/to/websocket-sharp/bin/Debug/websocket-sharp.dll) built yourself to the library references of your project. 31 | 32 | If you would like to use that websocket-sharp.dll in your **[Unity]** project, you should add that dll to any folder of your project (e.g. Assets/Plugins) in the **Unity Editor**. 33 | 34 | ### NuGet Gallery ### 35 | 36 | websocket-sharp is available on the **[NuGet Gallery]**, as still a **prerelease** version. 37 | 38 | - **[NuGet Gallery: websocket-sharp]** 39 | 40 | You can add websocket-sharp to your project using the **NuGet Package Manager**, the following command in the **Package Manager Console**. 41 | 42 | PM> Install-Package WebSocketSharp -Pre 43 | 44 | ### Unity Asset Store ### 45 | 46 | websocket-sharp is available on the **Unity Asset Store**. 47 | 48 | - **[WebSocket-Sharp for Unity]** 49 | 50 | It's priced at **US$15**. I think your $15 makes this project more better and accelerated, **Thank you!** 51 | 52 | ## Usage ## 53 | 54 | ### WebSocket Client ### 55 | 56 | ```cs 57 | using System; 58 | using WebSocketSharp; 59 | 60 | namespace Example 61 | { 62 | public class Program 63 | { 64 | public static void Main (string [] args) 65 | { 66 | using (var ws = new WebSocket ("ws://dragonsnest.far/Laputa")) { 67 | ws.OnMessage += (sender, e) => 68 | Console.WriteLine ("Laputa says: " + e.Data); 69 | 70 | ws.Connect (); 71 | ws.Send ("BALUS"); 72 | Console.ReadKey (true); 73 | } 74 | } 75 | } 76 | } 77 | ``` 78 | 79 | #### Step 1 #### 80 | 81 | Required namespace. 82 | 83 | ```cs 84 | using WebSocketSharp; 85 | ``` 86 | 87 | The `WebSocket` class exists in the `WebSocketSharp` namespace. 88 | 89 | #### Step 2 #### 90 | 91 | Creating an instance of the `WebSocket` class with the WebSocket URL to connect. 92 | 93 | ```cs 94 | using (var ws = new WebSocket ("ws://example.com")) { 95 | ... 96 | } 97 | ``` 98 | 99 | The `WebSocket` class inherits the `System.IDisposable` interface, so you can use the `using` statement. 100 | 101 | #### Step 3 #### 102 | 103 | Setting the `WebSocket` events. 104 | 105 | ##### WebSocket.OnOpen Event ##### 106 | 107 | A `WebSocket.OnOpen` event occurs when the WebSocket connection has been established. 108 | 109 | ```cs 110 | ws.OnOpen += (sender, e) => { 111 | ... 112 | }; 113 | ``` 114 | 115 | `e` has passed as the `System.EventArgs.Empty`, so you don't use `e`. 116 | 117 | ##### WebSocket.OnMessage Event ##### 118 | 119 | A `WebSocket.OnMessage` event occurs when the `WebSocket` receives a WebSocket message. 120 | 121 | ```cs 122 | ws.OnMessage += (sender, e) => { 123 | ... 124 | }; 125 | ``` 126 | 127 | `e` has passed as a `WebSocketSharp.MessageEventArgs`. 128 | 129 | `e.Type` property returns either `WebSocketSharp.Opcode.TEXT` or `WebSocketSharp.Opcode.BINARY` that represents the type of the received message. So by checking it, you determine which item you should use. 130 | 131 | If `e.Type` is `Opcode.TEXT`, you should use `e.Data` property (returns a `string`) that represents the received **Text** message. 132 | 133 | Or if `e.Type` is `Opcode.BINARY`, you should use `e.RawData` property (returns a `byte []`) that represents the received **Binary** message. 134 | 135 | ```cs 136 | if (e.Type == Opcode.TEXT) { 137 | // Do something with e.Data 138 | return; 139 | } 140 | 141 | if (e.Type == Opcode.BINARY) { 142 | // Do something with e.RawData 143 | return; 144 | } 145 | ``` 146 | 147 | ##### WebSocket.OnError Event ##### 148 | 149 | A `WebSocket.OnError` event occurs when the `WebSocket` gets an error. 150 | 151 | ```cs 152 | ws.OnError += (sender, e) => { 153 | ... 154 | }; 155 | ``` 156 | 157 | `e` has passed as a `WebSocketSharp.ErrorEventArgs`. 158 | 159 | `e.Message` property returns a `string` that represents the error message. So you should use it to get the error message. 160 | 161 | ##### WebSocket.OnClose Event ##### 162 | 163 | A `WebSocket.OnClose` event occurs when the WebSocket connection has been closed. 164 | 165 | ```cs 166 | ws.OnClose += (sender, e) => { 167 | ... 168 | }; 169 | ``` 170 | 171 | `e` has passed as a `WebSocketSharp.CloseEventArgs`. 172 | 173 | `e.Code` property returns a `ushort` that represents the status code that indicates the reason for closure, and `e.Reason` property returns a `string` that represents the reason for closure. So you should use them to get the reason for closure. 174 | 175 | #### Step 4 #### 176 | 177 | Connecting to the WebSocket server. 178 | 179 | ```cs 180 | ws.Connect (); 181 | ``` 182 | 183 | If you would like to connect to the server asynchronously, you should use the `WebSocket.ConnectAsync ()` method. 184 | 185 | #### Step 5 #### 186 | 187 | Sending a data to the WebSocket server. 188 | 189 | ```cs 190 | ws.Send (data); 191 | ``` 192 | 193 | The `WebSocket.Send` method is overloaded. 194 | 195 | You can use the `WebSocket.Send (string)`, `WebSocket.Send (byte [])`, and `WebSocket.Send (System.IO.FileInfo)` methods to send a data. 196 | 197 | If you would like to send a data asynchronously, you should use the `WebSocket.SendAsync` method. 198 | 199 | ```cs 200 | ws.SendAsync (data, completed); 201 | ``` 202 | 203 | And if you would like to do something when the send is complete, you should set `completed` to any `Action`. 204 | 205 | #### Step 6 #### 206 | 207 | Closing the WebSocket connection. 208 | 209 | ```cs 210 | ws.Close (code, reason); 211 | ``` 212 | 213 | If you would like to close the connection explicitly, you should use the `WebSocket.Close` method. 214 | 215 | The `WebSocket.Close` method is overloaded. 216 | 217 | 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. 218 | 219 | If you would like to close the connection asynchronously, you should use the `WebSocket.CloseAsync` method. 220 | 221 | ### WebSocket Server ### 222 | 223 | ```cs 224 | using System; 225 | using WebSocketSharp; 226 | using WebSocketSharp.Server; 227 | 228 | namespace Example 229 | { 230 | public class Laputa : WebSocketService 231 | { 232 | protected override void OnMessage (MessageEventArgs e) 233 | { 234 | var msg = e.Data == "BALUS" 235 | ? "I've been balused already..." 236 | : "I'm not available now."; 237 | 238 | Send (msg); 239 | } 240 | } 241 | 242 | public class Program 243 | { 244 | public static void Main (string [] args) 245 | { 246 | var wssv = new WebSocketServer ("ws://dragonsnest.far"); 247 | wssv.AddWebSocketService ("/Laputa"); 248 | wssv.Start (); 249 | Console.ReadKey (true); 250 | wssv.Stop (); 251 | } 252 | } 253 | } 254 | ``` 255 | 256 | #### Step 1 #### 257 | 258 | Required namespace. 259 | 260 | ```cs 261 | using WebSocketSharp.Server; 262 | ``` 263 | 264 | The `WebSocketServer` and `WebSocketService` classes exist in the `WebSocketSharp.Server` namespace. 265 | 266 | #### Step 2 #### 267 | 268 | Creating the class that inherits the `WebSocketService` class. 269 | 270 | For example, if you would like to provide an echo service, 271 | 272 | ```cs 273 | using System; 274 | using WebSocketSharp; 275 | using WebSocketSharp.Server; 276 | 277 | public class Echo : WebSocketService 278 | { 279 | protected override void OnMessage (MessageEventArgs e) 280 | { 281 | Send (e.Data); 282 | } 283 | } 284 | ``` 285 | 286 | And if you would like to provide a chat service, 287 | 288 | ```cs 289 | using System; 290 | using WebSocketSharp; 291 | using WebSocketSharp.Server; 292 | 293 | public class Chat : WebSocketService 294 | { 295 | private string _suffix; 296 | 297 | public Chat () 298 | : this (null) 299 | { 300 | } 301 | 302 | public Chat (string suffix) 303 | { 304 | _suffix = suffix ?? String.Empty; 305 | } 306 | 307 | protected override void OnMessage (MessageEventArgs e) 308 | { 309 | Sessions.Broadcast (e.Data + _suffix); 310 | } 311 | } 312 | ``` 313 | 314 | If you override the `WebSocketService.OnMessage (MessageEventArgs)` method, that overridden method is called when the `OnMessage` event of the current session's `WebSocket` occurs. 315 | 316 | And if you override the `WebSocketService.OnOpen ()`, `WebSocketService.OnError (ErrorEventArgs)`, and `WebSocketService.OnClose (CloseEventArgs)` methods, each of them is called when each event of the current session's `WebSocket` (the `OnOpen`, `OnError`, and `OnClose` events) occurs. 317 | 318 | The `WebSocketService.Send` method sends a data to the client on the current session in the WebSocket service. 319 | 320 | If you would like to access the sessions in the WebSocket service, you should use the `WebSocketService.Sessions` property (returns a `WebSocketSharp.Server.WebSocketSessionManager`). 321 | 322 | The `WebSocketService.Sessions.Broadcast` method broadcasts a data to all clients of the WebSocket service. 323 | 324 | #### Step 3 #### 325 | 326 | Creating an instance of the `WebSocketServer` class. 327 | 328 | ```cs 329 | var wssv = new WebSocketServer (4649); 330 | wssv.AddWebSocketService ("/Echo"); 331 | wssv.AddWebSocketService ("/Chat"); 332 | wssv.AddWebSocketService ("/ChatWithNiceBoat", () => new Chat (" Nice boat.")); 333 | ``` 334 | 335 | You can add any WebSocket service to your `WebSocketServer` with the specified path to the service, using the `WebSocketServer.AddWebSocketService (string)` and `WebSocketServer.AddWebSocketService (string, Func)` methods. 336 | 337 | The type of `TWithNew` must inherit the `WebSocketService` class and must have a public parameterless constructor. 338 | 339 | The type of `T` must inherit the `WebSocketService` class. 340 | 341 | So you can use the classes created in **Step 2** to add the WebSocket service. 342 | 343 | If you create an instance of the `WebSocketServer` class without a port number, the `WebSocketServer` set the port number to **80** automatically. So it's necessary to run with root permission. 344 | 345 | $ sudo mono example2.exe 346 | 347 | #### Step 4 #### 348 | 349 | Starting the WebSocket server. 350 | 351 | ```cs 352 | wssv.Start (); 353 | ``` 354 | 355 | #### Step 5 #### 356 | 357 | Stopping the WebSocket server. 358 | 359 | ```cs 360 | wssv.Stop (code, reason); 361 | ``` 362 | 363 | The `WebSocketServer.Stop` method is overloaded. 364 | 365 | You can use the `WebSocketServer.Stop ()`, `WebSocketServer.Stop (ushort, string)`, or `WebSocketServer.Stop (WebSocketSharp.CloseStatusCode, string)` method to stop the server. 366 | 367 | ### HTTP Server with the WebSocket ### 368 | 369 | I modified the `System.Net.HttpListener`, `System.Net.HttpListenerContext`, and some other classes of **[Mono]** to create the HTTP server that allows to accept the WebSocket connection requests. 370 | 371 | So websocket-sharp provides the `WebSocketSharp.Server.HttpServer` class. 372 | 373 | You can add any WebSocket service to your `HttpServer` with the specified path to the service, using the `HttpServer.AddWebSocketService (string)` and `HttpServer.AddWebSocketService (string, Func)` methods. 374 | 375 | ```cs 376 | var httpsv = new HttpServer (4649); 377 | httpsv.AddWebSocketService ("/Echo"); 378 | httpsv.AddWebSocketService ("/Chat"); 379 | httpsv.AddWebSocketService ("/ChatWithNiceBoat", () => new Chat (" Nice boat.")); 380 | ``` 381 | 382 | For more information, could you see **[Example3]**? 383 | 384 | ### WebSocket Extensions ### 385 | 386 | #### Per-message Compression #### 387 | 388 | websocket-sharp supports the **[Per-message Compression][compression]** extension. (But it doesn't support with the [extension parameters].) 389 | 390 | If you would like to enable this extension as a WebSocket client, you should set like the following. 391 | 392 | ```cs 393 | ws.Compression = CompressionMethod.DEFLATE; 394 | ``` 395 | 396 | And then your client sends the following header with the connection request to the server. 397 | 398 | Sec-WebSocket-Extensions: permessage-deflate 399 | 400 | If the server supports this extension, it returns the same header. And when your client receives that header, it enables this extension. 401 | 402 | ### Secure Connection ### 403 | 404 | websocket-sharp supports the **Secure Connection (SSL)**. 405 | 406 | As a **WebSocket Client**, you should create an instance of the `WebSocket` class with the **wss** scheme WebSocket URL to connect. 407 | 408 | ```cs 409 | using (var ws = new WebSocket ("wss://example.com")) { 410 | ... 411 | } 412 | ``` 413 | 414 | And if you would like to set the custom validation for the server certificate, you should set the `WebSocket.ServerCertificateValidationCallback` property. 415 | 416 | ```cs 417 | ws.ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => { 418 | // Do something to validate the server certificate. 419 | return true; // If the server certificate is valid. 420 | }; 421 | ``` 422 | 423 | If you set this property to nothing, the validation does nothing with the server certificate and returns valid. 424 | 425 | As a **WebSocket Server**, you should create an instance of the `WebSocketServer` or `HttpServer` class with some settings for the secure connection. It's like the following. 426 | 427 | ```cs 428 | var wssv = new WebSocketServer (4649, true); 429 | wssv.Certificate = new X509Certificate2 ("/path/to/cert.pfx", "password for cert.pfx"); 430 | ``` 431 | 432 | ### HTTP Authentication ### 433 | 434 | websocket-sharp supports the **[HTTP Authentication (Basic/Digest)][rfc2617]**. 435 | 436 | As a **WebSocket Client**, you should set a pair of user name and password for the HTTP authentication, using the `WebSocket.SetCredentials (string, string, bool)` method before connecting. 437 | 438 | ```cs 439 | ws.SetCredentials (username, password, preAuth); 440 | ``` 441 | 442 | If `preAuth` is `true`, the `WebSocket` sends the Basic authentication credentials with the first connection request to the server. 443 | 444 | Or if `preAuth` is `false`, the `WebSocket` sends either the Basic or Digest authentication (determined by the unauthorized response to the first connection request) credentials with the second connection request to the server. 445 | 446 | As a **WebSocket Server**, you should set an HTTP authentication scheme, a realm, and any function to find the user credentials before starting. It's like the following. 447 | 448 | ```cs 449 | wssv.AuthenticationSchemes = AuthenticationSchemes.Basic; 450 | wssv.Realm = "WebSocket Test"; 451 | wssv.UserCredentialsFinder = identity => { 452 | var expected = "nobita"; 453 | return identity.Name == expected 454 | ? new NetworkCredential (expected, "password", "gunfighter") // User name, password, and roles 455 | : null; // If the user credentials not found. 456 | }; 457 | ``` 458 | 459 | If you would like to provide the Digest authentication, you should set like the following. 460 | 461 | ```cs 462 | wssv.AuthenticationSchemes = AuthenticationSchemes.Digest; 463 | ``` 464 | 465 | ### Logging ### 466 | 467 | The `WebSocket` class includes the own logging function. 468 | 469 | You can access it with the `WebSocket.Log` property (returns a `WebSocketSharp.Logger`). 470 | 471 | 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. 472 | 473 | ```cs 474 | ws.Log.Level = LogLevel.DEBUG; 475 | ``` 476 | 477 | This means a log with less than `LogLevel.DEBUG` isn't outputted. 478 | 479 | 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`. 480 | 481 | ```cs 482 | ws.Log.Debug ("This is a debug message."); 483 | ``` 484 | 485 | The `WebSocketServer` and `HttpServer` classes include the same logging function. 486 | 487 | ## Examples ## 488 | 489 | Examples using **websocket-sharp**. 490 | 491 | ### Example ### 492 | 493 | **[Example]** connects to the **[Echo server]** with the WebSocket. 494 | 495 | ### Example1 ### 496 | 497 | **[Example1]** connects to the **[Audio Data delivery server]** with the WebSocket. (But it's only implemented the chat feature, still unfinished.) 498 | 499 | And Example1 uses **[Json.NET]**. 500 | 501 | ### Example2 ### 502 | 503 | **[Example2]** starts a WebSocket server. 504 | 505 | ### Example3 ### 506 | 507 | **[Example3]** starts an HTTP server that allows to accept the WebSocket connection requests. 508 | 509 | Could you access to [http://localhost:4649](http://localhost:4649) to do **WebSocket Echo Test** with your web browser after Example3 running? 510 | 511 | ## Supported WebSocket Specifications ## 512 | 513 | websocket-sharp supports **[RFC 6455][rfc6455]** and is based on the following WebSocket references. 514 | 515 | - **[The WebSocket Protocol][rfc6455]** 516 | - **[The WebSocket API][api]** 517 | - **[Compression Extensions for WebSocket][compression]** 518 | 519 | Thanks for translating to japanese. 520 | 521 | - **[The WebSocket Protocol 日本語訳][rfc6455_ja]** 522 | - **[The WebSocket API 日本語訳][api_ja]** 523 | 524 | ## License ## 525 | 526 | websocket-sharp is provided under **[The MIT License]**. 527 | 528 | 529 | [Audio Data delivery server]: http://agektmr.node-ninja.com:3000 530 | [Echo server]: http://www.websocket.org/echo.html 531 | [Example]: https://github.com/sta/websocket-sharp/tree/master/Example 532 | [Example1]: https://github.com/sta/websocket-sharp/tree/master/Example1 533 | [Example2]: https://github.com/sta/websocket-sharp/tree/master/Example2 534 | [Example3]: https://github.com/sta/websocket-sharp/tree/master/Example3 535 | [Json.NET]: http://james.newtonking.com/projects/json-net.aspx 536 | [Mono]: http://www.mono-project.com 537 | [MonoDevelop]: http://monodevelop.com 538 | [NuGet Gallery]: http://www.nuget.org 539 | [NuGet Gallery: websocket-sharp]: http://www.nuget.org/packages/WebSocketSharp 540 | [The MIT License]: https://raw.github.com/sta/websocket-sharp/master/LICENSE.txt 541 | [Unity]: http://unity3d.com 542 | [WebSocket-Sharp for Unity]: http://u3d.as/content/sta-blockhead/websocket-sharp-for-unity 543 | [api]: http://www.w3.org/TR/websockets 544 | [api_ja]: http://www.hcn.zaq.ne.jp/___/WEB/WebSocket-ja.html 545 | [compression]: http://tools.ietf.org/html/draft-ietf-hybi-permessage-compression-09 546 | [draft-hixie-thewebsocketprotocol-75]: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75 547 | [draft-ietf-hybi-thewebsocketprotocol-00]: http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-00 548 | [draft75]: https://github.com/sta/websocket-sharp/tree/draft75 549 | [extension parameters]: http://tools.ietf.org/html/draft-ietf-hybi-permessage-compression-09#section-8.1 550 | [hybi-00]: https://github.com/sta/websocket-sharp/tree/hybi-00 551 | [master]: https://github.com/sta/websocket-sharp/tree/master 552 | [rfc2617]: http://tools.ietf.org/html/rfc2617 553 | [rfc6455]: http://tools.ietf.org/html/rfc6455 554 | [rfc6455_ja]: http://www.hcn.zaq.ne.jp/___/WEB/RFC6455-ja.html 555 | -------------------------------------------------------------------------------- /websocket-sharp/Net/CookieCollection.cs: -------------------------------------------------------------------------------- 1 | // 2 | // CookieCollection.cs 3 | // Copied from System.Net.CookieCollection.cs 4 | // 5 | // Authors: 6 | // Lawrence Pit (loz@cable.a2000.nl) 7 | // Gonzalo Paniagua Javier (gonzalo@ximian.com) 8 | // Sebastien Pouliot (sebastien@ximian.com) 9 | // sta (sta.blockhead@gmail.com) 10 | // 11 | // Copyright (c) 2004,2009 Novell, Inc (http://www.novell.com) 12 | // Copyright (c) 2012-2013 sta.blockhead (sta.blockhead@gmail.com) 13 | // 14 | // Permission is hereby granted, free of charge, to any person obtaining 15 | // a copy of this software and associated documentation files (the 16 | // "Software"), to deal in the Software without restriction, including 17 | // without limitation the rights to use, copy, modify, merge, publish, 18 | // distribute, sublicense, and/or sell copies of the Software, and to 19 | // permit persons to whom the Software is furnished to do so, subject to 20 | // the following conditions: 21 | // 22 | // The above copyright notice and this permission notice shall be 23 | // included in all copies or substantial portions of the Software. 24 | // 25 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 26 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 27 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 28 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 29 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 30 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 31 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 32 | // 33 | 34 | using System; 35 | using System.Collections; 36 | using System.Collections.Generic; 37 | using System.Globalization; 38 | using System.Linq; 39 | using System.Runtime.Serialization; 40 | using System.Text; 41 | 42 | namespace WebSocketSharp.Net { 43 | 44 | /// 45 | /// Provides a collection container for instances of the class. 46 | /// 47 | [Serializable] 48 | public class CookieCollection : ICollection, IEnumerable 49 | { 50 | // not 100% identical to MS implementation 51 | sealed class CookieCollectionComparer : IComparer 52 | { 53 | public int Compare (Cookie x, Cookie y) 54 | { 55 | if (x == null || y == null) 56 | return 0; 57 | 58 | int c1 = x.Name.Length + x.Value.Length; 59 | int c2 = y.Name.Length + y.Value.Length; 60 | 61 | return (c1 - c2); 62 | } 63 | } 64 | 65 | #region Private Static Fields 66 | 67 | static CookieCollectionComparer Comparer = new CookieCollectionComparer (); 68 | 69 | #endregion 70 | 71 | #region Private Fields 72 | 73 | List list; 74 | object sync; 75 | 76 | #endregion 77 | 78 | #region Public Constructors 79 | 80 | /// 81 | /// Initializes a new instance of the class. 82 | /// 83 | public CookieCollection () 84 | { 85 | list = new List (); 86 | } 87 | 88 | #endregion 89 | 90 | #region Internal Properties 91 | 92 | internal IList List { 93 | get { return list; } 94 | } 95 | 96 | internal IEnumerable Sorted { 97 | get { 98 | return from cookie in list 99 | orderby cookie.Version, 100 | cookie.Name, 101 | cookie.Path.Length descending 102 | select cookie; 103 | } 104 | } 105 | 106 | #endregion 107 | 108 | #region Public Properties 109 | 110 | /// 111 | /// Gets the number of cookies contained in the . 112 | /// 113 | /// 114 | /// An that indicates the number of cookies contained in the . 115 | /// 116 | public int Count { 117 | get { return list.Count; } 118 | } 119 | 120 | // LAMESPEC: So how is one supposed to create a writable CookieCollection 121 | // instance?? We simply ignore this property, as this collection is always 122 | // writable. 123 | // 124 | /// 125 | /// Gets a value indicating whether the is read-only. 126 | /// 127 | /// 128 | /// true if the is read-only; otherwise, false. 129 | /// The default is true. 130 | /// 131 | public bool IsReadOnly { 132 | get { return true; } 133 | } 134 | 135 | /// 136 | /// Gets a value indicating whether access to the is thread safe. 137 | /// 138 | /// 139 | /// true if access to the is thread safe; otherwise, false. 140 | /// The default is false. 141 | /// 142 | public bool IsSynchronized { 143 | get { return false; } 144 | } 145 | 146 | /// 147 | /// Gets the with the specified from the . 148 | /// 149 | /// 150 | /// A with the specified in the . 151 | /// 152 | /// 153 | /// An is the zero-based index of the to find. 154 | /// 155 | /// 156 | /// is less than zero or is greater than or 157 | /// equal to . 158 | /// 159 | public Cookie this [int index] { 160 | get { 161 | if (index < 0 || index >= list.Count) 162 | throw new ArgumentOutOfRangeException ("index"); 163 | 164 | return list [index]; 165 | } 166 | } 167 | 168 | /// 169 | /// Gets the with the specified from the . 170 | /// 171 | /// 172 | /// A with the specified in the . 173 | /// 174 | /// 175 | /// A is the name of the to find. 176 | /// 177 | /// 178 | /// is . 179 | /// 180 | public Cookie this [string name] { 181 | get { 182 | if (name == null) 183 | throw new ArgumentNullException ("name"); 184 | 185 | foreach (var cookie in Sorted) { 186 | if (cookie.Name.Equals (name, StringComparison.InvariantCultureIgnoreCase)) 187 | return cookie; 188 | } 189 | 190 | return null; 191 | } 192 | } 193 | 194 | /// 195 | /// Gets an object to use to synchronize access to the . 196 | /// 197 | /// 198 | /// An to use to synchronize access to the . 199 | /// 200 | public Object SyncRoot { 201 | get { 202 | if (sync == null) 203 | sync = new object (); 204 | 205 | return sync; 206 | } 207 | } 208 | 209 | #endregion 210 | 211 | #region Private Methods 212 | 213 | static CookieCollection ParseRequest (string value) 214 | { 215 | var cookies = new CookieCollection (); 216 | 217 | Cookie cookie = null; 218 | int version = 0; 219 | string [] pairs = Split(value).ToArray(); 220 | for (int i = 0; i < pairs.Length; i++) { 221 | string pair = pairs [i].Trim (); 222 | if (pair.Length == 0) 223 | continue; 224 | 225 | if (pair.StartsWith ("$version", StringComparison.InvariantCultureIgnoreCase)) { 226 | version = Int32.Parse (pair.GetValueInternal ("=").Trim ('"')); 227 | } 228 | else if (pair.StartsWith ("$path", StringComparison.InvariantCultureIgnoreCase)) { 229 | if (cookie != null) 230 | cookie.Path = pair.GetValueInternal ("="); 231 | } 232 | else if (pair.StartsWith ("$domain", StringComparison.InvariantCultureIgnoreCase)) { 233 | if (cookie != null) 234 | cookie.Domain = pair.GetValueInternal ("="); 235 | } 236 | else if (pair.StartsWith ("$port", StringComparison.InvariantCultureIgnoreCase)) { 237 | var port = pair.Equals ("$port", StringComparison.InvariantCultureIgnoreCase) 238 | ? "\"\"" 239 | : pair.GetValueInternal ("="); 240 | 241 | if (cookie != null) 242 | cookie.Port = port; 243 | } 244 | else { 245 | if (cookie != null) 246 | cookies.Add (cookie); 247 | 248 | string name; 249 | string val = String.Empty; 250 | int pos = pair.IndexOf ('='); 251 | if (pos == -1) { 252 | name = pair; 253 | } 254 | else if (pos == pair.Length - 1) { 255 | name = pair.Substring (0, pos).TrimEnd (' '); 256 | } 257 | else { 258 | name = pair.Substring (0, pos).TrimEnd (' '); 259 | val = pair.Substring (pos + 1).TrimStart (' '); 260 | } 261 | 262 | cookie = new Cookie (name, val); 263 | if (version != 0) 264 | cookie.Version = version; 265 | } 266 | } 267 | 268 | if (cookie != null) 269 | cookies.Add (cookie); 270 | 271 | return cookies; 272 | } 273 | 274 | static CookieCollection ParseResponse (string value) 275 | { 276 | var cookies = new CookieCollection (); 277 | 278 | Cookie cookie = null; 279 | string [] pairs = Split(value).ToArray(); 280 | for (int i = 0; i < pairs.Length; i++) { 281 | string pair = pairs [i].Trim (); 282 | if (pair.Length == 0) 283 | continue; 284 | 285 | if (pair.StartsWith ("version", StringComparison.InvariantCultureIgnoreCase)) { 286 | if (cookie != null) 287 | cookie.Version = Int32.Parse (pair.GetValueInternal ("=").Trim ('"')); 288 | } 289 | else if (pair.StartsWith ("expires", StringComparison.InvariantCultureIgnoreCase)) { 290 | var buffer = new StringBuilder (pair.GetValueInternal ("="), 32); 291 | if (i < pairs.Length - 1) 292 | buffer.AppendFormat (", {0}", pairs [++i].Trim ()); 293 | 294 | DateTime expires; 295 | if (!DateTime.TryParseExact (buffer.ToString (), 296 | new string [] { "ddd, dd'-'MMM'-'yyyy HH':'mm':'ss 'GMT'", "r" }, 297 | CultureInfo.CreateSpecificCulture("en-US"), 298 | DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal, 299 | out expires)) 300 | expires = DateTime.Now; 301 | 302 | if (cookie != null && cookie.Expires == DateTime.MinValue) 303 | cookie.Expires = expires.ToLocalTime (); 304 | } 305 | else if (pair.StartsWith ("max-age", StringComparison.InvariantCultureIgnoreCase)) { 306 | int max = Int32.Parse (pair.GetValueInternal ("=").Trim ('"')); 307 | var expires = DateTime.Now.AddSeconds ((double) max); 308 | if (cookie != null) 309 | cookie.Expires = expires; 310 | } 311 | else if (pair.StartsWith ("path", StringComparison.InvariantCultureIgnoreCase)) { 312 | if (cookie != null) 313 | cookie.Path = pair.GetValueInternal ("="); 314 | } 315 | else if (pair.StartsWith ("domain", StringComparison.InvariantCultureIgnoreCase)) { 316 | if (cookie != null) 317 | cookie.Domain = pair.GetValueInternal ("="); 318 | } 319 | else if (pair.StartsWith ("port", StringComparison.InvariantCultureIgnoreCase)) { 320 | var port = pair.Equals ("port", StringComparison.InvariantCultureIgnoreCase) 321 | ? "\"\"" 322 | : pair.GetValueInternal ("="); 323 | 324 | if (cookie != null) 325 | cookie.Port = port; 326 | } 327 | else if (pair.StartsWith ("comment", StringComparison.InvariantCultureIgnoreCase)) { 328 | if (cookie != null) 329 | cookie.Comment = pair.GetValueInternal ("=").UrlDecode (); 330 | } 331 | else if (pair.StartsWith ("commenturl", StringComparison.InvariantCultureIgnoreCase)) { 332 | if (cookie != null) 333 | cookie.CommentUri = pair.GetValueInternal ("=").Trim ('"').ToUri (); 334 | } 335 | else if (pair.StartsWith ("discard", StringComparison.InvariantCultureIgnoreCase)) { 336 | if (cookie != null) 337 | cookie.Discard = true; 338 | } 339 | else if (pair.StartsWith ("secure", StringComparison.InvariantCultureIgnoreCase)) { 340 | if (cookie != null) 341 | cookie.Secure = true; 342 | } 343 | else if (pair.StartsWith ("httponly", StringComparison.InvariantCultureIgnoreCase)) { 344 | if (cookie != null) 345 | cookie.HttpOnly = true; 346 | } 347 | else { 348 | if (cookie != null) 349 | cookies.Add (cookie); 350 | 351 | string name; 352 | string val = String.Empty; 353 | int pos = pair.IndexOf ('='); 354 | if (pos == -1) { 355 | name = pair; 356 | } 357 | else if (pos == pair.Length - 1) { 358 | name = pair.Substring (0, pos).TrimEnd (' '); 359 | } 360 | else { 361 | name = pair.Substring (0, pos).TrimEnd (' '); 362 | val = pair.Substring (pos + 1).TrimStart (' '); 363 | } 364 | 365 | cookie = new Cookie (name, val); 366 | } 367 | } 368 | 369 | if (cookie != null) 370 | cookies.Add (cookie); 371 | 372 | return cookies; 373 | } 374 | 375 | int SearchCookie (Cookie cookie) 376 | { 377 | string name = cookie.Name; 378 | string path = cookie.Path; 379 | string domain = cookie.Domain; 380 | int version = cookie.Version; 381 | 382 | for (int i = list.Count - 1; i >= 0; i--) { 383 | Cookie c = list [i]; 384 | if (!c.Name.Equals (name, StringComparison.InvariantCultureIgnoreCase)) 385 | continue; 386 | 387 | if (!c.Path.Equals (path, StringComparison.InvariantCulture)) 388 | continue; 389 | 390 | if (!c.Domain.Equals (domain, StringComparison.InvariantCultureIgnoreCase)) 391 | continue; 392 | 393 | if (c.Version != version) 394 | continue; 395 | 396 | return i; 397 | } 398 | 399 | return -1; 400 | } 401 | 402 | static IEnumerable Split (string value) 403 | { 404 | return value.SplitHeaderValue (',', ';'); 405 | } 406 | 407 | #endregion 408 | 409 | #region Internal Methods 410 | 411 | internal static CookieCollection Parse (string value, bool response) 412 | { 413 | return response 414 | ? ParseResponse (value) 415 | : ParseRequest (value); 416 | } 417 | 418 | internal void SetOrRemove (Cookie cookie) 419 | { 420 | int pos = SearchCookie (cookie); 421 | if (pos == -1) { 422 | if (!cookie.Expired) 423 | list.Add (cookie); 424 | } 425 | else { 426 | if (!cookie.Expired) 427 | list [pos] = cookie; 428 | else 429 | list.RemoveAt (pos); 430 | } 431 | } 432 | 433 | internal void SetOrRemove (CookieCollection cookies) 434 | { 435 | foreach (Cookie cookie in cookies) 436 | SetOrRemove (cookie); 437 | } 438 | 439 | internal void Sort () 440 | { 441 | if (list.Count > 0) 442 | list.Sort (Comparer); 443 | } 444 | 445 | #endregion 446 | 447 | #region Public Methods 448 | 449 | /// 450 | /// Add the specified to the . 451 | /// 452 | /// 453 | /// A to add to the . 454 | /// 455 | /// 456 | /// is . 457 | /// 458 | public void Add (Cookie cookie) 459 | { 460 | if (cookie == null) 461 | throw new ArgumentNullException ("cookie"); 462 | 463 | int pos = SearchCookie (cookie); 464 | if (pos == -1) 465 | list.Add (cookie); 466 | else 467 | list [pos] = cookie; 468 | } 469 | 470 | /// 471 | /// Add the elements of the specified to the current . 472 | /// 473 | /// 474 | /// A to add to the current . 475 | /// 476 | /// 477 | /// is . 478 | /// 479 | public void Add (CookieCollection cookies) 480 | { 481 | if (cookies == null) 482 | throw new ArgumentNullException ("cookies"); 483 | 484 | foreach (Cookie cookie in cookies) 485 | Add (cookie); 486 | } 487 | 488 | /// 489 | /// Copies the elements of the to the specified , 490 | /// starting at the specified in the . 491 | /// 492 | /// 493 | /// An is the destination of the elements copied from the . 494 | /// 495 | /// 496 | /// An that indicates the zero-based index in at which copying begins. 497 | /// 498 | /// 499 | /// is . 500 | /// 501 | /// 502 | /// is less than zero. 503 | /// 504 | /// 505 | /// 506 | /// is multidimensional. 507 | /// 508 | /// 509 | /// -or- 510 | /// 511 | /// 512 | /// The number of elements in the is greater than the available space 513 | /// from index to the end of the destination . 514 | /// 515 | /// 516 | /// 517 | /// The elements in the cannot be cast automatically 518 | /// to the type of the destination . 519 | /// 520 | public void CopyTo (Array array, int index) 521 | { 522 | if (array == null) 523 | throw new ArgumentNullException ("array"); 524 | 525 | if (index < 0) 526 | throw new ArgumentOutOfRangeException ("index", "Must not be less than zero."); 527 | 528 | if (array.Rank > 1) 529 | throw new ArgumentException ("Must not be multidimensional.", "array"); 530 | 531 | if (array.Length - index < list.Count) 532 | throw new ArgumentException ( 533 | "The number of elements in this collection is greater than the available space of the destination array."); 534 | 535 | if (!array.GetType ().GetElementType ().IsAssignableFrom (typeof (Cookie))) 536 | throw new InvalidCastException ( 537 | "The elements in this collection cannot be cast automatically to the type of the destination array."); 538 | 539 | (list as IList).CopyTo (array, index); 540 | } 541 | 542 | /// 543 | /// Copies the elements of the to the specified array of , 544 | /// starting at the specified in the . 545 | /// 546 | /// 547 | /// An array of is the destination of the elements copied from the . 548 | /// 549 | /// 550 | /// An that indicates the zero-based index in at which copying begins. 551 | /// 552 | /// 553 | /// is . 554 | /// 555 | /// 556 | /// is less than zero. 557 | /// 558 | /// 559 | /// The number of elements in the is greater than the available space 560 | /// from index to the end of the destination . 561 | /// 562 | public void CopyTo (Cookie [] array, int index) 563 | { 564 | if (array == null) 565 | throw new ArgumentNullException ("array"); 566 | 567 | if (index < 0) 568 | throw new ArgumentOutOfRangeException ("index", "Must not be less than zero."); 569 | 570 | if (array.Length - index < list.Count) 571 | throw new ArgumentException ( 572 | "The number of elements in this collection is greater than the available space of the destination array."); 573 | 574 | list.CopyTo (array, index); 575 | } 576 | 577 | /// 578 | /// Gets the enumerator to use to iterate through the . 579 | /// 580 | /// 581 | /// An instance of an implementation of the interface 582 | /// to use to iterate through the . 583 | /// 584 | public IEnumerator GetEnumerator () 585 | { 586 | return list.GetEnumerator (); 587 | } 588 | 589 | #endregion 590 | } 591 | } 592 | -------------------------------------------------------------------------------- /websocket-sharp/WsFrame.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | /* 3 | * WsFrame.cs 4 | * 5 | * The MIT License 6 | * 7 | * Copyright (c) 2012-2013 sta.blockhead 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | */ 27 | #endregion 28 | 29 | using System; 30 | using System.IO; 31 | using System.Collections; 32 | using System.Collections.Generic; 33 | using System.Text; 34 | 35 | namespace WebSocketSharp 36 | { 37 | internal class WsFrame : IEnumerable 38 | { 39 | #region Internal Static Fields 40 | 41 | internal static readonly byte [] EmptyUnmaskPingData; 42 | 43 | #endregion 44 | 45 | #region Static Constructor 46 | 47 | static WsFrame () 48 | { 49 | EmptyUnmaskPingData = CreatePingFrame (Mask.UNMASK).ToByteArray (); 50 | } 51 | 52 | #endregion 53 | 54 | #region Private Constructors 55 | 56 | private WsFrame () 57 | { 58 | } 59 | 60 | #endregion 61 | 62 | #region Public Constructors 63 | 64 | public WsFrame (Opcode opcode, PayloadData payload) 65 | : this (opcode, Mask.MASK, payload) 66 | { 67 | } 68 | 69 | public WsFrame (Opcode opcode, Mask mask, PayloadData payload) 70 | : this (Fin.FINAL, opcode, mask, payload) 71 | { 72 | } 73 | 74 | public WsFrame (Fin fin, Opcode opcode, Mask mask, PayloadData payload) 75 | : this (fin, opcode, mask, payload, false) 76 | { 77 | } 78 | 79 | public WsFrame ( 80 | Fin fin, Opcode opcode, Mask mask, PayloadData payload, bool compressed) 81 | { 82 | Fin = fin; 83 | Rsv1 = isData (opcode) && compressed ? Rsv.ON : Rsv.OFF; 84 | Rsv2 = Rsv.OFF; 85 | Rsv3 = Rsv.OFF; 86 | Opcode = opcode; 87 | Mask = mask; 88 | 89 | /* PayloadLen */ 90 | 91 | var dataLen = payload.Length; 92 | var payloadLen = dataLen < 126 93 | ? (byte) dataLen 94 | : dataLen < 0x010000 95 | ? (byte) 126 96 | : (byte) 127; 97 | 98 | PayloadLen = payloadLen; 99 | 100 | /* ExtPayloadLen */ 101 | 102 | ExtPayloadLen = payloadLen < 126 103 | ? new byte []{} 104 | : payloadLen == 126 105 | ? ((ushort) dataLen).ToByteArrayInternally (ByteOrder.BIG) 106 | : dataLen.ToByteArrayInternally (ByteOrder.BIG); 107 | 108 | /* MaskingKey */ 109 | 110 | var masking = mask == Mask.MASK; 111 | var maskingKey = masking 112 | ? createMaskingKey () 113 | : new byte []{}; 114 | 115 | MaskingKey = maskingKey; 116 | 117 | /* PayloadData */ 118 | 119 | if (masking) 120 | payload.Mask (maskingKey); 121 | 122 | PayloadData = payload; 123 | } 124 | 125 | #endregion 126 | 127 | #region Internal Properties 128 | 129 | internal bool IsBinary { 130 | get { 131 | return Opcode == Opcode.BINARY; 132 | } 133 | } 134 | 135 | internal bool IsClose { 136 | get { 137 | return Opcode == Opcode.CLOSE; 138 | } 139 | } 140 | 141 | internal bool IsCompressed { 142 | get { 143 | return Rsv1 == Rsv.ON; 144 | } 145 | } 146 | 147 | internal bool IsContinuation { 148 | get { 149 | return Opcode == Opcode.CONT; 150 | } 151 | } 152 | 153 | internal bool IsControl { 154 | get { 155 | var opcode = Opcode; 156 | return opcode == Opcode.CLOSE || opcode == Opcode.PING || opcode == Opcode.PONG; 157 | } 158 | } 159 | 160 | internal bool IsData { 161 | get { 162 | var opcode = Opcode; 163 | return opcode == Opcode.BINARY || opcode == Opcode.TEXT; 164 | } 165 | } 166 | 167 | internal bool IsFinal { 168 | get { 169 | return Fin == Fin.FINAL; 170 | } 171 | } 172 | 173 | internal bool IsFragmented { 174 | get { 175 | return Fin == Fin.MORE || Opcode == Opcode.CONT; 176 | } 177 | } 178 | 179 | internal bool IsMasked { 180 | get { 181 | return Mask == Mask.MASK; 182 | } 183 | } 184 | 185 | internal bool IsPerMessageCompressed { 186 | get { 187 | var opcode = Opcode; 188 | return (opcode == Opcode.BINARY || opcode == Opcode.TEXT) && Rsv1 == Rsv.ON; 189 | } 190 | } 191 | 192 | internal bool IsPing { 193 | get { 194 | return Opcode == Opcode.PING; 195 | } 196 | } 197 | 198 | internal bool IsPong { 199 | get { 200 | return Opcode == Opcode.PONG; 201 | } 202 | } 203 | 204 | internal bool IsText { 205 | get { 206 | return Opcode == Opcode.TEXT; 207 | } 208 | } 209 | 210 | internal ulong Length { 211 | get { 212 | return 2 + (ulong) (ExtPayloadLen.Length + MaskingKey.Length) + PayloadData.Length; 213 | } 214 | } 215 | 216 | #endregion 217 | 218 | #region Public Properties 219 | 220 | public Fin Fin { get; private set; } 221 | 222 | public Rsv Rsv1 { get; private set; } 223 | 224 | public Rsv Rsv2 { get; private set; } 225 | 226 | public Rsv Rsv3 { get; private set; } 227 | 228 | public Opcode Opcode { get; private set; } 229 | 230 | public Mask Mask { get; private set; } 231 | 232 | public byte PayloadLen { get; private set; } 233 | 234 | public byte [] ExtPayloadLen { get; private set; } 235 | 236 | public byte [] MaskingKey { get; private set; } 237 | 238 | public PayloadData PayloadData { get; private set; } 239 | 240 | #endregion 241 | 242 | #region Private Methods 243 | 244 | private static byte [] createMaskingKey () 245 | { 246 | var key = new byte [4]; 247 | var rand = new Random (); 248 | rand.NextBytes (key); 249 | 250 | return key; 251 | } 252 | 253 | private static string dump (WsFrame frame) 254 | { 255 | var len = frame.Length; 256 | var count = (long) (len / 4); 257 | var rem = (int) (len % 4); 258 | 259 | int countDigit; 260 | string countFmt; 261 | if (count < 10000) 262 | { 263 | countDigit = 4; 264 | countFmt = "{0,4}"; 265 | } 266 | else if (count < 0x010000) 267 | { 268 | countDigit = 4; 269 | countFmt = "{0,4:X}"; 270 | } 271 | else if (count < 0x0100000000) 272 | { 273 | countDigit = 8; 274 | countFmt = "{0,8:X}"; 275 | } 276 | else 277 | { 278 | countDigit = 16; 279 | countFmt = "{0,16:X}"; 280 | } 281 | 282 | var spFmt = String.Format ("{{0,{0}}}", countDigit); 283 | var headerFmt = String.Format ( 284 | @"{0} 01234567 89ABCDEF 01234567 89ABCDEF 285 | {0}+--------+--------+--------+--------+\n", spFmt); 286 | var footerFmt = String.Format ("{0}+--------+--------+--------+--------+", spFmt); 287 | 288 | var buffer = new StringBuilder (64); 289 | Func> linePrinter = () => 290 | { 291 | long lineCount = 0; 292 | var lineFmt = String.Format ("{0}|{{1,8}} {{2,8}} {{3,8}} {{4,8}}|\n", countFmt); 293 | return (arg1, arg2, arg3, arg4) => 294 | buffer.AppendFormat (lineFmt, ++lineCount, arg1, arg2, arg3, arg4); 295 | }; 296 | var printLine = linePrinter (); 297 | 298 | buffer.AppendFormat (headerFmt, String.Empty); 299 | 300 | var frameAsBytes = frame.ToByteArray (); 301 | int i, j; 302 | for (i = 0; i <= count; i++) 303 | { 304 | j = i * 4; 305 | if (i < count) 306 | printLine ( 307 | Convert.ToString (frameAsBytes [j], 2).PadLeft (8, '0'), 308 | Convert.ToString (frameAsBytes [j + 1], 2).PadLeft (8, '0'), 309 | Convert.ToString (frameAsBytes [j + 2], 2).PadLeft (8, '0'), 310 | Convert.ToString (frameAsBytes [j + 3], 2).PadLeft (8, '0')); 311 | else if (rem > 0) 312 | printLine ( 313 | Convert.ToString (frameAsBytes [j], 2).PadLeft (8, '0'), 314 | rem >= 2 ? Convert.ToString (frameAsBytes [j + 1], 2).PadLeft (8, '0') : String.Empty, 315 | rem == 3 ? Convert.ToString (frameAsBytes [j + 2], 2).PadLeft (8, '0') : String.Empty, 316 | String.Empty); 317 | } 318 | 319 | buffer.AppendFormat (footerFmt, String.Empty); 320 | return buffer.ToString (); 321 | } 322 | 323 | private static bool isBinary (Opcode opcode) 324 | { 325 | return opcode == Opcode.BINARY; 326 | } 327 | 328 | private static bool isClose (Opcode opcode) 329 | { 330 | return opcode == Opcode.CLOSE; 331 | } 332 | 333 | private static bool isContinuation (Opcode opcode) 334 | { 335 | return opcode == Opcode.CONT; 336 | } 337 | 338 | private static bool isControl (Opcode opcode) 339 | { 340 | return opcode == Opcode.CLOSE || opcode == Opcode.PING || opcode == Opcode.PONG; 341 | } 342 | 343 | private static bool isData (Opcode opcode) 344 | { 345 | return opcode == Opcode.TEXT || opcode == Opcode.BINARY; 346 | } 347 | 348 | private static bool isFinal (Fin fin) 349 | { 350 | return fin == Fin.FINAL; 351 | } 352 | 353 | private static bool isMasked (Mask mask) 354 | { 355 | return mask == Mask.MASK; 356 | } 357 | 358 | private static bool isPing (Opcode opcode) 359 | { 360 | return opcode == Opcode.PING; 361 | } 362 | 363 | private static bool isPong (Opcode opcode) 364 | { 365 | return opcode == Opcode.PONG; 366 | } 367 | 368 | private static bool isText (Opcode opcode) 369 | { 370 | return opcode == Opcode.TEXT; 371 | } 372 | 373 | private static WsFrame parse (byte [] header, Stream stream, bool unmask) 374 | { 375 | /* Header */ 376 | 377 | // FIN 378 | var fin = (header [0] & 0x80) == 0x80 ? Fin.FINAL : Fin.MORE; 379 | // RSV1 380 | var rsv1 = (header [0] & 0x40) == 0x40 ? Rsv.ON : Rsv.OFF; 381 | // RSV2 382 | var rsv2 = (header [0] & 0x20) == 0x20 ? Rsv.ON : Rsv.OFF; 383 | // RSV3 384 | var rsv3 = (header [0] & 0x10) == 0x10 ? Rsv.ON : Rsv.OFF; 385 | // Opcode 386 | var opcode = (Opcode) (header [0] & 0x0f); 387 | // MASK 388 | var mask = (header [1] & 0x80) == 0x80 ? Mask.MASK : Mask.UNMASK; 389 | // Payload len 390 | var payloadLen = (byte) (header [1] & 0x7f); 391 | 392 | // Check if correct frame. 393 | var incorrect = isControl (opcode) && fin == Fin.MORE 394 | ? "A control frame is fragmented." 395 | : !isData (opcode) && rsv1 == Rsv.ON 396 | ? "A non data frame is compressed." 397 | : null; 398 | 399 | if (incorrect != null) 400 | throw new WebSocketException (CloseStatusCode.INCORRECT_DATA, incorrect); 401 | 402 | // Check if consistent frame. 403 | if (isControl (opcode) && payloadLen > 125) 404 | throw new WebSocketException ( 405 | CloseStatusCode.INCONSISTENT_DATA, 406 | "The payload data length of a control frame is greater than 125 bytes."); 407 | 408 | var frame = new WsFrame { 409 | Fin = fin, 410 | Rsv1 = rsv1, 411 | Rsv2 = rsv2, 412 | Rsv3 = rsv3, 413 | Opcode = opcode, 414 | Mask = mask, 415 | PayloadLen = payloadLen 416 | }; 417 | 418 | /* Extended Payload Length */ 419 | 420 | var extLen = payloadLen < 126 421 | ? 0 422 | : payloadLen == 126 423 | ? 2 424 | : 8; 425 | 426 | var extPayloadLen = extLen > 0 427 | ? stream.ReadBytes (extLen) 428 | : new byte []{}; 429 | 430 | if (extLen > 0 && extPayloadLen.Length != extLen) 431 | throw new WebSocketException ( 432 | "The 'Extended Payload Length' of a frame cannot be read from the data source."); 433 | 434 | frame.ExtPayloadLen = extPayloadLen; 435 | 436 | /* Masking Key */ 437 | 438 | var masked = mask == Mask.MASK; 439 | var maskingKey = masked 440 | ? stream.ReadBytes (4) 441 | : new byte []{}; 442 | 443 | if (masked && maskingKey.Length != 4) 444 | throw new WebSocketException ( 445 | "The 'Masking Key' of a frame cannot be read from the data source."); 446 | 447 | frame.MaskingKey = maskingKey; 448 | 449 | /* Payload Data */ 450 | 451 | ulong dataLen = payloadLen < 126 452 | ? payloadLen 453 | : payloadLen == 126 454 | ? extPayloadLen.ToUInt16 (ByteOrder.BIG) 455 | : extPayloadLen.ToUInt64 (ByteOrder.BIG); 456 | 457 | byte [] data = null; 458 | if (dataLen > 0) 459 | { 460 | // Check if allowable payload data length. 461 | if (payloadLen > 126 && dataLen > PayloadData.MaxLength) 462 | throw new WebSocketException ( 463 | CloseStatusCode.TOO_BIG, "The 'Payload Data' length is greater than the allowable length."); 464 | 465 | data = payloadLen > 126 466 | ? stream.ReadBytes ((long) dataLen, 1024) 467 | : stream.ReadBytes ((int) dataLen); 468 | 469 | if (data.LongLength != (long) dataLen) 470 | throw new WebSocketException ( 471 | "The 'Payload Data' of a frame cannot be read from the data source."); 472 | } 473 | else 474 | { 475 | data = new byte []{}; 476 | } 477 | 478 | var payload = new PayloadData (data, masked); 479 | if (masked && unmask) 480 | { 481 | payload.Mask (maskingKey); 482 | frame.Mask = Mask.UNMASK; 483 | frame.MaskingKey = new byte []{}; 484 | } 485 | 486 | frame.PayloadData = payload; 487 | return frame; 488 | } 489 | 490 | private static string print (WsFrame frame) 491 | { 492 | /* Opcode */ 493 | 494 | var opcode = frame.Opcode.ToString (); 495 | 496 | /* Payload Len */ 497 | 498 | var payloadLen = frame.PayloadLen; 499 | 500 | /* Extended Payload Len */ 501 | 502 | var ext = frame.ExtPayloadLen; 503 | var size = ext.Length; 504 | var extLen = size == 2 505 | ? ext.ToUInt16 (ByteOrder.BIG).ToString () 506 | : size == 8 507 | ? ext.ToUInt64 (ByteOrder.BIG).ToString () 508 | : String.Empty; 509 | 510 | /* Masking Key */ 511 | 512 | var masked = frame.IsMasked; 513 | var key = masked 514 | ? BitConverter.ToString (frame.MaskingKey) 515 | : String.Empty; 516 | 517 | /* Payload Data */ 518 | 519 | var data = payloadLen == 0 520 | ? String.Empty 521 | : size > 0 522 | ? String.Format ("A {0} data with {1} bytes.", opcode.ToLower (), extLen) 523 | : masked || frame.IsFragmented || frame.IsBinary || frame.IsClose 524 | ? BitConverter.ToString (frame.PayloadData.ToByteArray ()) 525 | : Encoding.UTF8.GetString (frame.PayloadData.ApplicationData); 526 | 527 | var format = 528 | @" FIN: {0} 529 | RSV1: {1} 530 | RSV2: {2} 531 | RSV3: {3} 532 | Opcode: {4} 533 | MASK: {5} 534 | Payload Len: {6} 535 | Extended Payload Len: {7} 536 | Masking Key: {8} 537 | Payload Data: {9}"; 538 | 539 | return String.Format ( 540 | format, 541 | frame.Fin, 542 | frame.Rsv1, 543 | frame.Rsv2, 544 | frame.Rsv3, 545 | opcode, 546 | frame.Mask, 547 | payloadLen, 548 | extLen, 549 | key, 550 | data); 551 | } 552 | 553 | #endregion 554 | 555 | #region Internal Methods 556 | 557 | internal static WsFrame CreateCloseFrame (Mask mask, PayloadData payload) 558 | { 559 | return new WsFrame (Opcode.CLOSE, mask, payload); 560 | } 561 | 562 | internal static WsFrame CreatePongFrame (Mask mask, PayloadData payload) 563 | { 564 | return new WsFrame (Opcode.PONG, mask, payload); 565 | } 566 | 567 | #endregion 568 | 569 | #region Public Methods 570 | 571 | public static WsFrame CreateCloseFrame (Mask mask, byte [] data) 572 | { 573 | return new WsFrame (Opcode.CLOSE, mask, new PayloadData (data)); 574 | } 575 | 576 | public static WsFrame CreateCloseFrame (Mask mask, CloseStatusCode code, string reason) 577 | { 578 | return new WsFrame (Opcode.CLOSE, mask, new PayloadData (((ushort) code).Append (reason))); 579 | } 580 | 581 | public static WsFrame CreateFrame ( 582 | Fin fin, Opcode opcode, Mask mask, byte [] data, bool compressed) 583 | { 584 | return new WsFrame (fin, opcode, mask, new PayloadData (data), compressed); 585 | } 586 | 587 | public static WsFrame CreatePingFrame (Mask mask) 588 | { 589 | return new WsFrame (Opcode.PING, mask, new PayloadData ()); 590 | } 591 | 592 | public static WsFrame CreatePingFrame (Mask mask, byte [] data) 593 | { 594 | return new WsFrame (Opcode.PING, mask, new PayloadData (data)); 595 | } 596 | 597 | public IEnumerator GetEnumerator () 598 | { 599 | foreach (byte b in ToByteArray ()) 600 | yield return b; 601 | } 602 | 603 | public static WsFrame Parse (byte [] src) 604 | { 605 | return Parse (src, true); 606 | } 607 | 608 | public static WsFrame Parse (Stream stream) 609 | { 610 | return Parse (stream, true); 611 | } 612 | 613 | public static WsFrame Parse (byte [] src, bool unmask) 614 | { 615 | using (var stream = new MemoryStream (src)) 616 | { 617 | return Parse (stream, unmask); 618 | } 619 | } 620 | 621 | public static WsFrame Parse (Stream stream, bool unmask) 622 | { 623 | var header = stream.ReadBytes (2); 624 | if (header.Length != 2) 625 | throw new WebSocketException ( 626 | "The header part of a frame cannot be read from the data source."); 627 | 628 | return parse (header, stream, unmask); 629 | } 630 | 631 | public static void ParseAsync (Stream stream, Action completed) 632 | { 633 | ParseAsync (stream, true, completed, null); 634 | } 635 | 636 | public static void ParseAsync (Stream stream, Action completed, Action error) 637 | { 638 | ParseAsync (stream, true, completed, error); 639 | } 640 | 641 | public static void ParseAsync ( 642 | Stream stream, bool unmask, Action completed, Action error) 643 | { 644 | stream.ReadBytesAsync ( 645 | 2, 646 | header => 647 | { 648 | if (header.Length != 2) 649 | throw new WebSocketException ( 650 | "The header part of a frame cannot be read from the data source."); 651 | 652 | var frame = parse (header, stream, unmask); 653 | if (completed != null) 654 | completed (frame); 655 | }, 656 | error); 657 | } 658 | 659 | public void Print (bool dumped) 660 | { 661 | Console.WriteLine (dumped ? dump (this) : print (this)); 662 | } 663 | 664 | public string PrintToString (bool dumped) 665 | { 666 | return dumped ? dump (this) : print (this); 667 | } 668 | 669 | public byte [] ToByteArray() 670 | { 671 | using (var buffer = new MemoryStream ()) 672 | { 673 | int header = (int) Fin; 674 | header = (header << 1) + (int) Rsv1; 675 | header = (header << 1) + (int) Rsv2; 676 | header = (header << 1) + (int) Rsv3; 677 | header = (header << 4) + (int) Opcode; 678 | header = (header << 1) + (int) Mask; 679 | header = (header << 7) + (int) PayloadLen; 680 | buffer.Write (((ushort) header).ToByteArrayInternally (ByteOrder.BIG), 0, 2); 681 | 682 | if (PayloadLen > 125) 683 | buffer.Write (ExtPayloadLen, 0, ExtPayloadLen.Length); 684 | 685 | if (Mask == Mask.MASK) 686 | buffer.Write (MaskingKey, 0, MaskingKey.Length); 687 | 688 | if (PayloadLen > 0) 689 | { 690 | var payload = PayloadData.ToByteArray (); 691 | if (PayloadLen < 127) 692 | buffer.Write (payload, 0, payload.Length); 693 | else 694 | buffer.WriteBytes (payload); 695 | } 696 | 697 | buffer.Close (); 698 | return buffer.ToArray (); 699 | } 700 | } 701 | 702 | public override string ToString () 703 | { 704 | return BitConverter.ToString (ToByteArray ()); 705 | } 706 | 707 | #endregion 708 | 709 | #region Explicitly Implemented Interface Members 710 | 711 | IEnumerator IEnumerable.GetEnumerator () 712 | { 713 | return GetEnumerator (); 714 | } 715 | 716 | #endregion 717 | } 718 | } 719 | -------------------------------------------------------------------------------- /websocket-sharp/Net/Cookie.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Cookie.cs 3 | // Copied from System.Net.Cookie.cs 4 | // 5 | // Authors: 6 | // Lawrence Pit (loz@cable.a2000.nl) 7 | // Gonzalo Paniagua Javier (gonzalo@ximian.com) 8 | // Daniel Nauck (dna@mono-project.de) 9 | // Sebastien Pouliot (sebastien@ximian.com) 10 | // sta (sta.blockhead@gmail.com) 11 | // 12 | // Copyright (c) 2004,2009 Novell, Inc (http://www.novell.com) 13 | // Copyright (c) 2012-2013 sta.blockhead (sta.blockhead@gmail.com) 14 | // 15 | // Permission is hereby granted, free of charge, to any person obtaining 16 | // a copy of this software and associated documentation files (the 17 | // "Software"), to deal in the Software without restriction, including 18 | // without limitation the rights to use, copy, modify, merge, publish, 19 | // distribute, sublicense, and/or sell copies of the Software, and to 20 | // permit persons to whom the Software is furnished to do so, subject to 21 | // the following conditions: 22 | // 23 | // The above copyright notice and this permission notice shall be 24 | // included in all copies or substantial portions of the Software. 25 | // 26 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 27 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 28 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 29 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 30 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 31 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 32 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 33 | // 34 | 35 | using System; 36 | using System.Text; 37 | using System.Globalization; 38 | using System.Collections; 39 | 40 | namespace WebSocketSharp.Net 41 | { 42 | /// 43 | /// Provides a set of properties and methods used to manage an HTTP Cookie. 44 | /// 45 | /// 46 | /// 47 | /// The Cookie class supports the following cookie formats: 48 | /// Netscape specification, 49 | /// RFC 2109 and 50 | /// RFC 2965. 51 | /// 52 | /// 53 | /// The Cookie class cannot be inherited. 54 | /// 55 | /// 56 | [Serializable] 57 | public sealed class Cookie 58 | { 59 | #region Static Private Fields 60 | 61 | static char [] reservedCharsForName = new char [] {' ', '=', ';', ',', '\n', '\r', '\t'}; 62 | static char [] reservedCharsForValue = new char [] {';', ','}; 63 | 64 | #endregion 65 | 66 | #region Private Fields 67 | 68 | string comment; 69 | Uri commentUri; 70 | bool discard; 71 | string domain; 72 | DateTime expires; 73 | bool httpOnly; 74 | string name; 75 | string path; 76 | string port; 77 | int [] ports; 78 | bool secure; 79 | DateTime timestamp; 80 | string val; 81 | int version; 82 | 83 | #endregion 84 | 85 | #region Public Constructors 86 | 87 | /// 88 | /// Initializes a new instance of the class. 89 | /// 90 | public Cookie () 91 | { 92 | comment = String.Empty; 93 | domain = String.Empty; 94 | expires = DateTime.MinValue; 95 | name = String.Empty; 96 | path = String.Empty; 97 | port = String.Empty; 98 | ports = new int [] {}; 99 | timestamp = DateTime.Now; 100 | val = String.Empty; 101 | version = 0; 102 | } 103 | 104 | /// 105 | /// Initializes a new instance of the class 106 | /// with the specified and . 107 | /// 108 | /// 109 | /// A that contains the Name of the cookie. 110 | /// 111 | /// 112 | /// A that contains the Value of the cookie. 113 | /// 114 | /// 115 | /// 116 | /// is or . 117 | /// 118 | /// 119 | /// - or - 120 | /// 121 | /// 122 | /// contains an invalid character. 123 | /// 124 | /// 125 | /// - or - 126 | /// 127 | /// 128 | /// is . 129 | /// 130 | /// 131 | /// - or - 132 | /// 133 | /// 134 | /// contains a string not enclosed in double quotes 135 | /// that contains an invalid character. 136 | /// 137 | /// 138 | public Cookie (string name, string value) 139 | : this () 140 | { 141 | Name = name; 142 | Value = value; 143 | } 144 | 145 | /// 146 | /// Initializes a new instance of the class 147 | /// with the specified , and . 148 | /// 149 | /// 150 | /// A that contains the Name of the cookie. 151 | /// 152 | /// 153 | /// A that contains the Value of the cookie. 154 | /// 155 | /// 156 | /// A that contains the value of the Path attribute of the cookie. 157 | /// 158 | /// 159 | /// 160 | /// is or . 161 | /// 162 | /// 163 | /// - or - 164 | /// 165 | /// 166 | /// contains an invalid character. 167 | /// 168 | /// 169 | /// - or - 170 | /// 171 | /// 172 | /// is . 173 | /// 174 | /// 175 | /// - or - 176 | /// 177 | /// 178 | /// contains a string not enclosed in double quotes 179 | /// that contains an invalid character. 180 | /// 181 | /// 182 | public Cookie (string name, string value, string path) 183 | : this (name, value) 184 | { 185 | Path = path; 186 | } 187 | 188 | /// 189 | /// Initializes a new instance of the class 190 | /// with the specified , , 191 | /// and . 192 | /// 193 | /// 194 | /// A that contains the Name of the cookie. 195 | /// 196 | /// 197 | /// A that contains the Value of the cookie. 198 | /// 199 | /// 200 | /// A that contains the value of the Path attribute of the cookie. 201 | /// 202 | /// 203 | /// A that contains the value of the Domain attribute of the cookie. 204 | /// 205 | /// 206 | /// 207 | /// is or . 208 | /// 209 | /// 210 | /// - or - 211 | /// 212 | /// 213 | /// contains an invalid character. 214 | /// 215 | /// 216 | /// - or - 217 | /// 218 | /// 219 | /// is . 220 | /// 221 | /// 222 | /// - or - 223 | /// 224 | /// 225 | /// contains a string not enclosed in double quotes 226 | /// that contains an invalid character. 227 | /// 228 | /// 229 | public Cookie (string name, string value, string path, string domain) 230 | : this (name, value, path) 231 | { 232 | Domain = domain; 233 | } 234 | 235 | #endregion 236 | 237 | #region Internal Properties 238 | 239 | internal bool ExactDomain { get; set; } 240 | 241 | internal int MaxAge { 242 | get { 243 | if (expires == DateTime.MinValue || Expired) 244 | return 0; 245 | 246 | var tmp = expires.Kind == DateTimeKind.Local 247 | ? expires 248 | : expires.ToLocalTime (); 249 | 250 | var span = tmp - DateTime.Now; 251 | return span <= TimeSpan.Zero 252 | ? 0 253 | : (int) span.TotalSeconds; 254 | } 255 | } 256 | 257 | internal int [] Ports { 258 | get { return ports; } 259 | } 260 | 261 | #endregion 262 | 263 | #region Public Properties 264 | 265 | /// 266 | /// Gets or sets the value of the Comment attribute of the cookie. 267 | /// 268 | /// 269 | /// A that contains a comment to document intended use of the cookie. 270 | /// 271 | public string Comment { 272 | get { return comment; } 273 | set { comment = value ?? String.Empty; } 274 | } 275 | 276 | /// 277 | /// Gets or sets the value of the CommentURL attribute of the cookie. 278 | /// 279 | /// 280 | /// A that contains a URI that provides the comment 281 | /// to document intended use of the cookie. 282 | /// 283 | public Uri CommentUri { 284 | get { return commentUri; } 285 | set { commentUri = value; } 286 | } 287 | 288 | /// 289 | /// Gets or sets a value indicating whether the client discards the cookie unconditionally 290 | /// when the client terminates. 291 | /// 292 | /// 293 | /// true if the client discards the cookie unconditionally when the client terminates; 294 | /// otherwise, false. The default is false. 295 | /// 296 | public bool Discard { 297 | get { return discard; } 298 | set { discard = value; } 299 | } 300 | 301 | /// 302 | /// Gets or sets the value of the Domain attribute of the cookie. 303 | /// 304 | /// 305 | /// A that contains a URI for which the cookie is valid. 306 | /// 307 | public string Domain { 308 | get { return domain; } 309 | set { 310 | if (value.IsNullOrEmpty ()) { 311 | domain = String.Empty; 312 | ExactDomain = true; 313 | } else { 314 | domain = value; 315 | ExactDomain = value [0] != '.'; 316 | } 317 | } 318 | } 319 | 320 | /// 321 | /// Gets or sets a value indicating whether the cookie has expired. 322 | /// 323 | /// 324 | /// true if the cookie has expired; otherwise, false. The default is false. 325 | /// 326 | public bool Expired { 327 | get { 328 | return expires <= DateTime.Now && 329 | expires != DateTime.MinValue; 330 | } 331 | set { 332 | expires = value 333 | ? DateTime.Now 334 | : DateTime.MinValue; 335 | } 336 | } 337 | 338 | /// 339 | /// Gets or sets the value of the Expires attribute of the cookie. 340 | /// 341 | /// 342 | /// A that contains the date and time at which the cookie expires. 343 | /// The default is . 344 | /// 345 | public DateTime Expires { 346 | get { return expires; } 347 | set { expires = value; } 348 | } 349 | 350 | /// 351 | /// Gets or sets a value indicating non-HTTP APIs can access the cookie. 352 | /// 353 | /// 354 | /// true if non-HTTP APIs can not access the cookie; otherwise, false. 355 | /// 356 | public bool HttpOnly { 357 | get { return httpOnly; } 358 | set { httpOnly = value; } 359 | } 360 | 361 | /// 362 | /// Gets or sets the Name of the cookie. 363 | /// 364 | /// 365 | /// A that contains the Name of the cookie. 366 | /// 367 | /// 368 | /// 369 | /// The value specified for a set operation is or . 370 | /// 371 | /// 372 | /// - or - 373 | /// 374 | /// 375 | /// The value specified for a set operation contains an invalid character. 376 | /// 377 | /// 378 | public string Name { 379 | get { return name; } 380 | set { 381 | string msg; 382 | if (!CanSetName (value, out msg)) 383 | throw new CookieException (msg); 384 | 385 | name = value; 386 | } 387 | } 388 | 389 | /// 390 | /// Gets or sets the value of the Path attribute of the cookie. 391 | /// 392 | /// 393 | /// A that contains a subset of URI on the origin server 394 | /// to which the cookie applies. 395 | /// 396 | public string Path { 397 | get { return path; } 398 | set { path = value ?? String.Empty; } 399 | } 400 | 401 | /// 402 | /// Gets or sets the value of the Port attribute of the cookie. 403 | /// 404 | /// 405 | /// A that contains a list of the TCP ports to which the cookie applies. 406 | /// 407 | /// 408 | /// The value specified for a set operation is not enclosed in double quotes or could not be parsed. 409 | /// 410 | public string Port { 411 | get { return port; } 412 | set { 413 | if (value.IsNullOrEmpty ()) { 414 | port = String.Empty; 415 | ports = new int [] {}; 416 | return; 417 | } 418 | 419 | if (!value.IsEnclosedIn ('"')) 420 | throw new CookieException (String.Format ( 421 | "The 'Port={0}' attribute of the cookie is invalid. The value must be enclosed in double quotes.", value)); 422 | 423 | string error; 424 | if (!TryCreatePorts (value, out ports, out error)) 425 | throw new CookieException (String.Format ( 426 | "The 'Port={0}' attribute of the cookie is invalid. Invalid value: {1}", value, error)); 427 | 428 | port = value; 429 | } 430 | } 431 | 432 | /// 433 | /// Gets or sets a value indicating whether the security level of the cookie is secure. 434 | /// 435 | /// 436 | /// When this property is true, the cookie may be included in the HTTP request 437 | /// only if the request is transmitted over the HTTPS. 438 | /// 439 | /// 440 | /// true if the security level of the cookie is secure; otherwise, false. 441 | /// The default is false. 442 | /// 443 | public bool Secure { 444 | get { return secure; } 445 | set { secure = value; } 446 | } 447 | 448 | /// 449 | /// Gets the time when the cookie was issued. 450 | /// 451 | /// 452 | /// A that contains the time when the cookie was issued. 453 | /// 454 | public DateTime TimeStamp { 455 | get { return timestamp; } 456 | } 457 | 458 | /// 459 | /// Gets or sets the Value of the cookie. 460 | /// 461 | /// 462 | /// A that contains the Value of the cookie. 463 | /// 464 | /// 465 | /// 466 | /// The value specified for a set operation is . 467 | /// 468 | /// 469 | /// - or - 470 | /// 471 | /// 472 | /// The value specified for a set operation contains a string not enclosed in double quotes 473 | /// that contains an invalid character. 474 | /// 475 | /// 476 | public string Value { 477 | get { return val; } 478 | set { 479 | string msg; 480 | if (!CanSetValue (value, out msg)) 481 | throw new CookieException (msg); 482 | 483 | val = value.Length == 0 ? "\"\"" : value; 484 | } 485 | } 486 | 487 | /// 488 | /// Gets or sets the value of the Version attribute of the cookie. 489 | /// 490 | /// 491 | /// An that contains the version of the HTTP state management 492 | /// to which the cookie conforms. 493 | /// 494 | /// 495 | /// The value specified for a set operation is not allowed. The value must be 0 or 1. 496 | /// 497 | public int Version { 498 | get { return version; } 499 | set { 500 | if (value < 0 || value > 1) 501 | throw new ArgumentOutOfRangeException ("value", "Must be 0 or 1."); 502 | else 503 | version = value; 504 | } 505 | } 506 | 507 | #endregion 508 | 509 | #region Private Methods 510 | 511 | static bool CanSetName (string name, out string message) 512 | { 513 | if (name.IsNullOrEmpty ()) { 514 | message = "Name must not be null or empty."; 515 | return false; 516 | } 517 | 518 | if (name [0] == '$' || name.Contains (reservedCharsForName)) { 519 | message = "Name contains an invalid character."; 520 | return false; 521 | } 522 | 523 | message = String.Empty; 524 | return true; 525 | } 526 | 527 | static bool CanSetValue (string value, out string message) 528 | { 529 | if (value == null) { 530 | message = "Value must not be null."; 531 | return false; 532 | } 533 | 534 | if (value.Contains (reservedCharsForValue)) { 535 | if (!value.IsEnclosedIn ('"')) { 536 | message = "Value contains an invalid character."; 537 | return false; 538 | } 539 | } 540 | 541 | message = String.Empty; 542 | return true; 543 | } 544 | 545 | static int Hash (int i, int j, int k, int l, int m) 546 | { 547 | return i ^ (j << 13 | j >> 19) ^ (k << 26 | k >> 6) ^ (l << 7 | l >> 25) ^ (m << 20 | m >> 12); 548 | } 549 | 550 | string ToResponseStringVersion0 () 551 | { 552 | var result = new StringBuilder (64); 553 | result.AppendFormat ("{0}={1}", name, val); 554 | if (expires != DateTime.MinValue) 555 | result.AppendFormat ("; Expires={0}", 556 | expires.ToUniversalTime ().ToString ("ddd, dd'-'MMM'-'yyyy HH':'mm':'ss 'GMT'", 557 | CultureInfo.CreateSpecificCulture("en-US"))); 558 | 559 | if (!path.IsNullOrEmpty ()) 560 | result.AppendFormat ("; Path={0}", path); 561 | 562 | if (!domain.IsNullOrEmpty ()) 563 | result.AppendFormat ("; Domain={0}", domain); 564 | 565 | if (secure) 566 | result.Append ("; Secure"); 567 | 568 | if (httpOnly) 569 | result.Append ("; HttpOnly"); 570 | 571 | return result.ToString (); 572 | } 573 | 574 | string ToResponseStringVersion1 () 575 | { 576 | var result = new StringBuilder (64); 577 | result.AppendFormat ("{0}={1}; Version={2}", name, val, version); 578 | if (expires != DateTime.MinValue) 579 | result.AppendFormat ("; Max-Age={0}", MaxAge); 580 | 581 | if (!path.IsNullOrEmpty ()) 582 | result.AppendFormat ("; Path={0}", path); 583 | 584 | if (!domain.IsNullOrEmpty ()) 585 | result.AppendFormat ("; Domain={0}", domain); 586 | 587 | if (!port.IsNullOrEmpty ()) 588 | if (port == "\"\"") 589 | result.Append ("; Port"); 590 | else 591 | result.AppendFormat ("; Port={0}", port); 592 | 593 | if (!comment.IsNullOrEmpty ()) 594 | result.AppendFormat ("; Comment={0}", comment.UrlEncode ()); 595 | 596 | if (commentUri != null) 597 | result.AppendFormat ("; CommentURL={0}", commentUri.OriginalString.Quote ()); 598 | 599 | if (discard) 600 | result.Append ("; Discard"); 601 | 602 | if (secure) 603 | result.Append ("; Secure"); 604 | 605 | return result.ToString (); 606 | } 607 | 608 | static bool TryCreatePorts (string value, out int [] result, out string parseError) 609 | { 610 | var values = value.Trim ('"').Split (','); 611 | var tmp = new int [values.Length]; 612 | for (int i = 0; i < values.Length; i++) { 613 | tmp [i] = int.MinValue; 614 | var v = values [i].Trim (); 615 | if (v.Length == 0) 616 | continue; 617 | 618 | if (!int.TryParse (v, out tmp [i])) { 619 | result = new int [] {}; 620 | parseError = v; 621 | return false; 622 | } 623 | } 624 | 625 | result = tmp; 626 | parseError = String.Empty; 627 | return true; 628 | } 629 | 630 | #endregion 631 | 632 | #region Internal Methods 633 | 634 | // From client to server 635 | internal string ToRequestString (Uri uri) 636 | { 637 | if (name.Length == 0) 638 | return String.Empty; 639 | 640 | if (version == 0) 641 | return String.Format ("{0}={1}", name, val); 642 | 643 | var result = new StringBuilder (64); 644 | result.AppendFormat ("$Version={0}; {1}={2}", version, name, val); 645 | if (!path.IsNullOrEmpty ()) 646 | result.AppendFormat ("; $Path={0}", path); 647 | else if (uri != null) 648 | result.AppendFormat ("; $Path={0}", uri.GetAbsolutePath ()); 649 | else 650 | result.Append ("; $Path=/"); 651 | 652 | bool append_domain = uri == null || uri.Host != domain; 653 | if (append_domain && !domain.IsNullOrEmpty ()) 654 | result.AppendFormat ("; $Domain={0}", domain); 655 | 656 | if (!port.IsNullOrEmpty ()) 657 | if (port == "\"\"") 658 | result.Append ("; $Port"); 659 | else 660 | result.AppendFormat ("; $Port={0}", port); 661 | 662 | return result.ToString (); 663 | } 664 | 665 | // From server to client 666 | internal string ToResponseString () 667 | { 668 | return name.Length == 0 669 | ? String.Empty 670 | : version == 0 671 | ? ToResponseStringVersion0 () 672 | : ToResponseStringVersion1 (); 673 | } 674 | 675 | #endregion 676 | 677 | #region Public Methods 678 | 679 | /// 680 | /// Determines whether the specified is equal to the current . 681 | /// 682 | /// 683 | /// An to compare with the current . 684 | /// 685 | /// 686 | /// true if the specified is equal to the current ; 687 | /// otherwise, false. 688 | /// 689 | public override bool Equals (Object comparand) 690 | { 691 | var cookie = comparand as Cookie; 692 | return cookie != null && 693 | name.Equals (cookie.Name, StringComparison.InvariantCultureIgnoreCase) && 694 | val.Equals (cookie.Value, StringComparison.InvariantCulture) && 695 | path.Equals (cookie.Path, StringComparison.InvariantCulture) && 696 | domain.Equals (cookie.Domain, StringComparison.InvariantCultureIgnoreCase) && 697 | version == cookie.Version; 698 | } 699 | 700 | /// 701 | /// Serves as a hash function for a object. 702 | /// 703 | /// 704 | /// An that contains a hash code for this instance. 705 | /// 706 | public override int GetHashCode () 707 | { 708 | return Hash ( 709 | StringComparer.InvariantCultureIgnoreCase.GetHashCode (name), 710 | val.GetHashCode (), 711 | path.GetHashCode (), 712 | StringComparer.InvariantCultureIgnoreCase.GetHashCode (domain), 713 | version); 714 | } 715 | 716 | /// 717 | /// Returns a that represents the current . 718 | /// 719 | /// 720 | /// This method returns a to use to send an HTTP Cookie to an origin server. 721 | /// 722 | /// 723 | /// A that represents the current . 724 | /// 725 | public override string ToString () 726 | { 727 | // i.e., only used for clients 728 | // see para 4.2.2 of RFC 2109 and para 3.3.4 of RFC 2965 729 | // see also bug #316017 730 | return ToRequestString (null); 731 | } 732 | 733 | #endregion 734 | } 735 | } 736 | --------------------------------------------------------------------------------