Skip to content

turnloop-http servers close with unread peer bytes (RST drops the final response on macOS/Windows) #21

Closed

Description

@proggeramlug

turnloop-http servers close their TCP socket as soon as a connection is drained. server::http2 does this on core.is_drained() and server::http1 after the final response. The close happens even if the peer's last frames are still unread (for example HTTP/2 SETTINGS or WINDOW_UPDATE acknowledgements, or a pipelined HTTP/1 request). Closing a socket with unread bytes sends RST. On macOS and Windows, a received RST discards data the client hasn't read yet, so the client can lose the tail of the final response.

Evidence: PR #20's CI run 34932385239 (test-native (macos-15, all-features)) failed node_https_and_http2_against_async_tls_server. The Node HTTP/2 client got Error: read ECONNRESET on its ClientHttp2Stream before its end event, so child.wait().success() failed. Linux, Windows and web passed in the same run. The same RST mechanism caused the async TLS test failure fixed in #5 (eeb626e).

Expected (lingering close, as nginx lingering_close and Node's graceful server shutdown do):

  1. Flush the final response and GOAWAY.
  2. Half-close the write side (shutdown(SHUT_WR)); the backend already supports Operation::Shutdown.
  3. Read and discard until peer EOF or a bounded linger deadline.
  4. Close.

Needed pieces:

  • A half-close API on turnloop_io::AsyncIo / TlsStream: TLS close_notify followed by TCP shutdown, without closing the handle.
  • Lingering close in server::http1 and server::http2, with a bounded, configurable linger timeout.
  • Tests with a peer that writes after the final response (for example unread WINDOW_UPDATE frames, or a pipelined extra HTTP/1 request). They must assert that the client receives the whole final response on macOS, Linux and Windows, with no-spin and zero allocation in steady state.
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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions