Skip to content

Responder reuse leaks request-scoped HTTP framing state #180

Description

@kentbull

Problem

Responder instances are reused across requests on a persistent HTTP connection, but their request-scoped response state is not fully refreshed:

  • Responder.reset() checks the old self.chunkable value instead of the supplied chunkable argument.
  • Server.serviceReqs() derives chunkability only when creating a responder, not when reusing one.
  • Responder.reset() does not clear evented, so a prior text/event-stream response leaves stale response classification behind.

The chunkability defect occurs on ordinary responder reuse; it does not require a prior SSE response. Because reset() is called without a chunkable argument, the existing boolean can be replaced with None on the first reuse.

Production impact

If the next HTTP/1.1 response does not provide Content-Length, HIO no longer adds Transfer-Encoding: chunked. The response is then only close-delimited even though HIO can keep the persistent connection open and admit another request.

A client or intermediary may consequently:

  • wait indefinitely for the response boundary;
  • time out after receiving the visible body bytes;
  • buffer a response whose completion it cannot determine; or
  • interpret subsequent response bytes as part of the prior response body.

Applications that always provide Content-Length mask the defect, but HIO cannot require every WSGI response to do so.

The stale evented flag is a secondary state-correctness problem. Current server code does not otherwise consume that flag, so clearing it is primarily preventative hardening for subsequent response and lifecycle handling. It is not the source of the framing failure above.

Expected behavior

For every parsed request, Server.serviceReqs() should derive chunkability from that request's HTTP version and pass it to both responder creation and responder reuse. Responder.reset() should apply the supplied value and clear event-stream state.

A reused responder handling an HTTP/1.1 response without Content-Length must therefore emit Transfer-Encoding: chunked, regardless of the prior response type.

Regression

Use one persistent connection and an existing responder whose prior HTTP/1.1 response was text/event-stream. Admit the next HTTP/1.1 request through Server.serviceReqs() and verify that:

  • the same responder is reset with chunkable = True;
  • stale evented state is cleared; and
  • a subsequent response without Content-Length emits Transfer-Encoding: chunked.

Proposed scope

  • Correct the chunkable argument check in Responder.reset().
  • Reset evented for the next response.
  • Pass the current request's derived chunkability through the responder-reuse path.
  • Add the focused responder-reuse regression above.

This issue intentionally excludes producer-abort cleanup, parser EOF semantics, transport draining, and TLS shutdown. It is a generic HIO bug and is independently upstreamable.

No activity

Activity on this issue will appear here.

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions