Repository navigation
Release native socket listener slot before closing the connection - #21442
David Fowler (davidfowl) wants to merge 1 commit into
Conversation
A client that observes the server closing its connection could reconnect before the serving task completed, so the accept loop still counted the old connection against MaximumConnections and rejected the reconnect. Track active connections explicitly and release the slot before the socket is closed. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 21442Or
iex "& { $(irm https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 21442" |
Tests selector2 / 100 PR test projects · 5 PR jobs, from 2 changed files. Selected PR test projects (2 / 100)
Selected PR jobs (5)
How these were chosen — grouped by what changed📦 affected project 🧪 Job reasons
Selection computed for commit |
There was a problem hiding this comment.
🟢 Approval recommended
No active review issues remain for the socket listener’s connection-capacity race fix.
No open issues 🎉
What changed in this PR
Fixes a connection-capacity race in Aspire’s native socket listener that could reject a replacement client after the previous connection had closed.
Changes:
- Tracks active connections explicitly and releases capacity before disposing the socket.
- Adds a 2,000-connection regression test verifying that clients can reconnect after observing closure at capacity.
| File | Description |
|---|---|
| tests/Aspire.Hosting.Native.Core.Tests/SocketConnectionListenerTests.cs | Adds repeated reconnect coverage with a one-connection limit. |
| src/Aspire.Hosting.Native.Server/Transport/SocketConnectionListener.cs | Releases the active-connection slot before closing the socket. |
Review effort: Balanced
|
Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt. |
|
Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt. |
|
CI status for exact head
🤖 Posted by AspireAgent (AI Agent) |
|
Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt. |
|
Another occurrence of the same signature, on an unrelated PR branch (#21626, Native hosting validation run 41277664307, job 124763008835, 2026-11-01). 🤖 Posted by AspireAgent (AI Agent) |
Description
Fixes an intermittent connection-slot race in
SocketConnectionListener. It causedNativeCliProtocolTests.SilentAndPartialFramesExpireWithoutBlockingOtherCliClients(frame: "")to fail on Ubuntu.Failure: main run 40802338563, job 123215117869 (2026-10-29,
84881cc54). The test threwOperationCanceledExceptionafter 20 s atNativeCliProtocolTests.cs:120. That line is the replacement client'sWaitForConnectionAsync, so the reconnecting client had been rejected.Window / denominator: main CI attempt-1 runs, 2026-10-16 to 2026-10-30. Hosting.Native.Core on Ubuntu failed 1 of 64 jobs that ran; on Windows it failed 0 of 64. The test and listener source were unchanged since
f4ae2cc42(2026-10-21), and all 64 jobs ran on that source, so the passes are source-comparable.Root cause
The listener enforced
MaximumConnectionsby counting the not-yet-completedServeAsynctasks. However,ServeAsyncdisposes the socket (using var owned) before its task completes. A client can observe the close and reconnect inside that window. The accept loop then still counts the old task as active and disposes the new connection as over capacity. WithMaximumConnections = 2, the test's replacement client sometimes hit this window.Fix
activeConnectionscounter. The counter is incremented before the connection's task is added.ServeAsyncreleases its slot infinallybefore disposing the socket. A peer can therefore only observe the close after the slot is free.Assertions, timeouts and connection limits are unchanged. The fix adds no sleeps or retries.
Regression test
New test:
SocketConnectionListenerTests.ClientThatObservedCloseCanReconnectAtCapacity.maximumConnections: 1and runs 2000 sequential connections.Native before/after qualification
Both runs used Hosting.Native.Core on ubuntu-latest, filtered to
SocketConnectionListenerTestsandNativeCliProtocolTests.5b6a341c5)Connection 279 was rejected after the previous connection's close was observed.f8237a496)I also ran a standalone harness that compiles the real listener source:
Checklist
🤖 Posted by AspireAgent (AI Agent)