├── .gitattributes ├── .gitignore ├── .nuget ├── NuGet.Config ├── NuGet.exe ├── NuGet.md └── NuGet.targets ├── Cowboy.WebSockets ├── Cowboy.WebSockets.UnityWebSocketClient │ ├── Buffer │ │ ├── BufferValidator.cs │ │ ├── ISegmentBufferManager.cs │ │ ├── SegmentBufferDeflector.cs │ │ ├── SegmentBufferManager.cs │ │ ├── UnableToAllocateBufferException.cs │ │ └── UnableToCreateMemoryException .cs │ ├── Cowboy.WebSockets.UnityWebSocketClient.csproj │ ├── EventArgs │ │ ├── WebSocketServerBinaryReceivedEventArgs.cs │ │ ├── WebSocketServerConnectedEventArgs.cs │ │ ├── WebSocketServerDisconnectedEventArgs.cs │ │ └── WebSocketServerTextReceivedEventArgs.cs │ ├── Exceptions │ │ ├── WebSocketException.cs │ │ └── WebSocketHandshakeException.cs │ ├── Framing │ │ ├── BinaryFragmentationFrame.cs │ │ ├── BinaryFrame.cs │ │ ├── Builder │ │ │ ├── IFrameBuilder.cs │ │ │ └── WebSocketFrameBuilder.cs │ │ ├── CloseFrame.cs │ │ ├── ControlFrame.cs │ │ ├── DataFrame.cs │ │ ├── Frame.cs │ │ ├── Header │ │ │ └── Header.cs │ │ ├── OpCode.cs │ │ ├── PingFrame.cs │ │ ├── PongFrame.cs │ │ └── TextFrame.cs │ ├── Helpers │ │ ├── Consts.cs │ │ ├── HttpKnownHeaderNames.cs │ │ ├── KeepAliveTracker.cs │ │ ├── StringBuilderExtensions.cs │ │ └── WebSocketHelpers.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── WebSocketClient.cs │ ├── WebSocketClientConfiguration.cs │ ├── WebSocketClientHandshaker.cs │ ├── WebSocketCloseCode.cs │ └── WebSocketState.cs ├── Cowboy.WebSockets.sln ├── Cowboy.WebSockets │ ├── Buffer │ │ ├── BufferValidator.cs │ │ ├── ISegmentBufferManager.cs │ │ ├── SegmentBufferDeflector.cs │ │ ├── SegmentBufferManager.cs │ │ ├── UnableToAllocateBufferException.cs │ │ └── UnableToCreateMemoryException .cs │ ├── Client │ │ ├── AsyncWebSocketClient.cs │ │ ├── AsyncWebSocketClientConfiguration.cs │ │ ├── IAsyncWebSocketClientMessageDispatcher.cs │ │ ├── InternalAsyncWebSocketClientMessageDispatcherImplementation.cs │ │ └── WebSocketClientHandshaker.cs │ ├── Cowboy.WebSockets.csproj │ ├── Cowboy.WebSockets.nuspec │ ├── Exceptions │ │ ├── WebSocketException.cs │ │ └── WebSocketHandshakeException.cs │ ├── Extensions │ │ ├── IWebSocketExtension.cs │ │ ├── IWebSocketExtensionNegotiator.cs │ │ ├── Parameters │ │ │ ├── AbsentableValueParameter.cs │ │ │ ├── AgreedExtensionParameter.cs │ │ │ ├── AgreedSingleParameter.cs │ │ │ ├── AgreedValuableParameter.cs │ │ │ ├── ExtensionParameter.cs │ │ │ ├── ExtensionParameterType.cs │ │ │ ├── SingleParameter.cs │ │ │ └── ValuableParameter.cs │ │ ├── PerMessageExtensions │ │ │ └── PMCE │ │ │ │ ├── DeflateCompression.cs │ │ │ │ ├── PerMessageCompressionExtension.cs │ │ │ │ ├── PerMessageCompressionExtensionNegotiator.cs │ │ │ │ └── PerMessageCompressionExtensionParameters.cs │ │ └── WebSocketExtensionOfferDescription.cs │ ├── Framing │ │ ├── BinaryFragmentationFrame.cs │ │ ├── BinaryFrame.cs │ │ ├── Builder │ │ │ ├── IFrameBuilder.cs │ │ │ └── WebSocketFrameBuilder.cs │ │ ├── CloseFrame.cs │ │ ├── ControlFrame.cs │ │ ├── DataFrame.cs │ │ ├── Frame.cs │ │ ├── Header │ │ │ └── Header.cs │ │ ├── OpCode.cs │ │ ├── PingFrame.cs │ │ ├── PongFrame.cs │ │ └── TextFrame.cs │ ├── Helpers │ │ ├── Consts.cs │ │ ├── HttpKnownHeaderNames.cs │ │ ├── KeepAliveTracker.cs │ │ ├── StringBuilderExtensions.cs │ │ ├── TplExtensions.cs │ │ └── WebSocketHelpers.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── Server │ │ ├── AsyncWebSocketServer.cs │ │ ├── AsyncWebSocketServerConfiguration.cs │ │ ├── AsyncWebSocketSession.cs │ │ ├── Module │ │ │ ├── AsyncWebSocketRouteResolver.cs │ │ │ ├── AsyncWebSocketServerModule.cs │ │ │ ├── AsyncWebSocketServerModuleCatalog.cs │ │ │ └── IAsyncWebSocketServerMessageDispatcher.cs │ │ └── WebSocketServerHandshaker.cs │ ├── SubProtocols │ │ ├── IWebSocketSubProtocol.cs │ │ ├── IWebSocketSubProtocolNegotiator.cs │ │ └── WebSocketSubProtocolRequestDescription.cs │ ├── WebSocketCloseCode.cs │ ├── WebSocketState.cs │ └── packages.config ├── SolutionVersion.cs └── Tests │ ├── Certificates │ ├── Cowboy.cer │ ├── Cowboy.pvk │ ├── Cowboy.spc │ ├── RootCowboy.cer │ └── RootCowboy.pvk │ ├── Cowboy.WebSockets.TestAsyncWebSocketClient │ ├── App.config │ ├── Cowboy.WebSockets.TestAsyncWebSocketClient.csproj │ ├── Program.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ └── packages.config │ ├── Cowboy.WebSockets.TestAsyncWebSocketServer │ ├── App.config │ ├── Cowboy.WebSockets.TestAsyncWebSocketServer.csproj │ ├── Program.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── TestWebSocketModule.cs │ └── packages.config │ └── Cowboy.WebSockets.TestUnityWebSocketClient │ ├── Cowboy.WebSockets.TestUnityWebSocketClient.csproj │ ├── Program.cs │ ├── Properties │ └── AssemblyInfo.cs │ └── app.config ├── LICENSE └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | build/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # Visual Studo 2015 cache/options directory 26 | .vs/ 27 | 28 | # MSTest test Results 29 | [Tt]est[Rr]esult*/ 30 | [Bb]uild[Ll]og.* 31 | 32 | # NUNIT 33 | *.VisualState.xml 34 | TestResult.xml 35 | 36 | # Build Results of an ATL Project 37 | [Dd]ebugPS/ 38 | [Rr]eleasePS/ 39 | dlldata.c 40 | 41 | *_i.c 42 | *_p.c 43 | *_i.h 44 | *.ilk 45 | *.meta 46 | *.obj 47 | *.pch 48 | *.pdb 49 | *.pgc 50 | *.pgd 51 | *.rsp 52 | *.sbr 53 | *.tlb 54 | *.tli 55 | *.tlh 56 | *.tmp 57 | *.tmp_proj 58 | *.log 59 | *.vspscc 60 | *.vssscc 61 | .builds 62 | *.pidb 63 | *.svclog 64 | *.scc 65 | 66 | # Chutzpah Test files 67 | _Chutzpah* 68 | 69 | # Visual C++ cache files 70 | ipch/ 71 | *.aps 72 | *.ncb 73 | *.opensdf 74 | *.sdf 75 | *.cachefile 76 | 77 | # Visual Studio profiler 78 | *.psess 79 | *.vsp 80 | *.vspx 81 | 82 | # TFS 2012 Local Workspace 83 | $tf/ 84 | 85 | # Guidance Automation Toolkit 86 | *.gpState 87 | 88 | # ReSharper is a .NET coding add-in 89 | _ReSharper*/ 90 | *.[Rr]e[Ss]harper 91 | *.DotSettings.user 92 | 93 | # JustCode is a .NET coding addin-in 94 | .JustCode 95 | 96 | # TeamCity is a build add-in 97 | _TeamCity* 98 | 99 | # DotCover is a Code Coverage Tool 100 | *.dotCover 101 | 102 | # NCrunch 103 | _NCrunch_* 104 | .*crunch*.local.xml 105 | 106 | # MightyMoose 107 | *.mm.* 108 | AutoTest.Net/ 109 | 110 | # Web workbench (sass) 111 | .sass-cache/ 112 | 113 | # Installshield output folder 114 | [Ee]xpress/ 115 | 116 | # DocProject is a documentation generator add-in 117 | DocProject/buildhelp/ 118 | DocProject/Help/*.HxT 119 | DocProject/Help/*.HxC 120 | DocProject/Help/*.hhc 121 | DocProject/Help/*.hhk 122 | DocProject/Help/*.hhp 123 | DocProject/Help/Html2 124 | DocProject/Help/html 125 | 126 | # Click-Once directory 127 | publish/ 128 | 129 | # Publish Web Output 130 | *.[Pp]ublish.xml 131 | *.azurePubxml 132 | # TODO: Comment the next line if you want to checkin your web deploy settings 133 | # but database connection strings (with potential passwords) will be unencrypted 134 | *.pubxml 135 | *.publishproj 136 | 137 | # NuGet Packages 138 | *.nupkg 139 | # The packages folder can be ignored because of Package Restore 140 | **/packages/* 141 | # except build/, which is used as an MSBuild target. 142 | !**/packages/build/ 143 | # Uncomment if necessary however generally it will be regenerated when needed 144 | #!**/packages/repositories.config 145 | 146 | # Windows Azure Build Output 147 | csx/ 148 | *.build.csdef 149 | 150 | # Windows Store app package directory 151 | AppPackages/ 152 | 153 | # Others 154 | *.[Cc]ache 155 | ClientBin/ 156 | [Ss]tyle[Cc]op.* 157 | ~$* 158 | *~ 159 | *.dbmdl 160 | *.dbproj.schemaview 161 | *.pfx 162 | *.publishsettings 163 | node_modules/ 164 | bower_components/ 165 | 166 | # RIA/Silverlight projects 167 | Generated_Code/ 168 | 169 | # Backup & report files from converting an old project file 170 | # to a newer Visual Studio version. Backup files are not needed, 171 | # because we have git ;-) 172 | _UpgradeReport_Files/ 173 | Backup*/ 174 | UpgradeLog*.XML 175 | UpgradeLog*.htm 176 | 177 | # SQL Server files 178 | *.mdf 179 | *.ldf 180 | 181 | # Business Intelligence projects 182 | *.rdl.data 183 | *.bim.layout 184 | *.bim_*.settings 185 | 186 | # Microsoft Fakes 187 | FakesAssemblies/ 188 | 189 | # Node.js Tools for Visual Studio 190 | .ntvs_analysis.dat 191 | 192 | # Visual Studio 6 build log 193 | *.plg 194 | 195 | # Visual Studio 6 workspace options file 196 | *.opt 197 | -------------------------------------------------------------------------------- /.nuget/NuGet.Config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.nuget/NuGet.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaochundong/Cowboy.WebSockets/1d75f8f1f71b1d439c2117ebd956c79d87742e35/.nuget/NuGet.exe -------------------------------------------------------------------------------- /.nuget/NuGet.md: -------------------------------------------------------------------------------- 1 | Commands 2 | ------------ 3 | nuget setApiKey xxx-xxx-xxxx-xxxx 4 | 5 | nuget push .\packages\Cowboy.WebSockets.1.0.0.0.nupkg 6 | nuget pack ..\Cowboy.WebSockets\Cowboy.WebSockets\Cowboy.WebSockets.csproj -IncludeReferencedProjects -Symbols -Build -Prop Configuration=Release -OutputDirectory ".\packages" 7 | -------------------------------------------------------------------------------- /.nuget/NuGet.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(MSBuildProjectDirectory)\..\ 5 | 6 | 7 | false 8 | 9 | 10 | false 11 | 12 | 13 | true 14 | 15 | 16 | false 17 | 18 | 19 | 20 | 21 | 22 | 26 | 27 | 28 | 29 | 30 | $([System.IO.Path]::Combine($(SolutionDir), ".nuget")) 31 | 32 | 33 | 34 | 35 | $(SolutionDir).nuget 36 | 37 | 38 | 39 | $(MSBuildProjectDirectory)\packages.$(MSBuildProjectName.Replace(' ', '_')).config 40 | $(MSBuildProjectDirectory)\packages.$(MSBuildProjectName).config 41 | 42 | 43 | 44 | $(MSBuildProjectDirectory)\packages.config 45 | $(PackagesProjectConfig) 46 | 47 | 48 | 49 | 50 | $(NuGetToolsPath)\NuGet.exe 51 | @(PackageSource) 52 | 53 | "$(NuGetExePath)" 54 | mono --runtime=v4.0.30319 "$(NuGetExePath)" 55 | 56 | $(TargetDir.Trim('\\')) 57 | 58 | -RequireConsent 59 | -NonInteractive 60 | 61 | "$(SolutionDir) " 62 | "$(SolutionDir)" 63 | 64 | 65 | $(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(NonInteractiveSwitch) $(RequireConsentSwitch) -solutionDir $(PaddedSolutionDir) 66 | $(NuGetCommand) pack "$(ProjectPath)" -Properties "Configuration=$(Configuration);Platform=$(Platform)" $(NonInteractiveSwitch) -OutputDirectory "$(PackageOutputDir)" -symbols 67 | 68 | 69 | 70 | RestorePackages; 71 | $(BuildDependsOn); 72 | 73 | 74 | 75 | 76 | $(BuildDependsOn); 77 | BuildPackage; 78 | 79 | 80 | 81 | 82 | 83 | 84 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 99 | 100 | 103 | 104 | 105 | 106 | 108 | 109 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 141 | 142 | 143 | 144 | 145 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets.UnityWebSocketClient/Buffer/BufferValidator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Cowboy.WebSockets 4 | { 5 | public class BufferValidator 6 | { 7 | public static void ValidateBuffer(byte[] buffer, int offset, int count, 8 | string bufferParameterName = null, 9 | string offsetParameterName = null, 10 | string countParameterName = null) 11 | { 12 | if (buffer == null) 13 | { 14 | throw new ArgumentNullException(!string.IsNullOrEmpty(bufferParameterName) ? bufferParameterName : "buffer"); 15 | } 16 | 17 | if (offset < 0 || offset > buffer.Length) 18 | { 19 | throw new ArgumentOutOfRangeException(!string.IsNullOrEmpty(offsetParameterName) ? offsetParameterName : "offset"); 20 | } 21 | 22 | if (count < 0 || count > (buffer.Length - offset)) 23 | { 24 | throw new ArgumentOutOfRangeException(!string.IsNullOrEmpty(countParameterName) ? countParameterName : "count"); 25 | } 26 | } 27 | 28 | public static void ValidateArraySegment(ArraySegment arraySegment, string arraySegmentParameterName = null) 29 | { 30 | if (arraySegment.Array == null) 31 | { 32 | throw new ArgumentNullException((!string.IsNullOrEmpty(arraySegmentParameterName) ? arraySegmentParameterName : "arraySegment") + ".Array"); 33 | } 34 | 35 | if (arraySegment.Offset < 0 || arraySegment.Offset > arraySegment.Array.Length) 36 | { 37 | throw new ArgumentOutOfRangeException((!string.IsNullOrEmpty(arraySegmentParameterName) ? arraySegmentParameterName : "arraySegment") + ".Offset"); 38 | } 39 | 40 | if (arraySegment.Count < 0 || arraySegment.Count > (arraySegment.Array.Length - arraySegment.Offset)) 41 | { 42 | throw new ArgumentOutOfRangeException((!string.IsNullOrEmpty(arraySegmentParameterName) ? arraySegmentParameterName : "arraySegment") + ".Count"); 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets.UnityWebSocketClient/Buffer/ISegmentBufferManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Cowboy.WebSockets 5 | { 6 | public interface ISegmentBufferManager 7 | { 8 | ArraySegment BorrowBuffer(); 9 | IEnumerable> BorrowBuffers(int count); 10 | void ReturnBuffer(ArraySegment buffer); 11 | void ReturnBuffers(IEnumerable> buffers); 12 | void ReturnBuffers(params ArraySegment[] buffers); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets.UnityWebSocketClient/Buffer/SegmentBufferDeflector.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Cowboy.WebSockets 4 | { 5 | public class SegmentBufferDeflector 6 | { 7 | public static void AppendBuffer( 8 | ISegmentBufferManager bufferManager, 9 | ref ArraySegment receiveBuffer, 10 | int receiveCount, 11 | ref ArraySegment sessionBuffer, 12 | ref int sessionBufferCount) 13 | { 14 | if (sessionBuffer.Count < (sessionBufferCount + receiveCount)) 15 | { 16 | ArraySegment autoExpandedBuffer = bufferManager.BorrowBuffer(); 17 | if (autoExpandedBuffer.Count < (sessionBufferCount + receiveCount) * 2) 18 | { 19 | bufferManager.ReturnBuffer(autoExpandedBuffer); 20 | autoExpandedBuffer = new ArraySegment(new byte[(sessionBufferCount + receiveCount) * 2]); 21 | } 22 | 23 | Array.Copy(sessionBuffer.Array, sessionBuffer.Offset, autoExpandedBuffer.Array, autoExpandedBuffer.Offset, sessionBufferCount); 24 | 25 | var discardBuffer = sessionBuffer; 26 | sessionBuffer = autoExpandedBuffer; 27 | bufferManager.ReturnBuffer(discardBuffer); 28 | } 29 | 30 | Array.Copy(receiveBuffer.Array, receiveBuffer.Offset, sessionBuffer.Array, sessionBuffer.Offset + sessionBufferCount, receiveCount); 31 | sessionBufferCount = sessionBufferCount + receiveCount; 32 | } 33 | 34 | public static void ShiftBuffer( 35 | ISegmentBufferManager bufferManager, 36 | int shiftStart, 37 | ref ArraySegment sessionBuffer, 38 | ref int sessionBufferCount) 39 | { 40 | if ((sessionBufferCount - shiftStart) < shiftStart) 41 | { 42 | Array.Copy(sessionBuffer.Array, sessionBuffer.Offset + shiftStart, sessionBuffer.Array, sessionBuffer.Offset, sessionBufferCount - shiftStart); 43 | sessionBufferCount = sessionBufferCount - shiftStart; 44 | } 45 | else 46 | { 47 | ArraySegment copyBuffer = bufferManager.BorrowBuffer(); 48 | if (copyBuffer.Count < (sessionBufferCount - shiftStart)) 49 | { 50 | bufferManager.ReturnBuffer(copyBuffer); 51 | copyBuffer = new ArraySegment(new byte[sessionBufferCount - shiftStart]); 52 | } 53 | 54 | Array.Copy(sessionBuffer.Array, sessionBuffer.Offset + shiftStart, copyBuffer.Array, copyBuffer.Offset, sessionBufferCount - shiftStart); 55 | Array.Copy(copyBuffer.Array, copyBuffer.Offset, sessionBuffer.Array, sessionBuffer.Offset, sessionBufferCount - shiftStart); 56 | sessionBufferCount = sessionBufferCount - shiftStart; 57 | 58 | bufferManager.ReturnBuffer(copyBuffer); 59 | } 60 | } 61 | 62 | public static void ReplaceBuffer( 63 | ISegmentBufferManager bufferManager, 64 | ref ArraySegment receiveBuffer, 65 | ref int receiveBufferOffset, 66 | int receiveCount) 67 | { 68 | if ((receiveBufferOffset + receiveCount) < receiveBuffer.Count) 69 | { 70 | receiveBufferOffset = receiveBufferOffset + receiveCount; 71 | } 72 | else 73 | { 74 | ArraySegment autoExpandedBuffer = bufferManager.BorrowBuffer(); 75 | if (autoExpandedBuffer.Count < (receiveBufferOffset + receiveCount) * 2) 76 | { 77 | bufferManager.ReturnBuffer(autoExpandedBuffer); 78 | autoExpandedBuffer = new ArraySegment(new byte[(receiveBufferOffset + receiveCount) * 2]); 79 | } 80 | 81 | Array.Copy(receiveBuffer.Array, receiveBuffer.Offset, autoExpandedBuffer.Array, autoExpandedBuffer.Offset, receiveBufferOffset + receiveCount); 82 | receiveBufferOffset = receiveBufferOffset + receiveCount; 83 | 84 | var discardBuffer = receiveBuffer; 85 | receiveBuffer = autoExpandedBuffer; 86 | bufferManager.ReturnBuffer(discardBuffer); 87 | } 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets.UnityWebSocketClient/Buffer/UnableToAllocateBufferException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Cowboy.WebSockets 4 | { 5 | [Serializable] 6 | public class UnableToAllocateBufferException : Exception 7 | { 8 | public UnableToAllocateBufferException() 9 | : base("Cannot allocate buffer after few trials.") 10 | { 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets.UnityWebSocketClient/Buffer/UnableToCreateMemoryException .cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Cowboy.WebSockets 4 | { 5 | [Serializable] 6 | public class UnableToCreateMemoryException : Exception 7 | { 8 | public UnableToCreateMemoryException() 9 | : base("All buffers were in use and acquiring more memory has been disabled.") 10 | { 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets.UnityWebSocketClient/Cowboy.WebSockets.UnityWebSocketClient.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {671E566B-878A-4642-8011-AA4168773ADF} 8 | Library 9 | Properties 10 | Cowboy.WebSockets.UnityWebSocketClient 11 | Cowboy.WebSockets.UnityWebSocketClient 12 | v2.0 13 | 512 14 | 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | false 25 | 26 | 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | false 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | SolutionVersion.cs 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 | 82 | 89 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets.UnityWebSocketClient/EventArgs/WebSocketServerBinaryReceivedEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Cowboy.WebSockets 4 | { 5 | public class WebSocketServerBinaryReceivedEventArgs : EventArgs 6 | { 7 | public WebSocketServerBinaryReceivedEventArgs(WebSocketClient client, byte[] data) 8 | : this(client, data, 0, data.Length) 9 | { 10 | } 11 | 12 | public WebSocketServerBinaryReceivedEventArgs(WebSocketClient client, byte[] data, int dataOffset, int dataLength) 13 | { 14 | Client = client; 15 | Data = data; 16 | DataOffset = dataOffset; 17 | DataLength = dataLength; 18 | } 19 | 20 | public WebSocketClient Client { get; private set; } 21 | public byte[] Data { get; private set; } 22 | public int DataOffset { get; private set; } 23 | public int DataLength { get; private set; } 24 | 25 | public override string ToString() 26 | { 27 | return string.Format("{0}", this.Client); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets.UnityWebSocketClient/EventArgs/WebSocketServerConnectedEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | 4 | namespace Cowboy.WebSockets 5 | { 6 | public class WebSocketServerConnectedEventArgs : EventArgs 7 | { 8 | public WebSocketServerConnectedEventArgs(IPEndPoint remoteEP) 9 | : this(remoteEP, null) 10 | { 11 | } 12 | 13 | public WebSocketServerConnectedEventArgs(IPEndPoint remoteEP, IPEndPoint localEP) 14 | { 15 | if (remoteEP == null) 16 | throw new ArgumentNullException("remoteEP"); 17 | 18 | this.RemoteEndPoint = remoteEP; 19 | this.LocalEndPoint = localEP; 20 | } 21 | 22 | public IPEndPoint RemoteEndPoint { get; private set; } 23 | public IPEndPoint LocalEndPoint { get; private set; } 24 | 25 | public override string ToString() 26 | { 27 | return this.RemoteEndPoint.ToString(); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets.UnityWebSocketClient/EventArgs/WebSocketServerDisconnectedEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | 4 | namespace Cowboy.WebSockets 5 | { 6 | public class WebSocketServerDisconnectedEventArgs : EventArgs 7 | { 8 | public WebSocketServerDisconnectedEventArgs(IPEndPoint remoteEP) 9 | : this(remoteEP, null) 10 | { 11 | } 12 | 13 | public WebSocketServerDisconnectedEventArgs(IPEndPoint remoteEP, IPEndPoint localEP) 14 | { 15 | if (remoteEP == null) 16 | throw new ArgumentNullException("remoteEP"); 17 | 18 | this.RemoteEndPoint = remoteEP; 19 | this.LocalEndPoint = localEP; 20 | } 21 | 22 | public IPEndPoint RemoteEndPoint { get; private set; } 23 | public IPEndPoint LocalEndPoint { get; private set; } 24 | 25 | public override string ToString() 26 | { 27 | return this.RemoteEndPoint.ToString(); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets.UnityWebSocketClient/EventArgs/WebSocketServerTextReceivedEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Cowboy.WebSockets 4 | { 5 | public class WebSocketServerTextReceivedEventArgs : EventArgs 6 | { 7 | public WebSocketServerTextReceivedEventArgs(WebSocketClient client, string text) 8 | { 9 | Client = client; 10 | Text = text; 11 | } 12 | 13 | public WebSocketClient Client { get; private set; } 14 | public string Text { get; private set; } 15 | 16 | public override string ToString() 17 | { 18 | return string.Format("{0}", this.Client); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets.UnityWebSocketClient/Exceptions/WebSocketException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Cowboy.WebSockets 4 | { 5 | [Serializable] 6 | public class WebSocketException : Exception 7 | { 8 | public WebSocketException(string message) 9 | : base(message) 10 | { 11 | } 12 | 13 | public WebSocketException(string message, Exception innerException) 14 | : base(message, innerException) 15 | { 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets.UnityWebSocketClient/Exceptions/WebSocketHandshakeException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Cowboy.WebSockets 4 | { 5 | [Serializable] 6 | public sealed class WebSocketHandshakeException : WebSocketException 7 | { 8 | public WebSocketHandshakeException(string message) 9 | : base(message) 10 | { 11 | } 12 | 13 | public WebSocketHandshakeException(string message, Exception innerException) 14 | : base(message, innerException) 15 | { 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets.UnityWebSocketClient/Framing/BinaryFragmentationFrame.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Cowboy.WebSockets 4 | { 5 | public sealed class BinaryFragmentationFrame : Frame 6 | { 7 | private OpCode _opCode; 8 | 9 | public BinaryFragmentationFrame(OpCode opCode, byte[] data, int offset, int count, bool isFin = false, bool isMasked = true) 10 | { 11 | BufferValidator.ValidateBuffer(data, offset, count, "data"); 12 | 13 | _opCode = opCode; 14 | this.Data = data; 15 | this.Offset = offset; 16 | this.Count = count; 17 | this.IsFin = isFin; 18 | this.IsMasked = isMasked; 19 | } 20 | 21 | public byte[] Data { get; private set; } 22 | public int Offset { get; private set; } 23 | public int Count { get; private set; } 24 | public bool IsFin { get; private set; } 25 | public bool IsMasked { get; private set; } 26 | 27 | public override OpCode OpCode 28 | { 29 | get { return _opCode; } 30 | } 31 | 32 | public byte[] ToArray(IFrameBuilder builder) 33 | { 34 | if (builder == null) 35 | throw new ArgumentNullException("builder"); 36 | return builder.EncodeFrame(this); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets.UnityWebSocketClient/Framing/BinaryFrame.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Cowboy.WebSockets 4 | { 5 | public sealed class BinaryFrame : DataFrame 6 | { 7 | public BinaryFrame(ArraySegment segment, bool isMasked = true) 8 | { 9 | BufferValidator.ValidateArraySegment(segment, "segment"); 10 | 11 | this.Data = segment.Array; 12 | this.Offset = segment.Offset; 13 | this.Count = segment.Count; 14 | this.IsMasked = isMasked; 15 | } 16 | 17 | public BinaryFrame(byte[] data, int offset, int count, bool isMasked = true) 18 | { 19 | BufferValidator.ValidateBuffer(data, offset, count, "data"); 20 | 21 | this.Data = data; 22 | this.Offset = offset; 23 | this.Count = count; 24 | this.IsMasked = isMasked; 25 | } 26 | 27 | public byte[] Data { get; private set; } 28 | public int Offset { get; private set; } 29 | public int Count { get; private set; } 30 | public bool IsMasked { get; private set; } 31 | 32 | public override OpCode OpCode 33 | { 34 | get { return OpCode.Binary; } 35 | } 36 | 37 | public byte[] ToArray(IFrameBuilder builder) 38 | { 39 | if (builder == null) 40 | throw new ArgumentNullException("builder"); 41 | return builder.EncodeFrame(this); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets.UnityWebSocketClient/Framing/Builder/IFrameBuilder.cs: -------------------------------------------------------------------------------- 1 | namespace Cowboy.WebSockets 2 | { 3 | public interface IFrameBuilder 4 | { 5 | byte[] EncodeFrame(PingFrame frame); 6 | byte[] EncodeFrame(PongFrame frame); 7 | byte[] EncodeFrame(CloseFrame frame); 8 | byte[] EncodeFrame(TextFrame frame); 9 | byte[] EncodeFrame(BinaryFrame frame); 10 | byte[] EncodeFrame(BinaryFragmentationFrame frame); 11 | 12 | bool TryDecodeFrameHeader(byte[] buffer, int offset, int count, out Header frameHeader); 13 | void DecodePayload(byte[] buffer, int offset, Header frameHeader, out byte[] payload, out int payloadOffset, out int payloadCount); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets.UnityWebSocketClient/Framing/CloseFrame.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Cowboy.WebSockets 4 | { 5 | public sealed class CloseFrame : ControlFrame 6 | { 7 | public CloseFrame(bool isMasked = true) 8 | { 9 | this.IsMasked = isMasked; 10 | } 11 | 12 | public CloseFrame(WebSocketCloseCode closeCode, string closeReason, bool isMasked = true) 13 | : this(isMasked) 14 | { 15 | this.CloseCode = closeCode; 16 | this.CloseReason = closeReason; 17 | } 18 | 19 | public WebSocketCloseCode CloseCode { get; private set; } 20 | public string CloseReason { get; private set; } 21 | public bool IsMasked { get; private set; } 22 | 23 | public override OpCode OpCode 24 | { 25 | get { return OpCode.Close; } 26 | } 27 | 28 | public byte[] ToArray(IFrameBuilder builder) 29 | { 30 | if (builder == null) 31 | throw new ArgumentNullException("builder"); 32 | return builder.EncodeFrame(this); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets.UnityWebSocketClient/Framing/ControlFrame.cs: -------------------------------------------------------------------------------- 1 | namespace Cowboy.WebSockets 2 | { 3 | public abstract class ControlFrame : Frame 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets.UnityWebSocketClient/Framing/DataFrame.cs: -------------------------------------------------------------------------------- 1 | namespace Cowboy.WebSockets 2 | { 3 | public abstract class DataFrame : Frame 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets.UnityWebSocketClient/Framing/Frame.cs: -------------------------------------------------------------------------------- 1 | namespace Cowboy.WebSockets 2 | { 3 | public abstract class Frame 4 | { 5 | public abstract OpCode OpCode { get; } 6 | 7 | public override string ToString() 8 | { 9 | return string.Format("OpName[{0}], OpCode[{1}]", OpCode, (byte)OpCode); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets.UnityWebSocketClient/Framing/Header/Header.cs: -------------------------------------------------------------------------------- 1 | namespace Cowboy.WebSockets 2 | { 3 | public class FixedHeader 4 | { 5 | public bool IsFIN { get; set; } 6 | public bool IsRSV1 { get; set; } 7 | public bool IsRSV2 { get; set; } 8 | public bool IsRSV3 { get; set; } 9 | public OpCode OpCode { get; set; } 10 | public bool IsMasked { get; set; } 11 | 12 | public override string ToString() 13 | { 14 | return string.Format("IsFIN[{0}], IsRSV1[{1}], IsRSV2[{2}], IsRSV3[{3}], OpCode[{4}], IsMasked[{5}]", 15 | IsFIN, IsRSV1, IsRSV2, IsRSV3, OpCode, IsMasked); 16 | } 17 | } 18 | 19 | public class Header : FixedHeader 20 | { 21 | public int PayloadLength { get; set; } 22 | public int MaskingKeyOffset { get; set; } 23 | public int Length { get; set; } 24 | 25 | public override string ToString() 26 | { 27 | return string.Format("IsFIN[{0}], IsRSV1[{1}], IsRSV2[{2}], IsRSV3[{3}], OpCode[{4}], IsMasked[{5}], PayloadLength[{6}], MaskingKeyOffset[{7}], Length[{8}]", 28 | IsFIN, IsRSV1, IsRSV2, IsRSV3, OpCode, IsMasked, PayloadLength, MaskingKeyOffset, Length); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets.UnityWebSocketClient/Framing/OpCode.cs: -------------------------------------------------------------------------------- 1 | namespace Cowboy.WebSockets 2 | { 3 | // https://www.iana.org/assignments/websocket/websocket.xhtml 4 | // The opcode denotes the frame type of the WebSocket frame. 5 | // The opcode is an integer number between 0 and 15, inclusive. 6 | // Opcode Meaning Reference 7 | // 0 Continuation Frame [RFC6455] 8 | // 1 Text Frame [RFC6455] 9 | // 2 Binary Frame [RFC6455] 10 | // 3-7 Unassigned 11 | // 8 Connection Close Frame [RFC6455] 12 | // 9 Ping Frame [RFC6455] 13 | // 10 Pong Frame [RFC6455] 14 | // 11-15 Unassigned 15 | public enum OpCode : byte 16 | { 17 | Continuation = 0, 18 | Text = 1, 19 | Binary = 2, 20 | Close = 8, 21 | Ping = 9, 22 | Pong = 10, 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets.UnityWebSocketClient/Framing/PingFrame.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Cowboy.WebSockets 4 | { 5 | public sealed class PingFrame : ControlFrame 6 | { 7 | public PingFrame(bool isMasked = true) 8 | { 9 | this.IsMasked = isMasked; 10 | } 11 | 12 | public PingFrame(string data, bool isMasked = true) 13 | : this(isMasked) 14 | { 15 | this.Data = data; 16 | } 17 | 18 | public string Data { get; private set; } 19 | public bool IsMasked { get; private set; } 20 | 21 | public override OpCode OpCode 22 | { 23 | get { return OpCode.Ping; } 24 | } 25 | 26 | public byte[] ToArray(IFrameBuilder builder) 27 | { 28 | if (builder == null) 29 | throw new ArgumentNullException("builder"); 30 | return builder.EncodeFrame(this); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets.UnityWebSocketClient/Framing/PongFrame.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Cowboy.WebSockets 4 | { 5 | public sealed class PongFrame : ControlFrame 6 | { 7 | public PongFrame(bool isMasked = true) 8 | { 9 | this.IsMasked = isMasked; 10 | } 11 | 12 | public PongFrame(string data, bool isMasked = true) 13 | : this(isMasked) 14 | { 15 | this.Data = data; 16 | } 17 | 18 | public string Data { get; private set; } 19 | public bool IsMasked { get; private set; } 20 | 21 | public override OpCode OpCode 22 | { 23 | get { return OpCode.Pong; } 24 | } 25 | 26 | public byte[] ToArray(IFrameBuilder builder) 27 | { 28 | if (builder == null) 29 | throw new ArgumentNullException("builder"); 30 | return builder.EncodeFrame(this); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets.UnityWebSocketClient/Framing/TextFrame.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Cowboy.WebSockets 4 | { 5 | public sealed class TextFrame : DataFrame 6 | { 7 | public TextFrame(string text, bool isMasked = true) 8 | { 9 | if (string.IsNullOrEmpty(text)) 10 | throw new ArgumentNullException("text"); 11 | 12 | this.Text = text; 13 | this.IsMasked = isMasked; 14 | } 15 | 16 | public string Text { get; private set; } 17 | public bool IsMasked { get; private set; } 18 | 19 | public override OpCode OpCode 20 | { 21 | get { return OpCode.Text; } 22 | } 23 | 24 | public byte[] ToArray(IFrameBuilder builder) 25 | { 26 | if (builder == null) 27 | throw new ArgumentNullException("builder"); 28 | return builder.EncodeFrame(this); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets.UnityWebSocketClient/Helpers/Consts.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Text; 3 | 4 | namespace Cowboy.WebSockets 5 | { 6 | internal sealed class Consts 7 | { 8 | internal static readonly byte[] HttpMessageTerminator = Encoding.UTF8.GetBytes("\r\n\r\n"); 9 | 10 | internal static readonly List WebSocketSchemes = new List() { "ws", "wss" }; 11 | 12 | internal const string HttpHeaderLineFormat = "{0}: {1}"; 13 | 14 | internal const string HttpStatusCodeName = "HttpStatusCode"; 15 | internal const string HttpStatusCodeDescription = "HttpStatusCodeDescription"; 16 | internal const string HttpGetMethodName = "GET"; 17 | internal const string HttpVersionName = "HTTP"; 18 | internal const string HttpVersion = "1.1"; 19 | 20 | internal const string SecWebSocketKeyGuid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; 21 | 22 | internal const string WebSocketUpgradeToken = "websocket"; 23 | internal const string WebSocketConnectionToken = "Upgrade"; 24 | 25 | // https://www.iana.org/assignments/websocket/websocket.xhtml#version-number 26 | // Version Number Reference Status 27 | // 0 [draft-ietf-hybi-thewebsocketprotocol-00] Interim 28 | // 1 [draft-ietf-hybi-thewebsocketprotocol-01] Interim 29 | // 2 [draft-ietf-hybi-thewebsocketprotocol-02] Interim 30 | // 3 [draft-ietf-hybi-thewebsocketprotocol-03] Interim 31 | // 4 [draft-ietf-hybi-thewebsocketprotocol-04] Interim 32 | // 5 [draft-ietf-hybi-thewebsocketprotocol-05] Interim 33 | // 6 [draft-ietf-hybi-thewebsocketprotocol-06] Interim 34 | // 7 [draft-ietf-hybi-thewebsocketprotocol-07] Interim 35 | // 8 [draft-ietf-hybi-thewebsocketprotocol-08] Interim 36 | // 9 [Reserved] 37 | // 10 [Reserved] 38 | // 11 [Reserved] 39 | // 12 [Reserved] 40 | // 13 [RFC6455] Standard 41 | internal const string WebSocketVersion = "13"; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets.UnityWebSocketClient/Helpers/HttpKnownHeaderNames.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Collections.ObjectModel; 3 | 4 | namespace Cowboy.WebSockets 5 | { 6 | internal static class HttpKnownHeaderNames 7 | { 8 | public const string CacheControl = "Cache-Control"; 9 | public const string Connection = "Connection"; 10 | public const string Date = "Date"; 11 | public const string KeepAlive = "Keep-Alive"; 12 | public const string Pragma = "Pragma"; 13 | public const string ProxyConnection = "Proxy-Connection"; 14 | public const string Trailer = "Trailer"; 15 | public const string TransferEncoding = "Transfer-Encoding"; 16 | public const string Upgrade = "Upgrade"; 17 | public const string Via = "Via"; 18 | public const string Warning = "Warning"; 19 | public const string ContentLength = "Content-Length"; 20 | public const string ContentType = "Content-Type"; 21 | public const string ContentDisposition = "Content-Disposition"; 22 | public const string ContentEncoding = "Content-Encoding"; 23 | public const string ContentLanguage = "Content-Language"; 24 | public const string ContentLocation = "Content-Location"; 25 | public const string ContentRange = "Content-Range"; 26 | public const string Expires = "Expires"; 27 | public const string LastModified = "Last-Modified"; 28 | public const string Age = "Age"; 29 | public const string Location = "Location"; 30 | public const string ProxyAuthenticate = "Proxy-Authenticate"; 31 | public const string RetryAfter = "Retry-After"; 32 | public const string Server = "Server"; 33 | public const string SetCookie = "Set-Cookie"; 34 | public const string SetCookie2 = "Set-Cookie2"; 35 | public const string Vary = "Vary"; 36 | public const string WWWAuthenticate = "WWW-Authenticate"; 37 | public const string Accept = "Accept"; 38 | public const string AcceptCharset = "Accept-Charset"; 39 | public const string AcceptEncoding = "Accept-Encoding"; 40 | public const string AcceptLanguage = "Accept-Language"; 41 | public const string Authorization = "Authorization"; 42 | public const string Cookie = "Cookie"; 43 | public const string Cookie2 = "Cookie2"; 44 | public const string Expect = "Expect"; 45 | public const string From = "From"; 46 | public const string Host = "Host"; 47 | public const string IfMatch = "If-Match"; 48 | public const string IfModifiedSince = "If-Modified-Since"; 49 | public const string IfNoneMatch = "If-None-Match"; 50 | public const string IfRange = "If-Range"; 51 | public const string IfUnmodifiedSince = "If-Unmodified-Since"; 52 | public const string MaxForwards = "Max-Forwards"; 53 | public const string ProxyAuthorization = "Proxy-Authorization"; 54 | public const string Referer = "Referer"; 55 | public const string Range = "Range"; 56 | public const string UserAgent = "User-Agent"; 57 | public const string ContentMD5 = "Content-MD5"; 58 | public const string ETag = "ETag"; 59 | public const string TE = "TE"; 60 | public const string Allow = "Allow"; 61 | public const string AcceptRanges = "Accept-Ranges"; 62 | public const string P3P = "P3P"; 63 | public const string XPoweredBy = "X-Powered-By"; 64 | public const string XAspNetVersion = "X-AspNet-Version"; 65 | public const string SecWebSocketKey = "Sec-WebSocket-Key"; 66 | public const string SecWebSocketExtensions = "Sec-WebSocket-Extensions"; 67 | public const string SecWebSocketAccept = "Sec-WebSocket-Accept"; 68 | public const string Origin = "Origin"; 69 | public const string SecWebSocketProtocol = "Sec-WebSocket-Protocol"; 70 | public const string SecWebSocketVersion = "Sec-WebSocket-Version"; 71 | 72 | public static readonly ICollection All = 73 | new ReadOnlyCollection( 74 | new List() 75 | { 76 | CacheControl , 77 | Connection , 78 | Date, 79 | KeepAlive , 80 | Pragma, 81 | ProxyConnection , 82 | Trailer , 83 | TransferEncoding, 84 | Upgrade , 85 | Via , 86 | Warning , 87 | ContentLength , 88 | ContentType , 89 | ContentDisposition, 90 | ContentEncoding , 91 | ContentLanguage , 92 | ContentLocation, 93 | ContentRange , 94 | Expires , 95 | LastModified , 96 | Age , 97 | Location , 98 | ProxyAuthenticate , 99 | RetryAfter , 100 | Server , 101 | SetCookie , 102 | SetCookie2, 103 | Vary, 104 | WWWAuthenticate, 105 | Accept, 106 | AcceptCharset, 107 | AcceptEncoding, 108 | AcceptLanguage, 109 | Authorization, 110 | Cookie, 111 | Cookie2, 112 | Expect, 113 | From, 114 | Host, 115 | IfMatch, 116 | IfModifiedSince, 117 | IfNoneMatch, 118 | IfRange, 119 | IfUnmodifiedSince, 120 | MaxForwards, 121 | ProxyAuthorization, 122 | Referer, 123 | Range, 124 | UserAgent, 125 | ContentMD5, 126 | ETag, 127 | TE, 128 | Allow, 129 | AcceptRanges, 130 | P3P, 131 | XPoweredBy, 132 | XAspNetVersion, 133 | SecWebSocketKey, 134 | SecWebSocketExtensions, 135 | SecWebSocketAccept, 136 | Origin, 137 | SecWebSocketProtocol, 138 | SecWebSocketVersion, 139 | }); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets.UnityWebSocketClient/Helpers/KeepAliveTracker.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Threading; 4 | 5 | namespace Cowboy.WebSockets 6 | { 7 | internal abstract class KeepAliveTracker : IDisposable 8 | { 9 | public abstract void OnDataReceived(); 10 | public abstract void OnDataSent(); 11 | public abstract void StartTimer(); 12 | public abstract void StopTimer(); 13 | public abstract void ResetTimer(); 14 | public abstract bool ShouldSendKeepAlive(); 15 | public abstract void Dispose(); 16 | 17 | public static KeepAliveTracker Create(TimeSpan keepAliveInterval, TimerCallback keepAliveCallback) 18 | { 19 | if ((int)keepAliveInterval.TotalMilliseconds > 0) 20 | { 21 | return new DefaultKeepAliveTracker(keepAliveInterval, keepAliveCallback); 22 | } 23 | 24 | return new DisabledKeepAliveTracker(); 25 | } 26 | 27 | private class DisabledKeepAliveTracker : KeepAliveTracker 28 | { 29 | public override void OnDataReceived() 30 | { 31 | } 32 | 33 | public override void OnDataSent() 34 | { 35 | } 36 | 37 | public override void StartTimer() 38 | { 39 | } 40 | 41 | public override void StopTimer() 42 | { 43 | } 44 | 45 | public override void ResetTimer() 46 | { 47 | } 48 | 49 | public override bool ShouldSendKeepAlive() 50 | { 51 | return false; 52 | } 53 | 54 | public override void Dispose() 55 | { 56 | } 57 | } 58 | 59 | private class DefaultKeepAliveTracker : KeepAliveTracker 60 | { 61 | private readonly TimerCallback _keepAliveTimerElapsedCallback; 62 | private readonly TimeSpan _keepAliveInterval; 63 | private readonly Stopwatch _lastSendActivity; 64 | private readonly Stopwatch _lastReceiveActivity; 65 | private Timer _keepAliveTimer; 66 | 67 | public DefaultKeepAliveTracker(TimeSpan keepAliveInterval, TimerCallback keepAliveCallback) 68 | { 69 | _keepAliveInterval = keepAliveInterval; 70 | _keepAliveTimerElapsedCallback = keepAliveCallback; 71 | _lastSendActivity = new Stopwatch(); 72 | _lastReceiveActivity = new Stopwatch(); 73 | } 74 | 75 | public override void OnDataReceived() 76 | { 77 | _lastReceiveActivity.Stop(); 78 | _lastReceiveActivity.Reset(); 79 | _lastReceiveActivity.Start(); 80 | } 81 | 82 | public override void OnDataSent() 83 | { 84 | _lastSendActivity.Stop(); 85 | _lastSendActivity.Reset(); 86 | _lastSendActivity.Start(); 87 | } 88 | 89 | public override void StartTimer() 90 | { 91 | int keepAliveIntervalMilliseconds = (int)_keepAliveInterval.TotalMilliseconds; 92 | 93 | if (ExecutionContext.IsFlowSuppressed()) 94 | { 95 | _keepAliveTimer = new Timer(_keepAliveTimerElapsedCallback, null, Timeout.Infinite, Timeout.Infinite); 96 | _keepAliveTimer.Change(keepAliveIntervalMilliseconds, Timeout.Infinite); 97 | } 98 | else 99 | { 100 | using (ExecutionContext.SuppressFlow()) 101 | { 102 | _keepAliveTimer = new Timer(_keepAliveTimerElapsedCallback, null, Timeout.Infinite, Timeout.Infinite); 103 | _keepAliveTimer.Change(keepAliveIntervalMilliseconds, Timeout.Infinite); 104 | } 105 | } 106 | } 107 | 108 | public override void StopTimer() 109 | { 110 | if (_keepAliveTimer != null) 111 | { 112 | _keepAliveTimer.Change(Timeout.Infinite, Timeout.Infinite); 113 | } 114 | } 115 | 116 | public override void ResetTimer() 117 | { 118 | ResetTimer((int)_keepAliveInterval.TotalMilliseconds); 119 | } 120 | 121 | public override bool ShouldSendKeepAlive() 122 | { 123 | TimeSpan idleTime = GetIdleTime(); 124 | if (idleTime >= _keepAliveInterval) 125 | { 126 | return true; 127 | } 128 | 129 | ResetTimer((int)(_keepAliveInterval - idleTime).TotalMilliseconds); 130 | return false; 131 | } 132 | 133 | public override void Dispose() 134 | { 135 | if (_keepAliveTimer != null) 136 | { 137 | _keepAliveTimer.Dispose(); 138 | } 139 | } 140 | 141 | private void ResetTimer(int dueInMilliseconds) 142 | { 143 | if (_keepAliveTimer != null) 144 | { 145 | _keepAliveTimer.Change(dueInMilliseconds, Timeout.Infinite); 146 | } 147 | } 148 | 149 | private TimeSpan GetIdleTime() 150 | { 151 | TimeSpan sinceLastSendActivity = GetTimeElapsed(_lastSendActivity); 152 | TimeSpan sinceLastReceiveActivity = GetTimeElapsed(_lastReceiveActivity); 153 | 154 | if (sinceLastReceiveActivity < sinceLastSendActivity) 155 | { 156 | return sinceLastReceiveActivity; 157 | } 158 | 159 | return sinceLastSendActivity; 160 | } 161 | 162 | private TimeSpan GetTimeElapsed(Stopwatch watch) 163 | { 164 | if (watch.IsRunning) 165 | { 166 | return watch.Elapsed; 167 | } 168 | 169 | return _keepAliveInterval; 170 | } 171 | } 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets.UnityWebSocketClient/Helpers/StringBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | namespace Cowboy.WebSockets 4 | { 5 | internal static class StringBuilderExtensions 6 | { 7 | private static readonly char[] _crcf = new char[] { '\r', '\n' }; 8 | 9 | public static void AppendFormatWithCrCf(StringBuilder builder, string format, object arg) 10 | { 11 | builder.AppendFormat(format, arg); 12 | builder.Append(_crcf); 13 | } 14 | 15 | public static void AppendFormatWithCrCf(StringBuilder builder, string format, params object[] args) 16 | { 17 | builder.AppendFormat(format, args); 18 | builder.Append(_crcf); 19 | } 20 | 21 | public static void AppendWithCrCf(StringBuilder builder, string text) 22 | { 23 | builder.Append(text); 24 | builder.Append(_crcf); 25 | } 26 | 27 | public static void AppendWithCrCf(StringBuilder builder) 28 | { 29 | builder.Append(_crcf); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets.UnityWebSocketClient/Helpers/WebSocketHelpers.cs: -------------------------------------------------------------------------------- 1 | namespace Cowboy.WebSockets 2 | { 3 | internal sealed class WebSocketHelpers 4 | { 5 | internal static bool FindHttpMessageTerminator(byte[] buffer, int offset, int count, out int index) 6 | { 7 | index = -1; 8 | 9 | for (int i = 0; i < count; i++) 10 | { 11 | if (i + Consts.HttpMessageTerminator.Length <= count) 12 | { 13 | bool matched = true; 14 | for (int j = 0; j < Consts.HttpMessageTerminator.Length; j++) 15 | { 16 | if (buffer[offset + i + j] != Consts.HttpMessageTerminator[j]) 17 | { 18 | matched = false; 19 | break; 20 | } 21 | } 22 | 23 | if (matched) 24 | { 25 | index = i; 26 | return true; 27 | } 28 | } 29 | else 30 | { 31 | break; 32 | } 33 | } 34 | 35 | return false; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets.UnityWebSocketClient/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | [assembly: AssemblyTitle("Cowboy.WebSockets.UnityWebSocketClient")] 5 | [assembly: Guid("671e566b-878a-4642-8011-aa4168773adf")] 6 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets.UnityWebSocketClient/WebSocketClientConfiguration.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net.Sockets; 3 | using System.Security.Authentication; 4 | using System.Security.Cryptography.X509Certificates; 5 | 6 | namespace Cowboy.WebSockets 7 | { 8 | public sealed class WebSocketClientConfiguration 9 | { 10 | public WebSocketClientConfiguration() 11 | { 12 | BufferManager = new SegmentBufferManager(100, 8192, 1, true); 13 | ReceiveBufferSize = 8192; 14 | SendBufferSize = 8192; 15 | ReceiveTimeout = TimeSpan.Zero; 16 | SendTimeout = TimeSpan.Zero; 17 | NoDelay = true; 18 | LingerState = new LingerOption(false, 0); // The socket will linger for x seconds after Socket.Close is called. 19 | 20 | SslTargetHost = null; 21 | SslClientCertificates = new X509CertificateCollection(); 22 | SslEnabledProtocols = SslProtocols.Ssl3 | SslProtocols.Tls; 23 | SslCheckCertificateRevocation = false; 24 | SslPolicyErrorsBypassed = false; 25 | 26 | ConnectTimeout = TimeSpan.FromSeconds(10); 27 | CloseTimeout = TimeSpan.FromSeconds(5); 28 | KeepAliveInterval = TimeSpan.FromSeconds(30); 29 | KeepAliveTimeout = TimeSpan.FromSeconds(5); 30 | ReasonableFragmentSize = 4096; 31 | } 32 | 33 | public ISegmentBufferManager BufferManager { get; set; } 34 | public int ReceiveBufferSize { get; set; } 35 | public int SendBufferSize { get; set; } 36 | public TimeSpan ReceiveTimeout { get; set; } 37 | public TimeSpan SendTimeout { get; set; } 38 | public bool NoDelay { get; set; } 39 | public LingerOption LingerState { get; set; } 40 | 41 | public string SslTargetHost { get; set; } 42 | public X509CertificateCollection SslClientCertificates { get; set; } 43 | public SslProtocols SslEnabledProtocols { get; set; } 44 | public bool SslCheckCertificateRevocation { get; set; } 45 | public bool SslPolicyErrorsBypassed { get; set; } 46 | 47 | public TimeSpan ConnectTimeout { get; set; } 48 | public TimeSpan CloseTimeout { get; set; } 49 | public TimeSpan KeepAliveInterval { get; set; } 50 | public TimeSpan KeepAliveTimeout { get; set; } 51 | public int ReasonableFragmentSize { get; set; } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets.UnityWebSocketClient/WebSocketCloseCode.cs: -------------------------------------------------------------------------------- 1 | namespace Cowboy.WebSockets 2 | { 3 | // 0 - 999 Status codes in the range 0-999 are not used. 4 | // 1000 - 1999 Status codes in the range 1000-1999 are reserved for definition by this protocol. 5 | // 2000 - 2999 Status codes in the range 2000-2999 are reserved for use by extensions. 6 | // 3000 - 3999 Status codes in the range 3000-3999 MAY be used by libraries and frameworks. The 7 | // interpretation of these codes is undefined by this protocol. End applications MUST 8 | // NOT use status codes in this range. 9 | // 4000 - 4999 Status codes in the range 4000-4999 MAY be used by application code. The interpretation 10 | // of these codes is undefined by this protocol. 11 | public enum WebSocketCloseCode 12 | { 13 | NormalClosure = 1000, 14 | EndpointUnavailable = 1001, 15 | ProtocolError = 1002, 16 | InvalidMessageType = 1003, 17 | Empty = 1005, 18 | AbnormalClosure = 1006, // 1006 is reserved and should never be used by user 19 | InvalidPayloadData = 1007, 20 | PolicyViolation = 1008, 21 | MessageTooBig = 1009, 22 | MandatoryExtension = 1010, 23 | InternalServerError = 1011, 24 | TlsHandshakeFailed = 1015, // 1015 is reserved and should never be used by user 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets.UnityWebSocketClient/WebSocketState.cs: -------------------------------------------------------------------------------- 1 | namespace Cowboy.WebSockets 2 | { 3 | public enum WebSocketState 4 | { 5 | None = 0, 6 | Connecting = 1, 7 | Open = 2, 8 | Closing = 3, 9 | Closed = 5, 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.24720.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{0E04BEDA-90F9-4E44-91B9-FAE42EF01498}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D36E264D-BB94-4D14-A292-A852A3D6D5F7}" 9 | ProjectSection(SolutionItems) = preProject 10 | Cowboy.WebSockets\Cowboy.WebSockets.nuspec = Cowboy.WebSockets\Cowboy.WebSockets.nuspec 11 | ..\LICENSE = ..\LICENSE 12 | ..\.nuget\NuGet.md = ..\.nuget\NuGet.md 13 | ..\README.md = ..\README.md 14 | SolutionVersion.cs = SolutionVersion.cs 15 | EndProjectSection 16 | EndProject 17 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cowboy.WebSockets", "Cowboy.WebSockets\Cowboy.WebSockets.csproj", "{3A79448C-5D23-4325-8076-576E64BA65BE}" 18 | EndProject 19 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cowboy.WebSockets.TestAsyncWebSocketClient", "Tests\Cowboy.WebSockets.TestAsyncWebSocketClient\Cowboy.WebSockets.TestAsyncWebSocketClient.csproj", "{95639F67-5297-4D55-B126-2CB9226226CB}" 20 | EndProject 21 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cowboy.WebSockets.TestAsyncWebSocketServer", "Tests\Cowboy.WebSockets.TestAsyncWebSocketServer\Cowboy.WebSockets.TestAsyncWebSocketServer.csproj", "{31C790E5-1BB5-4004-82B8-43D1FCAF97C3}" 22 | EndProject 23 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cowboy.WebSockets.UnityWebSocketClient", "Cowboy.WebSockets.UnityWebSocketClient\Cowboy.WebSockets.UnityWebSocketClient.csproj", "{671E566B-878A-4642-8011-AA4168773ADF}" 24 | EndProject 25 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cowboy.WebSockets.TestUnityWebSocketClient", "Tests\Cowboy.WebSockets.TestUnityWebSocketClient\Cowboy.WebSockets.TestUnityWebSocketClient.csproj", "{D6DB086F-FFE4-461A-89BF-343195E09287}" 26 | EndProject 27 | Global 28 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 29 | Debug|Any CPU = Debug|Any CPU 30 | Release|Any CPU = Release|Any CPU 31 | EndGlobalSection 32 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 33 | {3A79448C-5D23-4325-8076-576E64BA65BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 34 | {3A79448C-5D23-4325-8076-576E64BA65BE}.Debug|Any CPU.Build.0 = Debug|Any CPU 35 | {3A79448C-5D23-4325-8076-576E64BA65BE}.Release|Any CPU.ActiveCfg = Release|Any CPU 36 | {3A79448C-5D23-4325-8076-576E64BA65BE}.Release|Any CPU.Build.0 = Release|Any CPU 37 | {95639F67-5297-4D55-B126-2CB9226226CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 38 | {95639F67-5297-4D55-B126-2CB9226226CB}.Debug|Any CPU.Build.0 = Debug|Any CPU 39 | {95639F67-5297-4D55-B126-2CB9226226CB}.Release|Any CPU.ActiveCfg = Release|Any CPU 40 | {95639F67-5297-4D55-B126-2CB9226226CB}.Release|Any CPU.Build.0 = Release|Any CPU 41 | {31C790E5-1BB5-4004-82B8-43D1FCAF97C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 42 | {31C790E5-1BB5-4004-82B8-43D1FCAF97C3}.Debug|Any CPU.Build.0 = Debug|Any CPU 43 | {31C790E5-1BB5-4004-82B8-43D1FCAF97C3}.Release|Any CPU.ActiveCfg = Release|Any CPU 44 | {31C790E5-1BB5-4004-82B8-43D1FCAF97C3}.Release|Any CPU.Build.0 = Release|Any CPU 45 | {671E566B-878A-4642-8011-AA4168773ADF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 46 | {671E566B-878A-4642-8011-AA4168773ADF}.Debug|Any CPU.Build.0 = Debug|Any CPU 47 | {671E566B-878A-4642-8011-AA4168773ADF}.Release|Any CPU.ActiveCfg = Release|Any CPU 48 | {671E566B-878A-4642-8011-AA4168773ADF}.Release|Any CPU.Build.0 = Release|Any CPU 49 | {D6DB086F-FFE4-461A-89BF-343195E09287}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 50 | {D6DB086F-FFE4-461A-89BF-343195E09287}.Debug|Any CPU.Build.0 = Debug|Any CPU 51 | {D6DB086F-FFE4-461A-89BF-343195E09287}.Release|Any CPU.ActiveCfg = Release|Any CPU 52 | {D6DB086F-FFE4-461A-89BF-343195E09287}.Release|Any CPU.Build.0 = Release|Any CPU 53 | EndGlobalSection 54 | GlobalSection(SolutionProperties) = preSolution 55 | HideSolutionNode = FALSE 56 | EndGlobalSection 57 | GlobalSection(NestedProjects) = preSolution 58 | {95639F67-5297-4D55-B126-2CB9226226CB} = {0E04BEDA-90F9-4E44-91B9-FAE42EF01498} 59 | {31C790E5-1BB5-4004-82B8-43D1FCAF97C3} = {0E04BEDA-90F9-4E44-91B9-FAE42EF01498} 60 | {D6DB086F-FFE4-461A-89BF-343195E09287} = {0E04BEDA-90F9-4E44-91B9-FAE42EF01498} 61 | EndGlobalSection 62 | EndGlobal 63 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets/Buffer/BufferValidator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Cowboy.Buffer 4 | { 5 | public class BufferValidator 6 | { 7 | public static void ValidateBuffer(byte[] buffer, int offset, int count, 8 | string bufferParameterName = null, 9 | string offsetParameterName = null, 10 | string countParameterName = null) 11 | { 12 | if (buffer == null) 13 | { 14 | throw new ArgumentNullException(!string.IsNullOrEmpty(bufferParameterName) ? bufferParameterName : "buffer"); 15 | } 16 | 17 | if (offset < 0 || offset > buffer.Length) 18 | { 19 | throw new ArgumentOutOfRangeException(!string.IsNullOrEmpty(offsetParameterName) ? offsetParameterName : "offset"); 20 | } 21 | 22 | if (count < 0 || count > (buffer.Length - offset)) 23 | { 24 | throw new ArgumentOutOfRangeException(!string.IsNullOrEmpty(countParameterName) ? countParameterName : "count"); 25 | } 26 | } 27 | 28 | public static void ValidateArraySegment(ArraySegment arraySegment, string arraySegmentParameterName = null) 29 | { 30 | if (arraySegment.Array == null) 31 | { 32 | throw new ArgumentNullException((!string.IsNullOrEmpty(arraySegmentParameterName) ? arraySegmentParameterName : "arraySegment") + ".Array"); 33 | } 34 | 35 | if (arraySegment.Offset < 0 || arraySegment.Offset > arraySegment.Array.Length) 36 | { 37 | throw new ArgumentOutOfRangeException((!string.IsNullOrEmpty(arraySegmentParameterName) ? arraySegmentParameterName : "arraySegment") + ".Offset"); 38 | } 39 | 40 | if (arraySegment.Count < 0 || arraySegment.Count > (arraySegment.Array.Length - arraySegment.Offset)) 41 | { 42 | throw new ArgumentOutOfRangeException((!string.IsNullOrEmpty(arraySegmentParameterName) ? arraySegmentParameterName : "arraySegment") + ".Count"); 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets/Buffer/ISegmentBufferManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Cowboy.Buffer 5 | { 6 | public interface ISegmentBufferManager 7 | { 8 | ArraySegment BorrowBuffer(); 9 | IEnumerable> BorrowBuffers(int count); 10 | void ReturnBuffer(ArraySegment buffer); 11 | void ReturnBuffers(IEnumerable> buffers); 12 | void ReturnBuffers(params ArraySegment[] buffers); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets/Buffer/SegmentBufferDeflector.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Cowboy.Buffer 4 | { 5 | public class SegmentBufferDeflector 6 | { 7 | public static void AppendBuffer( 8 | ISegmentBufferManager bufferManager, 9 | ref ArraySegment receiveBuffer, 10 | int receiveCount, 11 | ref ArraySegment sessionBuffer, 12 | ref int sessionBufferCount) 13 | { 14 | if (sessionBuffer.Count < (sessionBufferCount + receiveCount)) 15 | { 16 | ArraySegment autoExpandedBuffer = bufferManager.BorrowBuffer(); 17 | if (autoExpandedBuffer.Count < (sessionBufferCount + receiveCount) * 2) 18 | { 19 | bufferManager.ReturnBuffer(autoExpandedBuffer); 20 | autoExpandedBuffer = new ArraySegment(new byte[(sessionBufferCount + receiveCount) * 2]); 21 | } 22 | 23 | Array.Copy(sessionBuffer.Array, sessionBuffer.Offset, autoExpandedBuffer.Array, autoExpandedBuffer.Offset, sessionBufferCount); 24 | 25 | var discardBuffer = sessionBuffer; 26 | sessionBuffer = autoExpandedBuffer; 27 | bufferManager.ReturnBuffer(discardBuffer); 28 | } 29 | 30 | Array.Copy(receiveBuffer.Array, receiveBuffer.Offset, sessionBuffer.Array, sessionBuffer.Offset + sessionBufferCount, receiveCount); 31 | sessionBufferCount = sessionBufferCount + receiveCount; 32 | } 33 | 34 | public static void ShiftBuffer( 35 | ISegmentBufferManager bufferManager, 36 | int shiftStart, 37 | ref ArraySegment sessionBuffer, 38 | ref int sessionBufferCount) 39 | { 40 | if ((sessionBufferCount - shiftStart) < shiftStart) 41 | { 42 | Array.Copy(sessionBuffer.Array, sessionBuffer.Offset + shiftStart, sessionBuffer.Array, sessionBuffer.Offset, sessionBufferCount - shiftStart); 43 | sessionBufferCount = sessionBufferCount - shiftStart; 44 | } 45 | else 46 | { 47 | ArraySegment copyBuffer = bufferManager.BorrowBuffer(); 48 | if (copyBuffer.Count < (sessionBufferCount - shiftStart)) 49 | { 50 | bufferManager.ReturnBuffer(copyBuffer); 51 | copyBuffer = new ArraySegment(new byte[sessionBufferCount - shiftStart]); 52 | } 53 | 54 | Array.Copy(sessionBuffer.Array, sessionBuffer.Offset + shiftStart, copyBuffer.Array, copyBuffer.Offset, sessionBufferCount - shiftStart); 55 | Array.Copy(copyBuffer.Array, copyBuffer.Offset, sessionBuffer.Array, sessionBuffer.Offset, sessionBufferCount - shiftStart); 56 | sessionBufferCount = sessionBufferCount - shiftStart; 57 | 58 | bufferManager.ReturnBuffer(copyBuffer); 59 | } 60 | } 61 | 62 | public static void ReplaceBuffer( 63 | ISegmentBufferManager bufferManager, 64 | ref ArraySegment receiveBuffer, 65 | ref int receiveBufferOffset, 66 | int receiveCount) 67 | { 68 | if ((receiveBufferOffset + receiveCount) < receiveBuffer.Count) 69 | { 70 | receiveBufferOffset = receiveBufferOffset + receiveCount; 71 | } 72 | else 73 | { 74 | ArraySegment autoExpandedBuffer = bufferManager.BorrowBuffer(); 75 | if (autoExpandedBuffer.Count < (receiveBufferOffset + receiveCount) * 2) 76 | { 77 | bufferManager.ReturnBuffer(autoExpandedBuffer); 78 | autoExpandedBuffer = new ArraySegment(new byte[(receiveBufferOffset + receiveCount) * 2]); 79 | } 80 | 81 | Array.Copy(receiveBuffer.Array, receiveBuffer.Offset, autoExpandedBuffer.Array, autoExpandedBuffer.Offset, receiveBufferOffset + receiveCount); 82 | receiveBufferOffset = receiveBufferOffset + receiveCount; 83 | 84 | var discardBuffer = receiveBuffer; 85 | receiveBuffer = autoExpandedBuffer; 86 | bufferManager.ReturnBuffer(discardBuffer); 87 | } 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets/Buffer/UnableToAllocateBufferException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Cowboy.Buffer 4 | { 5 | [Serializable] 6 | public class UnableToAllocateBufferException : Exception 7 | { 8 | public UnableToAllocateBufferException() 9 | : base("Cannot allocate buffer after few trials.") 10 | { 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets/Buffer/UnableToCreateMemoryException .cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Cowboy.Buffer 4 | { 5 | [Serializable] 6 | public class UnableToCreateMemoryException : Exception 7 | { 8 | public UnableToCreateMemoryException() 9 | : base("All buffers were in use and acquiring more memory has been disabled.") 10 | { 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets/Client/AsyncWebSocketClientConfiguration.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net.Security; 4 | using System.Net.Sockets; 5 | using System.Security.Authentication; 6 | using System.Security.Cryptography.X509Certificates; 7 | using Cowboy.Buffer; 8 | using Cowboy.WebSockets.Extensions; 9 | using Cowboy.WebSockets.SubProtocols; 10 | 11 | namespace Cowboy.WebSockets 12 | { 13 | public sealed class AsyncWebSocketClientConfiguration 14 | { 15 | public AsyncWebSocketClientConfiguration() 16 | { 17 | BufferManager = new SegmentBufferManager(100, 8192, 1, true); 18 | ReceiveBufferSize = 8192; 19 | SendBufferSize = 8192; 20 | ReceiveTimeout = TimeSpan.Zero; 21 | SendTimeout = TimeSpan.Zero; 22 | NoDelay = true; 23 | LingerState = new LingerOption(false, 0); // The socket will linger for x seconds after Socket.Close is called. 24 | 25 | SslTargetHost = null; 26 | SslClientCertificates = new X509CertificateCollection(); 27 | SslEncryptionPolicy = EncryptionPolicy.RequireEncryption; 28 | SslEnabledProtocols = SslProtocols.Ssl3 | SslProtocols.Tls; 29 | SslCheckCertificateRevocation = false; 30 | SslPolicyErrorsBypassed = false; 31 | 32 | ConnectTimeout = TimeSpan.FromSeconds(10); 33 | CloseTimeout = TimeSpan.FromSeconds(5); 34 | KeepAliveInterval = TimeSpan.FromSeconds(30); 35 | KeepAliveTimeout = TimeSpan.FromSeconds(5); 36 | ReasonableFragmentSize = 4096; 37 | 38 | EnabledExtensions = new Dictionary() 39 | { 40 | { PerMessageCompressionExtension.RegisteredToken, new PerMessageCompressionExtensionNegotiator() }, 41 | }; 42 | EnabledSubProtocols = new Dictionary(); 43 | 44 | OfferedExtensions = new List() 45 | { 46 | new WebSocketExtensionOfferDescription(PerMessageCompressionExtension.RegisteredToken), 47 | }; 48 | RequestedSubProtocols = new List(); 49 | } 50 | 51 | public ISegmentBufferManager BufferManager { get; set; } 52 | public int ReceiveBufferSize { get; set; } 53 | public int SendBufferSize { get; set; } 54 | public TimeSpan ReceiveTimeout { get; set; } 55 | public TimeSpan SendTimeout { get; set; } 56 | public bool NoDelay { get; set; } 57 | public LingerOption LingerState { get; set; } 58 | 59 | public string SslTargetHost { get; set; } 60 | public X509CertificateCollection SslClientCertificates { get; set; } 61 | public EncryptionPolicy SslEncryptionPolicy { get; set; } 62 | public SslProtocols SslEnabledProtocols { get; set; } 63 | public bool SslCheckCertificateRevocation { get; set; } 64 | public bool SslPolicyErrorsBypassed { get; set; } 65 | 66 | public TimeSpan ConnectTimeout { get; set; } 67 | public TimeSpan CloseTimeout { get; set; } 68 | public TimeSpan KeepAliveInterval { get; set; } 69 | public TimeSpan KeepAliveTimeout { get; set; } 70 | public int ReasonableFragmentSize { get; set; } 71 | 72 | public Dictionary EnabledExtensions { get; set; } 73 | public Dictionary EnabledSubProtocols { get; set; } 74 | 75 | public List OfferedExtensions { get; set; } 76 | public List RequestedSubProtocols { get; set; } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets/Client/IAsyncWebSocketClientMessageDispatcher.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace Cowboy.WebSockets 4 | { 5 | public interface IAsyncWebSocketClientMessageDispatcher 6 | { 7 | Task OnServerConnected(AsyncWebSocketClient client); 8 | Task OnServerTextReceived(AsyncWebSocketClient client, string text); 9 | Task OnServerBinaryReceived(AsyncWebSocketClient client, byte[] data, int offset, int count); 10 | Task OnServerDisconnected(AsyncWebSocketClient client); 11 | 12 | Task OnServerFragmentationStreamOpened(AsyncWebSocketClient client, byte[] data, int offset, int count); 13 | Task OnServerFragmentationStreamContinued(AsyncWebSocketClient client, byte[] data, int offset, int count); 14 | Task OnServerFragmentationStreamClosed(AsyncWebSocketClient client, byte[] data, int offset, int count); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets/Client/InternalAsyncWebSocketClientMessageDispatcherImplementation.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | 4 | namespace Cowboy.WebSockets 5 | { 6 | internal class InternalAsyncWebSocketClientMessageDispatcherImplementation : IAsyncWebSocketClientMessageDispatcher 7 | { 8 | private Func _onServerTextReceived; 9 | private Func _onServerBinaryReceived; 10 | private Func _onServerConnected; 11 | private Func _onServerDisconnected; 12 | 13 | private Func _onServerFragmentationStreamOpened; 14 | private Func _onServerFragmentationStreamContinued; 15 | private Func _onServerFragmentationStreamClosed; 16 | 17 | public InternalAsyncWebSocketClientMessageDispatcherImplementation() 18 | { 19 | } 20 | 21 | public InternalAsyncWebSocketClientMessageDispatcherImplementation( 22 | Func onServerTextReceived, 23 | Func onServerDataReceived, 24 | Func onServerConnected, 25 | Func onServerDisconnected) 26 | : this() 27 | { 28 | _onServerTextReceived = onServerTextReceived; 29 | _onServerBinaryReceived = onServerDataReceived; 30 | _onServerConnected = onServerConnected; 31 | _onServerDisconnected = onServerDisconnected; 32 | } 33 | 34 | public InternalAsyncWebSocketClientMessageDispatcherImplementation( 35 | Func onServerTextReceived, 36 | Func onServerDataReceived, 37 | Func onServerConnected, 38 | Func onServerDisconnected, 39 | Func onServerFragmentationStreamOpened, 40 | Func onServerFragmentationStreamContinued, 41 | Func onServerFragmentationStreamClosed) 42 | : this() 43 | { 44 | _onServerTextReceived = onServerTextReceived; 45 | _onServerBinaryReceived = onServerDataReceived; 46 | _onServerConnected = onServerConnected; 47 | _onServerDisconnected = onServerDisconnected; 48 | 49 | _onServerFragmentationStreamOpened = onServerFragmentationStreamOpened; 50 | _onServerFragmentationStreamContinued = onServerFragmentationStreamContinued; 51 | _onServerFragmentationStreamClosed = onServerFragmentationStreamClosed; 52 | } 53 | 54 | public async Task OnServerConnected(AsyncWebSocketClient client) 55 | { 56 | if (_onServerConnected != null) 57 | await _onServerConnected(client); 58 | } 59 | 60 | public async Task OnServerTextReceived(AsyncWebSocketClient client, string text) 61 | { 62 | if (_onServerTextReceived != null) 63 | await _onServerTextReceived(client, text); 64 | } 65 | 66 | public async Task OnServerBinaryReceived(AsyncWebSocketClient client, byte[] data, int offset, int count) 67 | { 68 | if (_onServerBinaryReceived != null) 69 | await _onServerBinaryReceived(client, data, offset, count); 70 | } 71 | 72 | public async Task OnServerDisconnected(AsyncWebSocketClient client) 73 | { 74 | if (_onServerDisconnected != null) 75 | await _onServerDisconnected(client); 76 | } 77 | 78 | public async Task OnServerFragmentationStreamOpened(AsyncWebSocketClient client, byte[] data, int offset, int count) 79 | { 80 | if (_onServerFragmentationStreamOpened != null) 81 | await _onServerFragmentationStreamOpened(client, data, offset, count); 82 | } 83 | 84 | public async Task OnServerFragmentationStreamContinued(AsyncWebSocketClient client, byte[] data, int offset, int count) 85 | { 86 | if (_onServerFragmentationStreamContinued != null) 87 | await _onServerFragmentationStreamContinued(client, data, offset, count); 88 | } 89 | 90 | public async Task OnServerFragmentationStreamClosed(AsyncWebSocketClient client, byte[] data, int offset, int count) 91 | { 92 | if (_onServerFragmentationStreamClosed != null) 93 | await _onServerFragmentationStreamClosed(client, data, offset, count); 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets/Cowboy.WebSockets.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {3A79448C-5D23-4325-8076-576E64BA65BE} 8 | Library 9 | Properties 10 | Cowboy.WebSockets 11 | Cowboy.WebSockets 12 | v4.6 13 | 512 14 | 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | true 25 | 26 | 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | 36 | ..\packages\Logrila.Logging.1.0.3.0\lib\net46\Logrila.Logging.dll 37 | True 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | SolutionVersion.cs 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 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 121 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets/Cowboy.WebSockets.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Cowboy.WebSockets 5 | 1.3.14.0 6 | Cowboy.WebSockets 7 | Dennis Gao 8 | Dennis Gao 9 | https://github.com/gaochundong/Cowboy.WebSockets/blob/master/LICENSE 10 | https://github.com/gaochundong/Cowboy.WebSockets 11 | false 12 | Cowboy.WebSockets is a C# based library for building WebSocket services. 13 | Copyright © 2015-2016 Dennis Gao. 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets/Exceptions/WebSocketException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Cowboy.WebSockets 4 | { 5 | [Serializable] 6 | public class WebSocketException : Exception 7 | { 8 | public WebSocketException(string message) 9 | : base(message) 10 | { 11 | } 12 | 13 | public WebSocketException(string message, Exception innerException) 14 | : base(message, innerException) 15 | { 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets/Exceptions/WebSocketHandshakeException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Cowboy.WebSockets 4 | { 5 | [Serializable] 6 | public sealed class WebSocketHandshakeException : WebSocketException 7 | { 8 | public WebSocketHandshakeException(string message) 9 | : base(message) 10 | { 11 | } 12 | 13 | public WebSocketHandshakeException(string message, Exception innerException) 14 | : base(message, innerException) 15 | { 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets/Extensions/IWebSocketExtension.cs: -------------------------------------------------------------------------------- 1 | namespace Cowboy.WebSockets.Extensions 2 | { 3 | public interface IWebSocketExtension 4 | { 5 | string Name { get; } 6 | 7 | bool Rsv1BitOccupied { get; } 8 | bool Rsv2BitOccupied { get; } 9 | bool Rsv3BitOccupied { get; } 10 | 11 | string GetAgreedOffer(); 12 | 13 | byte[] BuildExtensionData(byte[] payload, int offset, int count); 14 | 15 | byte[] ProcessIncomingMessagePayload(byte[] payload, int offset, int count); 16 | byte[] ProcessOutgoingMessagePayload(byte[] payload, int offset, int count); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets/Extensions/IWebSocketExtensionNegotiator.cs: -------------------------------------------------------------------------------- 1 | namespace Cowboy.WebSockets.Extensions 2 | { 3 | public interface IWebSocketExtensionNegotiator 4 | { 5 | bool NegotiateAsServer(string offer, out string invalidParameter, out IWebSocketExtension negotiatedExtension); 6 | bool NegotiateAsClient(string offer, out string invalidParameter, out IWebSocketExtension negotiatedExtension); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets/Extensions/Parameters/AbsentableValueParameter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Cowboy.WebSockets.Extensions 4 | { 5 | public class AbsentableValueParameter : ExtensionParameter 6 | { 7 | public AbsentableValueParameter(string name, Func valueValidator, T defaultValue) 8 | : base(name) 9 | { 10 | if (valueValidator == null) 11 | throw new ArgumentNullException("valueValidator"); 12 | 13 | this.ValueValidator = valueValidator; 14 | this.DefaultValue = defaultValue; 15 | } 16 | 17 | public override ExtensionParameterType ParameterType 18 | { 19 | get 20 | { 21 | return ExtensionParameterType.Single | ExtensionParameterType.Valuable; 22 | } 23 | } 24 | 25 | public Func ValueValidator { get; private set; } 26 | 27 | public T DefaultValue { get; private set; } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets/Extensions/Parameters/AgreedExtensionParameter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Cowboy.WebSockets.Extensions 4 | { 5 | public abstract class AgreedExtensionParameter 6 | { 7 | public AgreedExtensionParameter(string name) 8 | { 9 | if (string.IsNullOrWhiteSpace(name)) 10 | throw new ArgumentNullException("name"); 11 | 12 | this.Name = name; 13 | } 14 | 15 | public string Name { get; private set; } 16 | public abstract ExtensionParameterType ParameterType { get; } 17 | 18 | public override string ToString() 19 | { 20 | return string.Format("{0}", this.Name); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets/Extensions/Parameters/AgreedSingleParameter.cs: -------------------------------------------------------------------------------- 1 | namespace Cowboy.WebSockets.Extensions 2 | { 3 | public class AgreedSingleParameter : AgreedExtensionParameter 4 | { 5 | public AgreedSingleParameter(string name) 6 | : base(name) 7 | { 8 | } 9 | 10 | public override ExtensionParameterType ParameterType 11 | { 12 | get 13 | { 14 | return ExtensionParameterType.Single; 15 | } 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets/Extensions/Parameters/AgreedValuableParameter.cs: -------------------------------------------------------------------------------- 1 | namespace Cowboy.WebSockets.Extensions 2 | { 3 | public class AgreedValuableParameter : AgreedExtensionParameter 4 | { 5 | public AgreedValuableParameter(string name, T @value) 6 | : base(name) 7 | { 8 | this.Value = @value; 9 | } 10 | 11 | public override ExtensionParameterType ParameterType 12 | { 13 | get 14 | { 15 | return ExtensionParameterType.Valuable; 16 | } 17 | } 18 | 19 | public T Value { get; private set; } 20 | 21 | public override string ToString() 22 | { 23 | return string.Format("{0}={1}", this.Name, this.Value); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets/Extensions/Parameters/ExtensionParameter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Cowboy.WebSockets.Extensions 4 | { 5 | public abstract class ExtensionParameter 6 | { 7 | public ExtensionParameter(string name) 8 | { 9 | if (string.IsNullOrWhiteSpace(name)) 10 | throw new ArgumentNullException("name"); 11 | 12 | this.Name = name; 13 | } 14 | 15 | public string Name { get; private set; } 16 | public abstract ExtensionParameterType ParameterType { get; } 17 | 18 | public override string ToString() 19 | { 20 | return string.Format("{0}", this.Name); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets/Extensions/Parameters/ExtensionParameterType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Cowboy.WebSockets.Extensions 4 | { 5 | [Flags] 6 | public enum ExtensionParameterType : byte 7 | { 8 | Single = 0x1, 9 | Valuable = 0x2, 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets/Extensions/Parameters/SingleParameter.cs: -------------------------------------------------------------------------------- 1 | namespace Cowboy.WebSockets.Extensions 2 | { 3 | public class SingleParameter : ExtensionParameter 4 | { 5 | public SingleParameter(string name) 6 | : base(name) 7 | { 8 | } 9 | 10 | public override ExtensionParameterType ParameterType 11 | { 12 | get 13 | { 14 | return ExtensionParameterType.Single; 15 | } 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets/Extensions/Parameters/ValuableParameter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Cowboy.WebSockets.Extensions 4 | { 5 | public class ValuableParameter : ExtensionParameter 6 | { 7 | public ValuableParameter(string name, Func valueValidator) 8 | : base(name) 9 | { 10 | if (valueValidator == null) 11 | throw new ArgumentNullException("valueValidator"); 12 | 13 | this.ValueValidator = valueValidator; 14 | } 15 | 16 | public override ExtensionParameterType ParameterType 17 | { 18 | get 19 | { 20 | return ExtensionParameterType.Valuable; 21 | } 22 | } 23 | 24 | public Func ValueValidator { get; private set; } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets/Extensions/PerMessageExtensions/PMCE/DeflateCompression.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | using System.IO; 4 | using System.IO.Compression; 5 | using Cowboy.Buffer; 6 | 7 | namespace Cowboy.WebSockets.Extensions 8 | { 9 | public class DeflateCompression 10 | { 11 | private readonly ISegmentBufferManager _bufferAllocator; 12 | 13 | public DeflateCompression(ISegmentBufferManager bufferAllocator) 14 | { 15 | if (bufferAllocator == null) 16 | throw new ArgumentNullException("bufferAllocator"); 17 | _bufferAllocator = bufferAllocator; 18 | } 19 | 20 | public byte[] Compress(byte[] raw) 21 | { 22 | return Compress(raw, 0, raw.Length); 23 | } 24 | 25 | [SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times")] 26 | public byte[] Compress(byte[] raw, int offset, int count) 27 | { 28 | using (var memory = new MemoryStream()) 29 | { 30 | using (var deflate = new DeflateStream(memory, CompressionMode.Compress, leaveOpen: true)) 31 | { 32 | deflate.Write(raw, offset, count); 33 | } 34 | 35 | return memory.ToArray(); 36 | } 37 | } 38 | 39 | public byte[] Decompress(byte[] raw) 40 | { 41 | return Decompress(raw, 0, raw.Length); 42 | } 43 | 44 | [SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times")] 45 | public byte[] Decompress(byte[] raw, int offset, int count) 46 | { 47 | var buffer = _bufferAllocator.BorrowBuffer(); 48 | 49 | try 50 | { 51 | using (var input = new MemoryStream(raw, offset, count)) 52 | using (var deflate = new DeflateStream(input, CompressionMode.Decompress, leaveOpen: true)) 53 | using (var memory = new MemoryStream()) 54 | { 55 | int readCount = 0; 56 | do 57 | { 58 | readCount = deflate.Read(buffer.Array, buffer.Offset, buffer.Count); 59 | if (readCount > 0) 60 | { 61 | memory.Write(buffer.Array, buffer.Offset, readCount); 62 | } 63 | } 64 | while (readCount > 0); 65 | 66 | return memory.ToArray(); 67 | } 68 | } 69 | finally 70 | { 71 | _bufferAllocator.ReturnBuffer(buffer); 72 | } 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets/Extensions/PerMessageExtensions/PMCE/PerMessageCompressionExtension.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Text; 4 | using Cowboy.Buffer; 5 | 6 | namespace Cowboy.WebSockets.Extensions 7 | { 8 | public sealed class PerMessageCompressionExtension : IWebSocketExtension 9 | { 10 | // Any extension-token used MUST be a registered token (see 11 | // Section 11.4). The parameters supplied with any given extension MUST 12 | // be defined for that extension. Note that the client is only offering 13 | // to use any advertised extensions and MUST NOT use them unless the 14 | // server indicates that it wishes to use the extension. 15 | public static readonly string RegisteredToken = @"permessage-deflate"; 16 | 17 | private readonly DeflateCompression _deflater; 18 | private SortedList _agreedParameters; 19 | 20 | public PerMessageCompressionExtension() 21 | { 22 | var bufferAllocator = new SegmentBufferManager(100, 8192, 1, true); 23 | _deflater = new DeflateCompression(bufferAllocator); 24 | } 25 | 26 | public PerMessageCompressionExtension(SortedList agreedParameters) 27 | : this() 28 | { 29 | _agreedParameters = agreedParameters; 30 | } 31 | 32 | public string Name { get { return RegisteredToken; } } 33 | 34 | // PMCEs use the RSV1 bit of the WebSocket frame header to indicate whether a 35 | // message is compressed or not so that an endpoint can choose not to 36 | // compress messages with incompressible contents. 37 | public bool Rsv1BitOccupied { get { return true; } } 38 | public bool Rsv2BitOccupied { get { return false; } } 39 | public bool Rsv3BitOccupied { get { return false; } } 40 | 41 | public string GetAgreedOffer() 42 | { 43 | var sb = new StringBuilder(); 44 | 45 | sb.Append(this.Name); 46 | 47 | if (_agreedParameters != null && _agreedParameters.Any()) 48 | { 49 | foreach (var parameter in _agreedParameters.Values) 50 | { 51 | sb.Append("; "); 52 | sb.Append(parameter.ToString()); 53 | } 54 | } 55 | 56 | return sb.ToString(); 57 | } 58 | 59 | public byte[] BuildExtensionData(byte[] payload, int offset, int count) 60 | { 61 | // Payload data: (x+y) bytes 62 | // 63 | // The "Payload data" is defined as "Extension data" concatenated 64 | // with "Application data". 65 | // 66 | // Extension data: x bytes 67 | // 68 | // The "Extension data" is 0 bytes unless an extension has been 69 | // negotiated. Any extension MUST specify the length of the 70 | // "Extension data", or how that length may be calculated, and how 71 | // the extension use MUST be negotiated during the opening handshake. 72 | // If present, the "Extension data" is included in the total payload 73 | // length. 74 | // 75 | // Application data: y bytes 76 | // 77 | // Arbitrary "Application data", taking up the remainder of the frame 78 | // after any "Extension data". The length of the "Application data" 79 | // is equal to the payload length minus the length of the "Extension 80 | // data". 81 | return null; // PMCE doesn't have an extension data definition. 82 | } 83 | 84 | public byte[] ProcessIncomingMessagePayload(byte[] payload, int offset, int count) 85 | { 86 | return _deflater.Decompress(payload, offset, count); 87 | } 88 | 89 | public byte[] ProcessOutgoingMessagePayload(byte[] payload, int offset, int count) 90 | { 91 | return _deflater.Compress(payload, offset, count); 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets/Extensions/PerMessageExtensions/PMCE/PerMessageCompressionExtensionNegotiator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Cowboy.WebSockets.Extensions 5 | { 6 | public sealed class PerMessageCompressionExtensionNegotiator : IWebSocketExtensionNegotiator 7 | { 8 | private static readonly char[] TrimableChars = new char[] { ' ', ';', '\r', '\n' }; 9 | 10 | public bool NegotiateAsServer(string offer, out string invalidParameter, out IWebSocketExtension negotiatedExtension) 11 | { 12 | return Negotiate(offer, AgreeAsServer, out invalidParameter, out negotiatedExtension); 13 | } 14 | 15 | public bool NegotiateAsClient(string offer, out string invalidParameter, out IWebSocketExtension negotiatedExtension) 16 | { 17 | return Negotiate(offer, AgreeAsClient, out invalidParameter, out negotiatedExtension); 18 | } 19 | 20 | private bool Negotiate(string offer, Func agree, out string invalidParameter, out IWebSocketExtension negotiatedExtension) 21 | { 22 | invalidParameter = null; 23 | negotiatedExtension = null; 24 | 25 | if (string.IsNullOrWhiteSpace(offer)) 26 | { 27 | invalidParameter = offer; 28 | return false; 29 | } 30 | 31 | var segements = offer.Replace('\r', ' ').Replace('\n', ' ').TrimStart(TrimableChars).TrimEnd(TrimableChars).Split(';'); 32 | 33 | var offeredExtensionName = segements[0].TrimStart(TrimableChars).TrimEnd(TrimableChars); 34 | if (string.IsNullOrEmpty(offeredExtensionName)) 35 | { 36 | invalidParameter = offer; 37 | return false; 38 | } 39 | 40 | if (string.Compare(offeredExtensionName, PerMessageCompressionExtension.RegisteredToken, StringComparison.OrdinalIgnoreCase) != 0) 41 | { 42 | invalidParameter = offeredExtensionName; 43 | return false; 44 | } 45 | 46 | if (segements.Length == 1) 47 | { 48 | negotiatedExtension = new PerMessageCompressionExtension(); 49 | return true; 50 | } 51 | 52 | // This set of elements MAY include multiple PMCEs with the same extension 53 | // name to offer the possibility to use the same algorithm with 54 | // different configuration parameters. 55 | for (int i = 1; i < segements.Length; i++) 56 | { 57 | var offeredParameter = segements[i]; 58 | if (!PerMessageCompressionExtensionParameters.ValidateParameter(offeredParameter)) 59 | { 60 | invalidParameter = offeredParameter; 61 | return false; 62 | } 63 | } 64 | 65 | // The order of elements is important as it specifies the client's preference. 66 | // An element preceding another element has higher preference. It is recommended 67 | // that a server accepts PMCEs with higher preference if the server supports them. 68 | var agreedSet = new SortedList(); 69 | 70 | for (int i = 1; i < segements.Length; i++) 71 | { 72 | var offeredParameter = segements[i]; 73 | var agreeingParameter = PerMessageCompressionExtensionParameters.ResolveParameter(offeredParameter); 74 | if (agree(agreeingParameter)) 75 | { 76 | agreedSet.Add(i, agreeingParameter); 77 | } 78 | } 79 | 80 | negotiatedExtension = new PerMessageCompressionExtension(agreedSet); 81 | return true; 82 | } 83 | 84 | private bool AgreeAsServer(AgreedExtensionParameter parameter) 85 | { 86 | if (parameter == null) 87 | return false; 88 | 89 | switch (parameter.Name) 90 | { 91 | case PerMessageCompressionExtensionParameters.ServerNoContextTakeOverParameterName: 92 | case PerMessageCompressionExtensionParameters.ClientNoContextTakeOverParameterName: 93 | { 94 | return false; 95 | } 96 | case PerMessageCompressionExtensionParameters.ServerMaxWindowBitsParameterName: 97 | case PerMessageCompressionExtensionParameters.ClientMaxWindowBitsParameterName: 98 | { 99 | return false; 100 | } 101 | default: 102 | throw new NotSupportedException("Invalid parameter name."); 103 | } 104 | } 105 | 106 | private bool AgreeAsClient(AgreedExtensionParameter parameter) 107 | { 108 | if (parameter == null) 109 | return false; 110 | 111 | switch (parameter.Name) 112 | { 113 | case PerMessageCompressionExtensionParameters.ServerNoContextTakeOverParameterName: 114 | case PerMessageCompressionExtensionParameters.ClientNoContextTakeOverParameterName: 115 | { 116 | return false; 117 | } 118 | case PerMessageCompressionExtensionParameters.ServerMaxWindowBitsParameterName: 119 | case PerMessageCompressionExtensionParameters.ClientMaxWindowBitsParameterName: 120 | { 121 | return false; 122 | } 123 | default: 124 | throw new NotSupportedException("Invalid parameter name."); 125 | } 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets/Extensions/PerMessageExtensions/PMCE/PerMessageCompressionExtensionParameters.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace Cowboy.WebSockets.Extensions 6 | { 7 | public sealed class PerMessageCompressionExtensionParameters 8 | { 9 | public const string ServerNoContextTakeOverParameterName = @"server_no_context_takeover"; 10 | public const string ClientNoContextTakeOverParameterName = @"client_no_context_takeover"; 11 | public const string ServerMaxWindowBitsParameterName = @"server_max_window_bits"; 12 | public const string ClientMaxWindowBitsParameterName = @"client_max_window_bits"; 13 | 14 | public static readonly SingleParameter ServerNoContextTakeOver = new SingleParameter(ServerNoContextTakeOverParameterName); 15 | public static readonly SingleParameter ClientNoContextTakeOver = new SingleParameter(ClientNoContextTakeOverParameterName); 16 | public static readonly AbsentableValueParameter ServerMaxWindowBits = new AbsentableValueParameter(ServerMaxWindowBitsParameterName, ValidateServerMaxWindowBitsParameterValue, 15); 17 | public static readonly AbsentableValueParameter ClientMaxWindowBits = new AbsentableValueParameter(ClientMaxWindowBitsParameterName, ValidateClientMaxWindowBitsParameterValue, 15); 18 | 19 | public static readonly IEnumerable AllAvailableParameters = new List() 20 | { 21 | ServerNoContextTakeOver, 22 | ClientNoContextTakeOver, 23 | ServerMaxWindowBits, 24 | ClientMaxWindowBits, 25 | }; 26 | 27 | public static readonly IEnumerable AllAvailableParameterNames = AllAvailableParameters.Select(p => p.Name); 28 | 29 | private static bool ValidateServerMaxWindowBitsParameterValue(string @value) 30 | { 31 | // A client MAY include the "server_max_window_bits" extension parameter 32 | // in an extension negotiation offer. This parameter has a decimal 33 | // integer value without leading zeroes between 8 to 15, inclusive, 34 | // indicating the base-2 logarithm of the LZ77 sliding window size, and 35 | // MUST conform to the ABNF below. 36 | // server-max-window-bits = 1*DIGIT 37 | 38 | if (string.IsNullOrWhiteSpace(@value)) 39 | return false; 40 | 41 | int paramValue = -1; 42 | if (int.TryParse(@value, out paramValue)) 43 | { 44 | if (8 <= paramValue && paramValue <= 15) 45 | return true; 46 | } 47 | 48 | return false; 49 | } 50 | 51 | private static bool ValidateClientMaxWindowBitsParameterValue(string @value) 52 | { 53 | // A client MAY include the "client_max_window_bits" extension parameter 54 | // in an extension negotiation offer. This parameter has no value or a 55 | // decimal integer value without leading zeroes between 8 to 15 56 | // inclusive indicating the base-2 logarithm of the LZ77 sliding window 57 | // size. If a value is specified for this parameter, the value MUST 58 | // conform to the ABNF below. 59 | // client-max-window-bits = 1*DIGIT 60 | 61 | if (string.IsNullOrWhiteSpace(@value)) 62 | return false; 63 | 64 | int paramValue = -1; 65 | if (int.TryParse(@value, out paramValue)) 66 | { 67 | if (8 <= paramValue && paramValue <= 15) 68 | return true; 69 | } 70 | 71 | return false; 72 | } 73 | 74 | public static bool ValidateParameter(string parameter) 75 | { 76 | if (string.IsNullOrWhiteSpace(parameter)) 77 | return false; 78 | 79 | var keyValuePair = parameter.TrimStart().TrimEnd().Split('='); 80 | var inputParameterName = keyValuePair[0].TrimStart().TrimEnd(); 81 | ExtensionParameter matchedParameter = null; 82 | 83 | foreach (var @param in AllAvailableParameters) 84 | { 85 | if (string.Compare(inputParameterName, @param.Name, StringComparison.OrdinalIgnoreCase) == 0) 86 | { 87 | matchedParameter = @param; 88 | break; 89 | } 90 | } 91 | 92 | if (matchedParameter == null) 93 | return false; 94 | 95 | switch (matchedParameter.ParameterType) 96 | { 97 | case ExtensionParameterType.Single: 98 | { 99 | if (keyValuePair.Length == 1) 100 | return true; 101 | } 102 | break; 103 | case ExtensionParameterType.Valuable: 104 | { 105 | if (keyValuePair.Length != 2) 106 | return false; 107 | 108 | var inputParameterValue = keyValuePair[1].TrimStart().TrimEnd(); 109 | if (((ValuableParameter)matchedParameter).ValueValidator.Invoke(inputParameterValue)) 110 | return true; 111 | } 112 | break; 113 | case ExtensionParameterType.Single | ExtensionParameterType.Valuable: 114 | { 115 | if (keyValuePair.Length == 1) 116 | return true; 117 | 118 | if (keyValuePair.Length > 2) 119 | return false; 120 | 121 | var inputParameterValue = keyValuePair[1].TrimStart().TrimEnd(); 122 | if (((AbsentableValueParameter)matchedParameter).ValueValidator.Invoke(inputParameterValue)) 123 | return true; 124 | } 125 | break; 126 | default: 127 | throw new NotSupportedException("Invalid parameter type."); 128 | } 129 | 130 | return false; 131 | } 132 | 133 | public static AgreedExtensionParameter ResolveParameter(string parameter) 134 | { 135 | if (!ValidateParameter(parameter)) 136 | return null; 137 | 138 | var keyValuePair = parameter.TrimStart().TrimEnd().Split('='); 139 | var inputParameterName = keyValuePair[0].TrimStart().TrimEnd(); 140 | ExtensionParameter matchedParameter = null; 141 | 142 | foreach (var @param in AllAvailableParameters) 143 | { 144 | if (string.Compare(inputParameterName, @param.Name, StringComparison.OrdinalIgnoreCase) == 0) 145 | { 146 | matchedParameter = @param; 147 | break; 148 | } 149 | } 150 | 151 | switch (matchedParameter.Name) 152 | { 153 | case ServerNoContextTakeOverParameterName: 154 | case ClientNoContextTakeOverParameterName: 155 | { 156 | return new AgreedSingleParameter(matchedParameter.Name); 157 | } 158 | case ServerMaxWindowBitsParameterName: 159 | case ClientMaxWindowBitsParameterName: 160 | { 161 | if (keyValuePair.Length == 1) 162 | return new AgreedValuableParameter(matchedParameter.Name, ((AbsentableValueParameter)matchedParameter).DefaultValue); 163 | 164 | var inputParameterValue = keyValuePair[1].TrimStart().TrimEnd(); 165 | return new AgreedValuableParameter(matchedParameter.Name, byte.Parse(inputParameterValue)); 166 | } 167 | default: 168 | throw new NotSupportedException("Invalid parameter type."); 169 | } 170 | } 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets/Extensions/WebSocketExtensionOfferDescription.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Cowboy.WebSockets.Extensions 4 | { 5 | public sealed class WebSocketExtensionOfferDescription 6 | { 7 | public WebSocketExtensionOfferDescription(string offer) 8 | { 9 | if (string.IsNullOrWhiteSpace(offer)) 10 | throw new ArgumentNullException("offer"); 11 | this.ExtensionNegotiationOffer = offer; 12 | } 13 | 14 | public string ExtensionNegotiationOffer { get; private set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets/Framing/BinaryFragmentationFrame.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Cowboy.Buffer; 3 | 4 | namespace Cowboy.WebSockets 5 | { 6 | public sealed class BinaryFragmentationFrame : Frame 7 | { 8 | private OpCode _opCode; 9 | 10 | public BinaryFragmentationFrame(OpCode opCode, byte[] data, int offset, int count, bool isFin = false, bool isMasked = true) 11 | { 12 | BufferValidator.ValidateBuffer(data, offset, count, "data"); 13 | 14 | _opCode = opCode; 15 | this.Data = data; 16 | this.Offset = offset; 17 | this.Count = count; 18 | this.IsFin = isFin; 19 | this.IsMasked = isMasked; 20 | } 21 | 22 | public byte[] Data { get; private set; } 23 | public int Offset { get; private set; } 24 | public int Count { get; private set; } 25 | public bool IsFin { get; private set; } 26 | public bool IsMasked { get; private set; } 27 | 28 | public override OpCode OpCode 29 | { 30 | get { return _opCode; } 31 | } 32 | 33 | public byte[] ToArray(IFrameBuilder builder) 34 | { 35 | if (builder == null) 36 | throw new ArgumentNullException("builder"); 37 | return builder.EncodeFrame(this); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets/Framing/BinaryFrame.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Cowboy.Buffer; 3 | 4 | namespace Cowboy.WebSockets 5 | { 6 | public sealed class BinaryFrame : DataFrame 7 | { 8 | public BinaryFrame(ArraySegment segment, bool isMasked = true) 9 | { 10 | BufferValidator.ValidateArraySegment(segment, "segment"); 11 | 12 | this.Data = segment.Array; 13 | this.Offset = segment.Offset; 14 | this.Count = segment.Count; 15 | this.IsMasked = isMasked; 16 | } 17 | 18 | public BinaryFrame(byte[] data, int offset, int count, bool isMasked = true) 19 | { 20 | BufferValidator.ValidateBuffer(data, offset, count, "data"); 21 | 22 | this.Data = data; 23 | this.Offset = offset; 24 | this.Count = count; 25 | this.IsMasked = isMasked; 26 | } 27 | 28 | public byte[] Data { get; private set; } 29 | public int Offset { get; private set; } 30 | public int Count { get; private set; } 31 | public bool IsMasked { get; private set; } 32 | 33 | public override OpCode OpCode 34 | { 35 | get { return OpCode.Binary; } 36 | } 37 | 38 | public byte[] ToArray(IFrameBuilder builder) 39 | { 40 | if (builder == null) 41 | throw new ArgumentNullException("builder"); 42 | return builder.EncodeFrame(this); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets/Framing/Builder/IFrameBuilder.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Cowboy.WebSockets.Extensions; 3 | 4 | namespace Cowboy.WebSockets 5 | { 6 | public interface IFrameBuilder 7 | { 8 | SortedList NegotiatedExtensions { get; set; } 9 | 10 | byte[] EncodeFrame(PingFrame frame); 11 | byte[] EncodeFrame(PongFrame frame); 12 | byte[] EncodeFrame(CloseFrame frame); 13 | byte[] EncodeFrame(TextFrame frame); 14 | byte[] EncodeFrame(BinaryFrame frame); 15 | byte[] EncodeFrame(BinaryFragmentationFrame frame); 16 | 17 | bool TryDecodeFrameHeader(byte[] buffer, int offset, int count, out Header frameHeader); 18 | void DecodePayload(byte[] buffer, int offset, Header frameHeader, out byte[] payload, out int payloadOffset, out int payloadCount); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets/Framing/CloseFrame.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Cowboy.WebSockets 4 | { 5 | public sealed class CloseFrame : ControlFrame 6 | { 7 | public CloseFrame(bool isMasked = true) 8 | { 9 | this.IsMasked = isMasked; 10 | } 11 | 12 | public CloseFrame(WebSocketCloseCode closeCode, string closeReason, bool isMasked = true) 13 | : this(isMasked) 14 | { 15 | this.CloseCode = closeCode; 16 | this.CloseReason = closeReason; 17 | } 18 | 19 | public WebSocketCloseCode CloseCode { get; private set; } 20 | public string CloseReason { get; private set; } 21 | public bool IsMasked { get; private set; } 22 | 23 | public override OpCode OpCode 24 | { 25 | get { return OpCode.Close; } 26 | } 27 | 28 | public byte[] ToArray(IFrameBuilder builder) 29 | { 30 | if (builder == null) 31 | throw new ArgumentNullException("builder"); 32 | return builder.EncodeFrame(this); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets/Framing/ControlFrame.cs: -------------------------------------------------------------------------------- 1 | namespace Cowboy.WebSockets 2 | { 3 | public abstract class ControlFrame : Frame 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets/Framing/DataFrame.cs: -------------------------------------------------------------------------------- 1 | namespace Cowboy.WebSockets 2 | { 3 | public abstract class DataFrame : Frame 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets/Framing/Frame.cs: -------------------------------------------------------------------------------- 1 | namespace Cowboy.WebSockets 2 | { 3 | public abstract class Frame 4 | { 5 | public abstract OpCode OpCode { get; } 6 | 7 | public override string ToString() 8 | { 9 | return string.Format("OpName[{0}], OpCode[{1}]", OpCode, (byte)OpCode); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets/Framing/Header/Header.cs: -------------------------------------------------------------------------------- 1 | namespace Cowboy.WebSockets 2 | { 3 | public class FixedHeader 4 | { 5 | public bool IsFIN { get; set; } 6 | public bool IsRSV1 { get; set; } 7 | public bool IsRSV2 { get; set; } 8 | public bool IsRSV3 { get; set; } 9 | public OpCode OpCode { get; set; } 10 | public bool IsMasked { get; set; } 11 | 12 | public override string ToString() 13 | { 14 | return string.Format("IsFIN[{0}], IsRSV1[{1}], IsRSV2[{2}], IsRSV3[{3}], OpCode[{4}], IsMasked[{5}]", 15 | IsFIN, IsRSV1, IsRSV2, IsRSV3, OpCode, IsMasked); 16 | } 17 | } 18 | 19 | public class Header : FixedHeader 20 | { 21 | public int PayloadLength { get; set; } 22 | public int MaskingKeyOffset { get; set; } 23 | public int Length { get; set; } 24 | 25 | public override string ToString() 26 | { 27 | return string.Format("IsFIN[{0}], IsRSV1[{1}], IsRSV2[{2}], IsRSV3[{3}], OpCode[{4}], IsMasked[{5}], PayloadLength[{6}], MaskingKeyOffset[{7}], Length[{8}]", 28 | IsFIN, IsRSV1, IsRSV2, IsRSV3, OpCode, IsMasked, PayloadLength, MaskingKeyOffset, Length); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets/Framing/OpCode.cs: -------------------------------------------------------------------------------- 1 | namespace Cowboy.WebSockets 2 | { 3 | // https://www.iana.org/assignments/websocket/websocket.xhtml 4 | // The opcode denotes the frame type of the WebSocket frame. 5 | // The opcode is an integer number between 0 and 15, inclusive. 6 | // Opcode Meaning Reference 7 | // 0 Continuation Frame [RFC6455] 8 | // 1 Text Frame [RFC6455] 9 | // 2 Binary Frame [RFC6455] 10 | // 3-7 Unassigned 11 | // 8 Connection Close Frame [RFC6455] 12 | // 9 Ping Frame [RFC6455] 13 | // 10 Pong Frame [RFC6455] 14 | // 11-15 Unassigned 15 | public enum OpCode : byte 16 | { 17 | Continuation = 0, 18 | Text = 1, 19 | Binary = 2, 20 | Close = 8, 21 | Ping = 9, 22 | Pong = 10, 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets/Framing/PingFrame.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Cowboy.WebSockets 4 | { 5 | public sealed class PingFrame : ControlFrame 6 | { 7 | public PingFrame(bool isMasked = true) 8 | { 9 | this.IsMasked = isMasked; 10 | } 11 | 12 | public PingFrame(string data, bool isMasked = true) 13 | : this(isMasked) 14 | { 15 | this.Data = data; 16 | } 17 | 18 | public string Data { get; private set; } 19 | public bool IsMasked { get; private set; } 20 | 21 | public override OpCode OpCode 22 | { 23 | get { return OpCode.Ping; } 24 | } 25 | 26 | public byte[] ToArray(IFrameBuilder builder) 27 | { 28 | if (builder == null) 29 | throw new ArgumentNullException("builder"); 30 | return builder.EncodeFrame(this); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets/Framing/PongFrame.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Cowboy.WebSockets 4 | { 5 | public sealed class PongFrame : ControlFrame 6 | { 7 | public PongFrame(bool isMasked = true) 8 | { 9 | this.IsMasked = isMasked; 10 | } 11 | 12 | public PongFrame(string data, bool isMasked = true) 13 | : this(isMasked) 14 | { 15 | this.Data = data; 16 | } 17 | 18 | public string Data { get; private set; } 19 | public bool IsMasked { get; private set; } 20 | 21 | public override OpCode OpCode 22 | { 23 | get { return OpCode.Pong; } 24 | } 25 | 26 | public byte[] ToArray(IFrameBuilder builder) 27 | { 28 | if (builder == null) 29 | throw new ArgumentNullException("builder"); 30 | return builder.EncodeFrame(this); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets/Framing/TextFrame.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Cowboy.WebSockets 4 | { 5 | public sealed class TextFrame : DataFrame 6 | { 7 | public TextFrame(string text, bool isMasked = true) 8 | { 9 | if (string.IsNullOrEmpty(text)) 10 | throw new ArgumentNullException("text"); 11 | 12 | this.Text = text; 13 | this.IsMasked = isMasked; 14 | } 15 | 16 | public string Text { get; private set; } 17 | public bool IsMasked { get; private set; } 18 | 19 | public override OpCode OpCode 20 | { 21 | get { return OpCode.Text; } 22 | } 23 | 24 | public byte[] ToArray(IFrameBuilder builder) 25 | { 26 | if (builder == null) 27 | throw new ArgumentNullException("builder"); 28 | return builder.EncodeFrame(this); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets/Helpers/Consts.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | namespace Cowboy.WebSockets 4 | { 5 | internal sealed class Consts 6 | { 7 | internal static readonly byte[] HttpMessageTerminator = Encoding.UTF8.GetBytes("\r\n\r\n"); 8 | 9 | internal static readonly string[] WebSocketSchemes = new string[] { "ws", "wss" }; 10 | 11 | internal const string HttpHeaderLineFormat = "{0}: {1}"; 12 | 13 | internal const string HttpStatusCodeName = "HttpStatusCode"; 14 | internal const string HttpStatusCodeDescription = "HttpStatusCodeDescription"; 15 | internal const string HttpGetMethodName = "GET"; 16 | internal const string HttpVersionName = "HTTP"; 17 | internal const string HttpVersion = "1.1"; 18 | 19 | internal const string SecWebSocketKeyGuid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; 20 | 21 | internal const string WebSocketUpgradeToken = "websocket"; 22 | internal const string WebSocketConnectionToken = "Upgrade"; 23 | 24 | // https://www.iana.org/assignments/websocket/websocket.xhtml#version-number 25 | // Version Number Reference Status 26 | // 0 [draft-ietf-hybi-thewebsocketprotocol-00] Interim 27 | // 1 [draft-ietf-hybi-thewebsocketprotocol-01] Interim 28 | // 2 [draft-ietf-hybi-thewebsocketprotocol-02] Interim 29 | // 3 [draft-ietf-hybi-thewebsocketprotocol-03] Interim 30 | // 4 [draft-ietf-hybi-thewebsocketprotocol-04] Interim 31 | // 5 [draft-ietf-hybi-thewebsocketprotocol-05] Interim 32 | // 6 [draft-ietf-hybi-thewebsocketprotocol-06] Interim 33 | // 7 [draft-ietf-hybi-thewebsocketprotocol-07] Interim 34 | // 8 [draft-ietf-hybi-thewebsocketprotocol-08] Interim 35 | // 9 [Reserved] 36 | // 10 [Reserved] 37 | // 11 [Reserved] 38 | // 12 [Reserved] 39 | // 13 [RFC6455] Standard 40 | internal const string WebSocketVersion = "13"; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets/Helpers/HttpKnownHeaderNames.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Collections.ObjectModel; 3 | 4 | namespace Cowboy.WebSockets 5 | { 6 | internal static class HttpKnownHeaderNames 7 | { 8 | public const string CacheControl = "Cache-Control"; 9 | public const string Connection = "Connection"; 10 | public const string Date = "Date"; 11 | public const string KeepAlive = "Keep-Alive"; 12 | public const string Pragma = "Pragma"; 13 | public const string ProxyConnection = "Proxy-Connection"; 14 | public const string Trailer = "Trailer"; 15 | public const string TransferEncoding = "Transfer-Encoding"; 16 | public const string Upgrade = "Upgrade"; 17 | public const string Via = "Via"; 18 | public const string Warning = "Warning"; 19 | public const string ContentLength = "Content-Length"; 20 | public const string ContentType = "Content-Type"; 21 | public const string ContentDisposition = "Content-Disposition"; 22 | public const string ContentEncoding = "Content-Encoding"; 23 | public const string ContentLanguage = "Content-Language"; 24 | public const string ContentLocation = "Content-Location"; 25 | public const string ContentRange = "Content-Range"; 26 | public const string Expires = "Expires"; 27 | public const string LastModified = "Last-Modified"; 28 | public const string Age = "Age"; 29 | public const string Location = "Location"; 30 | public const string ProxyAuthenticate = "Proxy-Authenticate"; 31 | public const string RetryAfter = "Retry-After"; 32 | public const string Server = "Server"; 33 | public const string SetCookie = "Set-Cookie"; 34 | public const string SetCookie2 = "Set-Cookie2"; 35 | public const string Vary = "Vary"; 36 | public const string WWWAuthenticate = "WWW-Authenticate"; 37 | public const string Accept = "Accept"; 38 | public const string AcceptCharset = "Accept-Charset"; 39 | public const string AcceptEncoding = "Accept-Encoding"; 40 | public const string AcceptLanguage = "Accept-Language"; 41 | public const string Authorization = "Authorization"; 42 | public const string Cookie = "Cookie"; 43 | public const string Cookie2 = "Cookie2"; 44 | public const string Expect = "Expect"; 45 | public const string From = "From"; 46 | public const string Host = "Host"; 47 | public const string IfMatch = "If-Match"; 48 | public const string IfModifiedSince = "If-Modified-Since"; 49 | public const string IfNoneMatch = "If-None-Match"; 50 | public const string IfRange = "If-Range"; 51 | public const string IfUnmodifiedSince = "If-Unmodified-Since"; 52 | public const string MaxForwards = "Max-Forwards"; 53 | public const string ProxyAuthorization = "Proxy-Authorization"; 54 | public const string Referer = "Referer"; 55 | public const string Range = "Range"; 56 | public const string UserAgent = "User-Agent"; 57 | public const string ContentMD5 = "Content-MD5"; 58 | public const string ETag = "ETag"; 59 | public const string TE = "TE"; 60 | public const string Allow = "Allow"; 61 | public const string AcceptRanges = "Accept-Ranges"; 62 | public const string P3P = "P3P"; 63 | public const string XPoweredBy = "X-Powered-By"; 64 | public const string XAspNetVersion = "X-AspNet-Version"; 65 | public const string SecWebSocketKey = "Sec-WebSocket-Key"; 66 | public const string SecWebSocketExtensions = "Sec-WebSocket-Extensions"; 67 | public const string SecWebSocketAccept = "Sec-WebSocket-Accept"; 68 | public const string Origin = "Origin"; 69 | public const string SecWebSocketProtocol = "Sec-WebSocket-Protocol"; 70 | public const string SecWebSocketVersion = "Sec-WebSocket-Version"; 71 | 72 | public static readonly IReadOnlyCollection All = 73 | new ReadOnlyCollection( 74 | new List() 75 | { 76 | CacheControl , 77 | Connection , 78 | Date, 79 | KeepAlive , 80 | Pragma, 81 | ProxyConnection , 82 | Trailer , 83 | TransferEncoding, 84 | Upgrade , 85 | Via , 86 | Warning , 87 | ContentLength , 88 | ContentType , 89 | ContentDisposition, 90 | ContentEncoding , 91 | ContentLanguage , 92 | ContentLocation, 93 | ContentRange , 94 | Expires , 95 | LastModified , 96 | Age , 97 | Location , 98 | ProxyAuthenticate , 99 | RetryAfter , 100 | Server , 101 | SetCookie , 102 | SetCookie2, 103 | Vary, 104 | WWWAuthenticate, 105 | Accept, 106 | AcceptCharset, 107 | AcceptEncoding, 108 | AcceptLanguage, 109 | Authorization, 110 | Cookie, 111 | Cookie2, 112 | Expect, 113 | From, 114 | Host, 115 | IfMatch, 116 | IfModifiedSince, 117 | IfNoneMatch, 118 | IfRange, 119 | IfUnmodifiedSince, 120 | MaxForwards, 121 | ProxyAuthorization, 122 | Referer, 123 | Range, 124 | UserAgent, 125 | ContentMD5, 126 | ETag, 127 | TE, 128 | Allow, 129 | AcceptRanges, 130 | P3P, 131 | XPoweredBy, 132 | XAspNetVersion, 133 | SecWebSocketKey, 134 | SecWebSocketExtensions, 135 | SecWebSocketAccept, 136 | Origin, 137 | SecWebSocketProtocol, 138 | SecWebSocketVersion, 139 | }); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets/Helpers/KeepAliveTracker.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Threading; 4 | 5 | namespace Cowboy.WebSockets 6 | { 7 | internal abstract class KeepAliveTracker : IDisposable 8 | { 9 | public abstract void OnDataReceived(); 10 | public abstract void OnDataSent(); 11 | public abstract void StartTimer(); 12 | public abstract void StopTimer(); 13 | public abstract void ResetTimer(); 14 | public abstract bool ShouldSendKeepAlive(); 15 | public abstract void Dispose(); 16 | 17 | public static KeepAliveTracker Create(TimeSpan keepAliveInterval, TimerCallback keepAliveCallback) 18 | { 19 | if ((int)keepAliveInterval.TotalMilliseconds > 0) 20 | { 21 | return new DefaultKeepAliveTracker(keepAliveInterval, keepAliveCallback); 22 | } 23 | 24 | return new DisabledKeepAliveTracker(); 25 | } 26 | 27 | private class DisabledKeepAliveTracker : KeepAliveTracker 28 | { 29 | public override void OnDataReceived() 30 | { 31 | } 32 | 33 | public override void OnDataSent() 34 | { 35 | } 36 | 37 | public override void StartTimer() 38 | { 39 | } 40 | 41 | public override void StopTimer() 42 | { 43 | } 44 | 45 | public override void ResetTimer() 46 | { 47 | } 48 | 49 | public override bool ShouldSendKeepAlive() 50 | { 51 | return false; 52 | } 53 | 54 | public override void Dispose() 55 | { 56 | } 57 | } 58 | 59 | private class DefaultKeepAliveTracker : KeepAliveTracker 60 | { 61 | private readonly TimerCallback _keepAliveTimerElapsedCallback; 62 | private readonly TimeSpan _keepAliveInterval; 63 | private readonly Stopwatch _lastSendActivity; 64 | private readonly Stopwatch _lastReceiveActivity; 65 | private Timer _keepAliveTimer; 66 | 67 | public DefaultKeepAliveTracker(TimeSpan keepAliveInterval, TimerCallback keepAliveCallback) 68 | { 69 | _keepAliveInterval = keepAliveInterval; 70 | _keepAliveTimerElapsedCallback = keepAliveCallback; 71 | _lastSendActivity = new Stopwatch(); 72 | _lastReceiveActivity = new Stopwatch(); 73 | } 74 | 75 | public override void OnDataReceived() 76 | { 77 | _lastReceiveActivity.Restart(); 78 | } 79 | 80 | public override void OnDataSent() 81 | { 82 | _lastSendActivity.Restart(); 83 | } 84 | 85 | public override void StartTimer() 86 | { 87 | int keepAliveIntervalMilliseconds = (int)_keepAliveInterval.TotalMilliseconds; 88 | 89 | if (ExecutionContext.IsFlowSuppressed()) 90 | { 91 | _keepAliveTimer = new Timer(_keepAliveTimerElapsedCallback, null, Timeout.Infinite, Timeout.Infinite); 92 | _keepAliveTimer.Change(keepAliveIntervalMilliseconds, Timeout.Infinite); 93 | } 94 | else 95 | { 96 | using (ExecutionContext.SuppressFlow()) 97 | { 98 | _keepAliveTimer = new Timer(_keepAliveTimerElapsedCallback, null, Timeout.Infinite, Timeout.Infinite); 99 | _keepAliveTimer.Change(keepAliveIntervalMilliseconds, Timeout.Infinite); 100 | } 101 | } 102 | } 103 | 104 | public override void StopTimer() 105 | { 106 | if (_keepAliveTimer != null) 107 | { 108 | _keepAliveTimer.Change(Timeout.Infinite, Timeout.Infinite); 109 | } 110 | } 111 | 112 | public override void ResetTimer() 113 | { 114 | ResetTimer((int)_keepAliveInterval.TotalMilliseconds); 115 | } 116 | 117 | public override bool ShouldSendKeepAlive() 118 | { 119 | TimeSpan idleTime = GetIdleTime(); 120 | if (idleTime >= _keepAliveInterval) 121 | { 122 | return true; 123 | } 124 | 125 | ResetTimer((int)(_keepAliveInterval - idleTime).TotalMilliseconds); 126 | return false; 127 | } 128 | 129 | public override void Dispose() 130 | { 131 | if (_keepAliveTimer != null) 132 | { 133 | _keepAliveTimer.Dispose(); 134 | } 135 | } 136 | 137 | private void ResetTimer(int dueInMilliseconds) 138 | { 139 | if (_keepAliveTimer != null) 140 | { 141 | _keepAliveTimer.Change(dueInMilliseconds, Timeout.Infinite); 142 | } 143 | } 144 | 145 | private TimeSpan GetIdleTime() 146 | { 147 | TimeSpan sinceLastSendActivity = GetTimeElapsed(_lastSendActivity); 148 | TimeSpan sinceLastReceiveActivity = GetTimeElapsed(_lastReceiveActivity); 149 | 150 | if (sinceLastReceiveActivity < sinceLastSendActivity) 151 | { 152 | return sinceLastReceiveActivity; 153 | } 154 | 155 | return sinceLastSendActivity; 156 | } 157 | 158 | private TimeSpan GetTimeElapsed(Stopwatch watch) 159 | { 160 | if (watch.IsRunning) 161 | { 162 | return watch.Elapsed; 163 | } 164 | 165 | return _keepAliveInterval; 166 | } 167 | } 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets/Helpers/StringBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | namespace Cowboy.WebSockets 4 | { 5 | internal static class StringBuilderExtensions 6 | { 7 | private static readonly char[] _crcf = new char[] { '\r', '\n' }; 8 | 9 | public static void AppendFormatWithCrCf(this StringBuilder builder, string format, object arg) 10 | { 11 | builder.AppendFormat(format, arg); 12 | builder.Append(_crcf); 13 | } 14 | 15 | public static void AppendFormatWithCrCf(this StringBuilder builder, string format, params object[] args) 16 | { 17 | builder.AppendFormat(format, args); 18 | builder.Append(_crcf); 19 | } 20 | 21 | public static void AppendWithCrCf(this StringBuilder builder, string text) 22 | { 23 | builder.Append(text); 24 | builder.Append(_crcf); 25 | } 26 | 27 | public static void AppendWithCrCf(this StringBuilder builder) 28 | { 29 | builder.Append(_crcf); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets/Helpers/TplExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace Cowboy.WebSockets 4 | { 5 | internal static class TplExtensions 6 | { 7 | public static void Forget(this Task task) 8 | { 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets/Helpers/WebSocketHelpers.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | 4 | namespace Cowboy.WebSockets 5 | { 6 | internal sealed class WebSocketHelpers 7 | { 8 | internal static bool FindHttpMessageTerminator(byte[] buffer, int offset, int count, out int index) 9 | { 10 | index = -1; 11 | 12 | for (int i = 0; i < count; i++) 13 | { 14 | if (i + Consts.HttpMessageTerminator.Length <= count) 15 | { 16 | bool matched = true; 17 | for (int j = 0; j < Consts.HttpMessageTerminator.Length; j++) 18 | { 19 | if (buffer[offset + i + j] != Consts.HttpMessageTerminator[j]) 20 | { 21 | matched = false; 22 | break; 23 | } 24 | } 25 | 26 | if (matched) 27 | { 28 | index = i; 29 | return true; 30 | } 31 | } 32 | else 33 | { 34 | break; 35 | } 36 | } 37 | 38 | return false; 39 | } 40 | 41 | internal static bool ValidateSubprotocol(string subProtocol) 42 | { 43 | if (string.IsNullOrWhiteSpace(subProtocol)) 44 | throw new ArgumentNullException("subProtocol"); 45 | 46 | string separators = "()<>@,;:\\\"/[]?={} "; 47 | 48 | char[] chars = subProtocol.ToCharArray(); 49 | string invalidChar = null; 50 | int i = 0; 51 | while (i < chars.Length) 52 | { 53 | char ch = chars[i]; 54 | if (ch < 0x21 || ch > 0x7e) 55 | { 56 | invalidChar = string.Format(CultureInfo.InvariantCulture, "[{0}]", (int)ch); 57 | break; 58 | } 59 | 60 | if (!char.IsLetterOrDigit(ch) && separators.IndexOf(ch) >= 0) 61 | { 62 | invalidChar = ch.ToString(); 63 | break; 64 | } 65 | 66 | i++; 67 | } 68 | 69 | return invalidChar == null; 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | [assembly: AssemblyTitle("Cowboy.WebSockets")] 5 | [assembly: Guid("3a79448c-5d23-4325-8076-576e64ba65be")] 6 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets/Server/AsyncWebSocketServerConfiguration.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net.Security; 4 | using System.Net.Sockets; 5 | using System.Security.Authentication; 6 | using System.Security.Cryptography.X509Certificates; 7 | using Cowboy.Buffer; 8 | using Cowboy.WebSockets.Extensions; 9 | using Cowboy.WebSockets.SubProtocols; 10 | 11 | namespace Cowboy.WebSockets 12 | { 13 | public sealed class AsyncWebSocketServerConfiguration 14 | { 15 | public AsyncWebSocketServerConfiguration() 16 | { 17 | BufferManager = new SegmentBufferManager(1024, 8192, 1, true); 18 | ReceiveBufferSize = 8192; 19 | SendBufferSize = 8192; 20 | ReceiveTimeout = TimeSpan.Zero; 21 | SendTimeout = TimeSpan.Zero; 22 | NoDelay = true; 23 | LingerState = new LingerOption(false, 0); // The socket will linger for x seconds after Socket.Close is called. 24 | 25 | PendingConnectionBacklog = 200; 26 | AllowNatTraversal = true; 27 | 28 | SslEnabled = false; 29 | SslServerCertificate = null; 30 | SslEncryptionPolicy = EncryptionPolicy.RequireEncryption; 31 | SslEnabledProtocols = SslProtocols.Ssl3 | SslProtocols.Tls; 32 | SslClientCertificateRequired = true; 33 | SslCheckCertificateRevocation = false; 34 | SslPolicyErrorsBypassed = false; 35 | 36 | ConnectTimeout = TimeSpan.FromSeconds(10); 37 | CloseTimeout = TimeSpan.FromSeconds(5); 38 | KeepAliveInterval = TimeSpan.FromSeconds(60); 39 | KeepAliveTimeout = TimeSpan.FromSeconds(15); 40 | ReasonableFragmentSize = 4096; 41 | 42 | EnabledExtensions = new Dictionary() 43 | { 44 | { PerMessageCompressionExtension.RegisteredToken, new PerMessageCompressionExtensionNegotiator() }, 45 | }; 46 | EnabledSubProtocols = new Dictionary(); 47 | } 48 | 49 | public ISegmentBufferManager BufferManager { get; set; } 50 | public int ReceiveBufferSize { get; set; } 51 | public int SendBufferSize { get; set; } 52 | public TimeSpan ReceiveTimeout { get; set; } 53 | public TimeSpan SendTimeout { get; set; } 54 | public bool NoDelay { get; set; } 55 | public LingerOption LingerState { get; set; } 56 | 57 | public int PendingConnectionBacklog { get; set; } 58 | public bool AllowNatTraversal { get; set; } 59 | 60 | public bool SslEnabled { get; set; } 61 | public X509Certificate2 SslServerCertificate { get; set; } 62 | public EncryptionPolicy SslEncryptionPolicy { get; set; } 63 | public SslProtocols SslEnabledProtocols { get; set; } 64 | public bool SslClientCertificateRequired { get; set; } 65 | public bool SslCheckCertificateRevocation { get; set; } 66 | public bool SslPolicyErrorsBypassed { get; set; } 67 | 68 | public TimeSpan ConnectTimeout { get; set; } 69 | public TimeSpan CloseTimeout { get; set; } 70 | public TimeSpan KeepAliveInterval { get; set; } 71 | public TimeSpan KeepAliveTimeout { get; set; } 72 | public int ReasonableFragmentSize { get; set; } 73 | 74 | public Dictionary EnabledExtensions { get; set; } 75 | public Dictionary EnabledSubProtocols { get; set; } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets/Server/Module/AsyncWebSocketRouteResolver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | namespace Cowboy.WebSockets 5 | { 6 | public class AsyncWebSocketRouteResolver 7 | { 8 | private AsyncWebSocketServerModuleCatalog _moduleCatalog; 9 | 10 | public AsyncWebSocketRouteResolver(AsyncWebSocketServerModuleCatalog moduleCatalog) 11 | { 12 | if (moduleCatalog == null) 13 | throw new ArgumentNullException("moduleCatalog"); 14 | _moduleCatalog = moduleCatalog; 15 | } 16 | 17 | public AsyncWebSocketServerModule Resolve(string path, string query) 18 | { 19 | var modules = _moduleCatalog.GetAllModules(); 20 | return modules.FirstOrDefault(m => 21 | string.Compare( 22 | m.ModulePath.Trim().TrimStart('/').TrimEnd('/').ToLowerInvariant(), 23 | path.Trim().TrimStart('/').TrimEnd('/').ToLowerInvariant(), 24 | StringComparison.OrdinalIgnoreCase 25 | ) == 0); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets/Server/Module/AsyncWebSocketServerModule.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Concurrent; 2 | using System.Diagnostics; 3 | using System.Text.RegularExpressions; 4 | using System.Threading.Tasks; 5 | 6 | namespace Cowboy.WebSockets 7 | { 8 | public abstract class AsyncWebSocketServerModule : IAsyncWebSocketServerMessageDispatcher 9 | { 10 | [DebuggerBrowsable(DebuggerBrowsableState.Never)] 11 | private static readonly Regex ModuleNameExpression = new Regex(@"(?[\w]+)Module$", RegexOptions.Compiled); 12 | 13 | private ConcurrentDictionary _sessions = new ConcurrentDictionary(); 14 | 15 | protected AsyncWebSocketServerModule() 16 | : this(string.Empty) 17 | { 18 | } 19 | 20 | protected AsyncWebSocketServerModule(string modulePath) 21 | { 22 | this.ModulePath = modulePath; 23 | this.ModuleName = GetModuleName(); 24 | } 25 | 26 | private string GetModuleName() 27 | { 28 | var typeName = this.GetType().Name; 29 | var nameMatch = ModuleNameExpression.Match(typeName); 30 | 31 | if (nameMatch.Success) 32 | { 33 | return nameMatch.Groups["name"].Value; 34 | } 35 | 36 | return typeName; 37 | } 38 | 39 | public string ModuleName { get; protected set; } 40 | 41 | public string ModulePath { get; protected set; } 42 | 43 | public int SessionCount { get { return _sessions.Count; } } 44 | 45 | #region Dispatcher 46 | 47 | public virtual async Task OnSessionStarted(AsyncWebSocketSession session) 48 | { 49 | _sessions.TryAdd(session.SessionKey, session); 50 | await Task.CompletedTask; 51 | } 52 | 53 | public virtual async Task OnSessionTextReceived(AsyncWebSocketSession session, string text) 54 | { 55 | await Task.CompletedTask; 56 | } 57 | 58 | public virtual async Task OnSessionBinaryReceived(AsyncWebSocketSession session, byte[] data, int offset, int count) 59 | { 60 | await Task.CompletedTask; 61 | } 62 | 63 | public virtual async Task OnSessionClosed(AsyncWebSocketSession session) 64 | { 65 | AsyncWebSocketSession throwAway; 66 | _sessions.TryRemove(session.SessionKey, out throwAway); 67 | await Task.CompletedTask; 68 | } 69 | 70 | #endregion 71 | 72 | #region Fragmentation 73 | 74 | public virtual async Task OnSessionFragmentationStreamOpened(AsyncWebSocketSession session, byte[] data, int offset, int count) 75 | { 76 | await Task.CompletedTask; 77 | } 78 | 79 | public virtual async Task OnSessionFragmentationStreamContinued(AsyncWebSocketSession session, byte[] data, int offset, int count) 80 | { 81 | await Task.CompletedTask; 82 | } 83 | 84 | public virtual async Task OnSessionFragmentationStreamClosed(AsyncWebSocketSession session, byte[] data, int offset, int count) 85 | { 86 | await Task.CompletedTask; 87 | } 88 | 89 | #endregion 90 | 91 | #region Send 92 | 93 | public async Task Broadcast(string text) 94 | { 95 | foreach (var session in _sessions.Values) 96 | { 97 | await session.SendTextAsync(text); 98 | } 99 | } 100 | 101 | public async Task Broadcast(byte[] binary) 102 | { 103 | await Broadcast(binary, 0, binary.Length); 104 | } 105 | 106 | public async Task Broadcast(byte[] binary, int offset, int count) 107 | { 108 | foreach (var session in _sessions.Values) 109 | { 110 | await session.SendBinaryAsync(binary, offset, count); 111 | } 112 | } 113 | 114 | #endregion 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets/Server/Module/AsyncWebSocketServerModuleCatalog.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Cowboy.WebSockets 5 | { 6 | public class AsyncWebSocketServerModuleCatalog 7 | { 8 | private Dictionary _modules = new Dictionary(); 9 | 10 | public IEnumerable GetAllModules() 11 | { 12 | return _modules.Values; 13 | } 14 | 15 | public AsyncWebSocketServerModule GetModule(Type moduleType) 16 | { 17 | return _modules[moduleType.FullName]; 18 | } 19 | 20 | public void RegisterModule(AsyncWebSocketServerModule module) 21 | { 22 | _modules.Add(module.GetType().FullName, module); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets/Server/Module/IAsyncWebSocketServerMessageDispatcher.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace Cowboy.WebSockets 4 | { 5 | public interface IAsyncWebSocketServerMessageDispatcher 6 | { 7 | Task OnSessionStarted(AsyncWebSocketSession session); 8 | Task OnSessionTextReceived(AsyncWebSocketSession session, string text); 9 | Task OnSessionBinaryReceived(AsyncWebSocketSession session, byte[] data, int offset, int count); 10 | Task OnSessionClosed(AsyncWebSocketSession session); 11 | 12 | Task OnSessionFragmentationStreamOpened(AsyncWebSocketSession session, byte[] data, int offset, int count); 13 | Task OnSessionFragmentationStreamContinued(AsyncWebSocketSession session, byte[] data, int offset, int count); 14 | Task OnSessionFragmentationStreamClosed(AsyncWebSocketSession session, byte[] data, int offset, int count); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets/SubProtocols/IWebSocketSubProtocol.cs: -------------------------------------------------------------------------------- 1 | namespace Cowboy.WebSockets.SubProtocols 2 | { 3 | public interface IWebSocketSubProtocol 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets/SubProtocols/IWebSocketSubProtocolNegotiator.cs: -------------------------------------------------------------------------------- 1 | namespace Cowboy.WebSockets.SubProtocols 2 | { 3 | public interface IWebSocketSubProtocolNegotiator 4 | { 5 | bool NegotiateAsClient(string protocolName, string protocolVersion, string protocolParameter, out string invalidParameter, out IWebSocketSubProtocol negotiatedSubProtocol); 6 | bool NegotiateAsServer(string protocolName, string protocolVersion, string protocolParameter, out string invalidParameter, out IWebSocketSubProtocol negotiatedSubProtocol); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets/SubProtocols/WebSocketSubProtocolRequestDescription.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Cowboy.WebSockets.SubProtocols 4 | { 5 | public sealed class WebSocketSubProtocolRequestDescription 6 | { 7 | public WebSocketSubProtocolRequestDescription(string requestedSubProtocol) 8 | { 9 | if (string.IsNullOrWhiteSpace(requestedSubProtocol)) 10 | throw new ArgumentNullException("requestedSubProtocol"); 11 | this.RequestedSubProtocol = requestedSubProtocol; 12 | } 13 | 14 | public string RequestedSubProtocol { get; private set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets/WebSocketCloseCode.cs: -------------------------------------------------------------------------------- 1 | namespace Cowboy.WebSockets 2 | { 3 | // 0 - 999 Status codes in the range 0-999 are not used. 4 | // 1000 - 1999 Status codes in the range 1000-1999 are reserved for definition by this protocol. 5 | // 2000 - 2999 Status codes in the range 2000-2999 are reserved for use by extensions. 6 | // 3000 - 3999 Status codes in the range 3000-3999 MAY be used by libraries and frameworks. The 7 | // interpretation of these codes is undefined by this protocol. End applications MUST 8 | // NOT use status codes in this range. 9 | // 4000 - 4999 Status codes in the range 4000-4999 MAY be used by application code. The interpretation 10 | // of these codes is undefined by this protocol. 11 | public enum WebSocketCloseCode 12 | { 13 | NormalClosure = 1000, 14 | EndpointUnavailable = 1001, 15 | ProtocolError = 1002, 16 | InvalidMessageType = 1003, 17 | Empty = 1005, 18 | AbnormalClosure = 1006, // 1006 is reserved and should never be used by user 19 | InvalidPayloadData = 1007, 20 | PolicyViolation = 1008, 21 | MessageTooBig = 1009, 22 | MandatoryExtension = 1010, 23 | InternalServerError = 1011, 24 | TlsHandshakeFailed = 1015, // 1015 is reserved and should never be used by user 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets/WebSocketState.cs: -------------------------------------------------------------------------------- 1 | namespace Cowboy.WebSockets 2 | { 3 | public enum WebSocketState 4 | { 5 | None = 0, 6 | Connecting = 1, 7 | Open = 2, 8 | Closing = 3, 9 | Closed = 5, 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Cowboy.WebSockets/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/SolutionVersion.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | [assembly: AssemblyDescription("Cowboy.WebSockets is a C# based library for building WebSocket services.")] 5 | [assembly: AssemblyCompany("Dennis Gao")] 6 | [assembly: AssemblyProduct("Cowboy.WebSockets")] 7 | [assembly: AssemblyCopyright("Copyright © 2015-2016 Dennis Gao")] 8 | [assembly: AssemblyTrademark("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCulture("")] 11 | [assembly: AssemblyVersion("1.3.14.0")] 12 | [assembly: AssemblyFileVersion("1.3.14.0")] 13 | [assembly: ComVisible(false)] 14 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Tests/Certificates/Cowboy.cer: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaochundong/Cowboy.WebSockets/1d75f8f1f71b1d439c2117ebd956c79d87742e35/Cowboy.WebSockets/Tests/Certificates/Cowboy.cer -------------------------------------------------------------------------------- /Cowboy.WebSockets/Tests/Certificates/Cowboy.pvk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaochundong/Cowboy.WebSockets/1d75f8f1f71b1d439c2117ebd956c79d87742e35/Cowboy.WebSockets/Tests/Certificates/Cowboy.pvk -------------------------------------------------------------------------------- /Cowboy.WebSockets/Tests/Certificates/Cowboy.spc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaochundong/Cowboy.WebSockets/1d75f8f1f71b1d439c2117ebd956c79d87742e35/Cowboy.WebSockets/Tests/Certificates/Cowboy.spc -------------------------------------------------------------------------------- /Cowboy.WebSockets/Tests/Certificates/RootCowboy.cer: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaochundong/Cowboy.WebSockets/1d75f8f1f71b1d439c2117ebd956c79d87742e35/Cowboy.WebSockets/Tests/Certificates/RootCowboy.cer -------------------------------------------------------------------------------- /Cowboy.WebSockets/Tests/Certificates/RootCowboy.pvk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaochundong/Cowboy.WebSockets/1d75f8f1f71b1d439c2117ebd956c79d87742e35/Cowboy.WebSockets/Tests/Certificates/RootCowboy.pvk -------------------------------------------------------------------------------- /Cowboy.WebSockets/Tests/Cowboy.WebSockets.TestAsyncWebSocketClient/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Tests/Cowboy.WebSockets.TestAsyncWebSocketClient/Cowboy.WebSockets.TestAsyncWebSocketClient.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {95639F67-5297-4D55-B126-2CB9226226CB} 8 | Exe 9 | Properties 10 | Cowboy.WebSockets.TestAsyncWebSocketClient 11 | Cowboy.WebSockets.TestAsyncWebSocketClient 12 | v4.6 13 | 512 14 | true 15 | 16 | 17 | 18 | AnyCPU 19 | true 20 | full 21 | false 22 | bin\Debug\ 23 | DEBUG;TRACE 24 | prompt 25 | 4 26 | 27 | 28 | AnyCPU 29 | pdbonly 30 | true 31 | bin\Release\ 32 | TRACE 33 | prompt 34 | 4 35 | 36 | 37 | 38 | ..\..\packages\Logrila.Logging.1.0.3.0\lib\net46\Logrila.Logging.dll 39 | True 40 | 41 | 42 | ..\..\packages\Logrila.Logging.NLogIntegration.1.0.3.0\lib\net46\Logrila.Logging.NLogIntegration.dll 43 | True 44 | 45 | 46 | ..\..\packages\NLog.4.4.1\lib\net45\NLog.dll 47 | True 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | {3a79448c-5d23-4325-8076-576e64ba65be} 66 | Cowboy.WebSockets 67 | 68 | 69 | 70 | 77 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Tests/Cowboy.WebSockets.TestAsyncWebSocketClient/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Text; 4 | using System.Threading.Tasks; 5 | using Logrila.Logging; 6 | using Logrila.Logging.NLogIntegration; 7 | 8 | namespace Cowboy.WebSockets.TestAsyncWebSocketClient 9 | { 10 | class Program 11 | { 12 | static AsyncWebSocketClient _client; 13 | 14 | static void Main(string[] args) 15 | { 16 | NLogLogger.Use(); 17 | 18 | Task.Run(async () => 19 | { 20 | try 21 | { 22 | var config = new AsyncWebSocketClientConfiguration(); 23 | //config.SslTargetHost = "Cowboy"; 24 | //config.SslClientCertificates.Add(new System.Security.Cryptography.X509Certificates.X509Certificate2(@"D:\\Cowboy.cer")); 25 | //config.SslPolicyErrorsBypassed = true; 26 | 27 | //var uri = new Uri("ws://echo.websocket.org/"); // connect to websocket.org website 28 | //var uri = new Uri("wss://127.0.0.1:22222/test"); // use wss with ssl 29 | var uri = new Uri("ws://127.0.0.1:22222/test"); // connect to localhost 30 | _client = new AsyncWebSocketClient(uri, 31 | OnServerTextReceived, 32 | OnServerBinaryReceived, 33 | OnServerConnected, 34 | OnServerDisconnected, 35 | config); 36 | await _client.Connect(); 37 | 38 | Console.WriteLine("WebSocket client has connected to server [{0}].", uri); 39 | Console.WriteLine("Type something to send to server..."); 40 | while (_client.State == WebSocketState.Open) 41 | { 42 | try 43 | { 44 | string text = Console.ReadLine(); 45 | if (text == "quit") 46 | break; 47 | Task.Run(async () => 48 | { 49 | if (text == "many") 50 | { 51 | text = new string('x', 1024); 52 | Stopwatch watch = Stopwatch.StartNew(); 53 | int count = 10000; 54 | for (int i = 1; i <= count; i++) 55 | { 56 | await _client.SendBinaryAsync(Encoding.UTF8.GetBytes(text)); 57 | Console.WriteLine("Client [{0}] send binary -> Sequence[{1}] -> TextLength[{2}].", 58 | _client.LocalEndPoint, i, text.Length); 59 | } 60 | watch.Stop(); 61 | Console.WriteLine("Client [{0}] send binary -> Count[{1}] -> Cost[{2}] -> PerSecond[{3}].", 62 | _client.LocalEndPoint, count, watch.ElapsedMilliseconds / 1000, count / (watch.ElapsedMilliseconds / 1000)); 63 | } 64 | else if (text == "big1") 65 | { 66 | text = new string('x', 1024 * 1024 * 1); 67 | await _client.SendBinaryAsync(Encoding.UTF8.GetBytes(text)); 68 | Console.WriteLine("Client [{0}] send binary -> [{1} Bytes].", _client.LocalEndPoint, text.Length); 69 | } 70 | else if (text == "big10") 71 | { 72 | text = new string('x', 1024 * 1024 * 10); 73 | await _client.SendBinaryAsync(Encoding.UTF8.GetBytes(text)); 74 | Console.WriteLine("Client [{0}] send binary -> [{1} Bytes].", _client.LocalEndPoint, text.Length); 75 | } 76 | else if (text == "big100") 77 | { 78 | text = new string('x', 1024 * 1024 * 100); 79 | await _client.SendBinaryAsync(Encoding.UTF8.GetBytes(text)); 80 | Console.WriteLine("Client [{0}] send binary -> [{1} Bytes].", _client.LocalEndPoint, text.Length); 81 | } 82 | else if (text == "big1000") 83 | { 84 | text = new string('x', 1024 * 1024 * 1000); 85 | await _client.SendBinaryAsync(Encoding.UTF8.GetBytes(text)); 86 | Console.WriteLine("Client [{0}] send binary -> [{1} Bytes].", _client.LocalEndPoint, text.Length); 87 | } 88 | else 89 | { 90 | await _client.SendBinaryAsync(Encoding.UTF8.GetBytes(text)); 91 | Console.WriteLine("Client [{0}] send binary -> [{1}].", _client.LocalEndPoint, text); 92 | 93 | //await _client.SendTextAsync(text); 94 | //Console.WriteLine("Client [{0}] send text -> [{1}].", _client.LocalEndPoint, text); 95 | } 96 | }).Forget(); 97 | } 98 | catch (Exception ex) 99 | { 100 | Logger.Get().Error(ex.Message, ex); 101 | } 102 | } 103 | 104 | await _client.Close(WebSocketCloseCode.NormalClosure); 105 | Console.WriteLine("WebSocket client has disconnected from server [{0}].", uri); 106 | } 107 | catch (Exception ex) 108 | { 109 | Logger.Get().Error(ex.Message, ex); 110 | } 111 | }).Wait(); 112 | 113 | Console.ReadKey(); 114 | } 115 | 116 | private static async Task OnServerConnected(AsyncWebSocketClient client) 117 | { 118 | Console.WriteLine(string.Format("WebSocket server [{0}] has connected.", client.RemoteEndPoint)); 119 | await Task.CompletedTask; 120 | } 121 | 122 | private static async Task OnServerTextReceived(AsyncWebSocketClient client, string text) 123 | { 124 | Console.Write(string.Format("WebSocket server [{0}] received Text --> ", client.RemoteEndPoint)); 125 | Console.WriteLine(string.Format("{0}", text)); 126 | 127 | await Task.CompletedTask; 128 | } 129 | 130 | private static async Task OnServerBinaryReceived(AsyncWebSocketClient client, byte[] data, int offset, int count) 131 | { 132 | var text = Encoding.UTF8.GetString(data, offset, count); 133 | Console.Write(string.Format("WebSocket server [{0}] received Binary --> ", client.RemoteEndPoint)); 134 | if (count < 1024 * 1024 * 1) 135 | { 136 | Console.WriteLine(text); 137 | } 138 | else 139 | { 140 | Console.WriteLine("{0} Bytes", count); 141 | } 142 | 143 | await Task.CompletedTask; 144 | } 145 | 146 | private static async Task OnServerDisconnected(AsyncWebSocketClient client) 147 | { 148 | Console.WriteLine(string.Format("WebSocket server [{0}] has disconnected.", client.RemoteEndPoint)); 149 | await Task.CompletedTask; 150 | } 151 | } 152 | 153 | public static class TplExtensions 154 | { 155 | public static void Forget(this Task task) 156 | { 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Tests/Cowboy.WebSockets.TestAsyncWebSocketClient/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyTitle("Cowboy.WebSockets.TestAsyncWebSocketClient")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("Cowboy.WebSockets.TestAsyncWebSocketClient")] 12 | [assembly: AssemblyCopyright("")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // Setting ComVisible to false makes the types in this assembly not visible 17 | // to COM components. If you need to access a type in this assembly from 18 | // COM, set the ComVisible attribute to true on that type. 19 | [assembly: ComVisible(false)] 20 | 21 | // The following GUID is for the ID of the typelib if this project is exposed to COM 22 | [assembly: Guid("95639f67-5297-4d55-b126-2cb9226226cb")] 23 | 24 | // Version information for an assembly consists of the following four values: 25 | // 26 | // Major Version 27 | // Minor Version 28 | // Build Number 29 | // Revision 30 | // 31 | // You can specify all the values or you can default the Build and Revision Numbers 32 | // by using the '*' as shown below: 33 | // [assembly: AssemblyVersion("1.0.*")] 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] 36 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Tests/Cowboy.WebSockets.TestAsyncWebSocketClient/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Tests/Cowboy.WebSockets.TestAsyncWebSocketServer/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Tests/Cowboy.WebSockets.TestAsyncWebSocketServer/Cowboy.WebSockets.TestAsyncWebSocketServer.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {31C790E5-1BB5-4004-82B8-43D1FCAF97C3} 8 | Exe 9 | Properties 10 | Cowboy.WebSockets.TestAsyncWebSocketServer 11 | Cowboy.WebSockets.TestAsyncWebSocketServer 12 | v4.6 13 | 512 14 | true 15 | 16 | 17 | 18 | AnyCPU 19 | true 20 | full 21 | false 22 | bin\Debug\ 23 | DEBUG;TRACE 24 | prompt 25 | 4 26 | 27 | 28 | AnyCPU 29 | pdbonly 30 | true 31 | bin\Release\ 32 | TRACE 33 | prompt 34 | 4 35 | 36 | 37 | 38 | ..\..\packages\Logrila.Logging.1.0.3.0\lib\net46\Logrila.Logging.dll 39 | True 40 | 41 | 42 | ..\..\packages\Logrila.Logging.NLogIntegration.1.0.3.0\lib\net46\Logrila.Logging.NLogIntegration.dll 43 | True 44 | 45 | 46 | ..\..\packages\NLog.4.4.1\lib\net45\NLog.dll 47 | True 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | {3a79448c-5d23-4325-8076-576e64ba65be} 67 | Cowboy.WebSockets 68 | 69 | 70 | 71 | 78 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Tests/Cowboy.WebSockets.TestAsyncWebSocketServer/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using System.Threading.Tasks; 4 | using Logrila.Logging; 5 | using Logrila.Logging.NLogIntegration; 6 | 7 | namespace Cowboy.WebSockets.TestAsyncWebSocketServer 8 | { 9 | class Program 10 | { 11 | static AsyncWebSocketServer _server; 12 | 13 | static void Main(string[] args) 14 | { 15 | NLogLogger.Use(); 16 | 17 | try 18 | { 19 | var catalog = new AsyncWebSocketServerModuleCatalog(); 20 | catalog.RegisterModule(new TestWebSocketModule()); 21 | 22 | var config = new AsyncWebSocketServerConfiguration(); 23 | //config.SslEnabled = true; 24 | //config.SslServerCertificate = new System.Security.Cryptography.X509Certificates.X509Certificate2(@"D:\\Cowboy.pfx", "Cowboy"); 25 | //config.SslPolicyErrorsBypassed = true; 26 | 27 | _server = new AsyncWebSocketServer(22222, catalog, config); 28 | _server.Listen(); 29 | 30 | Console.WriteLine("WebSocket server has been started on [{0}].", _server.ListenedEndPoint); 31 | Console.WriteLine("Type something to send to clients..."); 32 | while (true) 33 | { 34 | try 35 | { 36 | string text = Console.ReadLine(); 37 | if (text == "quit") 38 | break; 39 | Task.Run(async () => 40 | { 41 | if (text == "many") 42 | { 43 | text = new string('x', 8192); 44 | for (int i = 0; i < 1000000; i++) 45 | { 46 | await _server.BroadcastBinaryAsync(Encoding.UTF8.GetBytes(text)); 47 | Console.WriteLine("WebSocket server [{0}] broadcasts binary -> [{1}].", _server.ListenedEndPoint, text); 48 | } 49 | } 50 | else if (text == "big1") 51 | { 52 | text = new string('x', 1024 * 1024 * 1); 53 | await _server.BroadcastBinaryAsync(Encoding.UTF8.GetBytes(text)); 54 | Console.WriteLine("WebSocket server [{0}] broadcasts binary -> [{1} Bytes].", _server.ListenedEndPoint, text.Length); 55 | } 56 | else if (text == "big10") 57 | { 58 | text = new string('x', 1024 * 1024 * 10); 59 | await _server.BroadcastBinaryAsync(Encoding.UTF8.GetBytes(text)); 60 | Console.WriteLine("WebSocket server [{0}] broadcasts binary -> [{1} Bytes].", _server.ListenedEndPoint, text.Length); 61 | } 62 | else if (text == "big100") 63 | { 64 | text = new string('x', 1024 * 1024 * 100); 65 | await _server.BroadcastBinaryAsync(Encoding.UTF8.GetBytes(text)); 66 | Console.WriteLine("WebSocket server [{0}] broadcasts binary -> [{1} Bytes].", _server.ListenedEndPoint, text.Length); 67 | } 68 | else if (text == "big1000") 69 | { 70 | text = new string('x', 1024 * 1024 * 1000); 71 | await _server.BroadcastBinaryAsync(Encoding.UTF8.GetBytes(text)); 72 | Console.WriteLine("WebSocket server [{0}] broadcasts binary -> [{1} Bytes].", _server.ListenedEndPoint, text.Length); 73 | } 74 | else 75 | { 76 | await _server.BroadcastBinaryAsync(Encoding.UTF8.GetBytes(text)); 77 | Console.WriteLine("WebSocket server [{0}] broadcasts binary -> [{1}].", _server.ListenedEndPoint, text); 78 | } 79 | 80 | //await _server.BroadcastText(text); 81 | //Console.WriteLine("WebSocket server [{0}] broadcasts text -> [{1}].", _server.ListenedEndPoint, text); 82 | }); 83 | } 84 | catch (Exception ex) 85 | { 86 | Logger.Get().Error(ex.Message, ex); 87 | } 88 | } 89 | 90 | _server.Shutdown(); 91 | Console.WriteLine("WebSocket server has been stopped on [{0}].", _server.ListenedEndPoint); 92 | } 93 | catch (Exception ex) 94 | { 95 | Logger.Get().Error(ex.Message, ex); 96 | } 97 | 98 | Console.ReadKey(); 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Tests/Cowboy.WebSockets.TestAsyncWebSocketServer/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyTitle("Cowboy.WebSockets.TestAsyncWebSocketServer")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("Cowboy.WebSockets.TestAsyncWebSocketServer")] 12 | [assembly: AssemblyCopyright("")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // Setting ComVisible to false makes the types in this assembly not visible 17 | // to COM components. If you need to access a type in this assembly from 18 | // COM, set the ComVisible attribute to true on that type. 19 | [assembly: ComVisible(false)] 20 | 21 | // The following GUID is for the ID of the typelib if this project is exposed to COM 22 | [assembly: Guid("31c790e5-1bb5-4004-82b8-43d1fcaf97c3")] 23 | 24 | // Version information for an assembly consists of the following four values: 25 | // 26 | // Major Version 27 | // Minor Version 28 | // Build Number 29 | // Revision 30 | // 31 | // You can specify all the values or you can default the Build and Revision Numbers 32 | // by using the '*' as shown below: 33 | // [assembly: AssemblyVersion("1.0.*")] 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] 36 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Tests/Cowboy.WebSockets.TestAsyncWebSocketServer/TestWebSocketModule.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using System.Threading.Tasks; 4 | 5 | namespace Cowboy.WebSockets.TestAsyncWebSocketServer 6 | { 7 | public class TestWebSocketModule : AsyncWebSocketServerModule 8 | { 9 | public TestWebSocketModule() 10 | : base(@"/test") 11 | { 12 | } 13 | 14 | public override async Task OnSessionStarted(AsyncWebSocketSession session) 15 | { 16 | Console.WriteLine(string.Format("WebSocket session [{0}] has connected.", session.RemoteEndPoint)); 17 | await Task.CompletedTask; 18 | } 19 | 20 | public override async Task OnSessionTextReceived(AsyncWebSocketSession session, string text) 21 | { 22 | Console.Write(string.Format("WebSocket session [{0}] received Text --> ", session.RemoteEndPoint)); 23 | Console.WriteLine(string.Format("{0}", text)); 24 | 25 | await session.SendTextAsync(text); 26 | } 27 | 28 | public override async Task OnSessionBinaryReceived(AsyncWebSocketSession session, byte[] data, int offset, int count) 29 | { 30 | var text = Encoding.UTF8.GetString(data, offset, count); 31 | Console.Write(string.Format("WebSocket session [{0}] received Binary --> ", session.RemoteEndPoint)); 32 | if (count < 1024 * 1024 * 1) 33 | { 34 | Console.WriteLine(text); 35 | } 36 | else 37 | { 38 | Console.WriteLine("{0} Bytes", count); 39 | } 40 | 41 | await session.SendBinaryAsync(Encoding.UTF8.GetBytes(text)); 42 | } 43 | 44 | public override async Task OnSessionClosed(AsyncWebSocketSession session) 45 | { 46 | Console.WriteLine(string.Format("WebSocket session [{0}] has disconnected.", session.RemoteEndPoint)); 47 | await Task.CompletedTask; 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Tests/Cowboy.WebSockets.TestAsyncWebSocketServer/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Tests/Cowboy.WebSockets.TestUnityWebSocketClient/Cowboy.WebSockets.TestUnityWebSocketClient.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {D6DB086F-FFE4-461A-89BF-343195E09287} 8 | Exe 9 | Properties 10 | Cowboy.WebSockets.TestUnityWebSocketClient 11 | Cowboy.WebSockets.TestUnityWebSocketClient 12 | v4.5.2 13 | 512 14 | 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | false 26 | 27 | 28 | AnyCPU 29 | pdbonly 30 | true 31 | bin\Release\ 32 | TRACE 33 | prompt 34 | 4 35 | false 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | {671e566b-878a-4642-8011-aa4168773adf} 49 | Cowboy.WebSockets.UnityWebSocketClient 50 | 51 | 52 | 53 | 54 | 55 | 56 | 63 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Tests/Cowboy.WebSockets.TestUnityWebSocketClient/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Text; 4 | 5 | namespace Cowboy.WebSockets.TestUnityWebSocketClient 6 | { 7 | class Program 8 | { 9 | static WebSocketClient _client; 10 | static Action _log = (s) => Console.WriteLine(s); 11 | 12 | static void Main(string[] args) 13 | { 14 | try 15 | { 16 | var config = new WebSocketClientConfiguration(); 17 | //config.SslTargetHost = "Cowboy"; 18 | //config.SslClientCertificates.Add(new System.Security.Cryptography.X509Certificates.X509Certificate2(@"D:\\Cowboy.cer")); 19 | //config.SslPolicyErrorsBypassed = true; 20 | 21 | //var uri = new Uri("ws://echo.websocket.org/"); 22 | //var uri = new Uri("wss://127.0.0.1:22222/test"); 23 | var uri = new Uri("ws://127.0.0.1:22222/test"); 24 | _client = new WebSocketClient(uri, config, _log); 25 | _client.ServerConnected += OnServerConnected; 26 | _client.ServerDisconnected += OnServerDisconnected; 27 | _client.ServerTextReceived += OnServerTextReceived; 28 | _client.ServerBinaryReceived += OnServerBinaryReceived; 29 | _client.Connect(); 30 | 31 | Console.WriteLine("WebSocket client has connected to server [{0}].", uri); 32 | Console.WriteLine("Type something to send to server..."); 33 | while (_client.State == WebSocketState.Open) 34 | { 35 | try 36 | { 37 | string text = Console.ReadLine(); 38 | if (text == "quit") 39 | break; 40 | 41 | if (text == "many") 42 | { 43 | text = new string('x', 1024); 44 | Stopwatch watch = Stopwatch.StartNew(); 45 | int count = 10000; 46 | for (int i = 1; i <= count; i++) 47 | { 48 | _client.BeginSendBinary(Encoding.UTF8.GetBytes(text)); 49 | Console.WriteLine("Client [{0}] send binary -> Sequence[{1}] -> TextLength[{2}].", 50 | _client.LocalEndPoint, i, text.Length); 51 | } 52 | watch.Stop(); 53 | Console.WriteLine("Client [{0}] send binary -> Count[{1}] -> Cost[{2}] -> PerSecond[{3}].", 54 | _client.LocalEndPoint, count, watch.ElapsedMilliseconds / 1000, count / (watch.ElapsedMilliseconds / 1000)); 55 | } 56 | else if (text == "big1") 57 | { 58 | text = new string('x', 1024 * 1024 * 1); 59 | _client.BeginSendBinary(Encoding.UTF8.GetBytes(text)); 60 | Console.WriteLine("Client [{0}] send binary -> [{1} Bytes].", _client.LocalEndPoint, text.Length); 61 | } 62 | else if (text == "big10") 63 | { 64 | text = new string('x', 1024 * 1024 * 10); 65 | _client.BeginSendBinary(Encoding.UTF8.GetBytes(text)); 66 | Console.WriteLine("Client [{0}] send binary -> [{1} Bytes].", _client.LocalEndPoint, text.Length); 67 | } 68 | else if (text == "big100") 69 | { 70 | text = new string('x', 1024 * 1024 * 100); 71 | _client.BeginSendBinary(Encoding.UTF8.GetBytes(text)); 72 | Console.WriteLine("Client [{0}] send binary -> [{1} Bytes].", _client.LocalEndPoint, text.Length); 73 | } 74 | else if (text == "big1000") 75 | { 76 | text = new string('x', 1024 * 1024 * 1000); 77 | _client.BeginSendBinary(Encoding.UTF8.GetBytes(text)); 78 | Console.WriteLine("Client [{0}] send binary -> [{1} Bytes].", _client.LocalEndPoint, text.Length); 79 | } 80 | else 81 | { 82 | _client.BeginSendBinary(Encoding.UTF8.GetBytes(text)); 83 | Console.WriteLine("Client [{0}] send binary -> [{1}].", _client.LocalEndPoint, text); 84 | 85 | //_client.SendText(text); 86 | //Console.WriteLine("Client [{0}] send text -> [{1}].", _client.LocalEndPoint, text); 87 | } 88 | } 89 | catch (Exception ex) 90 | { 91 | Console.WriteLine(ex.Message); 92 | } 93 | } 94 | 95 | _client.Close(WebSocketCloseCode.NormalClosure); 96 | Console.WriteLine("WebSocket client has disconnected from server [{0}].", uri); 97 | } 98 | catch (Exception ex) 99 | { 100 | Console.WriteLine(ex.Message); 101 | } 102 | 103 | Console.ReadKey(); 104 | } 105 | 106 | private static void OnServerConnected(object sender, WebSocketServerConnectedEventArgs e) 107 | { 108 | Console.WriteLine(string.Format("WebSocket server [{0}] has connected.", e.RemoteEndPoint)); 109 | } 110 | 111 | private static void OnServerDisconnected(object sender, WebSocketServerDisconnectedEventArgs e) 112 | { 113 | Console.WriteLine(string.Format("WebSocket server [{0}] has disconnected.", e.RemoteEndPoint)); 114 | } 115 | 116 | private static void OnServerTextReceived(object sender, WebSocketServerTextReceivedEventArgs e) 117 | { 118 | Console.Write(string.Format("WebSocket server [{0}] received Text --> ", e.Client.RemoteEndPoint)); 119 | Console.WriteLine(string.Format("{0}", e.Text)); 120 | } 121 | 122 | private static void OnServerBinaryReceived(object sender, WebSocketServerBinaryReceivedEventArgs e) 123 | { 124 | var text = Encoding.UTF8.GetString(e.Data, e.DataOffset, e.DataLength); 125 | Console.Write(string.Format("WebSocket server [{0}] received Binary --> ", e.Client.RemoteEndPoint)); 126 | if (e.DataLength < 1024 * 1024 * 1) 127 | { 128 | Console.WriteLine(text); 129 | } 130 | else 131 | { 132 | Console.WriteLine("{0} Bytes", e.DataLength); 133 | } 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Tests/Cowboy.WebSockets.TestUnityWebSocketClient/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyTitle("Cowboy.WebSockets.TestUnityWebSocketClient")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("Cowboy.WebSockets.TestUnityWebSocketClient")] 12 | [assembly: AssemblyCopyright("")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // Setting ComVisible to false makes the types in this assembly not visible 17 | // to COM components. If you need to access a type in this assembly from 18 | // COM, set the ComVisible attribute to true on that type. 19 | [assembly: ComVisible(false)] 20 | 21 | // The following GUID is for the ID of the typelib if this project is exposed to COM 22 | [assembly: Guid("d6db086f-ffe4-461a-89bf-343195e09287")] 23 | 24 | // Version information for an assembly consists of the following four values: 25 | // 26 | // Major Version 27 | // Minor Version 28 | // Build Number 29 | // Revision 30 | // 31 | // You can specify all the values or you can default the Build and Revision Numbers 32 | // by using the '*' as shown below: 33 | // [assembly: AssemblyVersion("1.0.*")] 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] 36 | -------------------------------------------------------------------------------- /Cowboy.WebSockets/Tests/Cowboy.WebSockets.TestUnityWebSocketClient/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2015-2017 Dennis Gao 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 all 13 | 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 THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cowboy.WebSockets 2 | Cowboy.WebSockets is a C# based library for building WebSocket services. 3 | 4 | - WebSocket [[RFC6455]](https://tools.ietf.org/html/rfc6455) 5 | - WebSocket Client for [Unity](http://unity3d.com/) 6 | --------------------------------------------------------------------------------