Report sendable() from the transport, not from a missing slot (#177) - #178
Merged
Conversation
Contributor
CoverageTotal lines: 82.22% → 82.10% (-0.12 pp)
❌ Regression in touched files (> 1.0 pp drop)
Add |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Reduced from what this PR set out to do.
tryWrite()is not here — two review passes showed it cannot be honest on HTTP/1 yet, and the reason is worth writing down.What is left, and it is a real bug
sendable()is documented as backpressure advice. On a compressed response it answered a constant true:http_compression_maybe_install_stream_wrapperreplacesstream_opswithcompressing_stream_ops, which carried neither asendablenor anis_aliveslot, and a NULL slot is defined as "this transport has no queue of its own — report writable". The queue was still there, one layer down. HTTP/3 had nosendableeither, for a stream that does queue. Both now answer from the transport.Why tryWrite() was dropped
A non-blocking append needs a transport that can refuse. Threading a
nonblockingflag intoappend_chunkworks on HTTP/2, HTTP/3 and the worker path. On HTTP/1 it does not: the streaming writer ishttp_connection_send→send_raw, which awaits its ownuv_write, so an accepted chunk parks regardless.Routing the non-blocking case through
http_connection_send_batched— the recipeWebSocket::trySend()uses — gives a second, unordered channel on a connection whose chunked framing assumes one: a chunk body waits inout_pending_bufbehind an in-flight write whilemark_ended's0\r\n\r\ngoes out directly and jumps ahead of it. That corrupts the body exactly when a refusal-and-retry happens, which is the case the feature exists for. On TLS it is worse: the high-water mark countsout_pending_len, which a TLS connection never fills, so the gate never fires and the failure becomes a spurious 499 instead of a refusal.Filed as #179: one serialized outbound path per HTTP/1 connection, which
tryWrite()needs and which also removes a park from the streaming hot path. #177 stays open behind it.Suite: 276 passed, 0 failed.