]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: h2: honor WINDOW_UPDATE frames
authorWilly Tarreau <w@1wt.eu>
Thu, 27 Jul 2017 15:18:30 +0000 (17:18 +0200)
committerWilly Tarreau <w@1wt.eu>
Tue, 31 Oct 2017 17:16:18 +0000 (18:16 +0100)
Now they really increase the window size of connections and streams.
If a stream was not queued but requested to send, it means it was
flow-controlled so it's added again into the connection's send list.

src/mux_h2.c

index d9f0b565f71a6867d2bea522239fc738cabf7d69..855bd50b9ed32b54b71ac40d0086c6e24d70b4ee 100644 (file)
@@ -952,6 +952,91 @@ static int h2c_ack_ping(struct h2c *h2c)
        return ret;
 }
 
+/* processes a WINDOW_UPDATE frame whose payload is <payload> for <plen> bytes.
+ * Returns > 0 on success or zero on missing data. It may return an error in
+ * h2c or h2s. Described in RFC7540#6.9.
+ */
+static int h2c_handle_window_update(struct h2c *h2c, struct h2s *h2s)
+{
+       int32_t inc;
+       int error;
+
+       if (h2c->dfl != 4) {
+               error = H2_ERR_FRAME_SIZE_ERROR;
+               goto conn_err;
+       }
+
+       /* process full frame only */
+       if (h2c->dbuf->i < h2c->dfl)
+               return 0;
+
+       inc = h2_get_n32(h2c->dbuf, 0);
+
+       if (h2c->dsi != 0) {
+               /* stream window update */
+               if (h2s->st == H2_SS_IDLE) {
+                       error = H2_ERR_PROTOCOL_ERROR;
+                       goto conn_err;
+               }
+
+               /* it's not an error to receive WU on a closed stream */
+               if (h2s->st == H2_SS_CLOSED)
+                       return 1;
+
+               if (!inc) {
+                       error = H2_ERR_PROTOCOL_ERROR;
+                       goto strm_err;
+               }
+
+               if (h2s->mws >= 0 && h2s->mws + inc < 0) {
+                       error = H2_ERR_FLOW_CONTROL_ERROR;
+                       goto strm_err;
+               }
+
+               h2s->mws += inc;
+               if (h2s->mws > 0 && (h2s->flags & H2_SF_BLK_SFCTL)) {
+                       h2s->flags &= ~H2_SF_BLK_SFCTL;
+                       if (h2s->cs && LIST_ISEMPTY(&h2s->list) &&
+                           (h2s->cs->flags & CS_FL_DATA_WR_ENA)) {
+                               /* This stream wanted to send but could not due to its
+                                * own flow control. We can put it back into the send
+                                * list now, it will be handled upon next send() call.
+                                */
+                               LIST_ADDQ(&h2c->send_list, &h2s->list);
+                       }
+               }
+       }
+       else {
+               /* connection window update */
+               if (!inc) {
+                       error = H2_ERR_PROTOCOL_ERROR;
+                       goto conn_err;
+               }
+
+               if (h2c->mws >= 0 && h2c->mws + inc < 0) {
+                       error = H2_ERR_FLOW_CONTROL_ERROR;
+                       goto conn_err;
+               }
+
+               h2c->mws += inc;
+       }
+
+       return 1;
+
+ conn_err:
+       h2c_error(h2c, error);
+       return 0;
+
+ strm_err:
+       if (h2s) {
+               h2s_error(h2s, error);
+               h2c->st0 = H2_CS_FRAME_A;
+       }
+       else
+               h2c_error(h2c, error);
+       return 0;
+}
+
 /* process Rx frames to be demultiplexed */
 static void h2_process_demux(struct h2c *h2c)
 {
@@ -1056,6 +1141,11 @@ static void h2_process_demux(struct h2c *h2c)
                                ret = h2c_ack_ping(h2c);
                        break;
 
+               case H2_FT_WINDOW_UPDATE:
+                       if (h2c->st0 == H2_CS_FRAME_P)
+                               ret = h2c_handle_window_update(h2c, h2s);
+                       break;
+
                        /* FIXME: implement all supported frame types here */
                default:
                        /* drop frames that we ignore. They may be larger than