mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2025-04-07 09:42:34 +00:00
MEDIUM: mux-h2: support emitting CONTINUATION frames after HEADERS
There are some reports of users not being able to pass "enterprise" traffic through haproxy when using H2 because it doesn't emit CONTINUATION frames and as such is limited to headers no longer than the negociated max-frame-size which usually is 16 kB. This patch implements support form emitting CONTINUATION when a HEADERS frame cannot fit within a limit of mfs. It does this by first filling a buffer-wise frame, then truncating it starting from the tail to append CONTINUATION frames. This makes sure that we can truncate on any byte without being forced to stop on a header boundary, and ensures that the common case (no fragmentation) doesn't add any extra cost. By moving the tail first we make sure that each byte is moved only once, thus the performance impact remains negligible. This addresses github issue #249.
This commit is contained in:
parent
81bef7e899
commit
cb985a4da6
78
src/mux_h2.c
78
src/mux_h2.c
@ -1170,6 +1170,54 @@ static inline __maybe_unused int h2_get_frame_hdr(struct buffer *b, struct h2_fh
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* try to fragment the headers frame present at the beginning of buffer <b>,
|
||||
* enforcing a limit of <mfs> bytes per frame. Returns 0 on failure, 1 on
|
||||
* success. Typical causes of failure include a buffer not large enough to
|
||||
* add extra frame headers. The existing frame size is read in the current
|
||||
* frame. Its EH flag will be cleared if CONTINUATION frames need to be added,
|
||||
* and its length will be adjusted. The stream ID for continuation frames will
|
||||
* be copied from the initial frame's.
|
||||
*/
|
||||
static int h2_fragment_headers(struct buffer *b, uint32_t mfs)
|
||||
{
|
||||
size_t remain = b->data - 9;
|
||||
int extra_frames = (remain - 1) / mfs;
|
||||
size_t fsize;
|
||||
char *fptr;
|
||||
int frame;
|
||||
|
||||
if (b->data <= mfs + 9)
|
||||
return 1;
|
||||
|
||||
/* Too large a frame, we need to fragment it using CONTINUATION
|
||||
* frames. We start from the end and move tails as needed.
|
||||
*/
|
||||
if (b->data + extra_frames * 9 > b->size)
|
||||
return 0;
|
||||
|
||||
for (frame = extra_frames; frame; frame--) {
|
||||
fsize = ((remain - 1) % mfs) + 1;
|
||||
remain -= fsize;
|
||||
|
||||
/* move data */
|
||||
fptr = b->area + 9 + remain + (frame - 1) * 9;
|
||||
memmove(fptr + 9, b->area + 9 + remain, fsize);
|
||||
b->data += 9;
|
||||
|
||||
/* write new frame header */
|
||||
h2_set_frame_size(fptr, fsize);
|
||||
fptr[3] = H2_FT_CONTINUATION;
|
||||
fptr[4] = (frame == extra_frames) ? H2_F_HEADERS_END_HEADERS : 0;
|
||||
write_n32(fptr + 5, read_n32(b->area + 5));
|
||||
}
|
||||
|
||||
b->area[4] &= ~H2_F_HEADERS_END_HEADERS;
|
||||
h2_set_frame_size(b->area, remain);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* marks stream <h2s> as CLOSED and decrement the number of active streams for
|
||||
* its connection if the stream was not yet closed. Please use this exclusively
|
||||
* before closing a stream to ensure stream count is well maintained.
|
||||
@ -4623,6 +4671,18 @@ static size_t h2s_frt_make_resp_headers(struct h2s *h2s, struct htx *htx)
|
||||
}
|
||||
}
|
||||
|
||||
/* update the frame's size */
|
||||
h2_set_frame_size(outbuf.area, outbuf.data - 9);
|
||||
|
||||
if (outbuf.data > h2c->mfs + 9) {
|
||||
if (!h2_fragment_headers(&outbuf, h2c->mfs)) {
|
||||
/* output full */
|
||||
if (b_space_wraps(mbuf))
|
||||
goto realign_again;
|
||||
goto full;
|
||||
}
|
||||
}
|
||||
|
||||
/* we may need to add END_STREAM except for 1xx responses.
|
||||
* FIXME: we should also set it when we know for sure that the
|
||||
* content-length is zero as well as on 204/304
|
||||
@ -4634,9 +4694,6 @@ static size_t h2s_frt_make_resp_headers(struct h2s *h2s, struct htx *htx)
|
||||
if (!h2s->cs || h2s->cs->flags & CS_FL_SHW)
|
||||
es_now = 1;
|
||||
|
||||
/* update the frame's size */
|
||||
h2_set_frame_size(outbuf.area, outbuf.data - 9);
|
||||
|
||||
if (es_now)
|
||||
outbuf.area[4] |= H2_F_HEADERS_END_STREAM;
|
||||
|
||||
@ -4912,6 +4969,18 @@ static size_t h2s_bck_make_req_headers(struct h2s *h2s, struct htx *htx)
|
||||
}
|
||||
}
|
||||
|
||||
/* update the frame's size */
|
||||
h2_set_frame_size(outbuf.area, outbuf.data - 9);
|
||||
|
||||
if (outbuf.data > h2c->mfs + 9) {
|
||||
if (!h2_fragment_headers(&outbuf, h2c->mfs)) {
|
||||
/* output full */
|
||||
if (b_space_wraps(mbuf))
|
||||
goto realign_again;
|
||||
goto full;
|
||||
}
|
||||
}
|
||||
|
||||
/* we may need to add END_STREAM if we have no body :
|
||||
* - request already closed, or :
|
||||
* - no transfer-encoding, and :
|
||||
@ -4927,9 +4996,6 @@ static size_t h2s_bck_make_req_headers(struct h2s *h2s, struct htx *htx)
|
||||
if (!h2s->cs || h2s->cs->flags & CS_FL_SHW)
|
||||
es_now = 1;
|
||||
|
||||
/* update the frame's size */
|
||||
h2_set_frame_size(outbuf.area, outbuf.data - 9);
|
||||
|
||||
if (es_now)
|
||||
outbuf.area[4] |= H2_F_HEADERS_END_STREAM;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user