MEDIUM: mux-h2: make the send() function iterate over all mux buffers

Now send() uses a loop to iterate over all buffers to be sent. These
buffers are released and deleted from the vector once completely sent.
If any buffer gets released, offer_buffers() is called to wake up some
waiters.
diff --git a/src/mux_h2.c b/src/mux_h2.c
index 5f19ccf..e160e5c 100644
--- a/src/mux_h2.c
+++ b/src/mux_h2.c
@@ -2774,6 +2774,8 @@
 	done = 0;
 	while (!done) {
 		unsigned int flags = 0;
+		unsigned int released = 0;
+		struct buffer *buf;
 
 		/* fill as much as we can into the current buffer */
 		while (((h2c->flags & (H2_CF_MUX_MFULL|H2_CF_MUX_MALLOC)) == 0) && !done)
@@ -2788,15 +2790,23 @@
 		if (h2c->flags & (H2_CF_MUX_MFULL | H2_CF_DEM_MBUSY | H2_CF_DEM_MROOM))
 			flags |= CO_SFL_MSG_MORE;
 
-		if (br_data(h2c->mbuf)) {
-			int ret = conn->xprt->snd_buf(conn, conn->xprt_ctx, br_tail(h2c->mbuf), b_data(br_tail(h2c->mbuf)), flags);
-			if (!ret)
-				break;
-			sent = 1;
-			b_del(br_tail(h2c->mbuf), ret);
-			b_realign_if_empty(br_tail(h2c->mbuf));
+		for (buf = br_head(h2c->mbuf); b_size(buf); buf = br_del_head(h2c->mbuf)) {
+			if (b_data(buf)) {
+				int ret = conn->xprt->snd_buf(conn, conn->xprt_ctx, buf, b_data(buf), flags);
+				if (!ret)
+					break;
+				sent = 1;
+				b_del(buf, ret);
+				if (b_data(buf))
+					break;
+			}
+			b_free(buf);
+			released++;
 		}
 
+		if (released)
+			offer_buffers(h2c->buf_wait.target, tasks_run_queue);
+
 		/* wrote at least one byte, the buffer is not full anymore */
 		h2c->flags &= ~(H2_CF_MUX_MFULL | H2_CF_DEM_MROOM);
 	}
@@ -2987,11 +2997,24 @@
 		h2c->flags |= H2_CF_GOAWAY_FAILED;
 
 	if (br_data(h2c->mbuf) && !(h2c->flags & H2_CF_GOAWAY_FAILED) && conn_xprt_ready(h2c->conn)) {
-		int ret = h2c->conn->xprt->snd_buf(h2c->conn, h2c->conn->xprt_ctx, br_tail(h2c->mbuf), b_data(br_tail(h2c->mbuf)), 0);
-		if (ret > 0) {
-			b_del(br_tail(h2c->mbuf), ret);
-			b_realign_if_empty(br_tail(h2c->mbuf));
+		unsigned int released = 0;
+		struct buffer *buf;
+
+		for (buf = br_head(h2c->mbuf); b_size(buf); buf = br_del_head(h2c->mbuf)) {
+			if (b_data(buf)) {
+				int ret = h2c->conn->xprt->snd_buf(h2c->conn, h2c->conn->xprt_ctx, buf, b_data(buf), 0);
+				if (!ret)
+					break;
+				b_del(buf, ret);
+				if (b_data(buf))
+					break;
+				b_free(buf);
+				released++;
+			}
 		}
+
+		if (released)
+			offer_buffers(h2c->buf_wait.target, tasks_run_queue);
 	}
 
 	/* either we can release everything now or it will be done later once