You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Client half-close: payload of a FIN segment is dropped, and our FIN is sent immediately (no half-close) #111
Affected:1.0.1 (crates.io). 1.0.0 has the same arms.
Two related problems in TcpState::Established / CloseWait handling, both seen when a client sends data and then calls shutdown(SHUT_WR) (a TCP half-close) while the application on the ipstack side still has a reply to send. Related to #89, which covers the opposite direction (server shuts down first).
1. A data segment that carries FIN loses its payload
Linux puts the FIN on the last data segment when shutdown(SHUT_WR) is called with data still queued, so the segment arrives as ACK|FIN (or ACK|PSH|FIN) with a non-empty payload. In Established, the ACK | FIN arm takes the FIN without queuing the payload, and the ACK | PSH arm discards out-of-order data instead of queuing it. The last 100-500 bytes of the client's stream never reach the reader. With a 4 MiB upload the loss was 144 to 448 bytes per run.
2. A client FIN closes the whole connection (no half-close)
On a client FIN in Established, with nothing in flight, the stack immediately sends its own ACK|FIN and moves to LastAck; in CloseWait a timer forces the same. The server-to-client direction is therefore closed the moment the client half-closes, so a reply still being written is cut off (an echo service returned 216,612 of 4,194,304 bytes). poll_read also returns Pending rather than EOF in CloseWait, so a reader waits for the timer. Expected: ACK the FIN, report EOF to the reader, keep the write side open, and send our FIN when the application calls poll_shutdown (or drops the stream), with the existing timeout as a backstop.
Reproduction
A client that writes N bytes and then half-closes (e.g. sendall(data); sock.shutdown(SHUT_WR)) to a stream that copies its input back to its output (tokio::io::copy_bidirectional-style echo), then reads to EOF. Expected: N bytes echoed, then EOF. Actual: fewer than N bytes (problems 1 and 2), or a stall until the idle timeout.
What we did
We carry a patch to src/stream/tcp.rs (about 300 changed lines, with tests that fail when each change is reverted individually). It queues the payload of a FIN segment before taking the FIN, treats ACK|PSH as ACK, gives CloseWait readers their already-acknowledged bytes and then EOF, and sends our FIN from poll_shutdown/drop instead of from the client's FIN. Happy to turn it into a PR if that is useful; tell us how you would like it split.
Activity