]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
BUG/MINOR: mux-h2: send the preface along with the first request if needed
authorWilly Tarreau <w@1wt.eu>
Wed, 29 Oct 2025 16:38:07 +0000 (17:38 +0100)
committerWilly Tarreau <w@1wt.eu>
Thu, 30 Oct 2025 17:16:54 +0000 (18:16 +0100)
Tests involving 0-RTT and H2 on the backend show that 0-RTT is being
partially used but does not work. The analysis shows that only the
preface and settings are sent using early-data and the request is sent
separately. As explained in the previous patch, this is caused by the
fact that a wakeup of the iocb is needed just to send the preface, then
a new call to process_stream is needed to try sending again.

Here with this patch, we're making h2_snd_buf() able to send the preface
if it was not yet sent. Thanks to this, the preface, settings and first
request can now leave as a single TCP segment. In case of TLS with 0-RTT,
it now allows all the block to leave in early data.

Even in clear-text H2, we're now seeing a 15% lower context-switch count,
and the number of calls to process_stream() per connection dropped from 3
to 2. The connection rate increased by an extra 9.5%. Compared to without
the last 3 patches, this is a 22% reduction of context-switches, 33%
reduction of process_stream() calls, and 15.7% increase in connection
rate. And more importantly, 0-RTT now really works with H2 on the
backend, saving one full RTT on the first request.

This fix is only for a missed optimization and a non-functional 0-RTT
on the backend. It's worth backporting it, but it doesn't cause enough
harm to hurry a backport. Better wait for it to live a little bit in
3.3 (till at least a week or two after the final release) before
backporting it. It's not sure that it's worth going beyond 3.2 in any
case. It depends on the these two previous commits:

  MEDIUM: mux-h2: do not needlessly refrain from sending data early
  MINOR: mux-h2: extract the code to send preface+settings into its own function

src/mux_h2.c

index 03937dc336d05d596b4e4fdbaef7fe9aca1af926..a5232d0ca715bc1de2e103e91c52631e6d3ef955 100644 (file)
@@ -7867,6 +7867,16 @@ static size_t h2_snd_buf(struct stconn *sc, struct buffer *buf, size_t count, in
        }
        h2s->flags &= ~H2_SF_NOTIFIED;
 
+       if (unlikely(h2s->h2c->st0 < H2_CS_SETTINGS1 && (h2s->h2c->flags & H2_CF_IS_BACK))) {
+               /* The preface and settings were not sent yet. Let's just push
+                * them now instead of doing a wakeup dance. This function will
+                * possibly do nothing, or update the state to H2_CS_SETTINGS1
+                * on success, or H2_CS_ERROR* on error. Let's merge this path
+                * with the common checks below.
+                */
+               h2c_bck_send_preface_and_settings(h2s->h2c);
+       }
+
        if (h2s->h2c->st0 < ((h2s->h2c->flags & H2_CF_SETTINGS_NEEDED) ? H2_CS_FRAME_H : H2_CS_SETTINGS1)) {
                TRACE_DEVEL("connection not ready, leaving", H2_EV_H2S_SEND|H2_EV_H2S_BLK, h2s->h2c->conn, h2s);
                return 0;