├── .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 |
--------------------------------------------------------------------------------