After #59, when the local TCP peer closes its read side and the client hits EPIPE on write, the QUIC receiver should signal STOP_SENDING to halt inbound traffic. Currently we send both STOP_SENDING and RESET_STREAM, which aborts the send direction as well. That is consistent with a "full abort on local error" policy but is not strictly correct for half-close semantics.
We should decide on desired behavior and implement it explicitly.
Why this matters
- EPIPE means we cannot deliver inbound QUIC data to the local TCP peer.
- It does not necessarily mean we must abort our outbound (send) direction.
- Full resets prevent half-close behavior and may terminate valid upstream data.
Current behavior
- On local TCP read/write errors, we call:
- picoquic_stop_sending(...)
- picoquic_reset_stream(...)
- This aborts both directions and removes stream state.
Proposed behavior (if we choose half-close correctness)
- On write error (EPIPE):
- Send STOP_SENDING only (receiver-side abort).
- Keep stream state so local->remote traffic can continue.
- On read error / local close:
- Send RESET_STREAM (sender-side abort).
- Optionally also STOP_SENDING if we intend full abort.
Scope / likely changes
- In
crates/slipstream-client/src/streams.rs:
- Split handling for StreamWriteError vs StreamReadError.
- Avoid removing the stream on write errors if we want half-close semantics.
- Update tests:
- Add or adjust an e2e test that verifies half-close behavior for EPIPE.
- Ensure server observes STOP_SENDING in that case.
Notes
- In picoquic, RESET_STREAM alone does not schedule output; STOP_SENDING does.
- We recently added STOP_SENDING to fix missing on-wire resets; this issue is about policy correctness and half-close support.
- True half‑close isn’t fully supported today. When the target sends FIN, we mark target_fin_pending + close_after_flush and remove the stream after flushing the FIN, which drops any queued client→target data. That’s consistent with our current full‑close policy, but a real half‑close would need to keep the stream alive for the opposite direction. If we ever add half‑close, we should revisit the close_after_flush removal in StreamClosed/prepare_to_send and avoid dropping pending client→target data.
After #59, when the local TCP peer closes its read side and the client hits EPIPE on write, the QUIC receiver should signal STOP_SENDING to halt inbound traffic. Currently we send both STOP_SENDING and RESET_STREAM, which aborts the send direction as well. That is consistent with a "full abort on local error" policy but is not strictly correct for half-close semantics.
We should decide on desired behavior and implement it explicitly.
Why this matters
Current behavior
Proposed behavior (if we choose half-close correctness)
Scope / likely changes
crates/slipstream-client/src/streams.rs:Notes