Skip to content

Report end-of-stream to the reader once the peer's FIN is in - #104

Open
FrenchToblerone54 wants to merge 1 commit into
narrowlink:mainfrom
FrenchToblerone54:tcp-eof-after-fin
Open

FrenchToblerone54 wants to merge 1 commit into
narrowlink:mainfrom
FrenchToblerone54:tcp-eof-after-fin

Conversation

@FrenchToblerone54

@FrenchToblerone54 FrenchToblerone54 commented Oct 17, 2026 •

Copy link
Copy Markdown

Problem

After the peer closes its side, a task blocked in poll_read is never woken and the read stays pending until the idle timeout. Nothing in the FIN path wakes the reader, and CloseWait, LastAck and TimeWait are not treated as end of stream when the data channel is empty. A relay that copies from the stream to another socket does not learn that the peer is done, so it holds the connection open for up to the session timeout.

Reproduce

Open a connection, start a read that stays pending, then deliver an ACK|FIN from the peer at the expected sequence number. The reader is not woken. The new test does exactly this.

Fix

  • Wake the reader whenever a received segment carries FIN.
  • In poll_read, when the channel is empty and the state is CloseWait, LastAck or TimeWait, return Ready(Ok(())) with nothing read, which is end of stream. Data already queued is still delivered first: the channel is polled before the state is looked at, and acknowledged bytes that still wait in the Tcb for a free channel slot are taken from there (the same take_received the Closed branch uses) before end of stream is reported.

Test

reader_sees_end_of_stream_after_the_peers_fin in src/stream/tcp.rs. It fails without the change ("the peer's FIN did not wake the pending reader") and passes with it. queued_data_is_read_before_end_of_stream uses a one-slot data channel, two data segments and then FIN, and reads without ever yielding in between; it asserts every byte arrives before end of stream and fails without the take_received step ("bytes were lost before end-of-stream"). cargo test and cargo clippy --all-targets are clean.

poll_read stayed pending after the peer closed its side, because nothing woke
the reader and CloseWait, LastAck and TimeWait were not treated as end of
stream. Wake the reader on a FIN and return end-of-stream when no more data
can arrive.
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