Skip to content

Let a host connect to its own listener - #26

Merged
dhruvl merged 1 commit into
mainfrom
loopback-streams
Aug 4, 2026
Merged

Let a host connect to its own listener#26
dhruvl merged 1 commit into
mainfrom
loopback-streams

Conversation

@dhruvl

@dhruvl dhruvl commented Aug 4, 2026

Copy link
Copy Markdown
Owner

A host that opened a stream connection to a listener on itself deadlocked: the stream registry keyed each connection end by (conn, host), so both ends of a self-connection collapsed onto one key and the client transport overwrote the server's. The reply could never find its way back.

The fix keys each end by its own port as well. Data packets now address the peer end's port, and the accept — and the refusal — answer to the connector's port rather than the listener's, which matters twice on one machine: it is the only thing telling the two directions apart, and a refusal addressed to the listener's port would land on the in-order queue the syn already advanced and wait there forever, turning connect-to-closed-port into a hang instead of ConnectionRefusedError. A connect whose drawn ephemeral port would equal the target port on the same host takes the next number instead, so the two ends can never share a key.

Nothing changes for connections between distinct hosts: keys gain a field but keep their meaning, no packet, draw, or trace event is added or reordered, and the three pinned replay digests for the reference network workload are byte-identical before and after. Two tests cover the new ground: a loopback echo through asyncio.start_server / open_connection on a single host, and a loopback connect to a closed port raising ConnectionRefusedError.

Full suite (493), the slow replay-stability suite, and strict mypy are green.

The stream registry keyed each end by (conn, host), so the two ends of
a self-connection shared a key and the client overwrote the server: the
connect never completed and the run deadlocked. Ends are now keyed by
their own port as well, data packets address the peer's port, and the
accept and refusal answer to the connector's port rather than the
listener's — on one machine, port is the only thing telling the two
directions apart. A connect whose drawn ephemeral port would equal the
target port on the same host takes the next number instead.

Packets between distinct hosts keep their keys, order and trace events,
so recorded hashes stay where they were.
@dhruvl
dhruvl merged commit 9d13222 into main Aug 4, 2026
9 checks passed
@dhruvl
dhruvl deleted the loopback-streams branch August 4, 2026 08:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant