On the backend side, h2_snd_buf() assigns the stream ID before calling
h2s_snd_bhdrs() to encode the HEADERS frame. If the encoding fails, the
the HEADERS frame is never sent and the stream is put in error, but an
RST_STREAM is sent for this ID. The same happens via h2_do_shutr()/
h2_do_shutw() when the stream is aborted while its HEADERS frame is
still blocked. But for the server, this stream is still idle, and the
H2 spec says that receiving an RST_STREAM on an idle stream must be
treated as a connection error, so a compliant server will then close
the connection with a GOAWAY(PROTOCOL_ERROR).
Commit
f065d0009 ("BUG/MEDIUM: mux-h2: Don't send RST_STREAM frame for
streams with no ID") already addressed the case of streams without ID,
which was the first manifestation of this situation. Let's also skip
the RST_STREAM for backend streams which haven't sent their HEADERS
frame yet.
The protocol_upgrade reg-test, which expected the RST_STREAM on the idle
stream for requests which cannot be converted, was updated accordingly.
The issue has existed since outgoing streams were introduced in 1.9, so
the fix should be backported to all supported versions.
expect settings.ack == true
} -run
- stream 1 {
- rxrst
- } -run
+ # the request cannot be converted, and no frame (in particular no
+ # RST_STREAM) must be sent on stream 1 which is still idle
+ # (RFC9113#6.4).
} -start
# http2 server without support for RFC8441 : settings announced with value 0
expect settings.ack == true
} -run
- stream 1 {
- rxrst
- } -run
+ # the request cannot be converted, and no frame (in particular no
+ # RST_STREAM) must be sent on stream 1 which is still idle
+ # (RFC9113#6.4).
} -start
# http2 server with support for RFC8441 by H1 client is sending a request with payload
expect settings.ack == true
} -run
- stream 1 {
- rxrst
- } -run
+ # the request cannot be converted, and no frame (in particular no
+ # RST_STREAM) must be sent on stream 1 which is still idle
+ # (RFC9113#6.4).
} -start
# http/1.1 server
* RST_STREAM in response to a RST_STREAM frame.
*
* if h2s is not assigned yet (id == 0), don't send a RST_STREAM frame.
+ * Similarly, on the backend side, an ID may already have been assigned
+ * while the HEADERS frame was not sent (e.g. failed to be encoded or
+ * blocked), in which case the stream is still idle for the server,
+ * and RFC9113#6.4 says that an RST_STREAM on an idle stream is a
+ * connection error, which would kill all other streams. The ID will
+ * simply never be used (which implicitly closes it).
*/
- if ((h2s->id == 0) || (h2c->dsi == h2s->id && h2c->dft == H2_FT_RST_STREAM)) {
+ if ((h2s->id == 0) || (h2c->dsi == h2s->id && h2c->dft == H2_FT_RST_STREAM) ||
+ ((h2c->flags & H2_CF_IS_BACK) && !(h2s->flags & H2_SF_HEADERS_SENT))) {
ret = 1;
goto ignore;
}