Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
0f56e66
feat(engine): make the endpoint path configurable
Redouane64 Sep 7, 2026
87f88b8
feat(engine): let a layered protocol send its own packets
Redouane64 Sep 7, 2026
a7e3675
chore(socketio): scaffold the Socket.IO client and test projects
Redouane64 Sep 7, 2026
6cd6bd9
feat(socketio): add the packet model and serializer
Redouane64 Sep 7, 2026
87095d6
test(socketio): cover packet construction and encoding
Redouane64 Sep 7, 2026
12241d0
feat(socketio): add the client surface
Redouane64 Sep 7, 2026
c19e05c
chore(samples): add a Socket.IO test server
Redouane64 Sep 7, 2026
93ffcdb
docs: record the state of the Socket.IO layer
Redouane64 Sep 7, 2026
bf021f0
fix(engine): keep the upgrade URI off a doubled slash
Redouane64 Sep 8, 2026
1a43340
feat(engine): report whether a connection is up, and why one failed
Redouane64 Sep 8, 2026
8da220c
fix(engine): keep the connection when the websocket upgrade fails
Redouane64 Sep 8, 2026
6c46baf
fix(engine): let a connection be opened again after one ends
Redouane64 Sep 8, 2026
ea60441
test(socketio): follow the Engine.IO test naming convention
Redouane64 Sep 8, 2026
537263a
fix(socketio): reject the packets a decoder refuses
Redouane64 Sep 8, 2026
69b6a36
test(socketio): drive the client from a stubbed transport
Redouane64 Sep 8, 2026
5605fb5
fix(socketio): stop reporting a failed handshake as a connection
Redouane64 Sep 8, 2026
326f865
fix(socketio): keep a binary packet and its attachments together
Redouane64 Sep 8, 2026
856ac0a
docs: record the reference decoder and the client test seam
Redouane64 Sep 8, 2026
284ff14
refactor(socketio): split the packet type by direction
Redouane64 Sep 8, 2026
9dfe46a
feat(socketio): reassemble a packet that spans several messages
Redouane64 Sep 8, 2026
170b741
feat(socketio): implement ListenAsync over the decoded stream
Redouane64 Sep 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 14 additions & 5 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,19 @@ Task-based async (`Task`, `IAsyncEnumerable`, `Channel<T>`) rather than the even
.NET Socket.IO clients. Prefer an `await`-able or `await foreach`-able API over an `event` when adding surface area.

The Engine.IO layer is implemented (HTTP polling transport, WebSocket transport with upgrade, plain-text and binary
packets). The Socket.IO layer on top of it — namespaces, payload send/receive — is not yet written; see the TODOs in
`README.md`.
packets). The Socket.IO layer on top of it is half written: `src/SocketIO.Client` encodes packets and sends them, but
nothing parses an inbound one yet, so namespaces and acknowledgements are still open. See the TODOs in `README.md`.

Protocol references:
- Engine.IO: https://socket.io/docs/v4/engine-io-protocol
- Socket.IO: https://socket.io/docs/v4/socket-io-protocol

The reference implementation is in `node_modules` once `npm install` has run, and it is the arbiter when a question is
about what the wire actually accepts: `node_modules/socket.io-parser` decodes a string the C# side produced, which is a
faster and surer check than reading the spec. Its rules are stricter than they look — a binary packet must announce at
least one attachment, an event may not be named after a reserved one, and a decode error costs the whole connection
rather than the one packet.

## Commands

```bash
Expand All @@ -33,6 +39,7 @@ Running the sample end to end needs the Node test server, which lives in an npm
```bash
npm install # once, from the repo root
npm run start:server # Engine.IO server on http://127.0.0.1:9854 (scripts/run-server.sh wraps this)
npm run start:socket-server # Socket.IO server on http://127.0.0.1:9855
dotnet run --project samples/PingPong
```

Expand Down Expand Up @@ -78,9 +85,11 @@ values being the *ASCII byte* of the digit (`Open = 0x30`, i.e. `'0'`) so parsin
- `.editorconfig` is authoritative and unusually strict: `end_of_line = crlf`, `insert_final_newline = false`,
`var` is disallowed where the type is not apparent, and `using` groups are separated with `System` first. Run
`dotnet format` rather than hand-matching it.
- `AssemblyInfo.cs` grants `InternalsVisibleTo("EngineIO.Client.Tests")`. Transports expose `internal` constructors that
accept an injected `HttpClient` purely so tests can supply a mocked `HttpMessageHandler` — follow that pattern for new
transports instead of adding public seams.
- `AssemblyInfo.cs` grants `InternalsVisibleTo` to the matching test project, and `EngineIO.Client` also grants it to
`SocketIO.Client` so the layer above can reach the same seam. `Engine`, `IO` and the transports expose `internal`
constructors that accept an injected `HttpClient` purely so tests can supply a stubbed handler — follow that pattern
instead of adding public seams. `EngineIO.Client.Tests` mocks the handler with Moq; `SocketIO.Client.Tests` takes no
such dependency and uses the hand-rolled `FakePollingServer`.
- Logging is optional throughout: `ILoggerFactory` is nullable everywhere and callers may pass nothing.

## Other agent configs present
Expand Down
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,11 @@ The client implements `Engine.IO` and `Socket.IO` core protocols.

- **Socket.IO Client**

- [x] Packet model and wire-format serialization (events, acks, binary attachments)
- [x] Send plain text, JSON and binary payloads
- [ ] Packet parsing (receiving)
- [ ] Namespaces support
- [ ] Send and receive plain text and binary payloads
- [ ] Acknowledgement correlation

## Resources

Expand Down
14 changes: 14 additions & 0 deletions SocketIO.sln
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{D8B5DE1E
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EngineIO.Client.Tests", "tests\EngineIO.Client.Tests\EngineIO.Client.Tests.csproj", "{5D824F05-5793-4768-99DF-E4D993F16B75}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SocketIO.Client", "src\SocketIO.Client\SocketIO.Client.csproj", "{222889F5-6A36-4A8A-8C78-A910E40D4F82}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SocketIO.Client.Tests", "tests\SocketIO.Client.Tests\SocketIO.Client.Tests.csproj", "{E0C636E7-91B0-44AE-9752-F08326C7C5F2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -36,10 +40,20 @@ Global
{5D824F05-5793-4768-99DF-E4D993F16B75}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5D824F05-5793-4768-99DF-E4D993F16B75}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5D824F05-5793-4768-99DF-E4D993F16B75}.Release|Any CPU.Build.0 = Release|Any CPU
{222889F5-6A36-4A8A-8C78-A910E40D4F82}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{222889F5-6A36-4A8A-8C78-A910E40D4F82}.Debug|Any CPU.Build.0 = Debug|Any CPU
{222889F5-6A36-4A8A-8C78-A910E40D4F82}.Release|Any CPU.ActiveCfg = Release|Any CPU
{222889F5-6A36-4A8A-8C78-A910E40D4F82}.Release|Any CPU.Build.0 = Release|Any CPU
{E0C636E7-91B0-44AE-9752-F08326C7C5F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E0C636E7-91B0-44AE-9752-F08326C7C5F2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E0C636E7-91B0-44AE-9752-F08326C7C5F2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E0C636E7-91B0-44AE-9752-F08326C7C5F2}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{D1C745FE-52AF-4145-8243-9FCA91BE7916} = {80C612B0-98E1-4E6B-AF10-5BB3087F0864}
{4F6177C5-3234-4897-B61C-49C44D3E2EF8} = {577735D4-BC7E-464A-BC9B-93AD8B5B6738}
{5D824F05-5793-4768-99DF-E4D993F16B75} = {D8B5DE1E-C83F-40A9-9D27-D2FBB3866F0A}
{222889F5-6A36-4A8A-8C78-A910E40D4F82} = {577735D4-BC7E-464A-BC9B-93AD8B5B6738}
{E0C636E7-91B0-44AE-9752-F08326C7C5F2} = {D8B5DE1E-C83F-40A9-9D27-D2FBB3866F0A}
EndGlobalSection
EndGlobal
Loading