Skip to content

A streaming handler cannot offer a chunk without committing to the wait #177

Description

@EdmondDantes

send() parks the handler coroutine when the per-stream queue is full. That is the right default for delivering every byte, but a producer that would rather drop a frame than accumulate lag — an SSE ticker, a metrics feed, a progress stream — has no way to ask. sendable() is not that way either: between the check and the send() another coroutine can fill the queue, and its false also covers closed, sealed and detached responses.

The pattern already exists in this extension: WebSocket::send() parks, WebSocket::trySend() refuses without queueing, and Room repeats the pair. Both read the same high-water mark, HttpServerConfig::setStreamWriteBufferBytes(), which is what h2_stream_sendable consults.

Proposal

HttpResponse::tryWrite(string $chunk): bool beside send(). False means the queue had no room: nothing queued, no header committed, so the same chunk can be offered later. A gone peer throws HttpException 499 rather than returning false — the two answers need opposite reactions, and trySend() on WebSocket already draws that line by throwing WebSocketClosedException.

One caveat belongs in the docs: a refused chunk is a slice of one byte stream, so dropping it corrupts the body. Only the framed dialects carry droppable units, and their twins (trySseEvent(), tryWriteMessage()) come with the rename step.

Blocked on one thing found while writing this: compressing_stream_ops has neither a sendable nor an is_alive slot, so under compression both questions answer from a NULL op — "always writable, never full".

Part of the response body contract in dev/PLAN.md.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions