MINOR: h2: new function h2_peek_frame_hdr() to retrieve a new frame header
This function extracts the next frame header but doesn't consume it.
This will allow to detect a stream-id change and to perform a yielding
window update without losing information. The result is stored into a
temporary frame descriptor. We could also store the next frame header
into the connection but parsing the header again is much cheaper than
wasting bytes in the connection for a rare use case.
A function (h2_skip_frame_hdr()) is also provided to skip the parsed
header (always 9 bytes) and another one (h2_get_frame_hdr()) to do both
at once.
diff --git a/src/mux_h2.c b/src/mux_h2.c
index 7cdf31c..422fca2 100644
--- a/src/mux_h2.c
+++ b/src/mux_h2.c
@@ -401,6 +401,57 @@
write_n16(out + 1, len);
}
+/* Peeks an H2 frame header from buffer <b> into descriptor <h>. The algorithm
+ * is not obvious. It turns out that H2 headers are neither aligned nor do they
+ * use regular sizes. And to add to the trouble, the buffer may wrap so each
+ * byte read must be checked. The header is formed like this :
+ *
+ * b0 b1 b2 b3 b4 b5..b8
+ * +----------+---------+--------+----+----+----------------------+
+ * |len[23:16]|len[15:8]|len[7:0]|type|flag|sid[31:0] (big endian)|
+ * +----------+---------+--------+----+----+----------------------+
+ *
+ * Here we read a big-endian 64 bit word from h[1]. This way in a single read
+ * we get the sid properly aligned and ordered, and 16 bits of len properly
+ * ordered as well. The type and flags can be extracted using bit shifts from
+ * the word, and only one extra read is needed to fetch len[16:23].
+ * Returns zero if some bytes are missing, otherwise non-zero on success.
+ */
+static int h2_peek_frame_hdr(const struct buffer *b, struct h2_fh *h)
+{
+ uint64_t w;
+
+ if (b->i < 9)
+ return 0;
+
+ w = readv_n64(b_ptr(b,1), b_end(b) - b_ptr(b,1), b->data);
+ h->len = *b->p << 16;
+ h->sid = w & 0x7FFFFFFF; /* RFC7540#4.1: R bit must be ignored */
+ h->ff = w >> 32;
+ h->ft = w >> 40;
+ h->len += w >> 48;
+ return 1;
+}
+
+/* skip the next 9 bytes corresponding to the frame header possibly parsed by
+ * h2_peek_frame_hdr() above.
+ */
+static inline void h2_skip_frame_hdr(struct buffer *b)
+{
+ bi_del(b, 9);
+}
+
+/* same as above, automatically advances the buffer on success */
+static inline int h2_get_frame_hdr(struct buffer *b, struct h2_fh *h)
+{
+ int ret;
+
+ ret = h2_peek_frame_hdr(b, h);
+ if (ret > 0)
+ h2_skip_frame_hdr(b);
+ return ret;
+}
+
/*********************************************************/
/* functions below are I/O callbacks from the connection */