]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
BUG/MEDIUM: mux-h2: Always wakeup streams with no id to avoid frozen streams
authorChristopher Faulet <cfaulet@haproxy.com>
Thu, 7 Mar 2019 15:21:34 +0000 (16:21 +0100)
committerWilly Tarreau <w@1wt.eu>
Mon, 18 Mar 2019 14:50:23 +0000 (15:50 +0100)
This only happens for server streams because their id is assigned when the first
message is sent. If these streams are not woken up, some events can be lost
leading to frozen streams. For instance, it happens when a server closes its
connection before sending its preface.

This patch must be backported to 1.9.

src/mux_h2.c

index fe4d0928e4e85209b774397aadf9ef87f7b9289a..6d13b1011aebdc12395e40f3273a0fc270f3aa2f 100644 (file)
@@ -1384,10 +1384,35 @@ static int h2_send_empty_data_es(struct h2s *h2s)
        return ret;
 }
 
-/* wake the streams attached to the connection, whose id is greater than <last>,
- * and assign their conn_stream the CS_FL_* flags <flags> in addition to
- * CS_FL_ERROR in case of error and CS_FL_REOS in case of closed connection.
- * The stream's state is automatically updated accordingly.
+/* wake a specific stream and assign its conn_stream the CS_FL_* flags <flags>
+ * in addition to CS_FL_ERROR in case of error and CS_FL_REOS in case of close
+ * connection. The stream's state is automatically updated accordingly. If the
+ * stream is orphaned, it is destroyed.
+ */
+static void h2s_wake_one_stream(struct h2s *h2s, uint32_t flags)
+{
+       if (!h2s->cs) {
+               /* this stream was already orphaned */
+               h2s_destroy(h2s);
+               return;
+       }
+
+       h2s->cs->flags |= flags;
+       if ((flags & CS_FL_ERR_PENDING) && (h2s->cs->flags & CS_FL_EOS))
+               h2s->cs->flags |= CS_FL_ERROR;
+
+       h2s_alert(h2s);
+
+       if (flags & CS_FL_ERR_PENDING && h2s->st < H2_SS_ERROR)
+               h2s->st = H2_SS_ERROR;
+       else if (flags & CS_FL_REOS && h2s->st == H2_SS_OPEN)
+               h2s->st = H2_SS_HREM;
+       else if (flags & CS_FL_REOS && h2s->st == H2_SS_HLOC)
+               h2s_close(h2s);
+}
+
+/* wake the streams attached to the connection, whose id is greater than <last>
+ * or unassigned.
  */
 static void h2_wake_some_streams(struct h2c *h2c, int last, uint32_t flags)
 {
@@ -1400,31 +1425,24 @@ static void h2_wake_some_streams(struct h2c *h2c, int last, uint32_t flags)
        if (conn_xprt_read0_pending(h2c->conn))
                flags |= CS_FL_REOS;
 
+       /* Wake all streams with ID > last */
        node = eb32_lookup_ge(&h2c->streams_by_id, last + 1);
        while (node) {
                h2s = container_of(node, struct h2s, by_id);
                if (h2s->id <= last)
                        break;
                node = eb32_next(node);
+               h2s_wake_one_stream(h2s, flags);
+       }
 
-               if (!h2s->cs) {
-                       /* this stream was already orphaned */
-                       h2s_destroy(h2s);
-                       continue;
-               }
-
-               h2s->cs->flags |= flags;
-               if ((flags & CS_FL_ERR_PENDING) && (h2s->cs->flags & CS_FL_EOS))
-                       h2s->cs->flags |= CS_FL_ERROR;
-
-               h2s_alert(h2s);
-
-               if (flags & CS_FL_ERR_PENDING && h2s->st < H2_SS_ERROR)
-                       h2s->st = H2_SS_ERROR;
-               else if (flags & CS_FL_REOS && h2s->st == H2_SS_OPEN)
-                       h2s->st = H2_SS_HREM;
-               else if (flags & CS_FL_REOS && h2s->st == H2_SS_HLOC)
-                       h2s_close(h2s);
+       /* Wake all streams with unassigned ID (ID == 0) */
+       node = eb32_lookup(&h2c->streams_by_id, 0);
+       while (node) {
+               h2s = container_of(node, struct h2s, by_id);
+               if (h2s->id > 0)
+                       break;
+               node = eb32_next(node);
+               h2s_wake_one_stream(h2s, flags);
        }
 }