cmcfarlen commented on PR #13523:
URL: https://github.com/apache/trafficserver/pull/13523#issuecomment-5242737076
I traced every claim in the description and they all hold. Recording the
verification since the one-line change conceals a fair amount of reasoning.
**The two no-op overrides exist, which is what makes the routing work.**
`Http2Stream::do_io_shutdown()` is an inline empty body at
`include/proxy/http2/Http2Stream.h:71-74`, and
`HQTransaction::do_io_shutdown()` is an explicit `return;` at
`Http3Transaction.cc:187`. Worth noting for other reviewers: grepping for
`Http2Stream::do_io_shutdown` finds nothing because the H2 one is defined
unqualified in the header, which briefly had me thinking the override was
missing and the fix a no-op for the very protocol in the title.
**HTTP/1.x is genuinely unaffected.** `ProxyTransaction::do_io_shutdown()`
forwards to `_proxy_ssn->do_io_shutdown()` (`ProxyTransaction.cc:219-222`), and
`ProxySession::do_io_shutdown()` is `this->_vc->do_io_shutdown(howto)`
(`ProxySession.cc:337-340`) — the same NetVConnection the old code reached
directly.
**Retaining the `if (netvc)` guard is correct, and for a different reason
than before.** `ProxySession::do_io_shutdown()` dereferences `_vc` with no null
check, and `ProxyTransaction::get_netvc()` returns `_proxy_ssn ?
_proxy_ssn->get_netvc() : nullptr` where `ProxySession::get_netvc()` returns
`_vc`. So a non-null `netvc` proves both `_proxy_ssn` and `_proxy_ssn->_vc` are
live — exactly the precondition the unconditional dereference needs. The guard
is now HTTP/1.x-only in effect, since H2/H3 short-circuit in the transaction
before reaching the session, but it is precisely right rather than merely
leftover. The comment earns its place.
**The failure mode is exactly as described.**
`UnixNetVConnection::do_io_shutdown(IO_SHUTDOWN_READ)` does
`read.vio.buffer.clear()`, `read.vio.nbytes = 0`, `read.vio.cont = nullptr`,
`read.enabled = 0` — on the *session's* VIO. Then:
- Debug: `SSLNetVConnection::net_read_io()` asserts
`ink_assert(buf.writer())` at line 530, before any `ntodo` test. That is #9448.
- Release: the assert compiles out and execution reaches `if (ntodo <= 0 ||
!buf.writer()->write_avail() || s->vio.is_disabled())` at line 644. Since
`nbytes = 0` makes `ntodo() <= 0`, the `||` short-circuits left-to-right
*before* `buf.writer()->write_avail()` would dereference the now-null writer,
so it takes `read_disable()` and returns. A silent stall rather than a crash —
but only by short-circuit ordering, which is worth appreciating.
And the stall is permanent in the way you describe: `Http2CommonSession`
re-enables that same VIO every 128 frames (`Http2CommonSession.cc:430`,
`HTTP2_SESSION_EVENT_REENABLE`), each time landing back on the `ntodo <= 0`
disable. `read.vio.cont = nullptr` means the session has lost its read
continuation as well.
**The #12529 history checks out.** Its diff converted the branch it added
(`_ua.get_txn()->do_io_shutdown(IO_SHUTDOWN_READWRITE)` for the
background-fetch case) and replaced the old
`netvc->do_io_shutdown(IO_SHUTDOWN_READWRITE)` with
`_ua.get_txn()->do_io_close()`, but the `IO_SHUTDOWN_READ` call inside `if
(netvc)` appears as untouched context. So this really is the remaining half.
**The fix is also complete.** `HttpSM.cc:913` is the only site in the tree
that reaches past a transaction to a raw NetVConnection for `do_io_shutdown` —
every other call in `HttpSM` goes through `p->vc` / `c->vc`, which for the
user-agent side *is* the `ProxyTransaction`, so those already hit the no-op
override. No sibling instance to chase.
Nice choice using a response transform to reach the case: since the
transform stage buffers the whole body before anything is written back, the
stream has no write of its own and the client reset arrives as an EOS on the
stream's read VIO, which is precisely the `has_consumer_besides_client()`
branch. That is much more deterministic than trying to race a reset against a
normal proxied write.
**Backport note.** Given the `Crash` label and that this half of #12529 has
been latent since it merged, this looks like a 10.2.0 candidate. It should pick
cleanly: the new autest's conditions (`HasProxyVerifierVersion('2.8.0')`,
`HasOpenSSLVersion`, `PluginExists`) all exist on 10.2.x, that branch pins
Proxy Verifier v3.1.3 so the version gate is satisfied, and `null_transform` is
present there. Flagging it for the RM rather than assuming.
Nothing blocking from me.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]