Skip to content
This repository was archived by the owner on Aug 28, 2026. It is now read-only.
This repository was archived by the owner on Aug 28, 2026. It is now read-only.

Clarify EPIPE handling (STOP_SENDING vs RESET_STREAM) #60

Description

@Mygod

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions