├── .config └── dotnet-tools.json ├── .editorconfig ├── .gitignore ├── .husky ├── pre-commit └── pre-push ├── CHANGELOG.md ├── LICENSE ├── README.md ├── azure-devops ├── check-port.ps1 ├── publish.yml ├── templates │ └── socket.io-tpl.yml └── test.yml ├── package-lock.json ├── package.json ├── socket.io-client-csharp.sln ├── socket.io-client-csharp.sln.DotSettings ├── src ├── SocketIO.Core │ ├── EngineIO.cs │ ├── EngineIOProtocol.cs │ ├── IMessage.cs │ ├── MessageType.cs │ └── SocketIO.Core.csproj ├── SocketIO.Serializer.Core │ ├── EnumerableExtensions.cs │ ├── ISerializer.cs │ ├── SerializedItem.cs │ └── SocketIO.Serializer.Core.csproj ├── SocketIO.Serializer.MessagePack │ ├── DictionaryExtensions.cs │ ├── GenericMessage.cs │ ├── ObjectDataMessage.cs │ ├── PackMessage.cs │ ├── PackMessageOptions.cs │ ├── PackMessageType.cs │ ├── SocketIO.Serializer.MessagePack.csproj │ ├── SocketIOMessagePackSerializer.cs │ └── TypeExtensions.cs ├── SocketIO.Serializer.NewtonsoftJson │ ├── ByteArrayConverter.cs │ ├── JsonMessage.cs │ ├── NewtonsoftJsonSerializer.cs │ └── SocketIO.Serializer.NewtonsoftJson.csproj ├── SocketIO.Serializer.SystemTextJson │ ├── ByteArrayConverter.cs │ ├── JsonMessage.cs │ ├── SocketIO.Serializer.SystemTextJson.csproj │ └── SystemTextJsonSerializer.cs ├── SocketIOClient.Core │ ├── Messages │ │ ├── ConnectedMessage.cs │ │ ├── DisconnectedMessage.cs │ │ ├── ErrorMessage.cs │ │ ├── IAckMessage.cs │ │ ├── IBinaryAckMessage.cs │ │ ├── IBinaryMessage.cs │ │ ├── IEventMessage.cs │ │ ├── IMessage.cs │ │ ├── INamespaceMessage.cs │ │ ├── MessageType.cs │ │ ├── OpenedMessage.cs │ │ ├── PingMessage.cs │ │ └── PongMessage.cs │ ├── ProtocolMessage.cs │ ├── ProtocolMessageType.cs │ └── SocketIOClient.Core.csproj ├── SocketIOClient.Serializer.NewtonsoftJson │ ├── ByteArrayConverter.cs │ ├── INewtonJsonAckMessage.cs │ ├── INewtonJsonEventMessage.cs │ ├── NewtonJsonAckMessage.cs │ ├── NewtonJsonBinaryAckMessage.cs │ ├── NewtonJsonBinaryEventMessage.cs │ ├── NewtonJsonEngineIO3MessageAdapter.cs │ ├── NewtonJsonEngineIO4MessageAdapter.cs │ ├── NewtonJsonEventMessage.cs │ ├── NewtonJsonSerializer.cs │ └── SocketIOClient.Serializer.NewtonsoftJson.csproj ├── SocketIOClient.Serializer │ ├── BaseJsonSerializer.cs │ ├── Decapsulation │ │ ├── BinaryEventMessageResult.cs │ │ ├── DecapsulationResult.cs │ │ ├── Decapsulator.cs │ │ ├── IDecapsulable.cs │ │ └── MessageResult.cs │ ├── IEngineIOMessageAdapter.cs │ ├── ISerializer.cs │ ├── SerializationResult.cs │ └── SocketIOClient.Serializer.csproj ├── SocketIOClient.Windows7 │ ├── ClientWebSocketManaged.cs │ ├── SocketIOClient.Windows7.csproj │ └── SocketIOClient.Windows7.snk └── SocketIOClient │ ├── DisconnectReason.cs │ ├── EventHandlers.cs │ ├── Exceptions │ └── ConnectionException.cs │ ├── Extensions │ ├── CancellationTokenSourceExtensions.cs │ ├── CancellationTokenSourceWrapper.cs │ ├── DisposableExtensions.cs │ ├── EventHandlerExtensions.cs │ └── SocketIOEventExtensions.cs │ ├── SocketIO.cs │ ├── SocketIOClient.csproj │ ├── SocketIOClient.snk │ ├── SocketIOOptions.cs │ ├── SocketIOResponse.cs │ ├── Transport │ ├── BaseTransport.cs │ ├── Http │ │ ├── DefaultHttpClient.cs │ │ ├── Eio3HttpPollingHandler.cs │ │ ├── Eio4HttpPollingHandler.cs │ │ ├── HttpPollingHandler.cs │ │ ├── HttpTransport.cs │ │ ├── IHttpClient.cs │ │ └── IHttpPollingHandler.cs │ ├── ITransport.cs │ ├── TransportException.cs │ ├── TransportMessageType.cs │ ├── TransportOptions.cs │ ├── TransportProtocol.cs │ └── WebSockets │ │ ├── ChunkSize.cs │ │ ├── DefaultClientWebSocket.cs │ │ ├── IClientWebSocket.cs │ │ ├── WebSocketReceiveResult.cs │ │ ├── WebSocketState.cs │ │ └── WebSocketTransport.cs │ └── V2 │ ├── Core │ └── EngineIO.cs │ ├── DefaultSessionFactory.cs │ ├── ISocketIO.cs │ ├── Infrastructure │ ├── IRandom.cs │ ├── IRetriable.cs │ ├── IStopwatch.cs │ ├── RandomDelayRetryPolicy.cs │ ├── SystemRandom.cs │ └── SystemStopwatch.cs │ ├── Observers │ ├── IMyObservable.cs │ └── IMyObserver.cs │ ├── Protocol │ ├── Http │ │ ├── HttpAdapter.cs │ │ ├── HttpConstants.cs │ │ ├── HttpRequest.cs │ │ ├── IHttpAdapter.cs │ │ ├── IHttpClient.cs │ │ ├── IHttpRequest.cs │ │ ├── IHttpResponse.cs │ │ ├── IHttpResponseObserver.cs │ │ ├── SystemHttpClient.cs │ │ └── SystemHttpResponse.cs │ ├── IProtocolAdapter.cs │ └── WebSocket │ │ ├── IWebSocketAdapter.cs │ │ ├── IWebSocketClient.cs │ │ └── IWebSocketMessage.cs │ ├── Serializer │ └── SystemTextJson │ │ ├── ByteArrayConverter.cs │ │ ├── ISystemJsonAckMessage.cs │ │ ├── ISystemJsonEventMessage.cs │ │ ├── SystemJsonAckMessage.cs │ │ ├── SystemJsonBinaryAckMessage.cs │ │ ├── SystemJsonBinaryEventMessage.cs │ │ ├── SystemJsonEngineIO3MessageAdapter.cs │ │ ├── SystemJsonEngineIO4MessageAdapter.cs │ │ ├── SystemJsonEventMessage.cs │ │ └── SystemJsonSerializer.cs │ ├── Session │ ├── ConnectionFailedException.cs │ ├── EngineIOHttpAdapter │ │ ├── EngineIO3Adapter.cs │ │ ├── EngineIO4Adapter.cs │ │ └── IEngineIOAdapter.cs │ ├── HttpSession.cs │ ├── ISession.cs │ └── SessionOptions.cs │ ├── SocketIO.cs │ ├── SocketIOOptions.cs │ ├── SocketIOResponse.cs │ └── UriConverter │ ├── DefaultUriConverter.cs │ └── IUriConverter.cs └── tests ├── SocketIO.Serializer.MessagePack.Tests ├── MessagePackUserDto.cs ├── MessagePackUserPasswordDto.cs ├── SocketIO.Serializer.MessagePack.Tests.csproj ├── SocketIOMessagePackSerializerTests.cs └── Usings.cs ├── SocketIO.Serializer.NewtonsoftJson.Tests ├── Depth.cs ├── NewtonsoftJsonSerializerTests.cs ├── SocketIO.Serializer.NewtonsoftJson.Tests.csproj └── Usings.cs ├── SocketIO.Serializer.SystemTextJson.Tests ├── SocketIO.Serializer.SystemTextJson.Tests.csproj ├── SystemTextJsonSerializerTests.cs └── Usings.cs ├── SocketIO.Serializer.Tests.Models ├── Address.cs ├── FileDto.cs ├── SocketIO.Serializer.Tests.Models.csproj ├── User.cs └── UserPasswordDto.cs ├── SocketIOClient.IntegrationTests.Net472 ├── Properties │ └── AssemblyInfo.cs ├── SocketIOClient.IntegrationTests.Net472.csproj ├── V4WebSocketTests.cs ├── app.config └── packages.config ├── SocketIOClient.IntegrationTests ├── AutoReconnectTests.cs ├── MessagePackTests.cs ├── SocketIOClient.IntegrationTests.csproj ├── SocketIOTests.cs ├── SystemTextJsonTests.cs ├── Transport │ └── WebSockets │ │ ├── SystemNetWebSocketsClientWebSocketTests.cs │ │ ├── TestHelper.cs │ │ ├── WebSocketServer.cs │ │ └── WebSocketTransportTests.cs ├── Utils │ └── SerializerHelper.cs ├── V2 │ └── SocketIOTests.cs ├── V2HttpMpNspTests.cs ├── V2HttpMpTests.cs ├── V2HttpStjAuTests.cs ├── V2HttpStjNspAuTests.cs ├── V2HttpStjNspTests.cs ├── V2HttpStjTests.cs ├── V2WsMpNspTests.cs ├── V2WsMpTests.cs ├── V2WsStjNspTests.cs ├── V2WsStjTests.cs ├── V4HttpMpNspTests.cs ├── V4HttpMpTests.cs ├── V4HttpStjAuTests.cs ├── V4HttpStjNspAuTests.cs ├── V4HttpStjNspTests.cs ├── V4HttpStjTests.cs ├── V4WsMpNspTests.cs ├── V4WsMpTests.cs ├── V4WsStjNspTests.cs └── V4WsStjTests.cs ├── SocketIOClient.Serializer.NewtonsoftJson.Tests ├── NewtonJsonEngineIO3MessageAdapterTests.cs ├── NewtonJsonEngineIO4MessageAdapterTests.cs ├── NewtonJsonSerializerTests.cs └── SocketIOClient.Serializer.NewtonsoftJson.Tests.csproj ├── SocketIOClient.Serializer.Tests ├── Decapsulation │ └── DecapsulatorTests.cs └── SocketIOClient.Serializer.Tests.csproj ├── SocketIOClient.UnitTests ├── ArchUnit │ └── ArchitectureTests.cs ├── DefaultUriConverterTest.cs ├── SocketIOClient.UnitTests.csproj └── V2 │ ├── DefaultSessionFactoryTests.cs │ ├── Infrastructure │ └── RandomDelayRetryPolicyTests.cs │ ├── Protocol │ └── Http │ │ ├── HttpAdapterTests.cs │ │ ├── HttpRequestTests.cs │ │ ├── SystemHttpClientTests.cs │ │ └── SystemHttpResponseTests.cs │ ├── Serializer │ ├── SystemTextJson │ │ ├── SystemJsonEngineIO3MessageAdapterTests.cs │ │ ├── SystemJsonEngineIO4MessageAdapterTests.cs │ │ └── SystemJsonSerializerTests.cs │ └── TestFile.cs │ ├── Session │ ├── EngineIOHttpAdapter │ │ ├── EngineIO3AdapterTests.cs │ │ └── EngineIO4AdapterTests.cs │ └── HttpSessionTests.cs │ ├── SocketIOOptionsTests.cs │ └── SocketIOTests.cs └── socket.io ├── .dockerignore ├── Dockerfile ├── Dockerfile-Test ├── package-lock.json ├── package.json ├── v2 ├── index.js ├── package-lock.json ├── package.json └── template.js └── v4 ├── cert ├── cert.crt └── private.key ├── index.js ├── package-lock.json ├── package.json └── template.js /.config/dotnet-tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "isRoot": true, 4 | "tools": { 5 | "dotnet-format": { 6 | "version": "5.1.250801", 7 | "commands": [ 8 | "dotnet-format" 9 | ] 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | trim_trailing_whitespace = true -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | dotnet format --verify-no-changes -------------------------------------------------------------------------------- /.husky/pre-push: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | dotnet test --no-restore tests/SocketIOClient.UnitTests 5 | dotnet test --no-restore tests/SocketIOClient.Serializer.Tests 6 | dotnet test --no-restore tests/SocketIOClient.Serializer.NewtonsoftJson.Tests -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 HeroWong 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 | -------------------------------------------------------------------------------- /azure-devops/check-port.ps1: -------------------------------------------------------------------------------- 1 | $ports = 11400, 11401, 11402, 11403, 11404, 11410, 11411, 11412, 11413, 11414, 11200, 11201, 11202, 11203, 11210, 11211, 11212, 11213 2 | $retry = 10 3 | $delay = 3 4 | function Test-SocketIOConnection($Port) { 5 | for ($i = 1; $i -le $retry; $i++) { 6 | $result = Test-Connection -TargetName 127.0.0.1 -TcpPort $Port 7 | if ($result) { 8 | Write-Host "$Port opened" 9 | return $true 10 | } 11 | else { 12 | Write-Host "$Port not open, will retry($i) after $($delay)s ..." 13 | Start-Sleep -Seconds $delay 14 | } 15 | } 16 | Write-Host "$port is not open after $retry retries." 17 | return $false 18 | } 19 | 20 | foreach ($port in $ports) { 21 | $result = Test-SocketIOConnection -Port $port 22 | if (!$result) { 23 | throw "All socket.io server ports must be open, but port $port is closed" 24 | } 25 | } -------------------------------------------------------------------------------- /azure-devops/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish ${{ parameters.package }} 2 | trigger: none 3 | 4 | pool: 5 | vmImage: ubuntu-latest 6 | 7 | parameters: 8 | - name: package 9 | displayName: Package 10 | type: string 11 | values: 12 | - SocketIO.Core 13 | - SocketIO.Serializer.Core 14 | - SocketIOClient 15 | - SocketIOClient.Windows7 16 | - SocketIO.Serializer.MessagePack 17 | - SocketIO.Serializer.NewtonsoftJson 18 | - SocketIO.Serializer.SystemTextJson 19 | 20 | jobs: 21 | - job: 22 | displayName: Publish ${{ parameters.package }} 23 | steps: 24 | - pwsh: | 25 | dotnet pack -c Release --output $(Build.ArtifactStagingDirectory) 26 | $packageName = Get-ChildItem $(Build.ArtifactStagingDirectory)/${{ parameters.package }}.*.nupkg | Select-Object -First 1 -ExpandProperty Name 27 | Write-Host "##vso[task.setvariable variable=packageName;]$packageName" 28 | displayName: dotnet pack 29 | workingDirectory: $(System.DefaultWorkingDirectory)/src/${{ parameters.package }} 30 | - pwsh: | 31 | dotnet nuget push $(packageName) -k ${env:NUGET_APIKEY} -s nuget.org 32 | env: 33 | NUGET_APIKEY: $(NUGET_APIKEY) 34 | displayName: Publish ${{ parameters.package }} 35 | workingDirectory: $(Build.ArtifactStagingDirectory) 36 | -------------------------------------------------------------------------------- /azure-devops/templates/socket.io-tpl.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - pwsh: | 3 | npm install pm2 -g 4 | npm run install-all 5 | pm2 start v2/index.js --name v2/index.js 6 | pm2 start v4/index.js --name v4/index.js 7 | displayName: Run socket.io server 8 | workingDirectory: $(System.DefaultWorkingDirectory)/tests/socket.io 9 | - pwsh: $(System.DefaultWorkingDirectory)/azure-devops/check-port.ps1 10 | displayName: Check ports of socket.io servers 11 | failOnStderr: true -------------------------------------------------------------------------------- /azure-devops/test.yml: -------------------------------------------------------------------------------- 1 | name: Unit Tests and Integration Tests 2 | 3 | trigger: 4 | - master 5 | 6 | pool: 7 | vmImage: ubuntu-latest 8 | 9 | stages: 10 | - stage: CheckFormat 11 | jobs: 12 | - job: 13 | displayName: Check format 14 | steps: 15 | - script: | 16 | dotnet tool restore 17 | dotnet format --verify-no-changes 18 | displayName: Check format 19 | - stage: NET_Tests 20 | dependsOn: CheckFormat 21 | jobs: 22 | - job: 23 | displayName: Unit tests 24 | steps: 25 | - task: DotNetCoreCLI@2 26 | displayName: SocketIOClient.UnitTests 27 | inputs: 28 | workingDirectory: $(System.DefaultWorkingDirectory)/tests/SocketIOClient.UnitTests 29 | command: test 30 | - task: DotNetCoreCLI@2 31 | displayName: SocketIO.Serializer.SystemTextJson.Tests 32 | inputs: 33 | workingDirectory: $(System.DefaultWorkingDirectory)/tests/SocketIO.Serializer.SystemTextJson.Tests 34 | command: test 35 | - task: DotNetCoreCLI@2 36 | displayName: SocketIO.Serializer.NewtonsoftJson.Tests 37 | inputs: 38 | workingDirectory: $(System.DefaultWorkingDirectory)/tests/SocketIO.Serializer.NewtonsoftJson.Tests 39 | command: test 40 | - task: DotNetCoreCLI@2 41 | displayName: SocketIO.Serializer.MessagePack.Tests 42 | inputs: 43 | workingDirectory: $(System.DefaultWorkingDirectory)/tests/SocketIO.Serializer.MessagePack.Tests 44 | command: test 45 | # - job: 46 | # displayName: Integration tests 47 | # steps: 48 | # - template: templates/socket.io-tpl.yml 49 | # - task: DotNetCoreCLI@2 50 | # displayName: Integration tests 51 | # inputs: 52 | # workingDirectory: $(System.DefaultWorkingDirectory)/tests/SocketIOClient.IntegrationTests 53 | # command: test 54 | # - stage: NET_Framework_Tests 55 | # dependsOn: CheckFormat 56 | # pool: 57 | # vmImage: windows-latest 58 | # jobs: 59 | # - job: 60 | # displayName: Integration tests for .Net Framework 61 | # steps: 62 | # - template: templates/socket.io-tpl.yml 63 | # - pwsh: dotnet restore 64 | # displayName: dotnet restore 65 | # workingDirectory: $(System.DefaultWorkingDirectory) 66 | # - task: MSBuild@1 67 | # inputs: 68 | # solution: $(System.DefaultWorkingDirectory)/socket.io-client-csharp.sln 69 | # restoreNugetPackages: true 70 | # - task: VSTest@2 71 | # inputs: 72 | # testAssemblyVer2: SocketIOClient.IntegrationTests.Net472.dll 73 | # searchFolder: $(System.DefaultWorkingDirectory)/tests/SocketIOClient.IntegrationTests.Net472/bin/Debug -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "socket.io-client-csharp", 3 | "lockfileVersion": 3, 4 | "requires": true, 5 | "packages": { 6 | "": { 7 | "devDependencies": { 8 | "husky": "^9.1.7" 9 | } 10 | }, 11 | "node_modules/husky": { 12 | "version": "9.1.7", 13 | "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz", 14 | "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==", 15 | "dev": true, 16 | "bin": { 17 | "husky": "bin.js" 18 | }, 19 | "engines": { 20 | "node": ">=18" 21 | }, 22 | "funding": { 23 | "url": "https://github.com/sponsors/typicode" 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "husky": "^8.0.0" 4 | }, 5 | "scripts": { 6 | "prepare": "husky install" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /socket.io-client-csharp.sln.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | IO 3 | True 4 | True 5 | True 6 | True -------------------------------------------------------------------------------- /src/SocketIO.Core/EngineIO.cs: -------------------------------------------------------------------------------- 1 | namespace SocketIO.Core 2 | { 3 | public enum EngineIO 4 | { 5 | V3 = 3, 6 | V4 = 4 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/SocketIO.Core/EngineIOProtocol.cs: -------------------------------------------------------------------------------- 1 | namespace SocketIO.Core 2 | { 3 | public enum EngineIOProtocol 4 | { 5 | Open, 6 | Close, 7 | Ping, 8 | Pong, 9 | Message, 10 | Upgrade, 11 | Noop 12 | } 13 | } -------------------------------------------------------------------------------- /src/SocketIO.Core/IMessage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace SocketIO.Core 5 | { 6 | public interface IMessage 7 | { 8 | MessageType Type { get; } 9 | string Sid { get; set; } 10 | int PingInterval { get; set; } 11 | int PingTimeout { get; set; } 12 | List Upgrades { get; set; } 13 | int BinaryCount { get; set; } 14 | string Namespace { get; set; } 15 | TimeSpan Duration { get; set; } 16 | int Id { get; set; } 17 | 18 | string Event { get; } 19 | string Error { get; set; } 20 | 21 | // TODO: move this to sub class 22 | string ReceivedText { get; set; } 23 | List ReceivedBinary { get; set; } 24 | } 25 | } -------------------------------------------------------------------------------- /src/SocketIO.Core/MessageType.cs: -------------------------------------------------------------------------------- 1 | namespace SocketIO.Core 2 | { 3 | public enum MessageType 4 | { 5 | Opened, 6 | Ping = 2, 7 | Pong, 8 | Connected = 40, 9 | Disconnected, 10 | Event, 11 | Ack, 12 | Error, 13 | Binary, 14 | BinaryAck, 15 | } 16 | } -------------------------------------------------------------------------------- /src/SocketIO.Core/SocketIO.Core.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | true 6 | ../SocketIOClient/SocketIOClient.snk 7 | 3.1.2 8 | https://github.com/doghappy/socket.io-client-csharp 9 | doghappy 10 | https://github.com/doghappy/socket.io-client-csharp 11 | github 12 | MIT 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/SocketIO.Serializer.Core/EnumerableExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace SocketIO.Serializer.Core; 5 | 6 | public static class EnumerableExtensions 7 | { 8 | public static bool AnyBinary(this IEnumerable serializedItems) 9 | { 10 | return serializedItems.Any(x => x.Type == SerializedMessageType.Binary); 11 | } 12 | 13 | public static string FirstText(this IEnumerable serializedItems) 14 | { 15 | return serializedItems.First(x => x.Type == SerializedMessageType.Text).Text; 16 | } 17 | 18 | public static List AllBinary(this IEnumerable serializedItems) 19 | { 20 | return serializedItems 21 | .Where(x => x.Type == SerializedMessageType.Binary) 22 | .Select(x => x.Binary) 23 | .ToList(); 24 | } 25 | } -------------------------------------------------------------------------------- /src/SocketIO.Serializer.Core/ISerializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using SocketIO.Core; 4 | 5 | namespace SocketIO.Serializer.Core 6 | { 7 | public interface ISerializer 8 | { 9 | // string Serialize(object data); 10 | List Serialize(EngineIO eio, string eventName, int packetId, string ns, object[] data); 11 | List Serialize(EngineIO eio, int packetId, string nsp, object[] data); 12 | List Serialize(EngineIO eio, string eventName, string nsp, object[] data); 13 | T Deserialize(IMessage message, int index); 14 | IMessage Deserialize(EngineIO eio, string text); 15 | IMessage Deserialize(EngineIO eio, byte[] bytes); 16 | string MessageToJson(IMessage message); 17 | IMessage NewMessage(MessageType type); 18 | 19 | SerializedItem SerializeConnectedMessage(EngineIO eio, string ns, object auth, IEnumerable> queries); 20 | 21 | SerializedItem SerializePingMessage(); 22 | SerializedItem SerializePongMessage(); 23 | SerializedItem SerializeUpgradeMessage(); 24 | } 25 | } -------------------------------------------------------------------------------- /src/SocketIO.Serializer.Core/SerializedItem.cs: -------------------------------------------------------------------------------- 1 | namespace SocketIO.Serializer.Core 2 | { 3 | public class SerializedItem 4 | { 5 | public SerializedMessageType Type { get; set; } 6 | public string Text { get; set; } 7 | public byte[] Binary { get; set; } 8 | } 9 | 10 | public enum SerializedMessageType 11 | { 12 | Text, 13 | Binary, 14 | } 15 | } -------------------------------------------------------------------------------- /src/SocketIO.Serializer.Core/SocketIO.Serializer.Core.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | default 6 | true 7 | ../SocketIOClient/SocketIOClient.snk 8 | 3.1.2 9 | doghappy 10 | https://github.com/doghappy/socket.io-client-csharp 11 | https://github.com/doghappy/socket.io-client-csharp 12 | github 13 | MIT 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/SocketIO.Serializer.MessagePack/DictionaryExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using MessagePack; 6 | 7 | namespace SocketIO.Serializer.MessagePack; 8 | 9 | static class DictionaryExtensions 10 | { 11 | public static T ToObject(this IDictionary dictionary) 12 | { 13 | var type = typeof(T); 14 | var obj = dictionary.ToObject(type); 15 | return (T)obj; 16 | } 17 | 18 | public static object ToObject(this IDictionary dictionary, Type type) 19 | { 20 | var obj = Activator.CreateInstance(type); 21 | 22 | var props = type.GetProperties(BindingFlags.Instance | BindingFlags.Public); 23 | foreach (var prop in props) 24 | { 25 | if (!dictionary.TryGetValue(GetKey(prop), out var value)) 26 | { 27 | continue; 28 | } 29 | 30 | if (prop.PropertyType == typeof(byte[]) && value is string base64) 31 | { 32 | var bytes = Convert.FromBase64String(base64); 33 | prop.SetValue(obj, bytes); 34 | continue; 35 | } 36 | 37 | if (!prop.PropertyType.IsArray && !prop.PropertyType.IsSimpleType()) 38 | { 39 | var dic = ((IDictionary)value).ToObject(prop.PropertyType); 40 | prop.SetValue(obj, dic); 41 | continue; 42 | } 43 | 44 | prop.SetValue(obj, value); 45 | } 46 | 47 | return obj; 48 | } 49 | 50 | private static string GetKey(MemberInfo info) 51 | { 52 | var attribute = info.GetCustomAttributes(typeof(KeyAttribute), true).FirstOrDefault(); 53 | return (attribute as KeyAttribute)?.StringKey ?? info.Name; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/SocketIO.Serializer.MessagePack/GenericMessage.cs: -------------------------------------------------------------------------------- 1 | using MessagePack; 2 | 3 | namespace SocketIO.Serializer.MessagePack; 4 | 5 | [MessagePackObject] 6 | public class GenericMessage 7 | { 8 | [Key("type")] 9 | public int Type => PackMessageType.Connected; 10 | 11 | [Key("data")] 12 | public object Data { get; set; } 13 | 14 | [Key("nsp")] 15 | public string Nsp { get; set; } 16 | } -------------------------------------------------------------------------------- /src/SocketIO.Serializer.MessagePack/ObjectDataMessage.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using MessagePack; 3 | 4 | namespace SocketIO.Serializer.MessagePack; 5 | 6 | [MessagePackObject] 7 | public class ObjectDataMessage 8 | { 9 | [Key("type")] 10 | public int Type { get; set; } 11 | 12 | [Key("sid")] 13 | public string Sid { get; set; } 14 | 15 | [Key("pingInterval")] 16 | public int PingInterval { get; set; } 17 | 18 | [Key("pingTimeout")] 19 | public int PingTimeout { get; set; } 20 | 21 | [Key("upgrades")] 22 | public List Upgrades { get; set; } 23 | 24 | [Key("nsp")] 25 | public string Namespace { get; set; } 26 | 27 | [Key("data")] 28 | public object Data { get; set; } 29 | 30 | [Key("id")] 31 | public int Id { get; set; } = -1; 32 | } 33 | 34 | // [MessagePackObject] 35 | // public class ObjectDataMessage2 36 | // { 37 | // [Key("type")] 38 | // public int Type { get; set; } 39 | // 40 | // [Key("data")] 41 | // public ObjectDataMessageData Data { get; set; } 42 | // } 43 | // 44 | // [MessagePackObject] 45 | // public class ObjectDataMessageData 46 | // { 47 | // [Key("message")] 48 | // public string Message { get; set; } 49 | // 50 | // [Key("data")] 51 | // public object Data { get; set; } 52 | // } 53 | // 54 | // [MessagePackObject] 55 | // public class ObjectDataMessageDataData 56 | // { 57 | // [Key("type")] 58 | // public int Type { get; set; } 59 | // 60 | // [Key("buffer")] 61 | // public byte[] Buffer { get; set; } 62 | // } -------------------------------------------------------------------------------- /src/SocketIO.Serializer.MessagePack/PackMessage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using MessagePack; 5 | using SocketIO.Core; 6 | 7 | namespace SocketIO.Serializer.MessagePack 8 | { 9 | [MessagePackObject] 10 | public class PackMessage : IMessage 11 | { 12 | public PackMessage() 13 | { 14 | Options = new PackMessageOptions 15 | { 16 | Compress = true 17 | }; 18 | } 19 | 20 | public PackMessage(MessageType type) : this() 21 | { 22 | Type = type; 23 | Id = -1; 24 | } 25 | 26 | [Key("type")] 27 | public int RawType { get; set; } 28 | 29 | [Key("data")] 30 | public object Data { get; set; } 31 | 32 | [Key("options")] 33 | public PackMessageOptions Options { get; } 34 | 35 | [Key("id")] 36 | public int Id { get; set; } 37 | 38 | [Key("nsp")] 39 | public string Namespace { get; set; } 40 | 41 | [IgnoreMember] 42 | public MessageType Type { get; } 43 | 44 | [IgnoreMember] 45 | public string Sid { get; set; } 46 | 47 | [IgnoreMember] 48 | public int PingInterval { get; set; } 49 | 50 | [IgnoreMember] 51 | public int PingTimeout { get; set; } 52 | 53 | [IgnoreMember] 54 | public List Upgrades { get; set; } 55 | 56 | [IgnoreMember] 57 | public int BinaryCount { get; set; } 58 | 59 | [IgnoreMember] 60 | public TimeSpan Duration { get; set; } 61 | 62 | [IgnoreMember] 63 | public string Error { get; set; } 64 | 65 | [IgnoreMember] 66 | public string ReceivedText { get; set; } 67 | 68 | [IgnoreMember] 69 | public List ReceivedBinary { get; set; } 70 | 71 | private bool _parsed; 72 | private List _dataList; 73 | 74 | [IgnoreMember] 75 | public List DataList 76 | { 77 | get 78 | { 79 | Parse(); 80 | return _dataList; 81 | } 82 | } 83 | 84 | private string _event; 85 | 86 | [IgnoreMember] 87 | public string Event 88 | { 89 | get 90 | { 91 | Parse(); 92 | return _event; 93 | } 94 | // set => _event = value; 95 | } 96 | 97 | private void Parse() 98 | { 99 | if (_parsed) return; 100 | _dataList = new List(); 101 | if (Data is IEnumerable) 102 | { 103 | _dataList.AddRange((IEnumerable)Data); 104 | } 105 | else if (Data is not null) 106 | { 107 | _dataList.Add(Data); 108 | } 109 | 110 | if (Type is MessageType.Event or MessageType.Binary) 111 | { 112 | _event = _dataList[0].ToString(); 113 | _dataList.RemoveAt(0); 114 | } 115 | 116 | _parsed = true; 117 | } 118 | } 119 | } -------------------------------------------------------------------------------- /src/SocketIO.Serializer.MessagePack/PackMessageOptions.cs: -------------------------------------------------------------------------------- 1 | using MessagePack; 2 | 3 | namespace SocketIO.Serializer.MessagePack; 4 | 5 | [MessagePackObject] 6 | public class PackMessageOptions 7 | { 8 | [Key("compress")] 9 | public bool Compress { get; set; } 10 | } -------------------------------------------------------------------------------- /src/SocketIO.Serializer.MessagePack/PackMessageType.cs: -------------------------------------------------------------------------------- 1 | namespace SocketIO.Serializer.MessagePack; 2 | 3 | public static class PackMessageType 4 | { 5 | public const int Connected = 0; 6 | public const int Disconnected = 1; 7 | public const int Event = 2; 8 | public const int Ack = 3; 9 | public const int Error = 4; 10 | public const int BinaryEvent = 5; 11 | public const int BinaryAck = 6; 12 | } -------------------------------------------------------------------------------- /src/SocketIO.Serializer.MessagePack/SocketIO.Serializer.MessagePack.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | latest 6 | true 7 | ../SocketIOClient/SocketIOClient.snk 8 | 3.1.2 9 | doghappy 10 | https://github.com/doghappy/socket.io-client-csharp 11 | https://github.com/doghappy/socket.io-client-csharp 12 | github 13 | MIT 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/SocketIO.Serializer.MessagePack/TypeExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | 4 | namespace SocketIO.Serializer.MessagePack; 5 | 6 | public static class TypeExtensions 7 | { 8 | private static readonly ConcurrentDictionary IsSimpleTypeCache = new(); 9 | 10 | public static bool IsSimpleType(this Type type) 11 | { 12 | return IsSimpleTypeCache.GetOrAdd(type, t => 13 | type.IsPrimitive || 14 | type.IsEnum || 15 | type == typeof(string) || 16 | type == typeof(decimal) || 17 | type == typeof(DateTime) || 18 | // type == typeof(DateOnly) || 19 | // type == typeof(TimeOnly) || 20 | type == typeof(DateTimeOffset) || 21 | type == typeof(TimeSpan) || 22 | type == typeof(Guid) || 23 | IsNullableSimpleType(type)); 24 | 25 | static bool IsNullableSimpleType(Type t) 26 | { 27 | var underlyingType = Nullable.GetUnderlyingType(t); 28 | return underlyingType != null && IsSimpleType(underlyingType); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /src/SocketIO.Serializer.NewtonsoftJson/ByteArrayConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Newtonsoft.Json; 5 | 6 | namespace SocketIO.Serializer.NewtonsoftJson 7 | { 8 | internal class ByteArrayConverter : JsonConverter 9 | { 10 | public ByteArrayConverter() 11 | { 12 | Bytes = new List(); 13 | } 14 | 15 | public List Bytes { get; } 16 | 17 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 18 | { 19 | var source = (byte[])value; 20 | Bytes.Add(source.ToArray()); 21 | writer.WriteStartObject(); 22 | writer.WritePropertyName("_placeholder"); 23 | writer.WriteValue(true); 24 | writer.WritePropertyName("num"); 25 | writer.WriteValue(Bytes.Count - 1); 26 | writer.WriteEndObject(); 27 | } 28 | 29 | public override object ReadJson(JsonReader reader, Type objectType, object existingValue, 30 | JsonSerializer serializer) 31 | { 32 | if (reader.TokenType != JsonToken.StartObject) 33 | return null; 34 | reader.Read(); 35 | if (reader.TokenType != JsonToken.PropertyName || reader.Value?.ToString() != "_placeholder") 36 | return null; 37 | reader.Read(); 38 | if (reader.TokenType != JsonToken.Boolean || !(bool)reader.Value) 39 | return null; 40 | reader.Read(); 41 | if (reader.TokenType != JsonToken.PropertyName || reader.Value?.ToString() != "num") 42 | return null; 43 | reader.Read(); 44 | if (reader.Value == null) 45 | return null; 46 | if (!int.TryParse(reader.Value.ToString(), out var num)) 47 | return null; 48 | var bytes = Bytes[num]; 49 | reader.Read(); 50 | return bytes; 51 | } 52 | 53 | public override bool CanConvert(Type objectType) 54 | { 55 | return objectType == typeof(byte[]); 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /src/SocketIO.Serializer.NewtonsoftJson/JsonMessage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Newtonsoft.Json.Linq; 4 | using SocketIO.Core; 5 | 6 | namespace SocketIO.Serializer.NewtonsoftJson 7 | { 8 | public class JsonMessage : IMessage 9 | { 10 | public JsonMessage(MessageType type) 11 | { 12 | Type = type; 13 | Id = -1; 14 | } 15 | 16 | public MessageType Type { get; } 17 | public string Sid { get; set; } 18 | public int PingInterval { get; set; } 19 | public int PingTimeout { get; set; } 20 | public List Upgrades { get; set; } 21 | public int BinaryCount { get; set; } 22 | public string Namespace { get; set; } 23 | public TimeSpan Duration { get; set; } 24 | public int Id { get; set; } 25 | 26 | public string Error { get; set; } 27 | public string ReceivedText { get; set; } 28 | public List ReceivedBinary { get; set; } 29 | 30 | private bool _parsed; 31 | 32 | private JArray _jsonArray; 33 | 34 | public JArray JsonArray 35 | { 36 | get 37 | { 38 | Parse(); 39 | return _jsonArray; 40 | } 41 | } 42 | 43 | private string _event; 44 | 45 | public string Event 46 | { 47 | get 48 | { 49 | Parse(); 50 | return _event; 51 | } 52 | set => _event = value; 53 | } 54 | 55 | private void Parse() 56 | { 57 | if (_parsed) return; 58 | var jsonArray = JArray.Parse(ReceivedText); 59 | SetEvent(jsonArray); 60 | _jsonArray = jsonArray; 61 | _parsed = true; 62 | } 63 | 64 | private void SetEvent(JArray jsonArray) 65 | { 66 | if (Type != MessageType.Event && Type != MessageType.Binary) 67 | return; 68 | 69 | if (jsonArray.Count < 1) 70 | { 71 | throw new ArgumentException("Cannot get event name from an empty json array"); 72 | } 73 | 74 | if (jsonArray[0] is null) 75 | { 76 | throw new ArgumentException("Event name is null"); 77 | } 78 | 79 | Event = jsonArray[0].Value(); 80 | jsonArray.RemoveAt(0); 81 | } 82 | } 83 | } -------------------------------------------------------------------------------- /src/SocketIO.Serializer.NewtonsoftJson/SocketIO.Serializer.NewtonsoftJson.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | latest 6 | true 7 | ../SocketIOClient/SocketIOClient.snk 8 | 3.1.2 9 | doghappy 10 | https://github.com/doghappy/socket.io-client-csharp 11 | https://github.com/doghappy/socket.io-client-csharp 12 | github 13 | MIT 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/SocketIO.Serializer.SystemTextJson/ByteArrayConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text.Json; 4 | using System.Text.Json.Serialization; 5 | 6 | namespace SocketIO.Serializer.SystemTextJson 7 | { 8 | internal class ByteArrayConverter : JsonConverter 9 | { 10 | public ByteArrayConverter() 11 | { 12 | Bytes = new List(); 13 | } 14 | 15 | public List Bytes { get; } 16 | 17 | public override byte[] Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) 18 | { 19 | if (reader.TokenType != JsonTokenType.StartObject) return null; 20 | reader.Read(); 21 | if (reader.TokenType != JsonTokenType.PropertyName || reader.GetString() != "_placeholder") return null; 22 | reader.Read(); 23 | if (reader.TokenType != JsonTokenType.True || !reader.GetBoolean()) return null; 24 | reader.Read(); 25 | if (reader.TokenType != JsonTokenType.PropertyName || reader.GetString() != "num") return null; 26 | reader.Read(); 27 | var num = reader.GetInt32(); 28 | var bytes = Bytes[num]; 29 | reader.Read(); 30 | return bytes; 31 | } 32 | 33 | public override void Write(Utf8JsonWriter writer, byte[] value, JsonSerializerOptions options) 34 | { 35 | Bytes.Add(value); 36 | writer.WriteStartObject(); 37 | writer.WritePropertyName("_placeholder"); 38 | writer.WriteBooleanValue(true); 39 | writer.WritePropertyName("num"); 40 | writer.WriteNumberValue(Bytes.Count - 1); 41 | writer.WriteEndObject(); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/SocketIO.Serializer.SystemTextJson/JsonMessage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text.Json.Nodes; 4 | using SocketIO.Core; 5 | 6 | namespace SocketIO.Serializer.SystemTextJson 7 | { 8 | public class JsonMessage : IMessage 9 | { 10 | public JsonMessage(MessageType type) 11 | { 12 | Type = type; 13 | Id = -1; 14 | } 15 | 16 | public MessageType Type { get; } 17 | public string Sid { get; set; } 18 | public int PingInterval { get; set; } 19 | public int PingTimeout { get; set; } 20 | public List Upgrades { get; set; } 21 | public int BinaryCount { get; set; } 22 | public string Namespace { get; set; } 23 | public TimeSpan Duration { get; set; } 24 | public int Id { get; set; } 25 | 26 | public string Error { get; set; } 27 | public string ReceivedText { get; set; } 28 | public List ReceivedBinary { get; set; } 29 | 30 | private bool _parsed; 31 | 32 | private JsonArray _jsonArray; 33 | 34 | public JsonArray JsonArray 35 | { 36 | get 37 | { 38 | Parse(); 39 | return _jsonArray; 40 | } 41 | } 42 | 43 | private string _event; 44 | 45 | public string Event 46 | { 47 | get 48 | { 49 | Parse(); 50 | return _event; 51 | } 52 | set => _event = value; 53 | } 54 | 55 | private void Parse() 56 | { 57 | if (_parsed) return; 58 | var jsonNode = JsonNode.Parse(ReceivedText); 59 | if (jsonNode is null) 60 | { 61 | throw new ArgumentException($"Cannot parse '{ReceivedText}' to JsonNode"); 62 | } 63 | 64 | var jsonArray = jsonNode.AsArray(); 65 | SetEvent(jsonArray); 66 | _jsonArray = jsonArray; 67 | _parsed = true; 68 | } 69 | 70 | private void SetEvent(JsonArray jsonArray) 71 | { 72 | if (Type != MessageType.Event && Type != MessageType.Binary) 73 | return; 74 | 75 | if (jsonArray.Count < 1) 76 | { 77 | throw new ArgumentException("Cannot get event name from an empty json array"); 78 | } 79 | 80 | if (jsonArray[0] is null) 81 | { 82 | throw new ArgumentException("Event name is null"); 83 | } 84 | 85 | Event = jsonArray[0].GetValue(); 86 | jsonArray.RemoveAt(0); 87 | } 88 | } 89 | } -------------------------------------------------------------------------------- /src/SocketIO.Serializer.SystemTextJson/SocketIO.Serializer.SystemTextJson.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | latest 6 | true 7 | ../SocketIOClient/SocketIOClient.snk 8 | 3.1.2 9 | doghappy 10 | https://github.com/doghappy/socket.io-client-csharp 11 | https://github.com/doghappy/socket.io-client-csharp 12 | github 13 | MIT 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/SocketIOClient.Core/Messages/ConnectedMessage.cs: -------------------------------------------------------------------------------- 1 | namespace SocketIOClient.Core.Messages; 2 | 3 | public class ConnectedMessage : INamespaceMessage 4 | { 5 | public MessageType Type => MessageType.Connected; 6 | public string Namespace { get; set; } 7 | public string Sid { get; set; } 8 | } -------------------------------------------------------------------------------- /src/SocketIOClient.Core/Messages/DisconnectedMessage.cs: -------------------------------------------------------------------------------- 1 | namespace SocketIOClient.Core.Messages; 2 | 3 | public class DisconnectedMessage : IMessage 4 | { 5 | public MessageType Type => MessageType.Disconnected; 6 | public string Namespace { get; set; } 7 | } -------------------------------------------------------------------------------- /src/SocketIOClient.Core/Messages/ErrorMessage.cs: -------------------------------------------------------------------------------- 1 | namespace SocketIOClient.Core.Messages; 2 | 3 | public class ErrorMessage : INamespaceMessage 4 | { 5 | public MessageType Type => MessageType.Error; 6 | public string Namespace { get; set; } 7 | public string Error { get; set; } 8 | } -------------------------------------------------------------------------------- /src/SocketIOClient.Core/Messages/IAckMessage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SocketIOClient.Core.Messages; 4 | 5 | public interface IAckMessage : IMessage 6 | { 7 | string Namespace { get; set; } 8 | public int Id { get; set; } 9 | 10 | T GetDataValue(int index); 11 | object GetDataValue(Type type, int index); 12 | } -------------------------------------------------------------------------------- /src/SocketIOClient.Core/Messages/IBinaryAckMessage.cs: -------------------------------------------------------------------------------- 1 | namespace SocketIOClient.Core.Messages; 2 | 3 | public interface IBinaryAckMessage : IBinaryMessage, IAckMessage 4 | { 5 | } -------------------------------------------------------------------------------- /src/SocketIOClient.Core/Messages/IBinaryMessage.cs: -------------------------------------------------------------------------------- 1 | namespace SocketIOClient.Core.Messages; 2 | 3 | public interface IBinaryMessage : IMessage 4 | { 5 | bool ReadyDelivery { get; } 6 | void Add(byte[] bytes); 7 | } -------------------------------------------------------------------------------- /src/SocketIOClient.Core/Messages/IEventMessage.cs: -------------------------------------------------------------------------------- 1 | namespace SocketIOClient.Core.Messages; 2 | 3 | public interface IEventMessage : IAckMessage 4 | { 5 | public string Event { get; set; } 6 | } -------------------------------------------------------------------------------- /src/SocketIOClient.Core/Messages/IMessage.cs: -------------------------------------------------------------------------------- 1 | namespace SocketIOClient.Core.Messages 2 | { 3 | public interface IMessage 4 | { 5 | MessageType Type { get; } 6 | } 7 | } -------------------------------------------------------------------------------- /src/SocketIOClient.Core/Messages/INamespaceMessage.cs: -------------------------------------------------------------------------------- 1 | namespace SocketIOClient.Core.Messages; 2 | 3 | public interface INamespaceMessage : IMessage 4 | { 5 | string Namespace { get; set; } 6 | } -------------------------------------------------------------------------------- /src/SocketIOClient.Core/Messages/MessageType.cs: -------------------------------------------------------------------------------- 1 | namespace SocketIOClient.Core.Messages 2 | { 3 | public enum MessageType 4 | { 5 | Opened, 6 | Ping = 2, 7 | Pong, 8 | Connected = 40, 9 | Disconnected, 10 | Event, 11 | Ack, 12 | Error, 13 | Binary, 14 | BinaryAck, 15 | } 16 | } -------------------------------------------------------------------------------- /src/SocketIOClient.Core/Messages/OpenedMessage.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace SocketIOClient.Core.Messages; 4 | 5 | public class OpenedMessage : IMessage 6 | { 7 | public MessageType Type => MessageType.Opened; 8 | public string Sid { get; set; } 9 | public int PingInterval { get; set; } 10 | public int PingTimeout { get; set; } 11 | public List Upgrades { get; set; } 12 | } -------------------------------------------------------------------------------- /src/SocketIOClient.Core/Messages/PingMessage.cs: -------------------------------------------------------------------------------- 1 | namespace SocketIOClient.Core.Messages; 2 | 3 | public class PingMessage : IMessage 4 | { 5 | public MessageType Type => MessageType.Ping; 6 | } -------------------------------------------------------------------------------- /src/SocketIOClient.Core/Messages/PongMessage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SocketIOClient.Core.Messages; 4 | 5 | public class PongMessage : IMessage 6 | { 7 | public MessageType Type => MessageType.Pong; 8 | public TimeSpan Duration { get; set; } 9 | } -------------------------------------------------------------------------------- /src/SocketIOClient.Core/ProtocolMessage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SocketIOClient.Core 4 | { 5 | public class ProtocolMessage 6 | { 7 | public ProtocolMessageType Type { get; set; } 8 | public string Text { get; set; } 9 | public byte[] Bytes { get; set; } 10 | } 11 | } -------------------------------------------------------------------------------- /src/SocketIOClient.Core/ProtocolMessageType.cs: -------------------------------------------------------------------------------- 1 | namespace SocketIOClient.Core 2 | { 3 | public enum ProtocolMessageType 4 | { 5 | Text, 6 | Bytes, 7 | } 8 | } -------------------------------------------------------------------------------- /src/SocketIOClient.Core/SocketIOClient.Core.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | default 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/SocketIOClient.Serializer.NewtonsoftJson/ByteArrayConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Newtonsoft.Json; 4 | 5 | namespace SocketIOClient.Serializer.NewtonsoftJson; 6 | 7 | public class ByteArrayConverter : JsonConverter 8 | { 9 | private const string Placeholder = "_placeholder"; 10 | private const string Num = "num"; 11 | public IList Bytes { get; set; } = []; 12 | 13 | public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) 14 | { 15 | if (value == null) 16 | return; 17 | Bytes.Add((byte[])value); 18 | writer.WriteStartObject(); 19 | writer.WritePropertyName(Placeholder); 20 | writer.WriteValue(true); 21 | writer.WritePropertyName(Num); 22 | writer.WriteValue(Bytes.Count - 1); 23 | writer.WriteEndObject(); 24 | } 25 | 26 | public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) 27 | { 28 | if (reader.TokenType != JsonToken.StartObject) 29 | return null; 30 | reader.Read(); 31 | if (reader.TokenType != JsonToken.PropertyName || reader.Value?.ToString() != Placeholder) 32 | return null; 33 | reader.Read(); 34 | if (reader.TokenType != JsonToken.Boolean || !(bool)reader.Value) 35 | return null; 36 | reader.Read(); 37 | if (reader.TokenType != JsonToken.PropertyName || reader.Value?.ToString() != Num) 38 | return null; 39 | reader.Read(); 40 | if (reader.Value == null) 41 | return null; 42 | if (!int.TryParse(reader.Value.ToString(), out var num)) 43 | return null; 44 | var bytes = Bytes[num]; 45 | reader.Read(); 46 | return bytes; 47 | } 48 | 49 | public override bool CanConvert(Type objectType) 50 | { 51 | return objectType == typeof(byte[]); 52 | } 53 | } -------------------------------------------------------------------------------- /src/SocketIOClient.Serializer.NewtonsoftJson/INewtonJsonAckMessage.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Linq; 3 | using SocketIOClient.Core.Messages; 4 | 5 | namespace SocketIOClient.Serializer.NewtonsoftJson; 6 | 7 | public interface INewtonJsonAckMessage : IAckMessage 8 | { 9 | JArray DataItems { get; set; } 10 | JsonSerializerSettings JsonSerializerSettings { get; set; } 11 | } -------------------------------------------------------------------------------- /src/SocketIOClient.Serializer.NewtonsoftJson/INewtonJsonEventMessage.cs: -------------------------------------------------------------------------------- 1 | using SocketIOClient.Core.Messages; 2 | 3 | namespace SocketIOClient.Serializer.NewtonsoftJson; 4 | 5 | public interface INewtonJsonEventMessage : INewtonJsonAckMessage, IEventMessage 6 | { 7 | } -------------------------------------------------------------------------------- /src/SocketIOClient.Serializer.NewtonsoftJson/NewtonJsonAckMessage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json; 3 | using Newtonsoft.Json.Linq; 4 | using SocketIOClient.Core.Messages; 5 | 6 | namespace SocketIOClient.Serializer.NewtonsoftJson; 7 | 8 | public class NewtonJsonAckMessage : INewtonJsonAckMessage 9 | { 10 | public JArray DataItems { get; set; } 11 | 12 | public virtual MessageType Type => MessageType.Ack; 13 | public string Namespace { get; set; } 14 | public int Id { get; set; } 15 | public JsonSerializerSettings JsonSerializerSettings { get; set; } 16 | 17 | protected virtual JsonSerializerSettings GetSettings() 18 | { 19 | return JsonSerializerSettings; 20 | } 21 | 22 | public virtual T? GetDataValue(int index) 23 | { 24 | var settings = GetSettings(); 25 | var serializer = JsonSerializer.Create(settings); 26 | return DataItems[index].ToObject(serializer); 27 | } 28 | 29 | public virtual object? GetDataValue(Type type, int index) 30 | { 31 | var settings = GetSettings(); 32 | var serializer = JsonSerializer.Create(settings); 33 | return DataItems[index].ToObject(type, serializer); 34 | } 35 | } -------------------------------------------------------------------------------- /src/SocketIOClient.Serializer.NewtonsoftJson/NewtonJsonBinaryAckMessage.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Newtonsoft.Json; 3 | using SocketIOClient.Core.Messages; 4 | 5 | namespace SocketIOClient.Serializer.NewtonsoftJson; 6 | 7 | public class NewtonJsonBinaryAckMessage : NewtonJsonAckMessage, IBinaryAckMessage 8 | { 9 | public override MessageType Type => MessageType.BinaryAck; 10 | public IList Bytes { get; set; } 11 | public int BytesCount { get; set; } 12 | 13 | protected override JsonSerializerSettings GetSettings() 14 | { 15 | var settings = new JsonSerializerSettings(JsonSerializerSettings); 16 | var converter = new ByteArrayConverter 17 | { 18 | Bytes = Bytes, 19 | }; 20 | settings.Converters.Add(converter); 21 | return settings; 22 | } 23 | 24 | public bool ReadyDelivery 25 | { 26 | get 27 | { 28 | if (Bytes is null) 29 | { 30 | return false; 31 | } 32 | return BytesCount == Bytes.Count; 33 | } 34 | } 35 | 36 | public void Add(byte[] bytes) 37 | { 38 | Bytes ??= new List(BytesCount); 39 | Bytes.Add(bytes); 40 | } 41 | } -------------------------------------------------------------------------------- /src/SocketIOClient.Serializer.NewtonsoftJson/NewtonJsonBinaryEventMessage.cs: -------------------------------------------------------------------------------- 1 | using SocketIOClient.Core.Messages; 2 | 3 | namespace SocketIOClient.Serializer.NewtonsoftJson; 4 | 5 | public class NewtonJsonBinaryEventMessage : NewtonJsonBinaryAckMessage, INewtonJsonEventMessage 6 | { 7 | public override MessageType Type => MessageType.Binary; 8 | public string Event { get; set; } 9 | } -------------------------------------------------------------------------------- /src/SocketIOClient.Serializer.NewtonsoftJson/NewtonJsonEngineIO3MessageAdapter.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Linq; 2 | using SocketIOClient.Core.Messages; 3 | 4 | namespace SocketIOClient.Serializer.NewtonsoftJson; 5 | 6 | // ReSharper disable once InconsistentNaming 7 | public class NewtonJsonEngineIO3MessageAdapter : IEngineIOMessageAdapter 8 | { 9 | public ConnectedMessage DeserializeConnectedMessage(string text) 10 | { 11 | var message = new ConnectedMessage(); 12 | if (!string.IsNullOrEmpty(text)) 13 | { 14 | message.Namespace = text.TrimEnd(','); 15 | } 16 | return message; 17 | } 18 | 19 | public ErrorMessage DeserializeErrorMessage(string text) 20 | { 21 | var error = JToken.Parse(text).ToObject(); 22 | return new ErrorMessage 23 | { 24 | Error = error, 25 | }; 26 | } 27 | } -------------------------------------------------------------------------------- /src/SocketIOClient.Serializer.NewtonsoftJson/NewtonJsonEngineIO4MessageAdapter.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Linq; 2 | using SocketIOClient.Core.Messages; 3 | 4 | namespace SocketIOClient.Serializer.NewtonsoftJson; 5 | 6 | // ReSharper disable once InconsistentNaming 7 | public class NewtonJsonEngineIO4MessageAdapter : IEngineIOMessageAdapter 8 | { 9 | public ConnectedMessage DeserializeConnectedMessage(string text) 10 | { 11 | var message = new ConnectedMessage(); 12 | var rawJson = DecapsulateNamespace(text, message); 13 | message.Sid = JObject.Parse(rawJson).Value("sid"); 14 | return message; 15 | } 16 | 17 | private static string DecapsulateNamespace(string text, INamespaceMessage message) 18 | { 19 | var index = text.IndexOf('{'); 20 | if (index > 0) 21 | { 22 | message.Namespace = text.Substring(0, index - 1); 23 | text = text.Substring(index); 24 | } 25 | return text; 26 | } 27 | 28 | public ErrorMessage DeserializeErrorMessage(string text) 29 | { 30 | var message = new ErrorMessage(); 31 | var rawJson = DecapsulateNamespace(text, message); 32 | message.Error = JObject.Parse(rawJson).Value("message"); 33 | return message; 34 | } 35 | } -------------------------------------------------------------------------------- /src/SocketIOClient.Serializer.NewtonsoftJson/NewtonJsonEventMessage.cs: -------------------------------------------------------------------------------- 1 | using SocketIOClient.Core.Messages; 2 | 3 | namespace SocketIOClient.Serializer.NewtonsoftJson; 4 | 5 | public class NewtonJsonEventMessage : NewtonJsonAckMessage, INewtonJsonEventMessage 6 | { 7 | public override MessageType Type => MessageType.Event; 8 | public string Event { get; set; } 9 | } -------------------------------------------------------------------------------- /src/SocketIOClient.Serializer.NewtonsoftJson/SocketIOClient.Serializer.NewtonsoftJson.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | latest 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/SocketIOClient.Serializer/Decapsulation/BinaryEventMessageResult.cs: -------------------------------------------------------------------------------- 1 | namespace SocketIOClient.Serializer.Decapsulation; 2 | 3 | public class BinaryEventMessageResult : MessageResult 4 | { 5 | public int BytesCount { get; set; } 6 | } -------------------------------------------------------------------------------- /src/SocketIOClient.Serializer/Decapsulation/DecapsulationResult.cs: -------------------------------------------------------------------------------- 1 | 2 | using SocketIOClient.Core.Messages; 3 | 4 | namespace SocketIOClient.Serializer.Decapsulation; 5 | 6 | public class DecapsulationResult 7 | { 8 | public bool Success { get; set; } 9 | public MessageType? Type { get; set; } 10 | public string Data { get; set; } 11 | } -------------------------------------------------------------------------------- /src/SocketIOClient.Serializer/Decapsulation/Decapsulator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using SocketIOClient.Core.Messages; 3 | 4 | namespace SocketIOClient.Serializer.Decapsulation; 5 | 6 | public class Decapsulator : IDecapsulable 7 | { 8 | public DecapsulationResult DecapsulateRawText(string text) 9 | { 10 | var result = new DecapsulationResult(); 11 | var enums = Enum.GetValues(typeof(MessageType)); 12 | foreach (MessageType type in enums) 13 | { 14 | var prefix = ((int)type).ToString(); 15 | if (!text.StartsWith(prefix)) continue; 16 | 17 | var data = text.Substring(prefix.Length); 18 | 19 | result.Success = true; 20 | result.Type = type; 21 | result.Data = data; 22 | } 23 | return result; 24 | } 25 | 26 | public MessageResult DecapsulateEventMessage(string text) 27 | { 28 | var result = new MessageResult(); 29 | var index = text.IndexOf('['); 30 | var lastIndex = text.LastIndexOf(',', index); 31 | if (lastIndex > -1) 32 | { 33 | var subText = text.Substring(0, index); 34 | result.Namespace = subText.Substring(0, lastIndex); 35 | if (index - lastIndex > 1) 36 | { 37 | result.Id = int.Parse(subText.Substring(lastIndex + 1)); 38 | } 39 | } 40 | else 41 | { 42 | if (index > 0) 43 | { 44 | result.Id = int.Parse(text.Substring(0, index)); 45 | } 46 | } 47 | result.Data = text.Substring(index); 48 | return result; 49 | } 50 | 51 | public BinaryEventMessageResult DecapsulateBinaryEventMessage(string text) 52 | { 53 | var result = new BinaryEventMessageResult(); 54 | var index1 = text.IndexOf('-'); 55 | result.BytesCount = int.Parse(text.Substring(0, index1)); 56 | 57 | var index2 = text.IndexOf('['); 58 | 59 | var index3 = text.LastIndexOf(',', index2); 60 | if (index3 > -1) 61 | { 62 | result.Namespace = text.Substring(index1 + 1, index3 - index1 - 1); 63 | var idLength = index2 - index3 - 1; 64 | if (idLength > 0) 65 | { 66 | result.Id = int.Parse(text.Substring(index3 + 1, idLength)); 67 | } 68 | } 69 | else 70 | { 71 | var idLength = index2 - index1 - 1; 72 | if (idLength > 0) 73 | { 74 | result.Id = int.Parse(text.Substring(index1 + 1, idLength)); 75 | } 76 | } 77 | 78 | result.Data = text.Substring(index2); 79 | return result; 80 | } 81 | } -------------------------------------------------------------------------------- /src/SocketIOClient.Serializer/Decapsulation/IDecapsulable.cs: -------------------------------------------------------------------------------- 1 | namespace SocketIOClient.Serializer.Decapsulation; 2 | 3 | public interface IDecapsulable 4 | { 5 | DecapsulationResult DecapsulateRawText(string text); 6 | MessageResult DecapsulateEventMessage(string text); 7 | BinaryEventMessageResult DecapsulateBinaryEventMessage(string text); 8 | } -------------------------------------------------------------------------------- /src/SocketIOClient.Serializer/Decapsulation/MessageResult.cs: -------------------------------------------------------------------------------- 1 | namespace SocketIOClient.Serializer.Decapsulation; 2 | 3 | public class MessageResult 4 | { 5 | public string Namespace { get; set; } 6 | public int Id { get; set; } 7 | public string Data { get; set; } 8 | } -------------------------------------------------------------------------------- /src/SocketIOClient.Serializer/IEngineIOMessageAdapter.cs: -------------------------------------------------------------------------------- 1 | using SocketIOClient.Core.Messages; 2 | 3 | namespace SocketIOClient.Serializer; 4 | 5 | public interface IEngineIOMessageAdapter 6 | { 7 | ConnectedMessage DeserializeConnectedMessage(string text); 8 | ErrorMessage DeserializeErrorMessage(string text); 9 | } -------------------------------------------------------------------------------- /src/SocketIOClient.Serializer/ISerializer.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using SocketIOClient.Core; 3 | using SocketIOClient.Core.Messages; 4 | 5 | namespace SocketIOClient.Serializer 6 | { 7 | public interface ISerializer 8 | { 9 | List Serialize(object[] data); 10 | List Serialize(object[] data, int packetId); 11 | IMessage Deserialize(string text); 12 | ProtocolMessage NewPingMessage(); 13 | ProtocolMessage NewPongMessage(); 14 | } 15 | } -------------------------------------------------------------------------------- /src/SocketIOClient.Serializer/SerializationResult.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace SocketIOClient.Serializer 4 | { 5 | public class SerializationResult 6 | { 7 | public string Json { get; set; } 8 | public ICollection Bytes { get; set; } 9 | } 10 | } -------------------------------------------------------------------------------- /src/SocketIOClient.Serializer/SocketIOClient.Serializer.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | default 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/SocketIOClient.Windows7/SocketIOClient.Windows7.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | netstandard2.0 4 | 3.1.2 5 | socket.io-client implemention for .NET 6 | MIT 7 | https://github.com/doghappy/socket.io-client-csharp 8 | https://github.com/doghappy/socket.io-client-csharp 9 | github 10 | socket.io-client 11 | true 12 | SocketIOClient.Windows7.snk 13 | doghappy 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/SocketIOClient.Windows7/SocketIOClient.Windows7.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/doghappy/socket.io-client-csharp/fd3b2519ba3deaa676ed61b2781f042012eb9968/src/SocketIOClient.Windows7/SocketIOClient.Windows7.snk -------------------------------------------------------------------------------- /src/SocketIOClient/DisconnectReason.cs: -------------------------------------------------------------------------------- 1 | namespace SocketIOClient 2 | { 3 | public class DisconnectReason 4 | { 5 | public static string IOServerDisconnect = "io server disconnect"; 6 | public static string IOClientDisconnect = "io client disconnect"; 7 | public static string PingTimeout = "ping timeout"; 8 | public static string TransportClose = "transport close"; 9 | public static string TransportError = "transport error"; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/SocketIOClient/EventHandlers.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Text.Json; 3 | 4 | namespace SocketIOClient 5 | { 6 | public delegate void OnAnyHandler(string eventName, SocketIOResponse response); 7 | public delegate void OnOpenedHandler(string sid, int pingInterval, int pingTimeout); 8 | //public delegate void OnDisconnectedHandler(string sid, int pingInterval, int pingTimeout); 9 | public delegate void OnAck(int packetId, List array); 10 | public delegate void OnBinaryAck(int packetId, int totalCount, List array); 11 | public delegate void OnBinaryReceived(int packetId, int totalCount, string eventName, List array); 12 | public delegate void OnDisconnected(); 13 | public delegate void OnError(string error); 14 | public delegate void OnEventReceived(int packetId, string eventName, List array); 15 | public delegate void OnOpened(string sid, int pingInterval, int pingTimeout); 16 | public delegate void OnPing(); 17 | public delegate void OnPong(); 18 | } 19 | -------------------------------------------------------------------------------- /src/SocketIOClient/Exceptions/ConnectionException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SocketIOClient 4 | { 5 | public class ConnectionException : Exception 6 | { 7 | public ConnectionException(string message) : base(message) { } 8 | public ConnectionException(string message, Exception innerException) : base(message, innerException) { } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/SocketIOClient/Extensions/CancellationTokenSourceExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | 3 | namespace SocketIOClient.Extensions 4 | { 5 | internal static class CancellationTokenSourceExtensions 6 | { 7 | public static void TryDispose(this CancellationTokenSource cts) 8 | { 9 | cts?.Dispose(); 10 | } 11 | 12 | public static void TryCancel(this CancellationTokenSource cts) 13 | { 14 | if (cts != null && !cts.IsCancellationRequested) 15 | { 16 | cts.Cancel(); 17 | } 18 | } 19 | 20 | public static CancellationTokenSourceWrapper Renew(this CancellationTokenSourceWrapper cts) 21 | { 22 | if (cts != null) 23 | { 24 | cts.Dispose(); 25 | } 26 | 27 | return new CancellationTokenSourceWrapper(new CancellationTokenSource()); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/SocketIOClient/Extensions/CancellationTokenSourceWrapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | 4 | namespace SocketIOClient.Extensions 5 | { 6 | class CancellationTokenSourceWrapper(CancellationTokenSource cts) : IDisposable 7 | { 8 | private bool _cancelled; 9 | private bool _disposed; 10 | 11 | private void Cancel() 12 | { 13 | if (_cancelled) 14 | { 15 | return; 16 | } 17 | cts.Cancel(); 18 | _cancelled = true; 19 | } 20 | 21 | private void DisposeInternal() 22 | { 23 | if (_disposed) 24 | { 25 | return; 26 | } 27 | cts.Dispose(); 28 | _disposed = true; 29 | } 30 | 31 | public CancellationToken Token => cts.Token; 32 | 33 | public bool IsCancellationRequested => cts.IsCancellationRequested; 34 | 35 | public void Dispose() 36 | { 37 | Cancel(); 38 | DisposeInternal(); 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /src/SocketIOClient/Extensions/DisposableExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SocketIOClient.Extensions 4 | { 5 | internal static class DisposableExtensions 6 | { 7 | public static void TryDispose(this IDisposable disposable) 8 | { 9 | disposable?.Dispose(); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/SocketIOClient/Extensions/EventHandlerExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | 4 | namespace SocketIOClient.Extensions 5 | { 6 | internal static class EventHandlerExtensions 7 | { 8 | public static void TryInvoke(this EventHandler handler, object sender, T args) 9 | { 10 | handler?.Invoke(sender, args); 11 | } 12 | 13 | public static void TryInvoke(this EventHandler handler, object sender, EventArgs args) 14 | { 15 | handler?.Invoke(sender, args); 16 | } 17 | 18 | public static void TryInvoke(this Action action, T arg1) 19 | { 20 | action?.Invoke(arg1); 21 | } 22 | 23 | public static async Task TryInvokeAsync(this Func func, T arg1) 24 | { 25 | if (func is null) 26 | { 27 | return; 28 | } 29 | await func(arg1).ConfigureAwait(false); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /src/SocketIOClient/Extensions/SocketIOEventExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | 4 | namespace SocketIOClient.Extensions 5 | { 6 | internal static class SocketIOEventExtensions 7 | { 8 | public static void TryInvoke(this OnAnyHandler handler, string eventName, SocketIOResponse response) 9 | { 10 | Task.Run(() => handler(eventName, response)); 11 | } 12 | 13 | public static void TryInvoke(this Action handler, SocketIOResponse response) 14 | { 15 | Task.Run(() => handler(response)); 16 | } 17 | 18 | public static void TryInvoke(this Func handler, SocketIOResponse response) 19 | { 20 | handler(response); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /src/SocketIOClient/SocketIOClient.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | netstandard2 6 | SocketIOClient 7 | SocketIOClient 8 | doghappy 9 | socket.io-client implemention for .NET 10 | https://github.com/doghappy/socket.io-client-csharp 11 | https://github.com/doghappy/socket.io-client-csharp 12 | false 13 | socket.io-client 14 | github 15 | 3.1.2 16 | MIT 17 | true 18 | SocketIOClient.snk 19 | default 20 | 21 | 22 | 23 | bin\Release\SocketIOClient.xml 24 | bin\Release\ 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/SocketIOClient/SocketIOClient.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/doghappy/socket.io-client-csharp/fd3b2519ba3deaa676ed61b2781f042012eb9968/src/SocketIOClient/SocketIOClient.snk -------------------------------------------------------------------------------- /src/SocketIOClient/SocketIOOptions.cs: -------------------------------------------------------------------------------- 1 | using SocketIOClient.Transport; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Net; 5 | using System.Net.Security; 6 | using SocketIO.Core; 7 | 8 | namespace SocketIOClient 9 | { 10 | public sealed class SocketIOOptions 11 | { 12 | public SocketIOOptions() 13 | { 14 | RandomizationFactor = 0.5; 15 | ReconnectionDelay = 1000; 16 | ReconnectionDelayMax = 5000; 17 | ReconnectionAttempts = int.MaxValue; 18 | Path = "/socket.io"; 19 | ConnectionTimeout = TimeSpan.FromSeconds(20); 20 | Reconnection = true; 21 | Transport = TransportProtocol.Polling; 22 | EIO = EngineIO.V4; 23 | AutoUpgrade = true; 24 | } 25 | 26 | public string Path { get; set; } 27 | 28 | public TimeSpan ConnectionTimeout { get; set; } 29 | 30 | public IEnumerable> Query { get; set; } 31 | 32 | /// 33 | /// Whether to allow reconnection if accidentally disconnected 34 | /// 35 | public bool Reconnection { get; set; } 36 | 37 | public double ReconnectionDelay { get; set; } 38 | public int ReconnectionDelayMax { get; set; } 39 | public int ReconnectionAttempts { get; set; } 40 | 41 | double _randomizationFactor; 42 | public double RandomizationFactor 43 | { 44 | get => _randomizationFactor; 45 | set 46 | { 47 | if (value >= 0 && value <= 1) 48 | { 49 | _randomizationFactor = value; 50 | } 51 | else 52 | { 53 | throw new ArgumentException($"{nameof(RandomizationFactor)} should be greater than or equal to 0.0, and less than 1.0."); 54 | } 55 | } 56 | } 57 | 58 | public Dictionary ExtraHeaders { get; set; } 59 | 60 | public TransportProtocol Transport { get; set; } 61 | 62 | public EngineIO EIO { get; set; } 63 | 64 | public bool AutoUpgrade { get; set; } 65 | 66 | public object Auth { get; set; } 67 | public IWebProxy Proxy { get; set; } 68 | public RemoteCertificateValidationCallback RemoteCertificateValidationCallback { get; set; } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/SocketIOClient/SocketIOResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Text.Json; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | using SocketIO.Core; 9 | 10 | namespace SocketIOClient 11 | { 12 | public class SocketIOResponse 13 | { 14 | private readonly IMessage _message; 15 | 16 | public SocketIOResponse(IMessage message, SocketIO socket) 17 | { 18 | _message = message; 19 | _socketIO = socket; 20 | PacketId = -1; 21 | } 22 | 23 | public List InComingBytes => _message.ReceivedBinary; 24 | 25 | private readonly SocketIO _socketIO; 26 | public int PacketId { get; set; } 27 | 28 | public T GetValue(int index = 0) 29 | { 30 | return _socketIO.Serializer.Deserialize(_message, index); 31 | } 32 | 33 | public override string ToString() 34 | { 35 | return _socketIO.Serializer.MessageToJson(_message); 36 | } 37 | 38 | public async Task CallbackAsync(params object[] data) 39 | { 40 | await _socketIO.ClientAckAsync(PacketId, CancellationToken.None, data).ConfigureAwait(false); 41 | } 42 | 43 | public async Task CallbackAsync(CancellationToken cancellationToken, params object[] data) 44 | { 45 | await _socketIO.ClientAckAsync(PacketId, cancellationToken, data).ConfigureAwait(false); 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /src/SocketIOClient/Transport/Http/DefaultHttpClient.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Net.Http; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using System.Collections.Generic; 7 | using System.Net.Security; 8 | 9 | namespace SocketIOClient.Transport.Http 10 | { 11 | public class DefaultHttpClient : IHttpClient 12 | { 13 | public DefaultHttpClient(RemoteCertificateValidationCallback remoteCertificateValidationCallback) 14 | { 15 | _handler = new HttpClientHandler(); 16 | if (remoteCertificateValidationCallback is not null) 17 | { 18 | _handler.ClientCertificateOptions = ClientCertificateOption.Manual; 19 | _handler.ServerCertificateCustomValidationCallback = (req, cert, chain, policyErrors) => remoteCertificateValidationCallback(req, cert, chain, policyErrors); 20 | } 21 | _httpClient = new HttpClient(_handler); 22 | } 23 | 24 | readonly HttpClientHandler _handler; 25 | private readonly HttpClient _httpClient; 26 | 27 | private static readonly HashSet allowedHeaders = new HashSet 28 | { 29 | "user-agent", 30 | }; 31 | 32 | public void AddHeader(string name, string value) 33 | { 34 | if (_httpClient.DefaultRequestHeaders.Contains(name)) 35 | { 36 | _httpClient.DefaultRequestHeaders.Remove(name); 37 | } 38 | if (allowedHeaders.Contains(name.ToLower())) 39 | { 40 | _httpClient.DefaultRequestHeaders.TryAddWithoutValidation(name, value); 41 | } 42 | else 43 | { 44 | _httpClient.DefaultRequestHeaders.Add(name, value); 45 | } 46 | } 47 | 48 | public IEnumerable GetHeaderValues(string name) 49 | { 50 | return _httpClient.DefaultRequestHeaders.GetValues(name); 51 | } 52 | 53 | public void SetProxy(IWebProxy proxy) 54 | { 55 | _handler.Proxy = proxy; 56 | } 57 | 58 | public Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) 59 | { 60 | return _httpClient.SendAsync(request, cancellationToken); 61 | } 62 | 63 | public Task PostAsync(string requestUri, 64 | HttpContent content, 65 | CancellationToken cancellationToken) 66 | { 67 | return _httpClient.PostAsync(requestUri, content, cancellationToken); 68 | } 69 | 70 | public Task GetStringAsync(Uri requestUri) 71 | { 72 | return _httpClient.GetStringAsync(requestUri); 73 | } 74 | 75 | public void Dispose() 76 | { 77 | _httpClient.Dispose(); 78 | _handler.Dispose(); 79 | } 80 | } 81 | } -------------------------------------------------------------------------------- /src/SocketIOClient/Transport/Http/Eio3HttpPollingHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Net.Http; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using System.Linq; 6 | using System.Net.Http.Headers; 7 | using SocketIOClient.Extensions; 8 | 9 | namespace SocketIOClient.Transport.Http 10 | { 11 | public class Eio3HttpPollingHandler : HttpPollingHandler 12 | { 13 | public Eio3HttpPollingHandler(IHttpClient adapter) : base(adapter) 14 | { 15 | } 16 | 17 | public override async Task PostAsync(string uri, IEnumerable bytes, CancellationToken cancellationToken) 18 | { 19 | var list = new List(); 20 | foreach (var item in bytes) 21 | { 22 | list.Add(1); 23 | var length = SplitInt(item.Length + 1).Select(x => (byte)x); 24 | list.AddRange(length); 25 | list.Add(byte.MaxValue); 26 | list.Add(4); 27 | list.AddRange(item); 28 | } 29 | var content = new ByteArrayContent(list.ToArray()); 30 | content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); 31 | await HttpClient.PostAsync(AppendRandom(uri), content, cancellationToken).ConfigureAwait(false); 32 | } 33 | 34 | private static List SplitInt(int number) 35 | { 36 | List list = new List(); 37 | while (number > 0) 38 | { 39 | list.Add(number % 10); 40 | number /= 10; 41 | } 42 | list.Reverse(); 43 | return list; 44 | } 45 | 46 | protected override async Task ProduceText(string text) 47 | { 48 | int p = 0; 49 | while (true) 50 | { 51 | int index = text.IndexOf(':', p); 52 | if (index == -1) 53 | { 54 | break; 55 | } 56 | if (int.TryParse(text.Substring(p, index - p), out int length)) 57 | { 58 | string msg = text.Substring(index + 1, length); 59 | await OnTextReceived.TryInvokeAsync(msg); 60 | } 61 | else 62 | { 63 | break; 64 | } 65 | p = index + length + 1; 66 | if (p >= text.Length) 67 | { 68 | break; 69 | } 70 | } 71 | } 72 | 73 | public override async Task PostAsync(string uri, string content, CancellationToken cancellationToken) 74 | { 75 | if (string.IsNullOrEmpty(content)) 76 | { 77 | return; 78 | } 79 | content = content.Length + ":" + content; 80 | await base.PostAsync(uri, content, cancellationToken); 81 | } 82 | } 83 | } -------------------------------------------------------------------------------- /src/SocketIOClient/Transport/Http/Eio4HttpPollingHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using SocketIOClient.Extensions; 7 | 8 | namespace SocketIOClient.Transport.Http 9 | { 10 | public class Eio4HttpPollingHandler : HttpPollingHandler 11 | { 12 | public Eio4HttpPollingHandler(IHttpClient adapter) : base(adapter) 13 | { 14 | } 15 | 16 | const char Separator = '\u001E'; 17 | 18 | public override async Task PostAsync(string uri, IEnumerable bytes, CancellationToken cancellationToken) 19 | { 20 | var builder = new StringBuilder(); 21 | foreach (var item in bytes) 22 | { 23 | builder.Append('b').Append(Convert.ToBase64String(item)).Append(Separator); 24 | } 25 | if (builder.Length == 0) 26 | { 27 | return; 28 | } 29 | string text = builder.ToString().TrimEnd(Separator); 30 | await PostAsync(uri, text, cancellationToken).ConfigureAwait(false); 31 | } 32 | 33 | protected override async Task ProduceText(string text) 34 | { 35 | string[] items = text.Split(new[] { Separator }, StringSplitOptions.RemoveEmptyEntries); 36 | foreach (var item in items) 37 | { 38 | if (item[0] == 'b') 39 | { 40 | byte[] bytes = Convert.FromBase64String(item.Substring(1)); 41 | await OnBytes(bytes).ConfigureAwait(false); 42 | } 43 | else 44 | { 45 | await OnTextReceived.TryInvokeAsync(item).ConfigureAwait(false); 46 | } 47 | } 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /src/SocketIOClient/Transport/Http/IHttpClient.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Net.Http; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace SocketIOClient.Transport.Http 8 | { 9 | public interface IHttpClient : IDisposable 10 | { 11 | void AddHeader(string name, string value); 12 | void SetProxy(IWebProxy proxy); 13 | Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken); 14 | Task PostAsync(string requestUri, HttpContent content, CancellationToken cancellationToken); 15 | Task GetStringAsync(Uri requestUri); 16 | } 17 | } -------------------------------------------------------------------------------- /src/SocketIOClient/Transport/Http/IHttpPollingHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net; 4 | using System.Net.Http; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | 8 | namespace SocketIOClient.Transport.Http 9 | { 10 | public interface IHttpPollingHandler 11 | { 12 | Func OnTextReceived { get; set; } 13 | Func OnBytesReceived { get; set; } 14 | Task GetAsync(string uri, CancellationToken cancellationToken); 15 | Task SendAsync(HttpRequestMessage req, CancellationToken cancellationToken); 16 | Task PostAsync(string uri, string content, CancellationToken cancellationToken); 17 | Task PostAsync(string uri, IEnumerable bytes, CancellationToken cancellationToken); 18 | void AddHeader(string key, string val); 19 | void SetProxy(IWebProxy proxy); 20 | } 21 | } -------------------------------------------------------------------------------- /src/SocketIOClient/Transport/ITransport.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using SocketIO.Core; 7 | using SocketIO.Serializer.Core; 8 | 9 | namespace SocketIOClient.Transport 10 | { 11 | public interface ITransport : IDisposable 12 | { 13 | Action OnReceived { get; set; } 14 | Action OnError { get; set; } 15 | Task SendAsync(IList items, CancellationToken cancellationToken); 16 | Task ConnectAsync(CancellationToken cancellationToken); 17 | Task DisconnectAsync(CancellationToken cancellationToken); 18 | void AddHeader(string key, string val); 19 | void SetProxy(IWebProxy proxy); 20 | } 21 | } -------------------------------------------------------------------------------- /src/SocketIOClient/Transport/TransportException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SocketIOClient.Transport 4 | { 5 | public class TransportException : Exception 6 | { 7 | public TransportException() : base() { } 8 | public TransportException(string message) : base(message) { } 9 | public TransportException(string message, Exception innerException) : base(message, innerException) { } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/SocketIOClient/Transport/TransportMessageType.cs: -------------------------------------------------------------------------------- 1 | namespace SocketIOClient.Transport 2 | { 3 | public enum TransportMessageType 4 | { 5 | Text, 6 | Binary, 7 | Close 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/SocketIOClient/Transport/TransportOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using SocketIO.Core; 4 | 5 | namespace SocketIOClient.Transport 6 | { 7 | public class TransportOptions 8 | { 9 | public EngineIO EIO { get; set; } 10 | public IEnumerable> Query { get; set; } 11 | public object Auth { get; set; } 12 | public TimeSpan ConnectionTimeout { get; set; } 13 | public Uri ServerUri { get; set; } 14 | public string Path { get; set; } 15 | public bool AutoUpgrade { get; set; } 16 | public IMessage OpenedMessage { get; set; } 17 | public string Namespace { get; set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/SocketIOClient/Transport/TransportProtocol.cs: -------------------------------------------------------------------------------- 1 | namespace SocketIOClient.Transport 2 | { 3 | public enum TransportProtocol 4 | { 5 | Polling, 6 | WebSocket 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/SocketIOClient/Transport/WebSockets/ChunkSize.cs: -------------------------------------------------------------------------------- 1 | namespace SocketIOClient.Transport.WebSockets 2 | { 3 | public static class ChunkSize 4 | { 5 | public const int Size1K = 1024; 6 | public const int Size8K = 8 * Size1K; 7 | public const int Size16K = 16 * Size1K; 8 | public const int Size32K = 32 * Size1K; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/SocketIOClient/Transport/WebSockets/IClientWebSocket.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | namespace SocketIOClient.Transport.WebSockets 7 | { 8 | public interface IClientWebSocket : IDisposable 9 | { 10 | WebSocketState State { get; } 11 | 12 | Task ConnectAsync(Uri uri, CancellationToken cancellationToken); 13 | Task DisconnectAsync(CancellationToken cancellationToken); 14 | Task SendAsync(byte[] bytes, TransportMessageType type, bool endOfMessage, CancellationToken cancellationToken); 15 | Task ReceiveAsync(int bufferSize, CancellationToken cancellationToken); 16 | void AddHeader(string key, string val); 17 | void SetProxy(IWebProxy proxy); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/SocketIOClient/Transport/WebSockets/WebSocketReceiveResult.cs: -------------------------------------------------------------------------------- 1 | namespace SocketIOClient.Transport.WebSockets 2 | { 3 | public class WebSocketReceiveResult 4 | { 5 | public int Count { get; set; } 6 | public bool EndOfMessage { get; set; } 7 | public TransportMessageType MessageType { get; set; } 8 | public byte[] Buffer { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/SocketIOClient/Transport/WebSockets/WebSocketState.cs: -------------------------------------------------------------------------------- 1 | namespace SocketIOClient.Transport.WebSockets 2 | { 3 | public enum WebSocketState 4 | { 5 | None = 0, 6 | Connecting = 1, 7 | Open = 2, 8 | CloseSent = 3, 9 | CloseReceived = 4, 10 | Closed = 5, 11 | Aborted = 6 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/SocketIOClient/V2/Core/EngineIO.cs: -------------------------------------------------------------------------------- 1 | namespace SocketIOClient.V2; 2 | 3 | public enum EngineIO 4 | { 5 | V3 = 3, 6 | V4 = 4, 7 | } -------------------------------------------------------------------------------- /src/SocketIOClient/V2/DefaultSessionFactory.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Http; 2 | using SocketIOClient.Serializer; 3 | using SocketIOClient.Serializer.Decapsulation; 4 | using SocketIOClient.V2.Infrastructure; 5 | using SocketIOClient.V2.Protocol.Http; 6 | using SocketIOClient.V2.Serializer.SystemTextJson; 7 | using SocketIOClient.V2.Session; 8 | using SocketIOClient.V2.Session.EngineIOHttpAdapter; 9 | using SocketIOClient.V2.UriConverter; 10 | 11 | namespace SocketIOClient.V2; 12 | 13 | public interface ISessionFactory 14 | { 15 | ISession New(EngineIO eio, SessionOptions options); 16 | } 17 | 18 | public class DefaultSessionFactory : ISessionFactory 19 | { 20 | public ISession New(EngineIO eio, SessionOptions options) 21 | { 22 | var httpClient = new SystemHttpClient(new HttpClient()); 23 | var httpAdapter = new HttpAdapter(httpClient); 24 | var serializer = new SystemJsonSerializer(new Decapsulator()) 25 | { 26 | EngineIOMessageAdapter = NewEngineIOMessageAdapter(eio), 27 | }; 28 | var stopwatch = new SystemStopwatch(); 29 | var random = new SystemRandom(); 30 | var randomDelayRetryPolicy = new RandomDelayRetryPolicy(random); 31 | IEngineIOAdapter engineIOAdapter = eio == EngineIO.V3 32 | ? new EngineIO3Adapter(stopwatch, serializer, httpAdapter, options.Timeout, randomDelayRetryPolicy) 33 | : new EngineIO4Adapter(stopwatch, serializer, httpAdapter, options.Timeout, randomDelayRetryPolicy); 34 | return new HttpSession( 35 | options, 36 | engineIOAdapter, 37 | new HttpAdapter(httpClient), 38 | serializer, 39 | new DefaultUriConverter((int)eio)); 40 | } 41 | 42 | private static IEngineIOMessageAdapter NewEngineIOMessageAdapter(EngineIO eio) 43 | { 44 | // TODO: NewtonsoftJson 45 | if (eio == EngineIO.V3) 46 | { 47 | return new SystemJsonEngineIO3MessageAdapter(); 48 | } 49 | return new SystemJsonEngineIO4MessageAdapter(); 50 | } 51 | } -------------------------------------------------------------------------------- /src/SocketIOClient/V2/ISocketIO.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using SocketIOClient.Core.Messages; 4 | using SocketIOClient.Transport.Http; 5 | using SocketIOClient.V2.Observers; 6 | using SocketIOClient.V2.Session; 7 | 8 | namespace SocketIOClient.V2; 9 | 10 | public interface ISocketIO : IMyObserver 11 | { 12 | IHttpClient HttpClient { get; set; } 13 | ISessionFactory SessionFactory { get; set; } 14 | int PacketId { get; } 15 | Task ConnectAsync(); 16 | Task EmitAsync(string eventName, Action ack); 17 | } -------------------------------------------------------------------------------- /src/SocketIOClient/V2/Infrastructure/IRandom.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SocketIOClient.V2.Infrastructure; 4 | 5 | public interface IRandom 6 | { 7 | int Next(int max); 8 | } -------------------------------------------------------------------------------- /src/SocketIOClient/V2/Infrastructure/IRetriable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | 4 | namespace SocketIOClient.V2.Infrastructure; 5 | 6 | public interface IRetriable 7 | { 8 | Task RetryAsync(int times, Func func); 9 | } -------------------------------------------------------------------------------- /src/SocketIOClient/V2/Infrastructure/IStopwatch.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SocketIOClient.V2.Infrastructure; 4 | 5 | public interface IStopwatch 6 | { 7 | TimeSpan Elapsed { get; } 8 | void Restart(); 9 | void Stop(); 10 | } -------------------------------------------------------------------------------- /src/SocketIOClient/V2/Infrastructure/RandomDelayRetryPolicy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | 4 | namespace SocketIOClient.V2.Infrastructure; 5 | 6 | public class RandomDelayRetryPolicy : IRetriable 7 | { 8 | private readonly IRandom _random; 9 | 10 | public RandomDelayRetryPolicy(IRandom random) 11 | { 12 | _random = random; 13 | } 14 | 15 | public async Task RetryAsync(int times, Func func) 16 | { 17 | if (times < 1) 18 | { 19 | throw new ArgumentException("Times must be greater than 0", nameof(times)); 20 | } 21 | for (var i = 1; i < times; i++) 22 | { 23 | try 24 | { 25 | await func(); 26 | return; 27 | } 28 | catch 29 | { 30 | await Task.Delay(_random.Next(3)); 31 | } 32 | } 33 | await func(); 34 | } 35 | } -------------------------------------------------------------------------------- /src/SocketIOClient/V2/Infrastructure/SystemRandom.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SocketIOClient.V2.Infrastructure; 4 | 5 | public class SystemRandom : IRandom 6 | { 7 | private readonly Random _random = new(); 8 | 9 | public int Next(int max) 10 | { 11 | return _random.Next(max); 12 | } 13 | } -------------------------------------------------------------------------------- /src/SocketIOClient/V2/Infrastructure/SystemStopwatch.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | 4 | namespace SocketIOClient.V2.Infrastructure; 5 | 6 | public class SystemStopwatch : IStopwatch 7 | { 8 | private readonly Stopwatch _stopwatch = new(); 9 | 10 | public TimeSpan Elapsed => _stopwatch.Elapsed; 11 | 12 | public void Restart() => _stopwatch.Restart(); 13 | 14 | public void Stop() => _stopwatch.Stop(); 15 | } -------------------------------------------------------------------------------- /src/SocketIOClient/V2/Observers/IMyObservable.cs: -------------------------------------------------------------------------------- 1 | namespace SocketIOClient.V2.Observers; 2 | 3 | public interface IMyObservable 4 | { 5 | void Subscribe(IMyObserver observer); 6 | } -------------------------------------------------------------------------------- /src/SocketIOClient/V2/Observers/IMyObserver.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace SocketIOClient.V2.Observers; 4 | 5 | public interface IMyObserver 6 | { 7 | Task OnNextAsync(T protocolMessage); 8 | } -------------------------------------------------------------------------------- /src/SocketIOClient/V2/Protocol/Http/HttpAdapter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using SocketIOClient.Core; 6 | using SocketIOClient.V2.Observers; 7 | 8 | namespace SocketIOClient.V2.Protocol.Http; 9 | 10 | public class HttpAdapter(IHttpClient httpClient) : IHttpAdapter 11 | { 12 | private readonly List> _observers = []; 13 | 14 | private async Task SendProtocolMessageAsync(ProtocolMessage message, CancellationToken cancellationToken) 15 | { 16 | var req = new HttpRequest 17 | { 18 | Method = RequestMethod.Post, 19 | Uri = new Uri("http://localhost:3000"), 20 | }; 21 | 22 | if (message.Type == ProtocolMessageType.Text) 23 | { 24 | req.BodyText = message.Text; 25 | req.BodyType = RequestBodyType.Text; 26 | } 27 | else 28 | { 29 | req.BodyBytes = message.Bytes; 30 | req.BodyType = RequestBodyType.Bytes; 31 | } 32 | return await httpClient.SendAsync(req, cancellationToken); 33 | } 34 | 35 | private static async Task GetMessageAsync(IHttpResponse response) 36 | { 37 | var message = new ProtocolMessage(); 38 | if (response.MediaType.Equals(MediaTypeNames.Application.Octet, StringComparison.InvariantCultureIgnoreCase)) 39 | { 40 | message.Type = ProtocolMessageType.Bytes; 41 | message.Bytes = await response.ReadAsByteArrayAsync().ConfigureAwait(false); 42 | } 43 | else 44 | { 45 | message.Type = ProtocolMessageType.Text; 46 | message.Text = await response.ReadAsStringAsync().ConfigureAwait(false); 47 | } 48 | return message; 49 | } 50 | 51 | public async Task SendAsync(ProtocolMessage message, CancellationToken cancellationToken) 52 | { 53 | var response = await SendProtocolMessageAsync(message, cancellationToken); 54 | await HandleResponseAsync(response); 55 | } 56 | 57 | private async Task HandleResponseAsync(IHttpResponse response) 58 | { 59 | var incomingMessage = await GetMessageAsync(response).ConfigureAwait(false); 60 | foreach (var observer in _observers) 61 | { 62 | await observer.OnNextAsync(incomingMessage).ConfigureAwait(false); 63 | } 64 | } 65 | 66 | public async Task SendAsync(IHttpRequest req, CancellationToken cancellationToken) 67 | { 68 | var response = await httpClient.SendAsync(req, cancellationToken).ConfigureAwait(false); 69 | _ = HandleResponseAsync(response).ConfigureAwait(false); 70 | } 71 | 72 | public void Subscribe(IMyObserver observer) 73 | { 74 | if (_observers.Contains(observer)) 75 | { 76 | return; 77 | } 78 | _observers.Add(observer); 79 | } 80 | } -------------------------------------------------------------------------------- /src/SocketIOClient/V2/Protocol/Http/HttpConstants.cs: -------------------------------------------------------------------------------- 1 | namespace SocketIOClient.V2.Protocol.Http; 2 | 3 | public static class HttpHeaders 4 | { 5 | public const string ContentType = "Content-Type"; 6 | } 7 | 8 | public static class MediaTypeNames 9 | { 10 | public static class Application 11 | { 12 | public const string Octet = "application/octet-stream"; 13 | } 14 | } -------------------------------------------------------------------------------- /src/SocketIOClient/V2/Protocol/Http/HttpRequest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace SocketIOClient.V2.Protocol.Http; 5 | 6 | public class HttpRequest : IHttpRequest 7 | { 8 | public Uri Uri { get; set; } 9 | public RequestMethod Method { get; set; } = RequestMethod.Get; 10 | public RequestBodyType BodyType { get; set; } = RequestBodyType.Text; 11 | public Dictionary Headers { get; set; } = new(); 12 | public byte[] BodyBytes { get; set; } 13 | public string BodyText { get; set; } 14 | } 15 | -------------------------------------------------------------------------------- /src/SocketIOClient/V2/Protocol/Http/IHttpAdapter.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace SocketIOClient.V2.Protocol.Http; 5 | 6 | public interface IHttpAdapter : IProtocolAdapter 7 | { 8 | Task SendAsync(IHttpRequest req, CancellationToken cancellationToken); 9 | } -------------------------------------------------------------------------------- /src/SocketIOClient/V2/Protocol/Http/IHttpClient.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | 5 | namespace SocketIOClient.V2.Protocol.Http; 6 | 7 | public interface IHttpClient 8 | { 9 | TimeSpan Timeout { get; set; } 10 | // Task SendAsync(IHttpRequest req); 11 | Task SendAsync(IHttpRequest req, CancellationToken cancellationToken); 12 | } -------------------------------------------------------------------------------- /src/SocketIOClient/V2/Protocol/Http/IHttpRequest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace SocketIOClient.V2.Protocol.Http; 5 | 6 | public enum RequestMethod 7 | { 8 | Get, 9 | Post, 10 | } 11 | 12 | public enum RequestBodyType 13 | { 14 | Text, 15 | Bytes, 16 | } 17 | 18 | public interface IHttpRequest 19 | { 20 | Uri Uri { get; } 21 | RequestMethod Method { get; } 22 | RequestBodyType BodyType { get; } 23 | Dictionary Headers { get; } 24 | byte[] BodyBytes { get; } 25 | string BodyText { get; } 26 | } -------------------------------------------------------------------------------- /src/SocketIOClient/V2/Protocol/Http/IHttpResponse.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace SocketIOClient.V2.Protocol.Http; 4 | 5 | public interface IHttpResponse 6 | { 7 | string MediaType { get; } 8 | Task ReadAsByteArrayAsync(); 9 | Task ReadAsStringAsync(); 10 | } -------------------------------------------------------------------------------- /src/SocketIOClient/V2/Protocol/Http/IHttpResponseObserver.cs: -------------------------------------------------------------------------------- 1 | namespace SocketIOClient.V2.Http; 2 | 3 | // public interface IHttpResponseObserver 4 | // { 5 | // void OnNext(IHttpResponse response); 6 | // } -------------------------------------------------------------------------------- /src/SocketIOClient/V2/Protocol/Http/SystemHttpClient.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net.Http; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | namespace SocketIOClient.V2.Protocol.Http; 7 | 8 | public class SystemHttpClient(HttpMessageInvoker http) : IHttpClient 9 | { 10 | public TimeSpan Timeout { get; set; } = TimeSpan.FromSeconds(30); 11 | 12 | // public async Task SendAsync(IHttpRequest req) 13 | // { 14 | // using var cts = new CancellationTokenSource(Timeout); 15 | // return await SendAsync(req, cts.Token); 16 | // } 17 | 18 | public async Task SendAsync(IHttpRequest req, CancellationToken cancellationToken) 19 | { 20 | var request = new HttpRequestMessage(new HttpMethod(req.Method.ToString()), req.Uri); 21 | foreach (var header in req.Headers) 22 | { 23 | request.Headers.Add(header.Key, header.Value); 24 | } 25 | 26 | switch (req.BodyType) 27 | { 28 | case RequestBodyType.Text: 29 | if (!string.IsNullOrEmpty(req.BodyText)) 30 | request.Content = new StringContent(req.BodyText); 31 | break; 32 | case RequestBodyType.Bytes: 33 | request.Content = new ByteArrayContent(req.BodyBytes); 34 | break; 35 | default: 36 | throw new NotSupportedException(); 37 | } 38 | 39 | var res = await http.SendAsync(request, cancellationToken); 40 | return new SystemHttpResponse(res); 41 | } 42 | } -------------------------------------------------------------------------------- /src/SocketIOClient/V2/Protocol/Http/SystemHttpResponse.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Http; 2 | using System.Threading.Tasks; 3 | 4 | namespace SocketIOClient.V2.Protocol.Http; 5 | 6 | public class SystemHttpResponse(HttpResponseMessage response) : IHttpResponse 7 | { 8 | public string MediaType => response.Content.Headers.ContentType?.MediaType; 9 | 10 | public async Task ReadAsByteArrayAsync() 11 | { 12 | return await response.Content.ReadAsByteArrayAsync().ConfigureAwait(false); 13 | } 14 | 15 | public async Task ReadAsStringAsync() 16 | { 17 | return await response.Content.ReadAsStringAsync().ConfigureAwait(false); 18 | } 19 | } -------------------------------------------------------------------------------- /src/SocketIOClient/V2/Protocol/IProtocolAdapter.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | using SocketIOClient.Core; 4 | using SocketIOClient.V2.Observers; 5 | 6 | namespace SocketIOClient.V2.Protocol; 7 | 8 | public interface IProtocolAdapter : IMyObservable 9 | { 10 | Task SendAsync(ProtocolMessage message, CancellationToken cancellationToken); 11 | } -------------------------------------------------------------------------------- /src/SocketIOClient/V2/Protocol/WebSocket/IWebSocketAdapter.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace SocketIOClient.V2.Protocol.WebSocket; 5 | 6 | public interface IWebSocketAdapter : IProtocolAdapter 7 | { 8 | Task ConnectAsync(CancellationToken cancellationToken); 9 | } -------------------------------------------------------------------------------- /src/SocketIOClient/V2/Protocol/WebSocket/IWebSocketClient.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace SocketIOClient.V2.Protocol.WebSocket; 5 | 6 | public interface IWebSocketClient 7 | { 8 | Task SendAsync(IWebSocketMessage message, CancellationToken cancellationToken); 9 | } -------------------------------------------------------------------------------- /src/SocketIOClient/V2/Protocol/WebSocket/IWebSocketMessage.cs: -------------------------------------------------------------------------------- 1 | namespace SocketIOClient.V2.Protocol.WebSocket; 2 | 3 | public enum WebSocketMessageType 4 | { 5 | Text, 6 | Binary, 7 | Close 8 | } 9 | 10 | public interface IWebSocketMessage 11 | { 12 | public int Count { get; set; } 13 | public bool EndOfMessage { get; set; } 14 | public WebSocketMessageType Type { get; set; } 15 | public byte[] Bytes { get; set; } 16 | } -------------------------------------------------------------------------------- /src/SocketIOClient/V2/Serializer/SystemTextJson/ByteArrayConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text.Json; 4 | using System.Text.Json.Serialization; 5 | 6 | namespace SocketIOClient.V2.Serializer.SystemTextJson; 7 | 8 | public class ByteArrayConverter : JsonConverter 9 | { 10 | private const string Placeholder = "_placeholder"; 11 | private const string Num = "num"; 12 | public IList Bytes { get; set; } = []; 13 | 14 | public override byte[] Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) 15 | { 16 | if (reader.TokenType != JsonTokenType.StartObject) return null; 17 | reader.Read(); 18 | if (reader.TokenType != JsonTokenType.PropertyName || reader.GetString() != Placeholder) return null; 19 | reader.Read(); 20 | if (reader.TokenType != JsonTokenType.True || !reader.GetBoolean()) return null; 21 | reader.Read(); 22 | if (reader.TokenType != JsonTokenType.PropertyName || reader.GetString() != Num) return null; 23 | reader.Read(); 24 | var num = reader.GetInt32(); 25 | var bytes = Bytes[num]; 26 | reader.Read(); 27 | return bytes; 28 | } 29 | 30 | public override void Write(Utf8JsonWriter writer, byte[] value, JsonSerializerOptions options) 31 | { 32 | Bytes.Add(value); 33 | writer.WriteStartObject(); 34 | writer.WritePropertyName(Placeholder); 35 | writer.WriteBooleanValue(true); 36 | writer.WritePropertyName(Num); 37 | writer.WriteNumberValue(Bytes.Count - 1); 38 | writer.WriteEndObject(); 39 | } 40 | } -------------------------------------------------------------------------------- /src/SocketIOClient/V2/Serializer/SystemTextJson/ISystemJsonAckMessage.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | using System.Text.Json.Nodes; 3 | using SocketIOClient.Core.Messages; 4 | 5 | namespace SocketIOClient.V2.Serializer.SystemTextJson; 6 | 7 | public interface ISystemJsonAckMessage : IAckMessage 8 | { 9 | JsonArray DataItems { get; set; } 10 | JsonSerializerOptions JsonSerializerOptions { get; set; } 11 | } -------------------------------------------------------------------------------- /src/SocketIOClient/V2/Serializer/SystemTextJson/ISystemJsonEventMessage.cs: -------------------------------------------------------------------------------- 1 | using SocketIOClient.Core.Messages; 2 | 3 | namespace SocketIOClient.V2.Serializer.SystemTextJson; 4 | 5 | public interface ISystemJsonEventMessage : ISystemJsonAckMessage, IEventMessage 6 | { 7 | } -------------------------------------------------------------------------------- /src/SocketIOClient/V2/Serializer/SystemTextJson/SystemJsonAckMessage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text.Json; 3 | using System.Text.Json.Nodes; 4 | using SocketIOClient.Core.Messages; 5 | 6 | namespace SocketIOClient.V2.Serializer.SystemTextJson; 7 | 8 | public class SystemJsonAckMessage : ISystemJsonAckMessage 9 | { 10 | public JsonArray DataItems { get; set; } 11 | 12 | public virtual MessageType Type => MessageType.Ack; 13 | public string Namespace { get; set; } 14 | public int Id { get; set; } 15 | public JsonSerializerOptions JsonSerializerOptions { get; set; } 16 | 17 | protected virtual JsonSerializerOptions GetOptions() 18 | { 19 | return JsonSerializerOptions; 20 | } 21 | 22 | public virtual T GetDataValue(int index) 23 | { 24 | var options = GetOptions(); 25 | return DataItems[index]!.Deserialize(options); 26 | } 27 | 28 | public virtual object GetDataValue(Type type, int index) 29 | { 30 | var options = GetOptions(); 31 | return DataItems[index]!.Deserialize(type, options); 32 | } 33 | } -------------------------------------------------------------------------------- /src/SocketIOClient/V2/Serializer/SystemTextJson/SystemJsonBinaryAckMessage.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Text.Json; 3 | using SocketIOClient.Core.Messages; 4 | 5 | namespace SocketIOClient.V2.Serializer.SystemTextJson; 6 | 7 | public class SystemJsonBinaryAckMessage : SystemJsonAckMessage, IBinaryAckMessage 8 | { 9 | public override MessageType Type => MessageType.BinaryAck; 10 | public IList Bytes { get; set; } 11 | public int BytesCount { get; set; } 12 | 13 | protected override JsonSerializerOptions GetOptions() 14 | { 15 | var options = new JsonSerializerOptions(JsonSerializerOptions); 16 | var converter = new ByteArrayConverter 17 | { 18 | Bytes = Bytes, 19 | }; 20 | options.Converters.Add(converter); 21 | return options; 22 | } 23 | 24 | public bool ReadyDelivery 25 | { 26 | get 27 | { 28 | if (Bytes is null) 29 | { 30 | return false; 31 | } 32 | return BytesCount == Bytes.Count; 33 | } 34 | } 35 | 36 | public void Add(byte[] bytes) 37 | { 38 | Bytes ??= new List(BytesCount); 39 | Bytes.Add(bytes); 40 | } 41 | } -------------------------------------------------------------------------------- /src/SocketIOClient/V2/Serializer/SystemTextJson/SystemJsonBinaryEventMessage.cs: -------------------------------------------------------------------------------- 1 | using SocketIOClient.Core.Messages; 2 | 3 | namespace SocketIOClient.V2.Serializer.SystemTextJson; 4 | 5 | public class SystemJsonBinaryEventMessage : SystemJsonBinaryAckMessage, ISystemJsonEventMessage 6 | { 7 | public override MessageType Type => MessageType.Binary; 8 | public string Event { get; set; } 9 | } -------------------------------------------------------------------------------- /src/SocketIOClient/V2/Serializer/SystemTextJson/SystemJsonEngineIO3MessageAdapter.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | using System.Text.Json.Nodes; 3 | using SocketIOClient.Core.Messages; 4 | using SocketIOClient.Serializer; 5 | 6 | namespace SocketIOClient.V2.Serializer.SystemTextJson; 7 | 8 | // ReSharper disable once InconsistentNaming 9 | public class SystemJsonEngineIO3MessageAdapter : IEngineIOMessageAdapter 10 | { 11 | public ConnectedMessage DeserializeConnectedMessage(string text) 12 | { 13 | var message = new ConnectedMessage(); 14 | if (!string.IsNullOrEmpty(text)) 15 | { 16 | message.Namespace = text.TrimEnd(','); 17 | } 18 | return message; 19 | } 20 | 21 | public ErrorMessage DeserializeErrorMessage(string text) 22 | { 23 | var error = JsonNode.Parse(text).Deserialize(); 24 | return new ErrorMessage 25 | { 26 | Error = error, 27 | }; 28 | } 29 | } -------------------------------------------------------------------------------- /src/SocketIOClient/V2/Serializer/SystemTextJson/SystemJsonEngineIO4MessageAdapter.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | using SocketIOClient.Core.Messages; 3 | using SocketIOClient.Serializer; 4 | 5 | namespace SocketIOClient.V2.Serializer.SystemTextJson; 6 | 7 | // ReSharper disable once InconsistentNaming 8 | public class SystemJsonEngineIO4MessageAdapter : IEngineIOMessageAdapter 9 | { 10 | public ConnectedMessage DeserializeConnectedMessage(string text) 11 | { 12 | var message = new ConnectedMessage(); 13 | var rawJson = DecapsulateNamespace(text, message); 14 | message.Sid = JsonDocument.Parse(rawJson).RootElement.GetProperty("sid").GetString(); 15 | return message; 16 | } 17 | 18 | private static string DecapsulateNamespace(string text, INamespaceMessage message) 19 | { 20 | var index = text.IndexOf('{'); 21 | if (index > 0) 22 | { 23 | message.Namespace = text.Substring(0, index - 1); 24 | text = text.Substring(index); 25 | } 26 | return text; 27 | } 28 | 29 | public ErrorMessage DeserializeErrorMessage(string text) 30 | { 31 | var message = new ErrorMessage(); 32 | var rawJson = DecapsulateNamespace(text, message); 33 | message.Error = JsonDocument.Parse(rawJson).RootElement.GetProperty("message").GetString(); 34 | return message; 35 | } 36 | } -------------------------------------------------------------------------------- /src/SocketIOClient/V2/Serializer/SystemTextJson/SystemJsonEventMessage.cs: -------------------------------------------------------------------------------- 1 | using SocketIOClient.Core.Messages; 2 | 3 | namespace SocketIOClient.V2.Serializer.SystemTextJson; 4 | 5 | public class SystemJsonEventMessage : SystemJsonAckMessage, ISystemJsonEventMessage 6 | { 7 | public override MessageType Type => MessageType.Event; 8 | public string Event { get; set; } 9 | } -------------------------------------------------------------------------------- /src/SocketIOClient/V2/Session/ConnectionFailedException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SocketIOClient.V2.Session; 4 | 5 | public class ConnectionFailedException(string message, Exception innerException) : Exception(message, innerException) 6 | { 7 | public ConnectionFailedException(Exception innerException) : this("Failed to connect to the server", innerException) 8 | { 9 | } 10 | } -------------------------------------------------------------------------------- /src/SocketIOClient/V2/Session/EngineIOHttpAdapter/IEngineIOAdapter.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using SocketIOClient.Core; 4 | using SocketIOClient.Core.Messages; 5 | using SocketIOClient.V2.Observers; 6 | using SocketIOClient.V2.Protocol.Http; 7 | 8 | namespace SocketIOClient.V2.Session.EngineIOHttpAdapter; 9 | 10 | public interface IEngineIOAdapter : IMyObservable 11 | { 12 | IHttpRequest ToHttpRequest(ICollection bytes); 13 | IHttpRequest ToHttpRequest(string content); 14 | IEnumerable GetMessages(string text); 15 | Task ProcessMessageAsync(IMessage message); 16 | } -------------------------------------------------------------------------------- /src/SocketIOClient/V2/Session/ISession.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using SocketIOClient.Core; 5 | using SocketIOClient.Core.Messages; 6 | using SocketIOClient.V2.Observers; 7 | 8 | namespace SocketIOClient.V2.Session; 9 | 10 | public interface ISession : IMyObserver, IMyObservable, IMyObserver, IDisposable 11 | { 12 | int PendingDeliveryCount { get; } 13 | Task SendAsync(object[] data, CancellationToken cancellationToken); 14 | Task SendAsync(object[] data, int packetId, CancellationToken cancellationToken); 15 | 16 | Task ConnectAsync(CancellationToken cancellationToken); 17 | // Task DisconnectAsync(CancellationToken cancellationToken); 18 | } -------------------------------------------------------------------------------- /src/SocketIOClient/V2/Session/SessionOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace SocketIOClient.V2.Session; 5 | 6 | public class SessionOptions 7 | { 8 | public Uri ServerUri { get; set; } 9 | public string Path { get; set; } 10 | public IEnumerable> Query { get; set; } 11 | public TimeSpan Timeout { get; set; } 12 | } -------------------------------------------------------------------------------- /src/SocketIOClient/V2/SocketIOOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace SocketIOClient.V2; 5 | 6 | public class SocketIOOptions 7 | { 8 | // TODO: what will happen if user set an invalid value? 9 | public EngineIO EIO { get; set; } = EngineIO.V4; 10 | public TimeSpan ConnectionTimeout { get; set; } = TimeSpan.FromSeconds(30); 11 | public bool Reconnection { get; set; } = true; 12 | public int ReconnectionAttempts { get; set; } = 10; 13 | public int ReconnectionDelayMax { get; set; } = 5000; 14 | public string Path { get; set; } = "/socket.io"; 15 | public IEnumerable> Query { get; set; } 16 | } -------------------------------------------------------------------------------- /src/SocketIOClient/V2/SocketIOResponse.cs: -------------------------------------------------------------------------------- 1 | namespace SocketIOClient.V2; 2 | 3 | public class SocketIOResponse 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /src/SocketIOClient/V2/UriConverter/DefaultUriConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace SocketIOClient.V2.UriConverter 6 | { 7 | public class DefaultUriConverter(int eio) : IUriConverter 8 | { 9 | public Uri GetServerUri(bool ws, Uri serverUri, string path, IEnumerable> queryParams) 10 | { 11 | var builder = new StringBuilder(); 12 | SetSchema(ws, serverUri, builder); 13 | builder.Append(serverUri.Host); 14 | if (!serverUri.IsDefaultPort) 15 | { 16 | builder.Append(':').Append(serverUri.Port); 17 | } 18 | builder.Append(string.IsNullOrWhiteSpace(path) ? "/socket.io" : path); 19 | builder 20 | .Append("/?EIO=") 21 | .Append(eio) 22 | .Append("&transport=") 23 | .Append(ws ? "websocket" : "polling"); 24 | 25 | if (queryParams != null) 26 | { 27 | foreach (var item in queryParams) 28 | { 29 | builder.Append('&').Append(item.Key).Append('=').Append(item.Value); 30 | } 31 | } 32 | 33 | return new Uri(builder.ToString()); 34 | } 35 | 36 | private static void SetSchema(bool ws, Uri serverUri, StringBuilder builder) 37 | { 38 | switch (serverUri.Scheme) 39 | { 40 | case "https" or "wss": 41 | builder.Append(ws ? "wss://" : "https://"); 42 | break; 43 | case "http" or "ws": 44 | builder.Append(ws ? "ws://" : "http://"); 45 | break; 46 | default: 47 | throw new ArgumentException("Only supports 'http, https, ws, wss' protocol"); 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/SocketIOClient/V2/UriConverter/IUriConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace SocketIOClient.V2.UriConverter; 5 | 6 | public interface IUriConverter 7 | { 8 | Uri GetServerUri(bool ws, Uri serverUri, string path, IEnumerable> queryParams); 9 | } -------------------------------------------------------------------------------- /tests/SocketIO.Serializer.MessagePack.Tests/MessagePackUserDto.cs: -------------------------------------------------------------------------------- 1 | using MessagePack; 2 | 3 | namespace SocketIO.Serializer.MessagePack.Tests; 4 | 5 | [MessagePackObject] 6 | public class MessagePackUserDto 7 | { 8 | [Key("username")] 9 | public string Username { get; set; } = null!; 10 | 11 | [Key("email")] 12 | public string Email { get; set; } = null!; 13 | } 14 | -------------------------------------------------------------------------------- /tests/SocketIO.Serializer.MessagePack.Tests/MessagePackUserPasswordDto.cs: -------------------------------------------------------------------------------- 1 | using MessagePack; 2 | 3 | namespace SocketIO.Serializer.MessagePack.Tests; 4 | 5 | [MessagePackObject] 6 | public class MessagePackUserPasswordDto 7 | { 8 | [Key("User")] 9 | public string User { get; set; } = null!; 10 | 11 | [Key("Password")] 12 | public string Password { get; set; } = null!; 13 | } 14 | -------------------------------------------------------------------------------- /tests/SocketIO.Serializer.MessagePack.Tests/SocketIO.Serializer.MessagePack.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | false 8 | $(NoWarn);xUnit1026 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | runtime; build; native; contentfiles; analyzers; buildtransitive 17 | all 18 | 19 | 20 | runtime; build; native; contentfiles; analyzers; buildtransitive 21 | all 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /tests/SocketIO.Serializer.MessagePack.Tests/Usings.cs: -------------------------------------------------------------------------------- 1 | global using Xunit; 2 | global using FluentAssertions; -------------------------------------------------------------------------------- /tests/SocketIO.Serializer.NewtonsoftJson.Tests/Depth.cs: -------------------------------------------------------------------------------- 1 | namespace SocketIO.Serializer.NewtonsoftJson.Tests; 2 | 3 | public class Depth 4 | { 5 | public int Value { get; set; } 6 | public Depth Next { get; set; } = null!; 7 | } -------------------------------------------------------------------------------- /tests/SocketIO.Serializer.NewtonsoftJson.Tests/SocketIO.Serializer.NewtonsoftJson.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | false 8 | $(NoWarn);xUnit1026 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | runtime; build; native; contentfiles; analyzers; buildtransitive 17 | all 18 | 19 | 20 | runtime; build; native; contentfiles; analyzers; buildtransitive 21 | all 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /tests/SocketIO.Serializer.NewtonsoftJson.Tests/Usings.cs: -------------------------------------------------------------------------------- 1 | global using Xunit; 2 | global using FluentAssertions; -------------------------------------------------------------------------------- /tests/SocketIO.Serializer.SystemTextJson.Tests/SocketIO.Serializer.SystemTextJson.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | false 8 | $(NoWarn);xUnit1026 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | runtime; build; native; contentfiles; analyzers; buildtransitive 17 | all 18 | 19 | 20 | runtime; build; native; contentfiles; analyzers; buildtransitive 21 | all 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /tests/SocketIO.Serializer.SystemTextJson.Tests/Usings.cs: -------------------------------------------------------------------------------- 1 | global using Xunit; 2 | global using FluentAssertions; 3 | -------------------------------------------------------------------------------- /tests/SocketIO.Serializer.Tests.Models/Address.cs: -------------------------------------------------------------------------------- 1 | namespace SocketIO.Serializer.Tests.Models; 2 | 3 | public class Address 4 | { 5 | public string Planet { get; set; } = null!; 6 | } -------------------------------------------------------------------------------- /tests/SocketIO.Serializer.Tests.Models/FileDto.cs: -------------------------------------------------------------------------------- 1 | namespace SocketIO.Serializer.Tests.Models; 2 | 3 | public class FileDto 4 | { 5 | public int Size { get; set; } 6 | public string Name { get; set; } = null!; 7 | public byte[] Bytes { get; set; } = null!; 8 | 9 | public static FileDto IndexHtml = new() 10 | { 11 | Name = "index.html", 12 | Size = 1024, 13 | Bytes = "Hello World!"u8.ToArray() 14 | }; 15 | 16 | /// 17 | /// Niubility is a popular Chinglish word to describe great ability. 18 | /// Niubility => 牛比 => 🐮🍺 19 | /// 20 | public static FileDto Niubility = new() 21 | { 22 | Name = "Niubility", 23 | Size = 666, 24 | Bytes = "🐮🍺"u8.ToArray() 25 | }; 26 | } -------------------------------------------------------------------------------- /tests/SocketIO.Serializer.Tests.Models/SocketIO.Serializer.Tests.Models.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /tests/SocketIO.Serializer.Tests.Models/User.cs: -------------------------------------------------------------------------------- 1 | namespace SocketIO.Serializer.Tests.Models; 2 | 3 | public class User 4 | { 5 | public string Name { get; set; } = null!; 6 | public Address Address { get; set; } = null!; 7 | 8 | public static User SpaceJockey = new() 9 | { 10 | Name = "Space Jockey", 11 | Address = new Address 12 | { 13 | Planet = "LV-223" 14 | } 15 | }; 16 | } -------------------------------------------------------------------------------- /tests/SocketIO.Serializer.Tests.Models/UserPasswordDto.cs: -------------------------------------------------------------------------------- 1 | namespace SocketIO.Serializer.Tests.Models; 2 | 3 | public class UserPasswordDto 4 | { 5 | public string User { get; set; } = null!; 6 | public string Password { get; set; } = null!; 7 | } -------------------------------------------------------------------------------- /tests/SocketIOClient.IntegrationTests.Net472/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | [assembly: AssemblyTitle("SocketIOClient.IntegrationTests.Net472")] 6 | [assembly: AssemblyDescription("")] 7 | [assembly: AssemblyConfiguration("")] 8 | [assembly: AssemblyCompany("")] 9 | [assembly: AssemblyProduct("SocketIOClient.IntegrationTests.Net472")] 10 | [assembly: AssemblyCopyright("Copyright © 2022")] 11 | [assembly: AssemblyTrademark("")] 12 | [assembly: AssemblyCulture("")] 13 | 14 | [assembly: ComVisible(false)] 15 | 16 | [assembly: Guid("a12339f8-b60d-4296-a61e-a6fffa4c4a73")] 17 | 18 | // [assembly: AssemblyVersion("1.0.*")] 19 | [assembly: AssemblyVersion("1.0.0.0")] 20 | [assembly: AssemblyFileVersion("1.0.0.0")] 21 | -------------------------------------------------------------------------------- /tests/SocketIOClient.IntegrationTests.Net472/V4WebSocketTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using System.Collections.Generic; 4 | using System.Threading.Tasks; 5 | using FluentAssertions; 6 | using SocketIO.Core; 7 | using SocketIOClient.Transport; 8 | 9 | namespace SocketIOClient.IntegrationTests.Net472 10 | { 11 | [TestClass] 12 | public class V4WebSocketTests 13 | { 14 | [TestMethod] 15 | [DataRow("CustomHeader", "CustomHeader-Value")] 16 | [DataRow("User-Agent", "dotnet-socketio[client]/socket")] 17 | [DataRow("user-agent", "dotnet-socketio[client]/socket")] 18 | public async Task ExtraHeaders(string key, string value) 19 | { 20 | string actual = null; 21 | using (var io = new SocketIO("http://localhost:11400", new SocketIOOptions 22 | { 23 | Reconnection = false, 24 | EIO = EngineIO.V4, 25 | Transport = TransportProtocol.WebSocket, 26 | ExtraHeaders = new Dictionary 27 | { 28 | { key, value }, 29 | }, 30 | })) 31 | { 32 | await io.ConnectAsync(); 33 | await io.EmitAsync("get_header", 34 | res => actual = res.GetValue(), 35 | key.ToLower()); 36 | await Task.Delay(100); 37 | 38 | actual.Should().Be(value); 39 | }; 40 | } 41 | 42 | [TestMethod] 43 | public async Task Should_ignore_ws_SSL_error() 44 | { 45 | var callback = false; 46 | var io = new SocketIO("https://localhost:11404", new SocketIOOptions 47 | { 48 | EIO = EngineIO.V4, 49 | AutoUpgrade = false, 50 | Reconnection = false, 51 | Transport = TransportProtocol.WebSocket, 52 | ConnectionTimeout = TimeSpan.FromSeconds(2), 53 | RemoteCertificateValidationCallback = (sender, cert, chain, errs) => 54 | { 55 | callback = true; 56 | return true; 57 | } 58 | }); 59 | var connected = false; 60 | io.OnConnected += (s, e) => connected = true; 61 | await io.ConnectAsync(); 62 | 63 | connected.Should().BeTrue(); 64 | callback.Should().BeTrue(); 65 | } 66 | 67 | [TestMethod] 68 | public async Task Should_ignore_http_SSL_error() 69 | { 70 | var callback = false; 71 | var io = new SocketIO("https://localhost:11414", new SocketIOOptions 72 | { 73 | EIO = EngineIO.V4, 74 | AutoUpgrade = false, 75 | Reconnection = false, 76 | Transport = TransportProtocol.Polling, 77 | ConnectionTimeout = TimeSpan.FromSeconds(2), 78 | RemoteCertificateValidationCallback = (sender, cert, chain, errs) => 79 | { 80 | callback = true; 81 | return true; 82 | } 83 | }); 84 | var connected = false; 85 | io.OnConnected += (s, e) => connected = true; 86 | await io.ConnectAsync(); 87 | 88 | connected.Should().BeTrue(); 89 | callback.Should().BeTrue(); 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /tests/SocketIOClient.IntegrationTests/MessagePackTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using SocketIO.Serializer.Tests.Models; 4 | using SocketIOClient.IntegrationTests.Utils; 5 | 6 | namespace SocketIOClient.IntegrationTests 7 | { 8 | [TestClass] 9 | public abstract class MessagePackTests : SocketIOTests 10 | { 11 | protected override SocketIO CreateSocketIO(string url) 12 | { 13 | var io = base.CreateSocketIO(url); 14 | io.SetMessagePackSerializer(); 15 | return io; 16 | } 17 | 18 | protected override void ConfigureSerializerForEmitting1Parameter(SocketIO io) 19 | { 20 | } 21 | 22 | protected override IEnumerable<( 23 | string EventName, 24 | object Data, 25 | string ExpectedJson, 26 | IEnumerable? ExpectedBytes 27 | )> Emit1ParameterTupleCases => new ( 28 | string EventName, 29 | object Data, 30 | string ExpectedJson, 31 | IEnumerable? ExpectedBytes)[] 32 | { 33 | ("1:emit", null!, "[null]", null), 34 | ("1:emit", true, "[true]", null), 35 | ("1:emit", false, "[false]", null), 36 | ("1:emit", -1234567890, "[-1234567890]", null), 37 | ("1:emit", 1234567890, "[1234567890]", null), 38 | ("1:emit", -1.234567890, "[-1.23456789]", null), 39 | ("1:emit", 1.234567890, "[1.23456789]", null), 40 | ("1:emit", "hello\n世界\n🌍🌎🌏", "[\"hello\\n世界\\n🌍🌎🌏\"]", null), 41 | ("1:emit", new { User = "abc", Password = "123" }, "[{\"User\":\"abc\",\"Password\":\"123\"}]", null), 42 | ("1:emit", 43 | new { Result = true, Data = new { User = "abc", Password = "123" } }, 44 | "[{\"Result\":true,\"Data\":{\"User\":\"abc\",\"Password\":\"123\"}}]", 45 | null), 46 | ("1:emit", 47 | new { Result = true, Data = new[] { "a", "b" } }, 48 | "[{\"Result\":true,\"Data\":[\"a\",\"b\"]}]", 49 | null), 50 | ("1:emit", 51 | new { Result = true, Data = "🦊🐶🐱"u8.ToArray() }, 52 | "[{\"Result\":true,\"Data\":\"8J+mivCfkLbwn5Cx\"}]", 53 | null), 54 | ("1:emit", 55 | new { Result = "test"u8.ToArray(), Data = "🦊🐶🐱"u8.ToArray() }, 56 | "[{\"Result\":\"dGVzdA==\",\"Data\":\"8J+mivCfkLbwn5Cx\"}]", 57 | null), 58 | }; 59 | 60 | protected override IEnumerable<(object Data, string Expected, List Bytes)> AckCases => 61 | new (object Data, string Expected, List Bytes)[] 62 | { 63 | ("ack", "[\"ack\"]", null!), 64 | (FileDto.IndexHtml, 65 | "[{\"Size\":1024,\"Name\":\"index.html\",\"Bytes\":\"SGVsbG8gV29ybGQh\"}]", 66 | null!) 67 | }; 68 | } 69 | } -------------------------------------------------------------------------------- /tests/SocketIOClient.IntegrationTests/SocketIOClient.IntegrationTests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net8.0 4 | enable 5 | false 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | all 15 | runtime; build; native; contentfiles; analyzers; buildtransitive 16 | 17 | 18 | 19 | 20 | 21 | 22 | PreserveNewest 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /tests/SocketIOClient.IntegrationTests/SystemTextJsonTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using SocketIO.Serializer.Tests.Models; 4 | using SocketIOClient.IntegrationTests.Utils; 5 | 6 | namespace SocketIOClient.IntegrationTests 7 | { 8 | [TestClass] 9 | public abstract class SystemTextJsonTests : SocketIOTests 10 | { 11 | protected override void ConfigureSerializerForEmitting1Parameter(SocketIO io) 12 | { 13 | io.ConfigureSystemTextJsonSerializerForEmitting1Parameter(); 14 | } 15 | 16 | protected override IEnumerable<( 17 | string EventName, 18 | object Data, 19 | string ExpectedJson, 20 | IEnumerable? ExpectedBytes 21 | )> Emit1ParameterTupleCases => new ( 22 | string EventName, 23 | object Data, 24 | string ExpectedJson, 25 | IEnumerable? ExpectedBytes)[] 26 | { 27 | ("1:emit", null!, "[null]", null), 28 | ("1:emit", true, "[true]", null), 29 | ("1:emit", false, "[false]", null), 30 | ("1:emit", -1234567890, "[-1234567890]", null), 31 | ("1:emit", 1234567890, "[1234567890]", null), 32 | ("1:emit", -1.234567890, "[-1.23456789]", null), 33 | ("1:emit", 1.234567890, "[1.23456789]", null), 34 | ("1:emit", "hello\n世界\n🌍🌎🌏", "[\"hello\\n世界\\n\\uD83C\\uDF0D\\uD83C\\uDF0E\\uD83C\\uDF0F\"]", null), 35 | ("1:emit", new { User = "abc", Password = "123" }, "[{\"User\":\"abc\",\"Password\":\"123\"}]", null), 36 | ("1:emit", 37 | new { Result = true, Data = new { User = "abc", Password = "123" } }, 38 | "[{\"Result\":true,\"Data\":{\"User\":\"abc\",\"Password\":\"123\"}}]", 39 | null), 40 | ("1:emit", 41 | new { Result = true, Data = new[] { "a", "b" } }, 42 | "[{\"Result\":true,\"Data\":[\"a\",\"b\"]}]", 43 | null), 44 | ("1:emit", 45 | new { Result = true, Data = "🦊🐶🐱"u8.ToArray() }, 46 | "[{\"Result\":true,\"Data\":{\"_placeholder\":true,\"num\":0}}]", 47 | new[] { new byte[] { 0xf0, 0x9f, 0xa6, 0x8a, 0xf0, 0x9f, 0x90, 0xb6, 0xf0, 0x9f, 0x90, 0xb1 } }), 48 | ("1:emit", 49 | new { Result = "test"u8.ToArray(), Data = "🦊🐶🐱"u8.ToArray() }, 50 | "[{\"Result\":{\"_placeholder\":true,\"num\":0},\"Data\":{\"_placeholder\":true,\"num\":1}}]", 51 | new[] 52 | { 53 | new byte[] { 0x74, 0x65, 0x73, 0x74 }, 54 | new byte[] { 0xf0, 0x9f, 0xa6, 0x8a, 0xf0, 0x9f, 0x90, 0xb6, 0xf0, 0x9f, 0x90, 0xb1 } 55 | }), 56 | }; 57 | 58 | protected override IEnumerable<(object Data, string Expected, List Bytes)> AckCases => 59 | new (object Data, string Expected, List Bytes)[] 60 | { 61 | ("ack", "[\"ack\"]", null!), 62 | (FileDto.IndexHtml, 63 | "[{\"Size\":1024,\"Name\":\"index.html\",\"Bytes\":{\"_placeholder\":true,\"num\":0}}]", 64 | new List { FileDto.IndexHtml.Bytes }) 65 | }; 66 | } 67 | } -------------------------------------------------------------------------------- /tests/SocketIOClient.IntegrationTests/Transport/WebSockets/SystemNetWebSocketsClientWebSocketTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using FluentAssertions; 6 | using Microsoft.VisualStudio.TestTools.UnitTesting; 7 | using SocketIOClient.Transport; 8 | using SocketIOClient.Transport.WebSockets; 9 | 10 | namespace SocketIOClient.IntegrationTests.Transport.WebSockets 11 | { 12 | // [TestClass] 13 | // public class SystemNetWebSocketsClientWebSocketTests 14 | // { 15 | // [TestMethod] 16 | // public async Task Send_And_Receive_Should_Be_Work() 17 | // { 18 | // using var server = new WebSocketServer(); 19 | // _ = server.ListenAsync(); 20 | // 21 | // using var client = new DefaultClientWebSocket(); 22 | // await client.ConnectAsync(server.ServerUrl, CancellationToken.None); 23 | // 24 | // foreach (var item in TestHelper.TestMessages) 25 | // { 26 | // var bytes = Encoding.UTF8.GetBytes(item); 27 | // await client.SendAsync(bytes, TransportMessageType.Text, true, CancellationToken.None); 28 | // var buffer = new byte[1024]; 29 | // var result = await client.ReceiveAsync(ChunkSize.Size8K, CancellationToken.None); 30 | // result.Should().BeEquivalentTo(new WebSocketReceiveResult 31 | // { 32 | // Count = bytes.Length, 33 | // EndOfMessage = result.EndOfMessage, 34 | // MessageType = TransportMessageType.Text, 35 | // }); 36 | // } 37 | // 38 | // TestHelper.TestMessages.Should().HaveCountGreaterThan(0); 39 | // } 40 | // 41 | // [TestMethod] 42 | // public async Task State_Should_Be_Correct() 43 | // { 44 | // using var server = new WebSocketServer(); 45 | // server.Start(); 46 | // _ = server.ListenAsync(); 47 | // 48 | // Console.WriteLine($"URL:{server.ServerUrl}"); 49 | // 50 | // using var ws = new DefaultClientWebSocket(); 51 | // ws.State.Should().Be(WebSocketState.None); 52 | // 53 | // await ws.ConnectAsync(server.ServerUrl, CancellationToken.None); 54 | // ws.State.Should().Be(WebSocketState.Open); 55 | // 56 | // await ws.DisconnectAsync(CancellationToken.None); 57 | // ws.State.Should().Be(WebSocketState.Closed); 58 | // 59 | // server.AbortAll(); 60 | // ws.State.Should().Be(WebSocketState.Aborted); 61 | // } 62 | // } 63 | } -------------------------------------------------------------------------------- /tests/SocketIOClient.IntegrationTests/Transport/WebSockets/TestHelper.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Text; 3 | 4 | namespace SocketIOClient.IntegrationTests.Transport.WebSockets 5 | { 6 | public class TestHelper 7 | { 8 | public static readonly List TestMessages = new List 9 | { 10 | new string('a', 11), 11 | new string('酷', 11), 12 | "😎😎😎😎😎😎😎😎😎😎😎" 13 | // new string('a', 1024 * 9), 14 | // new string('酷', 1024 * 9), 15 | // CreateEmojiString("😎", 1024 * 9), 16 | }; 17 | 18 | static string CreateEmojiString(string emoji, int n) 19 | { 20 | var builder = new StringBuilder(n); 21 | for (int i = 0; i < n; i++) 22 | { 23 | builder.Append(emoji); 24 | } 25 | return builder.ToString(); 26 | } 27 | 28 | } 29 | } -------------------------------------------------------------------------------- /tests/SocketIOClient.IntegrationTests/Transport/WebSockets/WebSocketTransportTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using FluentAssertions; 7 | using Microsoft.VisualStudio.TestTools.UnitTesting; 8 | using SocketIOClient.Transport; 9 | using SocketIOClient.Transport.WebSockets; 10 | using SocketIO.Core; 11 | 12 | namespace SocketIOClient.IntegrationTests.Transport.WebSockets 13 | { 14 | [TestClass] 15 | public class WebSocketTransportTests 16 | { 17 | // [TestMethod] 18 | // public async Task Sending_And_Receiving_Should_Be_Work() 19 | // { 20 | // const string eventName = "event name"; 21 | // 22 | // var messages = new List(TestHelper.TestMessages.Count); 23 | // using var server = new WebSocketServer(); 24 | // _ = server.ListenAsync(); 25 | // 26 | // using var ws = new DefaultClientWebSocket(); 27 | // using var transport = new WebSocketTransport(new TransportOptions 28 | // { 29 | // EIO = EngineIO.V3, 30 | // }, ws); 31 | // transport.OnReceived = m => messages.Add(m); 32 | // await transport.ConnectAsync(server.ServerUrl, CancellationToken.None); 33 | // 34 | // foreach (var item in TestHelper.TestMessages) 35 | // { 36 | // var msg = new EventMessage 37 | // { 38 | // Event = eventName, 39 | // Json = $"[\"{item}\"]" 40 | // }; 41 | // // await transport.SendAsync(msg, CancellationToken.None); 42 | // } 43 | // await Task.Delay(200); 44 | // messages 45 | // .Should().HaveCount(TestHelper.TestMessages.Count) 46 | // .And.Equal(TestHelper.TestMessages, (a, b) => 47 | // { 48 | // var em = a as EventMessage; 49 | // if (em is null) 50 | // return false; 51 | // return em.JsonElements[0].GetString() == b; 52 | // }); 53 | // } 54 | 55 | // [TestMethod] 56 | // public async Task Should_Throw_An_Exception_When_SetProxy_After_Connected() 57 | // { 58 | // using var server = new WebSocketServer(); 59 | // _ = server.ListenAsync(); 60 | // 61 | // using var ws = new DefaultClientWebSocket(); 62 | // using var transport = new WebSocketTransport(new TransportOptions 63 | // { 64 | // EIO = EngineIO.V3, 65 | // }, ws); 66 | // await transport.ConnectAsync(server.ServerUrl, CancellationToken.None); 67 | // 68 | // transport 69 | // .Invoking(t => t.SetProxy(new WebProxy())) 70 | // .Should().Throw(); 71 | // } 72 | } 73 | } -------------------------------------------------------------------------------- /tests/SocketIOClient.IntegrationTests/Utils/SerializerHelper.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Encodings.Web; 2 | using System.Text.Json; 3 | using System.Text.Unicode; 4 | using MessagePack.Resolvers; 5 | using SocketIO.Core; 6 | using SocketIO.Serializer.MessagePack; 7 | using SocketIO.Serializer.SystemTextJson; 8 | 9 | namespace SocketIOClient.IntegrationTests.Utils; 10 | 11 | public static class SerializerHelper 12 | { 13 | public static void SetMessagePackSerializer(this SocketIO io) 14 | { 15 | io.Serializer = new SocketIOMessagePackSerializer(ContractlessStandardResolver.Options); 16 | } 17 | 18 | public static void ConfigureSystemTextJsonSerializer(this SocketIO io, JsonSerializerOptions options) 19 | { 20 | io.Serializer = new SystemTextJsonSerializer(options); 21 | } 22 | 23 | public static void ConfigureSystemTextJsonSerializerForEmitting1Parameter(this SocketIO io) 24 | { 25 | io.ConfigureSystemTextJsonSerializer(new JsonSerializerOptions 26 | { 27 | // https://learn.microsoft.com/zh-cn/dotnet/api/system.text.unicode.unicoderanges?view=net-8.0 28 | // Currently, the UnicodeRange class supports only named ranges in the Basic Multilingual Plane (BMP) 29 | // which extends from U+0000 to U+FFFF. 30 | Encoder = JavaScriptEncoder.Create(UnicodeRanges.BasicLatin, UnicodeRanges.CjkUnifiedIdeographs) 31 | }); 32 | } 33 | } -------------------------------------------------------------------------------- /tests/SocketIOClient.IntegrationTests/V2/SocketIOTests.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using FluentAssertions; 3 | using Microsoft.VisualStudio.TestTools.UnitTesting; 4 | using SocketIOClient.V2; 5 | 6 | namespace SocketIOClient.IntegrationTests.V2; 7 | 8 | [TestClass] 9 | public class SocketIOTests 10 | { 11 | public SocketIOTests() 12 | { 13 | _socket = new SocketIOClient.V2.SocketIO("http://localhost:11210", new SocketIOClient.V2.SocketIOOptions 14 | { 15 | EIO = EngineIO.V3, 16 | Reconnection = false, 17 | }); 18 | } 19 | 20 | private readonly SocketIOClient.V2.SocketIO _socket; 21 | 22 | [TestMethod] 23 | public async Task ConnectAsync_ConnectedToServer_IdAndConnectedHasValue() 24 | { 25 | await _socket.ConnectAsync(); 26 | 27 | _socket.Connected.Should().BeTrue(); 28 | _socket.Id.Should().NotBeNullOrEmpty(); 29 | } 30 | } -------------------------------------------------------------------------------- /tests/SocketIOClient.IntegrationTests/V2HttpMpNspTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using SocketIO.Core; 3 | using SocketIOClient.Transport; 4 | 5 | namespace SocketIOClient.IntegrationTests 6 | { 7 | [TestClass] 8 | public class V2HttpMpNspTests : MessagePackTests 9 | { 10 | protected override EngineIO Eio => EngineIO.V3; 11 | protected override TransportProtocol Transport => TransportProtocol.Polling; 12 | protected override bool AutoUpgrade => false; 13 | protected override string ServerUrl => "http://localhost:11212/nsp"; 14 | protected override string ServerTokenUrl => "http://localhost:11213/nsp"; 15 | } 16 | } -------------------------------------------------------------------------------- /tests/SocketIOClient.IntegrationTests/V2HttpMpTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using SocketIO.Core; 3 | using SocketIOClient.Transport; 4 | 5 | namespace SocketIOClient.IntegrationTests 6 | { 7 | [TestClass] 8 | public class V2HttpMpTests : MessagePackTests 9 | { 10 | protected override EngineIO Eio => EngineIO.V3; 11 | protected override TransportProtocol Transport => TransportProtocol.Polling; 12 | protected override bool AutoUpgrade => false; 13 | protected override string ServerUrl => "http://localhost:11212"; 14 | protected override string ServerTokenUrl => "http://localhost:11213"; 15 | } 16 | } -------------------------------------------------------------------------------- /tests/SocketIOClient.IntegrationTests/V2HttpStjAuTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using SocketIO.Core; 3 | using SocketIOClient.Transport; 4 | 5 | namespace SocketIOClient.IntegrationTests 6 | { 7 | [TestClass] 8 | public class V2HttpStjAuTests : SystemTextJsonTests 9 | { 10 | protected override EngineIO Eio => EngineIO.V3; 11 | protected override string ServerUrl => "http://localhost:11200"; 12 | protected override string ServerTokenUrl => "http://localhost:11201"; 13 | protected override TransportProtocol Transport => TransportProtocol.Polling; 14 | protected override bool AutoUpgrade => true; 15 | } 16 | } -------------------------------------------------------------------------------- /tests/SocketIOClient.IntegrationTests/V2HttpStjNspAuTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using SocketIO.Core; 3 | using SocketIOClient.Transport; 4 | 5 | namespace SocketIOClient.IntegrationTests 6 | { 7 | [TestClass] 8 | public class V2HttpStjNspAuTests : SystemTextJsonTests 9 | { 10 | protected override EngineIO Eio => EngineIO.V3; 11 | protected override string ServerUrl => "http://localhost:11200/nsp"; 12 | protected override string ServerTokenUrl => "http://localhost:11201/nsp"; 13 | protected override TransportProtocol Transport => TransportProtocol.Polling; 14 | protected override bool AutoUpgrade => true; 15 | } 16 | } -------------------------------------------------------------------------------- /tests/SocketIOClient.IntegrationTests/V2HttpStjNspTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using System.Threading.Tasks; 4 | using FluentAssertions; 5 | using Microsoft.VisualStudio.TestTools.UnitTesting; 6 | using SocketIO.Core; 7 | using SocketIOClient.Transport; 8 | using SocketIOClient.Transport.WebSockets; 9 | 10 | namespace SocketIOClient.IntegrationTests 11 | { 12 | [TestClass] 13 | public class V2HttpStjNspTests : SystemTextJsonTests 14 | { 15 | protected override EngineIO Eio => EngineIO.V3; 16 | protected override TransportProtocol Transport => TransportProtocol.Polling; 17 | protected override bool AutoUpgrade => false; 18 | protected override string ServerUrl => "http://localhost:11210/nsp"; 19 | protected override string ServerTokenUrl => "http://localhost:11211/nsp"; 20 | 21 | [TestMethod] 22 | public async Task Should_automatically_upgrade_to_websocket() 23 | { 24 | var io = new SocketIO("http://localhost:11200/nsp", new SocketIOOptions 25 | { 26 | EIO = EngineIO.V3, 27 | AutoUpgrade = true, 28 | Reconnection = false, 29 | Transport = TransportProtocol.Polling, 30 | ConnectionTimeout = TimeSpan.FromSeconds(2) 31 | }); 32 | await io.ConnectAsync(); 33 | var prop = io.GetType().GetProperty("Transport", BindingFlags.Instance | BindingFlags.NonPublic); 34 | var transport = prop!.GetValue(io); 35 | 36 | io.Options.Transport.Should().Be(TransportProtocol.WebSocket); 37 | transport.Should().BeOfType(); 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /tests/SocketIOClient.IntegrationTests/V2HttpStjTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using System.Threading.Tasks; 4 | using FluentAssertions; 5 | using Microsoft.VisualStudio.TestTools.UnitTesting; 6 | using SocketIO.Core; 7 | using SocketIOClient.Transport; 8 | using SocketIOClient.Transport.WebSockets; 9 | 10 | namespace SocketIOClient.IntegrationTests 11 | { 12 | [TestClass] 13 | public class V2HttpStjTests : SystemTextJsonTests 14 | { 15 | protected override EngineIO Eio => EngineIO.V3; 16 | protected override TransportProtocol Transport => TransportProtocol.Polling; 17 | protected override bool AutoUpgrade => false; 18 | protected override string ServerUrl => "http://localhost:11210"; 19 | protected override string ServerTokenUrl => "http://localhost:11211"; 20 | 21 | [TestMethod] 22 | public async Task Should_automatically_upgrade_to_websocket() 23 | { 24 | var io = new SocketIO("http://localhost:11200", new SocketIOOptions 25 | { 26 | EIO = EngineIO.V3, 27 | AutoUpgrade = true, 28 | Reconnection = false, 29 | Transport = TransportProtocol.Polling, 30 | ConnectionTimeout = TimeSpan.FromSeconds(2) 31 | }); 32 | await io.ConnectAsync(); 33 | var prop = io.GetType().GetProperty("Transport", BindingFlags.Instance | BindingFlags.NonPublic); 34 | var transport = prop!.GetValue(io); 35 | 36 | io.Options.Transport.Should().Be(TransportProtocol.WebSocket); 37 | transport.Should().BeOfType(); 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /tests/SocketIOClient.IntegrationTests/V2WsMpNspTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using SocketIO.Core; 3 | using SocketIOClient.Transport; 4 | 5 | namespace SocketIOClient.IntegrationTests 6 | { 7 | [TestClass] 8 | public class V2WsMpNspTests : MessagePackTests 9 | { 10 | protected override EngineIO Eio => EngineIO.V3; 11 | protected override TransportProtocol Transport => TransportProtocol.WebSocket; 12 | protected override bool AutoUpgrade => false; 13 | protected override string ServerUrl => "http://localhost:11202/nsp"; 14 | protected override string ServerTokenUrl => "http://localhost:11203/nsp"; 15 | } 16 | } -------------------------------------------------------------------------------- /tests/SocketIOClient.IntegrationTests/V2WsMpTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using SocketIO.Core; 3 | using SocketIOClient.Transport; 4 | 5 | namespace SocketIOClient.IntegrationTests 6 | { 7 | [TestClass] 8 | public class V2WsMpTests : MessagePackTests 9 | { 10 | protected override EngineIO Eio => EngineIO.V3; 11 | protected override TransportProtocol Transport => TransportProtocol.WebSocket; 12 | protected override bool AutoUpgrade => false; 13 | protected override string ServerUrl => "http://localhost:11202"; 14 | protected override string ServerTokenUrl => "http://localhost:11203"; 15 | } 16 | } -------------------------------------------------------------------------------- /tests/SocketIOClient.IntegrationTests/V2WsStjNspTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using SocketIO.Core; 3 | using SocketIOClient.Transport; 4 | 5 | namespace SocketIOClient.IntegrationTests 6 | { 7 | [TestClass] 8 | public class V2WsStjNspTests : SystemTextJsonTests 9 | { 10 | protected override EngineIO Eio => EngineIO.V3; 11 | protected override TransportProtocol Transport => TransportProtocol.WebSocket; 12 | protected override bool AutoUpgrade => false; 13 | protected override string ServerUrl => "http://localhost:11200/nsp"; 14 | protected override string ServerTokenUrl => "http://localhost:11201/nsp"; 15 | } 16 | } -------------------------------------------------------------------------------- /tests/SocketIOClient.IntegrationTests/V2WsStjTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using SocketIO.Core; 3 | using SocketIOClient.Transport; 4 | 5 | namespace SocketIOClient.IntegrationTests 6 | { 7 | [TestClass] 8 | public class V2WsStjTests : SystemTextJsonTests 9 | { 10 | protected override EngineIO Eio => EngineIO.V3; 11 | protected override TransportProtocol Transport => TransportProtocol.WebSocket; 12 | protected override bool AutoUpgrade => false; 13 | protected override string ServerUrl => "http://localhost:11200"; 14 | protected override string ServerTokenUrl => "http://localhost:11201"; 15 | } 16 | } -------------------------------------------------------------------------------- /tests/SocketIOClient.IntegrationTests/V4HttpMpNspTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using SocketIO.Core; 3 | using SocketIOClient.Transport; 4 | 5 | namespace SocketIOClient.IntegrationTests 6 | { 7 | [TestClass] 8 | public class V4HttpMpNspTests : MessagePackTests 9 | { 10 | protected override EngineIO Eio => EngineIO.V4; 11 | protected override string ServerUrl => "http://localhost:11412/nsp"; 12 | protected override string ServerTokenUrl => "http://localhost:11413/nsp"; 13 | protected override TransportProtocol Transport => TransportProtocol.Polling; 14 | protected override bool AutoUpgrade => false; 15 | } 16 | } -------------------------------------------------------------------------------- /tests/SocketIOClient.IntegrationTests/V4HttpMpTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using SocketIO.Core; 3 | using SocketIOClient.Transport; 4 | 5 | namespace SocketIOClient.IntegrationTests 6 | { 7 | [TestClass] 8 | public class V4HttpMpTests : MessagePackTests 9 | { 10 | protected override EngineIO Eio => EngineIO.V4; 11 | protected override TransportProtocol Transport => TransportProtocol.Polling; 12 | protected override bool AutoUpgrade => false; 13 | protected override string ServerUrl => "http://localhost:11412"; 14 | protected override string ServerTokenUrl => "http://localhost:11413"; 15 | } 16 | } -------------------------------------------------------------------------------- /tests/SocketIOClient.IntegrationTests/V4HttpStjAuTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using SocketIO.Core; 3 | using SocketIOClient.Transport; 4 | 5 | namespace SocketIOClient.IntegrationTests 6 | { 7 | [TestClass] 8 | public class V4HttpStjAuTests : SystemTextJsonTests 9 | { 10 | protected override EngineIO Eio => EngineIO.V4; 11 | protected override string ServerUrl => "http://localhost:11400"; 12 | protected override string ServerTokenUrl => "http://localhost:11401"; 13 | protected override TransportProtocol Transport => TransportProtocol.Polling; 14 | protected override bool AutoUpgrade => true; 15 | } 16 | } -------------------------------------------------------------------------------- /tests/SocketIOClient.IntegrationTests/V4HttpStjNspAuTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using SocketIO.Core; 3 | using SocketIOClient.Transport; 4 | 5 | namespace SocketIOClient.IntegrationTests 6 | { 7 | [TestClass] 8 | public class V4HttpStjNspAuTests : SystemTextJsonTests 9 | { 10 | protected override EngineIO Eio => EngineIO.V4; 11 | protected override string ServerUrl => "http://localhost:11400/nsp"; 12 | protected override string ServerTokenUrl => "http://localhost:11401/nsp"; 13 | protected override TransportProtocol Transport => TransportProtocol.Polling; 14 | protected override bool AutoUpgrade => true; 15 | } 16 | } -------------------------------------------------------------------------------- /tests/SocketIOClient.IntegrationTests/V4HttpStjNspTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using SocketIO.Core; 3 | using SocketIOClient.Transport; 4 | 5 | namespace SocketIOClient.IntegrationTests 6 | { 7 | [TestClass] 8 | public class V4HttpStjNspTests : SystemTextJsonTests 9 | { 10 | protected override EngineIO Eio => EngineIO.V4; 11 | protected override string ServerUrl => "http://localhost:11410/nsp"; 12 | protected override string ServerTokenUrl => "http://localhost:11411/nsp"; 13 | protected override TransportProtocol Transport => TransportProtocol.Polling; 14 | protected override bool AutoUpgrade => false; 15 | } 16 | } -------------------------------------------------------------------------------- /tests/SocketIOClient.IntegrationTests/V4HttpStjTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using System.Threading.Tasks; 4 | using FluentAssertions; 5 | using Microsoft.VisualStudio.TestTools.UnitTesting; 6 | using SocketIO.Core; 7 | using SocketIOClient.Transport; 8 | using SocketIOClient.Transport.WebSockets; 9 | 10 | namespace SocketIOClient.IntegrationTests 11 | { 12 | [TestClass] 13 | public class V4HttpStjTests : SystemTextJsonTests 14 | { 15 | protected override EngineIO Eio => EngineIO.V4; 16 | protected override string ServerUrl => "http://localhost:11410"; 17 | protected override string ServerTokenUrl => "http://localhost:11411"; 18 | protected override TransportProtocol Transport => TransportProtocol.Polling; 19 | protected override bool AutoUpgrade => false; 20 | 21 | [TestMethod] 22 | public async Task Should_ignore_SSL_error() 23 | { 24 | var callback = false; 25 | var io = new SocketIO("https://localhost:11414", new SocketIOOptions 26 | { 27 | EIO = EngineIO.V4, 28 | AutoUpgrade = false, 29 | Reconnection = false, 30 | Transport = TransportProtocol.Polling, 31 | ConnectionTimeout = TimeSpan.FromSeconds(2), 32 | RemoteCertificateValidationCallback = (_, _, _, _) => 33 | { 34 | callback = true; 35 | return true; 36 | } 37 | }); 38 | var connected = false; 39 | io.OnConnected += (_, _) => connected = true; 40 | await io.ConnectAsync(); 41 | 42 | connected.Should().BeTrue(); 43 | callback.Should().BeTrue(); 44 | } 45 | 46 | [TestMethod] 47 | public async Task Should_automatically_upgrade_to_websocket() 48 | { 49 | var io = new SocketIO("http://localhost:11400", new SocketIOOptions 50 | { 51 | EIO = EngineIO.V4, 52 | AutoUpgrade = true, 53 | Reconnection = false, 54 | Transport = TransportProtocol.Polling, 55 | ConnectionTimeout = TimeSpan.FromSeconds(2) 56 | }); 57 | await io.ConnectAsync(); 58 | await Task.Delay(100); 59 | var prop = io.GetType().GetProperty("Transport", BindingFlags.Instance | BindingFlags.NonPublic); 60 | var transport = prop!.GetValue(io); 61 | 62 | io.Options.Transport.Should().Be(TransportProtocol.WebSocket); 63 | transport.Should().BeOfType(); 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /tests/SocketIOClient.IntegrationTests/V4WsMpNspTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using SocketIO.Core; 3 | using SocketIOClient.Transport; 4 | 5 | namespace SocketIOClient.IntegrationTests 6 | { 7 | [TestClass] 8 | public class V4WsMpNspTests : MessagePackTests 9 | { 10 | protected override EngineIO Eio => EngineIO.V4; 11 | protected override TransportProtocol Transport => TransportProtocol.WebSocket; 12 | protected override bool AutoUpgrade => false; 13 | protected override string ServerUrl => "http://localhost:11402/nsp"; 14 | protected override string ServerTokenUrl => "http://localhost:11403/nsp"; 15 | } 16 | } -------------------------------------------------------------------------------- /tests/SocketIOClient.IntegrationTests/V4WsMpTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using SocketIO.Core; 3 | using SocketIOClient.Transport; 4 | 5 | namespace SocketIOClient.IntegrationTests 6 | { 7 | [TestClass] 8 | public class V4WsMpTests : MessagePackTests 9 | { 10 | protected override string ServerUrl => "http://localhost:11402"; 11 | protected override EngineIO Eio => EngineIO.V4; 12 | protected override string ServerTokenUrl => "http://localhost:11403"; 13 | protected override TransportProtocol Transport => TransportProtocol.WebSocket; 14 | protected override bool AutoUpgrade => false; 15 | } 16 | } -------------------------------------------------------------------------------- /tests/SocketIOClient.IntegrationTests/V4WsStjNspTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using SocketIO.Core; 3 | using SocketIOClient.Transport; 4 | 5 | namespace SocketIOClient.IntegrationTests 6 | { 7 | [TestClass] 8 | public class V4WsStjNspTests : SystemTextJsonTests 9 | { 10 | protected override EngineIO Eio => EngineIO.V4; 11 | protected override string ServerUrl => "http://localhost:11400/nsp"; 12 | protected override string ServerTokenUrl => "http://localhost:11401/nsp"; 13 | protected override TransportProtocol Transport => TransportProtocol.WebSocket; 14 | protected override bool AutoUpgrade => false; 15 | } 16 | } -------------------------------------------------------------------------------- /tests/SocketIOClient.Serializer.NewtonsoftJson.Tests/NewtonJsonEngineIO3MessageAdapterTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using SocketIOClient.Core.Messages; 3 | 4 | namespace SocketIOClient.Serializer.NewtonsoftJson.Tests; 5 | 6 | // ReSharper disable once InconsistentNaming 7 | public class NewtonJsonEngineIO3MessageAdapterTests 8 | { 9 | private readonly NewtonJsonEngineIO3MessageAdapter _adapter = new(); 10 | 11 | [Theory] 12 | [InlineData("", null)] 13 | [InlineData("/nsp,", "/nsp")] 14 | public void DeserializeConnectedMessage_WhenCalled_AlwaysPass(string text, string? ns) 15 | { 16 | var message = _adapter.DeserializeConnectedMessage(text); 17 | message.Should() 18 | .BeEquivalentTo(new ConnectedMessage 19 | { 20 | Namespace = ns, 21 | Sid = null, 22 | }); 23 | } 24 | 25 | [Theory] 26 | [InlineData("\"error message\"", "error message")] 27 | [InlineData("\"\\\"Authentication error\\\"\"", "\"Authentication error\"")] 28 | public void DeserializeErrorMessage_WhenCalled_AlwaysPass(string text, string error) 29 | { 30 | var message = _adapter.DeserializeErrorMessage(text); 31 | message.Should() 32 | .BeEquivalentTo(new ErrorMessage 33 | { 34 | // TODO: is namespace supported by eio3? 35 | Namespace = null, 36 | Error = error, 37 | }); 38 | } 39 | } -------------------------------------------------------------------------------- /tests/SocketIOClient.Serializer.NewtonsoftJson.Tests/NewtonJsonEngineIO4MessageAdapterTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using SocketIOClient.Core.Messages; 3 | 4 | namespace SocketIOClient.Serializer.NewtonsoftJson.Tests; 5 | 6 | // ReSharper disable once InconsistentNaming 7 | public class NewtonJsonEngineIO4MessageAdapterTests 8 | { 9 | private readonly NewtonJsonEngineIO4MessageAdapter _adapter = new(); 10 | 11 | [Theory] 12 | [InlineData("{\"sid\":\"123\"}", "123", null)] 13 | [InlineData("/test,{\"sid\":\"123\"}", "123", "/test")] 14 | public void DeserializeConnectedMessage_WhenCalled_AlwaysPass(string text, string sid, string? ns) 15 | { 16 | var message = _adapter.DeserializeConnectedMessage(text); 17 | message.Should() 18 | .BeEquivalentTo(new ConnectedMessage 19 | { 20 | Namespace = ns, 21 | Sid = sid, 22 | }); 23 | } 24 | 25 | [Theory] 26 | [InlineData("{\"message\":\"error message\"}", "error message", null)] 27 | [InlineData("/test,{\"message\":\"error message\"}", "error message", "/test")] 28 | public void DeserializeErrorMessage_WhenCalled_AlwaysPass(string text, string error, string? ns) 29 | { 30 | var message = _adapter.DeserializeErrorMessage(text); 31 | message.Should() 32 | .BeEquivalentTo(new ErrorMessage 33 | { 34 | Namespace = ns, 35 | Error = error, 36 | }); 37 | } 38 | } -------------------------------------------------------------------------------- /tests/SocketIOClient.Serializer.NewtonsoftJson.Tests/SocketIOClient.Serializer.NewtonsoftJson.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | false 9 | true 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | TestFile.cs 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /tests/SocketIOClient.Serializer.Tests/Decapsulation/DecapsulatorTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using SocketIOClient.Core.Messages; 3 | using SocketIOClient.Serializer.Decapsulation; 4 | 5 | namespace SocketIOClient.Serializer.Tests.Decapsulation; 6 | 7 | public class DecapsulatorTests 8 | { 9 | [Theory] 10 | [InlineData("", false, null, null)] 11 | [InlineData("0", true, MessageType.Opened, "")] 12 | [InlineData("2", true, MessageType.Ping, "")] 13 | [InlineData("3", true, MessageType.Pong, "")] 14 | [InlineData("40", true, MessageType.Connected, "")] 15 | [InlineData("40/test,", true, MessageType.Connected, "/test,")] 16 | [InlineData("42[\"hello\"]", true, MessageType.Event, "[\"hello\"]")] 17 | [InlineData("43/test,1[\"hello\"]", true, MessageType.Ack, "/test,1[\"hello\"]")] 18 | [InlineData("461-/test,2[]", true, MessageType.BinaryAck, "1-/test,2[]")] 19 | [InlineData( 20 | "0{\"sid\":\"123\",\"upgrades\":[],\"pingInterval\":10000,\"pingTimeout\":5000}", 21 | true, 22 | MessageType.Opened, 23 | "{\"sid\":\"123\",\"upgrades\":[],\"pingInterval\":10000,\"pingTimeout\":5000}")] 24 | public void DecapsulateRawText_WhenCalled_AlwaysPass(string text, bool success, MessageType? type, string data) 25 | { 26 | var decapsulator = new Decapsulator(); 27 | var result = decapsulator.DecapsulateRawText(text); 28 | 29 | result.Should() 30 | .BeEquivalentTo(new DecapsulationResult 31 | { 32 | Success = success, 33 | Type = type, 34 | Data = data, 35 | }); 36 | } 37 | 38 | [Theory] 39 | [InlineData("[\"hello\"]", 0, null, "[\"hello\"]")] 40 | [InlineData("1[\"hello\"]", 1, null, "[\"hello\"]")] 41 | [InlineData("/test,[\"hello\"]", 0, "/test", "[\"hello\"]")] 42 | [InlineData("/test,1[\"hello\"]", 1, "/test", "[\"hello\"]")] 43 | public void DecapsulateEventMessage_WhenCalled_AlwaysPass(string text, int id, string? ns, string data) 44 | { 45 | var decapsulator = new Decapsulator(); 46 | var result = decapsulator.DecapsulateEventMessage(text); 47 | 48 | result.Should() 49 | .BeEquivalentTo(new MessageResult 50 | { 51 | Id = id, 52 | Namespace = ns, 53 | Data = data, 54 | }); 55 | } 56 | 57 | [Theory] 58 | [InlineData("1-[\"event\",{\"_placeholder\":true,\"num\":0}]", 0, null, "[\"event\",{\"_placeholder\":true,\"num\":0}]", 1)] 59 | [InlineData("1-2[\"event\",{\"_placeholder\":true,\"num\":0}]", 2, null, "[\"event\",{\"_placeholder\":true,\"num\":0}]", 1)] 60 | [InlineData("1-8[\"event\"]", 8, null, "[\"event\"]", 1)] 61 | [InlineData("1-/test,8[\"event\"]", 8, "/test", "[\"event\"]", 1)] 62 | public void DecapsulateBinaryEventMessage_WhenCalled_AlwaysPass(string text, int id, string? ns, string data, int bytesCount) 63 | { 64 | var decapsulator = new Decapsulator(); 65 | var result = decapsulator.DecapsulateBinaryEventMessage(text); 66 | 67 | result.Should() 68 | .BeEquivalentTo(new BinaryEventMessageResult 69 | { 70 | Id = id, 71 | Namespace = ns, 72 | Data = data, 73 | BytesCount = bytesCount, 74 | }); 75 | } 76 | } -------------------------------------------------------------------------------- /tests/SocketIOClient.Serializer.Tests/SocketIOClient.Serializer.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | false 9 | true 10 | default 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /tests/SocketIOClient.UnitTests/ArchUnit/ArchitectureTests.cs: -------------------------------------------------------------------------------- 1 | using ArchUnitNET.xUnit; 2 | using ArchUnitNET.Domain; 3 | using ArchUnitNET.Fluent.Slices; 4 | using ArchUnitNET.Loader; 5 | using Xunit; 6 | 7 | namespace SocketIOClient.UnitTests.ArchUnit; 8 | 9 | public class ArchitectureTests 10 | { 11 | private static readonly Architecture Architecture = new ArchLoader().LoadAssemblies( 12 | System.Reflection.Assembly.Load("SocketIOClient"), 13 | System.Reflection.Assembly.Load("SocketIOClient.Core"), 14 | System.Reflection.Assembly.Load("SocketIOClient.Serializer") 15 | ).Build(); 16 | 17 | [Fact] 18 | public void CycleDependencies_NotExists() 19 | { 20 | var sliceRuleInitializer = SliceRuleDefinition.Slices(); 21 | var socketIOClientRule = sliceRuleInitializer.Matching($"{nameof(SocketIOClient)}.(*)").Should() 22 | .BeFreeOfCycles(); 23 | 24 | socketIOClientRule.Check(Architecture); 25 | } 26 | } -------------------------------------------------------------------------------- /tests/SocketIOClient.UnitTests/DefaultUriConverterTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using FluentAssertions; 4 | using JetBrains.Annotations; 5 | using Mono.Collections.Generic; 6 | using SocketIOClient.V2.UriConverter; 7 | using Xunit; 8 | 9 | namespace SocketIOClient.UnitTests 10 | { 11 | public class DefaultUriConverterTest 12 | { 13 | [Fact] 14 | public void GetServerUri_GivenQueryParams_AppendAsQueryString() 15 | { 16 | var serverUri = new Uri("http://localhost"); 17 | var kvs = new List> 18 | { 19 | new("token", "test"), 20 | }; 21 | var converter = new DefaultUriConverter(3); 22 | var result = converter.GetServerUri(false, serverUri, string.Empty, kvs); 23 | result.Should().Be("http://localhost/socket.io/?EIO=3&transport=polling&token=test"); 24 | } 25 | 26 | 27 | [Theory] 28 | [InlineData(null, "http://localhost/socket.io/?EIO=4&transport=polling")] 29 | [InlineData("", "http://localhost/socket.io/?EIO=4&transport=polling")] 30 | [InlineData(" ", "http://localhost/socket.io/?EIO=4&transport=polling")] 31 | [InlineData("/test", "http://localhost/test/?EIO=4&transport=polling")] 32 | public void GetServerUri_AppendPathToUri([CanBeNull] string? path, string expected) 33 | { 34 | var serverUri = new Uri("http://localhost"); 35 | var converter = new DefaultUriConverter(4); 36 | var result = converter.GetServerUri(false, serverUri, path, null); 37 | result.Should().Be(expected); 38 | } 39 | 40 | [Theory] 41 | [InlineData("http://localhost:80", "http://localhost/socket.io/?EIO=4&transport=polling")] 42 | [InlineData("https://localhost:443", "https://localhost/socket.io/?EIO=4&transport=polling")] 43 | [InlineData("http://localhost:443", "http://localhost:443/socket.io/?EIO=4&transport=polling")] 44 | [InlineData("https://localhost:80", "https://localhost:80/socket.io/?EIO=4&transport=polling")] 45 | public void GetServerUri_GivenPort_ShouldNotAppearInUriIfIsDefault(string uri, string expected) 46 | { 47 | var serverUri = new Uri(uri); 48 | var kvs = new ReadOnlyCollection>(Array.Empty>()); 49 | var converter = new DefaultUriConverter(4); 50 | var result = converter.GetServerUri(false, serverUri, string.Empty, kvs); 51 | result.Should().Be(expected); 52 | } 53 | 54 | [Theory] 55 | [InlineData(false, "http://localhost", "http://localhost/socket.io/?EIO=4&transport=polling")] 56 | [InlineData(false, "https://localhost", "https://localhost/socket.io/?EIO=4&transport=polling")] 57 | [InlineData(true, "ws://localhost", "ws://localhost/socket.io/?EIO=4&transport=websocket")] 58 | [InlineData(true, "wss://localhost", "wss://localhost/socket.io/?EIO=4&transport=websocket")] 59 | public void GetServerUri_DifferentProtocol(bool ws, string uri, string expected) 60 | { 61 | var serverUri = new Uri(uri); 62 | var converter = new DefaultUriConverter(4); 63 | var result = converter.GetServerUri(ws, serverUri, string.Empty, null); 64 | result.Should().Be(expected); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /tests/SocketIOClient.UnitTests/SocketIOClient.UnitTests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | false 9 | true 10 | default 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | all 20 | runtime; build; native; contentfiles; analyzers; buildtransitive 21 | 22 | 23 | 24 | 25 | 26 | 27 | all 28 | runtime; build; native; contentfiles; analyzers; buildtransitive 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /tests/SocketIOClient.UnitTests/V2/DefaultSessionFactoryTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using SocketIOClient.V2; 3 | using SocketIOClient.V2.Session; 4 | 5 | namespace SocketIOClient.UnitTests.V2; 6 | 7 | public class DefaultSessionFactoryTests 8 | { 9 | public DefaultSessionFactoryTests() 10 | { 11 | _sessionFactory = new DefaultSessionFactory(); 12 | } 13 | 14 | private readonly DefaultSessionFactory _sessionFactory; 15 | 16 | [Fact] 17 | public void New_OptionIsNull_ThrowNullReferenceException() 18 | { 19 | _sessionFactory.Invoking(x => x.New(EngineIO.V3, null)) 20 | .Should() 21 | .ThrowExactly(); 22 | } 23 | 24 | [Fact] 25 | public void New_WhenCalled_AlwaysReturnHttpSession() 26 | { 27 | var session = _sessionFactory.New(EngineIO.V3, new SessionOptions()); 28 | session.Should().BeOfType(); 29 | } 30 | } -------------------------------------------------------------------------------- /tests/SocketIOClient.UnitTests/V2/Infrastructure/RandomDelayRetryPolicyTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using NSubstitute; 3 | using SocketIOClient.V2.Infrastructure; 4 | 5 | namespace SocketIOClient.UnitTests.V2.Infrastructure; 6 | 7 | public class RandomDelayRetryPolicyTests 8 | { 9 | public RandomDelayRetryPolicyTests() 10 | { 11 | _random = Substitute.For(); 12 | _policy = new RandomDelayRetryPolicy(_random); 13 | } 14 | 15 | private readonly RandomDelayRetryPolicy _policy; 16 | private readonly IRandom _random; 17 | 18 | [Fact] 19 | public async Task RetryAsync_TimesLessThan1_ThrowException() 20 | { 21 | var func = Substitute.For>(); 22 | 23 | await _policy 24 | .Invoking(x => x.RetryAsync(0, func)) 25 | .Should() 26 | .ThrowAsync() 27 | .WithMessage("Times must be greater than 0 (Parameter 'times')"); 28 | } 29 | 30 | [Fact] 31 | public async Task RetryAsync_TimesIs2AndNoExceptionWhenFirstCall_FuncIsCalled1Time() 32 | { 33 | var func = Substitute.For>(); 34 | 35 | await _policy.RetryAsync(2, func); 36 | 37 | await func.Received(1).Invoke(); 38 | _random.DidNotReceive().Next(Arg.Any()); 39 | } 40 | 41 | [Fact] 42 | public async Task RetryAsync_FirstThrowThenOk_FuncIsCalled2TimesNoException() 43 | { 44 | var func = Substitute.For>(); 45 | func.Invoke().Returns(Task.FromException(new InvalidOperationException()), Task.CompletedTask); 46 | 47 | await _policy.RetryAsync(2, func); 48 | 49 | await func.Received(2).Invoke(); 50 | _random.Received(1).Next(Arg.Any()); 51 | } 52 | 53 | [Fact] 54 | public async Task RetryAsync_FirstOkThenThrow_FuncIsCalled1TimeNoException() 55 | { 56 | var func = Substitute.For>(); 57 | func.Invoke().Returns(Task.CompletedTask, Task.FromException(new InvalidOperationException())); 58 | 59 | await _policy.RetryAsync(2, func); 60 | 61 | await func.Received(1).Invoke(); 62 | _random.DidNotReceive().Next(Arg.Any()); 63 | } 64 | 65 | [Fact] 66 | public async Task RetryAsync_Throw2Times_FuncIsCalled2TimesAndThrowException() 67 | { 68 | var func = Substitute.For>(); 69 | func.Invoke().Returns( 70 | Task.FromException(new InvalidOperationException("1")), 71 | Task.FromException(new InvalidOperationException("2"))); 72 | 73 | await _policy 74 | .Invoking(x => x.RetryAsync(2, func)) 75 | .Should() 76 | .ThrowAsync() 77 | .WithMessage("2"); 78 | 79 | await func.Received(2).Invoke(); 80 | _random.Received(1).Next(Arg.Any()); 81 | } 82 | } -------------------------------------------------------------------------------- /tests/SocketIOClient.UnitTests/V2/Protocol/Http/HttpAdapterTests.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using FluentAssertions; 3 | using NSubstitute; 4 | using SocketIOClient.Core; 5 | using SocketIOClient.V2.Observers; 6 | using SocketIOClient.V2.Protocol.Http; 7 | 8 | namespace SocketIOClient.UnitTests.V2.Protocol.Http; 9 | 10 | public class HttpAdapterTests 11 | { 12 | public HttpAdapterTests() 13 | { 14 | _httpClient = Substitute.For(); 15 | _httpAdapter = new HttpAdapter(_httpClient); 16 | } 17 | 18 | private readonly HttpAdapter _httpAdapter; 19 | private readonly IHttpClient _httpClient; 20 | 21 | [Fact] 22 | public async Task SendProtocolMessageAsync_WhenCalled_OnNextShouldBeTriggered() 23 | { 24 | var observer = Substitute.For>(); 25 | _httpAdapter.Subscribe(observer); 26 | 27 | await _httpAdapter.SendAsync(new ProtocolMessage(), CancellationToken.None); 28 | 29 | await observer.Received().OnNextAsync(Arg.Any()); 30 | } 31 | 32 | [Fact] 33 | public async Task SendHttpRequestAsync_WhenCalled_OnNextShouldBeTriggered() 34 | { 35 | var observer = Substitute.For>(); 36 | _httpAdapter.Subscribe(observer); 37 | 38 | await _httpAdapter.SendAsync(new HttpRequest(), CancellationToken.None); 39 | 40 | await observer.Received().OnNextAsync(Arg.Any()); 41 | } 42 | 43 | [Fact] 44 | public async Task SendHttpRequestAsync_ObserverBlocked100Ms_SendAsyncNotBlockedByObserver() 45 | { 46 | var observer = Substitute.For>(); 47 | observer.OnNextAsync(Arg.Any()).Returns(async _ => await Task.Delay(100)); 48 | _httpAdapter.Subscribe(observer); 49 | 50 | var stopwatch = Stopwatch.StartNew(); 51 | await _httpAdapter.SendAsync(new HttpRequest(), CancellationToken.None); 52 | stopwatch.Stop(); 53 | 54 | stopwatch.ElapsedMilliseconds.Should().BeLessThan(20); 55 | } 56 | } -------------------------------------------------------------------------------- /tests/SocketIOClient.UnitTests/V2/Protocol/Http/HttpRequestTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using FluentAssertions; 3 | using SocketIOClient.V2.Protocol.Http; 4 | using Xunit; 5 | 6 | namespace SocketIOClient.UnitTests.V2.Protocol.Http; 7 | 8 | public class HttpRequestTests 9 | { 10 | [Fact] 11 | public void DefaultValues() 12 | { 13 | var req = new HttpRequest(); 14 | req.Should() 15 | .BeEquivalentTo(new HttpRequest 16 | { 17 | Uri = null, 18 | Method = RequestMethod.Get, 19 | Headers = new Dictionary(), 20 | BodyType = RequestBodyType.Text, 21 | BodyBytes = null, 22 | BodyText = null, 23 | }); 24 | } 25 | } -------------------------------------------------------------------------------- /tests/SocketIOClient.UnitTests/V2/Protocol/Http/SystemHttpClientTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Net.Http; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using FluentAssertions; 7 | using RichardSzalay.MockHttp; 8 | using SocketIOClient.V2.Protocol.Http; 9 | using Xunit; 10 | 11 | namespace SocketIOClient.UnitTests.V2.Protocol.Http; 12 | 13 | public class SystemHttpClientTests 14 | { 15 | public SystemHttpClientTests() 16 | { 17 | _httpMessageHandler = new MockHttpMessageHandler(); 18 | _httpClient = new SystemHttpClient(_httpMessageHandler.ToHttpClient()); 19 | } 20 | 21 | private readonly SystemHttpClient _httpClient; 22 | private readonly MockHttpMessageHandler _httpMessageHandler; 23 | 24 | [Fact] 25 | public async Task SendAsync_WhenReturnStringContent_AlwaysPass() 26 | { 27 | _httpMessageHandler 28 | .When("https://www.google.com") 29 | .Respond("text/plain", "Hello, Google!"); 30 | 31 | var res = await _httpClient.SendAsync(new HttpRequest 32 | { 33 | Uri = new Uri("https://www.google.com"), 34 | }, CancellationToken.None); 35 | 36 | res.MediaType.Should().Be("text/plain"); 37 | var body = await res.ReadAsStringAsync(); 38 | body.Should().Be("Hello, Google!"); 39 | } 40 | 41 | [Fact] 42 | public async Task SendAsync_WhenPassACanceledToken_ThrowTaskCanceledException() 43 | { 44 | _httpMessageHandler 45 | .When("https://www.google.com") 46 | .Respond("text/plain", "Hello, Google!"); 47 | 48 | await _httpClient.Invoking(async x => 49 | { 50 | using var cts = new CancellationTokenSource(); 51 | cts.Cancel(true); 52 | return await x.SendAsync(new HttpRequest 53 | { 54 | Uri = new Uri("https://www.google.com"), 55 | }, cts.Token); 56 | }) 57 | .Should() 58 | .ThrowAsync(); 59 | } 60 | 61 | [Fact] 62 | public async Task SendAsync_WhenResponseByteArrayContent_AlwaysPass() 63 | { 64 | var bytes = "Test Zip"u8.ToArray(); 65 | _httpMessageHandler 66 | .When(HttpMethod.Post, "https://www.google.com/test.zip") 67 | .WithContent("Download") 68 | .Respond(HttpStatusCode.OK, new ByteArrayContent(bytes)); 69 | 70 | var res = await _httpClient.SendAsync(new HttpRequest 71 | { 72 | Method = RequestMethod.Post, 73 | BodyText = "Download", 74 | Uri = new Uri("https://www.google.com/test.zip"), 75 | }, CancellationToken.None); 76 | 77 | res.MediaType.Should().BeNull(); 78 | var textBody = await res.ReadAsStringAsync(); 79 | textBody.Should().Be("Test Zip"); 80 | var byteBody = await res.ReadAsByteArrayAsync(); 81 | byteBody.Should().Equal(bytes); 82 | } 83 | } -------------------------------------------------------------------------------- /tests/SocketIOClient.UnitTests/V2/Protocol/Http/SystemHttpResponseTests.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Http; 2 | using System.Net.Http.Headers; 3 | using FluentAssertions; 4 | using SocketIOClient.V2.Protocol.Http; 5 | using Xunit; 6 | 7 | namespace SocketIOClient.UnitTests.V2.Protocol.Http; 8 | 9 | public class SystemHttpResponseTests 10 | { 11 | [Theory] 12 | [InlineData("text/plain")] 13 | [InlineData("text/html")] 14 | [InlineData("application/json")] 15 | [InlineData("application/octet-stream")] 16 | public void MediaType_WhenGet_AlwaysSameAsHttpResponseMessage(string mediaType) 17 | { 18 | var sysRes = new HttpResponseMessage 19 | { 20 | Content = new StringContent(string.Empty) 21 | { 22 | Headers = 23 | { 24 | ContentType = new MediaTypeHeaderValue(mediaType), 25 | }, 26 | }, 27 | }; 28 | 29 | var res = new SystemHttpResponse(sysRes); 30 | 31 | res.MediaType.Should().Be(mediaType); 32 | } 33 | } -------------------------------------------------------------------------------- /tests/SocketIOClient.UnitTests/V2/Serializer/SystemTextJson/SystemJsonEngineIO3MessageAdapterTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using JetBrains.Annotations; 3 | using SocketIOClient.Core.Messages; 4 | using SocketIOClient.V2.Serializer.SystemTextJson; 5 | using Xunit; 6 | 7 | namespace SocketIOClient.UnitTests.V2.Serializer.SystemTextJson; 8 | 9 | // ReSharper disable once InconsistentNaming 10 | public class SystemJsonEngineIO3MessageAdapterTests 11 | { 12 | private readonly SystemJsonEngineIO3MessageAdapter _adapter = new(); 13 | 14 | [Theory] 15 | [InlineData("", null)] 16 | [InlineData("/nsp,", "/nsp")] 17 | public void DeserializeConnectedMessage_WhenCalled_AlwaysPass(string text, [CanBeNull] string? ns) 18 | { 19 | var message = _adapter.DeserializeConnectedMessage(text); 20 | message.Should() 21 | .BeEquivalentTo(new ConnectedMessage 22 | { 23 | Namespace = ns, 24 | Sid = null, 25 | }); 26 | } 27 | 28 | [Theory] 29 | [InlineData("\"error message\"", "error message")] 30 | [InlineData("\"\\\"Authentication error\\\"\"", "\"Authentication error\"")] 31 | public void DeserializeErrorMessage_WhenCalled_AlwaysPass(string text, string error) 32 | { 33 | var message = _adapter.DeserializeErrorMessage(text); 34 | message.Should() 35 | .BeEquivalentTo(new ErrorMessage 36 | { 37 | // TODO: is namespace supported by eio3? 38 | Namespace = null, 39 | Error = error, 40 | }); 41 | } 42 | } -------------------------------------------------------------------------------- /tests/SocketIOClient.UnitTests/V2/Serializer/SystemTextJson/SystemJsonEngineIO4MessageAdapterTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using JetBrains.Annotations; 3 | using SocketIOClient.Core.Messages; 4 | using SocketIOClient.V2.Serializer.SystemTextJson; 5 | using Xunit; 6 | 7 | namespace SocketIOClient.UnitTests.V2.Serializer.SystemTextJson; 8 | 9 | // ReSharper disable once InconsistentNaming 10 | public class SystemJsonEngineIO4MessageAdapterTests 11 | { 12 | private readonly SystemJsonEngineIO4MessageAdapter _adapter = new(); 13 | 14 | [Theory] 15 | [InlineData("{\"sid\":\"123\"}", "123", null)] 16 | [InlineData("/test,{\"sid\":\"123\"}", "123", "/test")] 17 | public void DeserializeConnectedMessage_WhenCalled_AlwaysPass(string text, string sid, [CanBeNull] string? ns) 18 | { 19 | var message = _adapter.DeserializeConnectedMessage(text); 20 | message.Should() 21 | .BeEquivalentTo(new ConnectedMessage 22 | { 23 | Namespace = ns, 24 | Sid = sid, 25 | }); 26 | } 27 | 28 | [Theory] 29 | [InlineData("{\"message\":\"error message\"}", "error message", null)] 30 | [InlineData("/test,{\"message\":\"error message\"}", "error message", "/test")] 31 | public void DeserializeErrorMessage_WhenCalled_AlwaysPass(string text, string error, [CanBeNull] string? ns) 32 | { 33 | var message = _adapter.DeserializeErrorMessage(text); 34 | message.Should() 35 | .BeEquivalentTo(new ErrorMessage 36 | { 37 | Namespace = ns, 38 | Error = error, 39 | }); 40 | } 41 | } -------------------------------------------------------------------------------- /tests/SocketIOClient.UnitTests/V2/Serializer/TestFile.cs: -------------------------------------------------------------------------------- 1 | namespace SocketIOClient.UnitTests.V2.Serializer; 2 | 3 | public class TestFile 4 | { 5 | public int Size { get; set; } 6 | public string Name { get; set; } = null!; 7 | public byte[] Bytes { get; set; } = null!; 8 | 9 | public static readonly TestFile IndexHtml = new() 10 | { 11 | Name = "index.html", 12 | Size = 1024, 13 | Bytes = "Hello World!"u8.ToArray(), 14 | }; 15 | 16 | /// 17 | /// "NiuB" (牛B) is a slang term in Chinese, often used to describe someone or something that's really impressive or awesome. 18 | /// It’s like saying "cool," "amazing," or "badass" in English. 19 | /// NiuB => 牛B => 🐮🍺 20 | /// 21 | public static readonly TestFile NiuB = new() 22 | { 23 | Name = "NiuB", 24 | Size = 666, 25 | Bytes = "🐮🍺"u8.ToArray(), 26 | }; 27 | } -------------------------------------------------------------------------------- /tests/SocketIOClient.UnitTests/V2/SocketIOOptionsTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using SocketIOClient.V2; 3 | 4 | namespace SocketIOClient.UnitTests.V2; 5 | 6 | public class SocketIOOptionsTests 7 | { 8 | public SocketIOOptionsTests() 9 | { 10 | _options = new SocketIOClient.V2.SocketIOOptions(); 11 | } 12 | 13 | private readonly SocketIOClient.V2.SocketIOOptions _options; 14 | 15 | [Fact] 16 | public void DefaultValues() 17 | { 18 | _options.Should() 19 | .BeEquivalentTo(new SocketIOClient.V2.SocketIOOptions 20 | { 21 | EIO = EngineIO.V4, 22 | ConnectionTimeout = TimeSpan.FromSeconds(30), 23 | Reconnection = true, 24 | ReconnectionAttempts = 10, 25 | ReconnectionDelayMax = 5000, 26 | Path = "/socket.io", 27 | Query = null, 28 | }); 29 | } 30 | } -------------------------------------------------------------------------------- /tests/socket.io/.dockerignore: -------------------------------------------------------------------------------- 1 | **/node_modules -------------------------------------------------------------------------------- /tests/socket.io/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:21.7.3-alpine3.19 2 | WORKDIR /socket.io 3 | COPY . . 4 | RUN npm run install-all 5 | EXPOSE 11400 6 | EXPOSE 11401 7 | EXPOSE 11402 8 | EXPOSE 11403 9 | EXPOSE 11404 10 | EXPOSE 11410 11 | EXPOSE 11411 12 | EXPOSE 11412 13 | EXPOSE 11413 14 | EXPOSE 11414 15 | EXPOSE 11200 16 | EXPOSE 11201 17 | EXPOSE 11202 18 | EXPOSE 11203 19 | EXPOSE 11210 20 | EXPOSE 11211 21 | EXPOSE 11212 22 | EXPOSE 11213 23 | CMD ["npm", "start"] 24 | -------------------------------------------------------------------------------- /tests/socket.io/Dockerfile-Test: -------------------------------------------------------------------------------- 1 | # build: docker build -t test-image -f Dockerfile-Test . 2 | # test: docker run --rm test-image ls /app 3 | FROM alpine 4 | COPY . /app 5 | -------------------------------------------------------------------------------- /tests/socket.io/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "socket.io", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "scripts": { 7 | "v2": "cd v2 && npm start", 8 | "v4": "cd v4 && npm start", 9 | 10 | "install-all":"npm i && cd v4 && npm i && cd ../v2 && npm i", 11 | "start": "concurrently \"npm run v4\" \"npm run v2\"" 12 | }, 13 | "author": "https://github.com/doghappy", 14 | "license": "Apache", 15 | "dependencies": { 16 | "concurrently": "^7.2.0" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/socket.io/v2/index.js: -------------------------------------------------------------------------------- 1 | const https = require('https'); 2 | const http = require('http'); 3 | const socket = require('socket.io'); 4 | const fs = require('fs'); 5 | const path = require('path'); 6 | const template = require('./template'); 7 | 8 | const wsServers = [ 9 | { 10 | name: 'v2-ws', 11 | port: 11200, 12 | server: () => http.createServer(), 13 | options: { 14 | pingInterval: 5000, 15 | pingTimeout: 10000, 16 | }, 17 | onCreated: template.registerEvents 18 | }, 19 | { 20 | name: 'v2-ws-token', 21 | port: 11201, 22 | server: () => http.createServer(), 23 | options: {}, 24 | onCreated: template.useAuthMiddlewares 25 | }, 26 | { 27 | name: 'v2-ws-mp', 28 | port: 11202, 29 | server: () => http.createServer(), 30 | options: { 31 | parser: require('socket.io-msgpack-parser'), 32 | pingInterval: 5000, 33 | pingTimeout: 10000, 34 | }, 35 | onCreated: template.registerEvents 36 | }, 37 | { 38 | name: 'v2-ws-token-mp', 39 | port: 11203, 40 | server: () => http.createServer(), 41 | options: { 42 | parser: require('socket.io-msgpack-parser') 43 | }, 44 | onCreated: template.useAuthMiddlewares 45 | } 46 | ]; 47 | 48 | const httpServers = [ 49 | { 50 | name: 'v2-http', 51 | port: 11210, 52 | server: () => http.createServer(), 53 | options: { 54 | transports: ["polling"], 55 | pingInterval: 5000, 56 | pingTimeout: 10000, 57 | }, 58 | onCreated: template.registerEvents 59 | }, 60 | { 61 | name: 'v2-http-token', 62 | port: 11211, 63 | server: () => http.createServer(), 64 | options: { 65 | transports: ["polling"] 66 | }, 67 | onCreated: template.useAuthMiddlewares 68 | }, 69 | { 70 | name: 'v2-http-mp', 71 | port: 11212, 72 | server: () => http.createServer(), 73 | options: { 74 | parser: require('socket.io-msgpack-parser'), 75 | transports: ["polling"], 76 | pingInterval: 5000, 77 | pingTimeout: 10000, 78 | }, 79 | onCreated: template.registerEvents 80 | }, 81 | { 82 | name: 'v2-http-token-mp', 83 | port: 11213, 84 | server: () => http.createServer(), 85 | options: { 86 | parser: require('socket.io-msgpack-parser'), 87 | transports: ["polling"] 88 | }, 89 | onCreated: template.useAuthMiddlewares 90 | } 91 | ]; 92 | 93 | let servers = [ 94 | ...wsServers, 95 | ...httpServers 96 | ]; 97 | 98 | if (process.env.PORT && process.env.NAME){ 99 | const server = servers.find(s => s.name === process.env.NAME); 100 | server.port = process.env.PORT; 101 | servers = [server]; 102 | } 103 | 104 | for (const server of servers) { 105 | console.log(`Starting server '${server.name}' on port ${server.port}...`); 106 | template.start(server.name, server.port, server.server(), server.options, server.onCreated); 107 | } -------------------------------------------------------------------------------- /tests/socket.io/v2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "socket.io", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "scripts": { 7 | "start": "node index" 8 | }, 9 | "author": "https://github.com/doghappy", 10 | "license": "Apache", 11 | "dependencies": { 12 | "socket.io": "2.5.0", 13 | "socket.io-msgpack-parser": "2.2.1" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /tests/socket.io/v2/template.js: -------------------------------------------------------------------------------- 1 | function start(name, port, server, options, onCreated) { 2 | const socket = require('socket.io'); 3 | const io = socket(server, options); 4 | onCreated(io); 5 | server.listen(port, () => { 6 | console.log(`Started server '${name}' on port ${port}`); 7 | }); 8 | } 9 | 10 | function useAuthMiddlewares(io){ 11 | useIOAuthMiddleware(io); 12 | useNspAuthMiddleware(io); 13 | } 14 | 15 | function useIOAuthMiddleware(io){ 16 | io.use((socket, next) => { 17 | if (socket.handshake.query.token === "abc") { 18 | next(); 19 | } else { 20 | next(new Error("Authentication error")); 21 | } 22 | }); 23 | } 24 | 25 | function useNspAuthMiddleware(io){ 26 | const nsp = io.of("/nsp"); 27 | nsp.use((socket, next) => { 28 | if (socket.handshake.query.token === "abc") { 29 | next(); 30 | } else { 31 | next(new Error("Authentication error")); 32 | } 33 | }); 34 | } 35 | 36 | function registerEvents(io) { 37 | registerIOEvents(io); 38 | registerNspEvents(io); 39 | } 40 | 41 | function registerIOEvents(io) { 42 | io.on('connection', socket => { 43 | socket.on('1:emit', data => { 44 | socket.emit('1:emit', data); 45 | }); 46 | socket.on('2:emit', (d1, d2) => { 47 | socket.emit('2:emit', d1, d2); 48 | }); 49 | socket.on('1:ack', (data, cb) => { 50 | cb(data); 51 | }); 52 | socket.on('get_auth', cb => { 53 | cb(socket.handshake.auth); 54 | }); 55 | socket.on('get_header', (key, cb) => { 56 | cb(socket.handshake.headers[key]); 57 | }); 58 | socket.on('disconnect', close => { 59 | socket.disconnect(close); 60 | }); 61 | socket.on('client will be sending data to server', () => { 62 | socket.emit('client sending data to server', (arg) => { 63 | socket.emit("server received data", arg); 64 | }); 65 | }); 66 | }); 67 | } 68 | 69 | function registerNspEvents(io) { 70 | const nsp = io.of("/nsp"); 71 | nsp.on("connection", socket => { 72 | socket.on('1:emit', data => { 73 | socket.emit('1:emit', data); 74 | }); 75 | socket.on('2:emit', (d1, d2) => { 76 | socket.emit('2:emit', d1, d2); 77 | }); 78 | socket.on('1:ack', (data, cb) => { 79 | cb(data); 80 | }); 81 | socket.on('get_auth', cb => { 82 | cb(socket.handshake.auth); 83 | }); 84 | socket.on('get_header', (key, cb) => { 85 | cb(socket.handshake.headers[key]); 86 | }); 87 | socket.on('disconnect', close => { 88 | socket.disconnect(close); 89 | }); 90 | socket.on('client will be sending data to server', () => { 91 | socket.emit('client sending data to server', (arg) => { 92 | socket.emit("server received data", arg); 93 | }); 94 | }); 95 | }); 96 | } 97 | 98 | module.exports.start = start; 99 | module.exports.useAuthMiddlewares = useAuthMiddlewares; 100 | module.exports.useIOAuthMiddleware = useIOAuthMiddleware; 101 | module.exports.useNspAuthMiddleware = useNspAuthMiddleware; 102 | module.exports.registerEvents = registerEvents; 103 | module.exports.registerIOEvents = registerIOEvents; 104 | -------------------------------------------------------------------------------- /tests/socket.io/v4/cert/cert.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDETCCAfkCFHvkxrj6IG2A/aLKJSCEPRcNXz0CMA0GCSqGSIb3DQEBCwUAMEUx 3 | CzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRl 4 | cm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMjQwMzMxMTUxODE0WhcNMjUwMzMxMTUx 5 | ODE0WjBFMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UE 6 | CgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOC 7 | AQ8AMIIBCgKCAQEAzLYnBjWrkZE53X8F4VMIzO/uQyneipAWzPJpGErLLsgqM7Tq 8 | moUCjW1d7SEQNaSonYyAEIitHFujNJbZK2pN3gqaFI0nncXRYdX2wjqXY6zZR/ty 9 | p4FFyqcQPv5okpE3e37mAHCkzWnLgm5/BXNaqZCs5sGDiMEwHrn8mqLKGnHW+fL0 10 | al1LHh5SMGUadXa2UDrK6JJWO0oY03RZq8OnO1iktOsh49QkaDuq729m7c8ic3lj 11 | FwbNJTnlhWC6oTq+L017j00GoxA1FnFeuFpLVySC+/EmKX3d+jEMLJi3Jmz/tRWk 12 | 5pFRwfjJ5bUb/3XxJ3gdufQWxWFNw/4koE63HQIDAQABMA0GCSqGSIb3DQEBCwUA 13 | A4IBAQBDiC6OBwvwS/t+9+rC5YQ3OxjsTeTiarBOvRfheg6pibVrJBa/w/e+arQq 14 | +RZrBIM1aqWKzQwloPYugflet6ZJQpRebwb7TGNKVRSUcws5PMhbXzDrO2X6rqMS 15 | /g1tKZtLVL79LklmIvxFvcI0N27BXvShXLff8XxMJz+dZk9212NnZjuPtlvCmk4G 16 | 1n/bkl5YnDWnHLBEImfOtNEH5fOjO5LkOrz8O80AkiFwbbzDJPS2o3Yhs89zULo3 17 | Sa0ji/NM0OFN9fZZ0irNxGcBnW4KwpRqou+coWAzqpDFjLx49u9ODM5XCI23QpIJ 18 | ZSzcPSvmAIrHxpsH3oD+GpnGdUk8 19 | -----END CERTIFICATE----- 20 | -------------------------------------------------------------------------------- /tests/socket.io/v4/cert/private.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDMticGNauRkTnd 3 | fwXhUwjM7+5DKd6KkBbM8mkYSssuyCoztOqahQKNbV3tIRA1pKidjIAQiK0cW6M0 4 | ltkrak3eCpoUjSedxdFh1fbCOpdjrNlH+3KngUXKpxA+/miSkTd7fuYAcKTNacuC 5 | bn8Fc1qpkKzmwYOIwTAeufyaosoacdb58vRqXUseHlIwZRp1drZQOsroklY7ShjT 6 | dFmrw6c7WKS06yHj1CRoO6rvb2btzyJzeWMXBs0lOeWFYLqhOr4vTXuPTQajEDUW 7 | cV64WktXJIL78SYpfd36MQwsmLcmbP+1FaTmkVHB+MnltRv/dfEneB259BbFYU3D 8 | /iSgTrcdAgMBAAECggEATraVj8owQ8M7JEN0z9ydLCvvigy94LqhwcFODsSnlr/p 9 | lkMw2CRfxGCytny5nl7HZPkCvxjGs3o66Xw9WffApCmgcFmMS5qmNX/Pp4Re9Lkg 10 | PRDe17CZ1N/jG824CO9kjYxQRQgLHl7ZHTh+h+qAiGW0TfBHstxRs+bgzdbdbkf6 11 | WU5hbFQQpyy6wiwEEx9XDhm3cm/mF660ZD2f6ZZCJiicV4TNb2+EwZAb8miWr86c 12 | IfqjqVVCO+MeOz/Y+ri2a67vpkZ+0ysQv6VytwIEMZW1Qq3vLQKg7cIM7dxR2YPi 13 | nDeisIvzC/WDSd1p42M2Yv3Rts0hrr03BLZuAxZ1owKBgQDwFJVOXaP5IjE9PXbQ 14 | 8ePlacD9zOR0ObOyk8eBSFSf3cpVdd3QKx7uioXFNPmkJauP4kWU5lMqtzGj4hdw 15 | uVylb/GZ1Lp8uXkeQzVktbFI9O4EDsvIeC83aRxu9ZXtRMBHS8SHBufXlc4IcMJZ 16 | lsywKqGON3KakK0ACGOIs7HzPwKBgQDaSSzMDVlLCwibrsrE31mjxmGGpOr1Qa1b 17 | m5kddPN35uBp/lKrbXNx0sAbZIpcuenXBbJ4fBTvoZHsiGEqOXJpGcJ2mHuO5hoJ 18 | 5AJ/r+iEDRzM5GhE4+ATjYpULugYB+KXhqDCMpfR77xqwbUFYOdeLBSEj/PlLe82 19 | cbfzbEeqowKBgQCkIh5VXjWNTLAHIy9I+CaLIDreCSciwpQ1AU1C+LVKOnJq7NMB 20 | z4ktIi0EPwxxCYP6MYLKopC3QllApoDKAx/wxtCRD9uTC6ZfZyloucMDktfqlEcD 21 | vg7hvg2/Wkzu0rL1yzoH6lO0kukx4g0s/Kjhw7OBrCzAuSpdPF74BYoiNwKBgQCt 22 | W5AIPlG8F3curRK8Z+V4/ARYOoGfZhmXt2tSyZ7SirmPdDuTick1jHqlRqPcIIpm 23 | ClBC/8hgx6BsiaMhNZ53ec3HAjKeun/TexHA9qNivEczMfLdQ1yiKrbBRL9u1lRO 24 | oszpbeTFBfBNmKl7LAqT784buXepe2GPi6Db4hLIoQKBgFw7Bnu79raA6jjr+DPP 25 | bZh5kv9EaUhH2qdMXfrckRBb1dCbRYZAClWKl0ZXKTrVn7swFn5ngUbdbHqTxe8U 26 | NteUxtJg+oRYPu6BAnG4gXja/eGJaUeNsA2nUNdJu9pQX4Gl6yHRSXewWdvAwqfx 27 | O3koFVqHLXN2BUhY5t2gsQ7d 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /tests/socket.io/v4/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "socket.io", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "scripts": { 7 | "start": "node index" 8 | }, 9 | "author": "https://github.com/doghappy", 10 | "license": "Apache", 11 | "dependencies": { 12 | "socket.io": "4.5.0", 13 | "socket.io-msgpack-parser": "^3.0.2" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /tests/socket.io/v4/template.js: -------------------------------------------------------------------------------- 1 | function start(name, port, server, options, onCreated) { 2 | const socket = require('socket.io'); 3 | const io = socket(server, options); 4 | onCreated(io); 5 | server.listen(port, () => { 6 | console.log(`Started server '${name}' on port ${port}`); 7 | }); 8 | } 9 | 10 | function useAuthMiddlewares(io){ 11 | useIOAuthMiddleware(io); 12 | useNspAuthMiddleware(io); 13 | } 14 | 15 | function useIOAuthMiddleware(io){ 16 | io.use((socket, next) => { 17 | if (socket.handshake.query.token === "abc") { 18 | next(); 19 | } else { 20 | next(new Error("Authentication error")); 21 | } 22 | }); 23 | } 24 | 25 | function useNspAuthMiddleware(io){ 26 | const nsp = io.of("/nsp"); 27 | nsp.use((socket, next) => { 28 | if (socket.handshake.query.token === "abc") { 29 | next(); 30 | } else { 31 | next(new Error("Authentication error")); 32 | } 33 | }); 34 | } 35 | 36 | function registerEvents(io) { 37 | registerIOEvents(io); 38 | registerNspEvents(io); 39 | } 40 | 41 | function registerIOEvents(io) { 42 | io.on('connection', socket => { 43 | socket.on('1:emit', data => { 44 | socket.emit('1:emit', data); 45 | }); 46 | socket.on('2:emit', (d1, d2) => { 47 | socket.emit('2:emit', d1, d2); 48 | }); 49 | socket.on('1:ack', (data, cb) => { 50 | cb(data); 51 | }); 52 | socket.on('get_auth', cb => { 53 | cb(socket.handshake.auth); 54 | }); 55 | socket.on('get_header', (key, cb) => { 56 | cb(socket.handshake.headers[key]); 57 | }); 58 | socket.on('disconnect', close => { 59 | socket.disconnect(close); 60 | }); 61 | socket.on('client will be sending data to server', () => { 62 | socket.emit('client sending data to server', (arg) => { 63 | socket.emit("server received data", arg); 64 | }); 65 | }); 66 | }); 67 | } 68 | 69 | function registerNspEvents(io) { 70 | const nsp = io.of("/nsp"); 71 | nsp.on("connection", socket => { 72 | socket.on('1:emit', data => { 73 | socket.emit('1:emit', data); 74 | }); 75 | socket.on('2:emit', (d1, d2) => { 76 | socket.emit('2:emit', d1, d2); 77 | }); 78 | socket.on('1:ack', (data, cb) => { 79 | cb(data); 80 | }); 81 | socket.on('get_auth', cb => { 82 | cb(socket.handshake.auth); 83 | }); 84 | socket.on('get_header', (key, cb) => { 85 | cb(socket.handshake.headers[key]); 86 | }); 87 | socket.on('disconnect', close => { 88 | socket.disconnect(close); 89 | }); 90 | socket.on('client will be sending data to server', () => { 91 | socket.emit('client sending data to server', (arg) => { 92 | socket.emit("server received data", arg); 93 | }); 94 | }); 95 | }); 96 | } 97 | 98 | module.exports.start = start; 99 | module.exports.useAuthMiddlewares = useAuthMiddlewares; 100 | module.exports.useIOAuthMiddleware = useIOAuthMiddleware; 101 | module.exports.useNspAuthMiddleware = useNspAuthMiddleware; 102 | module.exports.registerEvents = registerEvents; 103 | module.exports.registerIOEvents = registerIOEvents; 104 | --------------------------------------------------------------------------------