MEDIUM: mux-h2: Remove support of the legacy HTTP mode
Now the H2 multiplexer only works in HTX. Code relying on the legacy HTTP mode
was removed.
diff --git a/src/mux_h2.c b/src/mux_h2.c
index 7866c78..271d19a 100644
--- a/src/mux_h2.c
+++ b/src/mux_h2.c
@@ -2745,8 +2745,8 @@
do {
b_realign_if_empty(buf);
- if (!b_data(buf) && (h2c->proxy->options2 & PR_O2_USE_HTX)) {
- /* HTX in use : try to pre-align the buffer like the
+ if (!b_data(buf)) {
+ /* try to pre-align the buffer like the
* rxbufs will be to optimize memory copies. We'll make
* sure that the frame header lands at the end of the
* HTX block to alias it upon recv. We cannot use the
@@ -3194,8 +3194,7 @@
h2s_destroy(h2s);
- if (h2c->flags & H2_CF_IS_BACK &&
- (h2c->proxy->options2 & PR_O2_USE_HTX)) {
+ if (h2c->flags & H2_CF_IS_BACK) {
if (!(h2c->conn->flags &
(CO_FL_ERROR | CO_FL_SOCK_RD_SH | CO_FL_SOCK_WR_SH))) {
if (!h2c->conn->owner) {
@@ -3424,12 +3423,12 @@
h2_do_shutw(h2s);
}
-/* Decode the payload of a HEADERS frame and produce the equivalent HTTP/1 or
- * HTX request or response depending on the connection's side. Returns a
- * positive value on success, a negative value on failure, or 0 if it couldn't
- * proceed. May report connection errors in h2c->errcode if the frame is
- * non-decodable and the connection unrecoverable. In absence of connection
- * error when a failure is reported, the caller must assume a stream error.
+/* Decode the payload of a HEADERS frame and produce the HTX request or response
+ * depending on the connection's side. Returns a positive value on success, a
+ * negative value on failure, or 0 if it couldn't proceed. May report connection
+ * errors in h2c->errcode if the frame is non-decodable and the connection
+ * unrecoverable. In absence of connection error when a failure is reported, the
+ * caller must assume a stream error.
*
* The function may fold CONTINUATION frames into the initial HEADERS frame
* by removing padding and next frame header, then moving the CONTINUATION
@@ -3491,7 +3490,6 @@
int ret = 0;
int outlen;
int wrap;
- int try = 0;
next_frame:
if (b_data(&h2c->dbuf) - hole < h2c->dfl)
@@ -3595,21 +3593,10 @@
* always empty except maybe for trailers, in which case we simply have
* to wait for the upper layer to finish consuming what is available.
*/
-
- if (h2c->proxy->options2 & PR_O2_USE_HTX) {
- htx = htx_from_buf(rxbuf);
- if (!htx_is_empty(htx)) {
- h2c->flags |= H2_CF_DEM_SFULL;
- goto leave;
- }
- } else {
- if (b_data(rxbuf)) {
- h2c->flags |= H2_CF_DEM_SFULL;
- goto leave;
- }
-
- rxbuf->head = 0;
- try = b_size(rxbuf);
+ htx = htx_from_buf(rxbuf);
+ if (!htx_is_empty(htx)) {
+ h2c->flags |= H2_CF_DEM_SFULL;
+ goto leave;
}
/* past this point we cannot roll back in case of error */
@@ -3635,18 +3622,10 @@
goto trailers;
/* This is the first HEADERS frame so it's a headers block */
- if (htx) {
- /* HTX mode */
- if (h2c->flags & H2_CF_IS_BACK)
- outlen = h2_make_htx_response(list, htx, &msgf, body_len);
- else
- outlen = h2_make_htx_request(list, htx, &msgf, body_len);
- } else {
- /* HTTP/1 mode */
- outlen = h2_make_h1_request(list, b_tail(rxbuf), try, &msgf, body_len);
- if (outlen > 0)
- b_add(rxbuf, outlen);
- }
+ if (h2c->flags & H2_CF_IS_BACK)
+ outlen = h2_make_htx_response(list, htx, &msgf, body_len);
+ else
+ outlen = h2_make_htx_request(list, htx, &msgf, body_len);
if (outlen < 0) {
/* too large headers? this is a stream error only */
@@ -3657,8 +3636,7 @@
/* a payload is present */
if (msgf & H2_MSGF_BODY_CL) {
*flags |= H2_SF_DATA_CLEN;
- if (htx)
- htx->extra = *body_len;
+ htx->extra = *body_len;
}
else if (!(msgf & H2_MSGF_BODY_TUNNEL) && !htx)
*flags |= H2_SF_DATA_CHNK;
@@ -3673,21 +3651,9 @@
*flags |= H2_SF_HEADERS_RCVD;
if ((h2c->dff & H2_F_HEADERS_END_STREAM)) {
- /* Mark the end of message, either using EOM in HTX or with the
- * trailing CRLF after the end of trailers. Note that DATA_CHNK
- * is not set during headers with END_STREAM. For HTX trailers,
- * we must not leave an HTX trailers block not followed by an
- * EOM block, the two must be atomic. Thus if we fail to emit
- * the EOM block we must remove the TLR block we've just added.
- */
- if (htx) {
- if (!htx_add_endof(htx, HTX_BLK_EOM))
- goto fail;
- }
- else if (*flags & H2_SF_DATA_CHNK) {
- if (!b_putblk(rxbuf, "\r\n", 2))
- goto fail;
- }
+ /* Mark the end of message using EOM */
+ if (!htx_add_endof(htx, HTX_BLK_EOM))
+ goto fail;
}
/* success */
@@ -3721,44 +3687,19 @@
trailers:
/* This is the last HEADERS frame hence a trailer */
-
if (!(h2c->dff & H2_F_HEADERS_END_STREAM)) {
/* It's a trailer but it's missing ES flag */
h2c_error(h2c, H2_ERR_PROTOCOL_ERROR);
goto fail;
}
- /* Trailers terminate a DATA sequence. In HTX we always handle them. In
- * legacy, when using chunks, we have to emit the 0 CRLF marker first
- * and then handle the trailers. For other modes, the trailers are
- * silently dropped.
- */
- if (htx) {
- if (h2_make_htx_trailers(list, htx) <= 0)
- goto fail;
- }
- else if (*flags & H2_SF_DATA_CHNK) {
- /* Legacy mode with chunked encoding : we must finalize the
- * data block message emit the trailing CRLF */
- if (!b_putblk(rxbuf, "0\r\n", 3))
- goto fail;
-
- outlen = h2_make_h1_trailers(list, b_tail(rxbuf), try);
- if (outlen > 0)
- b_add(rxbuf, outlen);
- else
- goto fail;
- }
-
+ /* Trailers terminate a DATA sequence */
+ if (h2_make_htx_trailers(list, htx) <= 0)
+ goto fail;
goto done;
}
-/* Transfer the payload of a DATA frame to the HTTP/1 side. When content-length
- * or a tunnel is used, the contents are copied as-is. When chunked encoding is
- * in use, a new chunk is emitted for each frame. This is supposed to fit
- * because the smallest chunk takes 1 byte for the size, 2 for CRLF, X for the
- * data, 2 for the extra CRLF, so that's 5+X, while on the H2 side the smallest
- * frame will be 9+X bytes based on the same buffer size. The HTTP/2 frame
+/* Transfer the payload of a DATA frame to the HTTP/1 side. The HTTP/2 frame
* parser state is automatically updated. Returns > 0 if it could completely
* send the current frame, 0 if it couldn't complete, in which case
* CS_FL_RCV_MORE must be checked to know if some data remain pending (an empty
@@ -3770,11 +3711,11 @@
static int h2_frt_transfer_data(struct h2s *h2s)
{
struct h2c *h2c = h2s->h2c;
- int block1, block2;
+ int block;
unsigned int flen = 0;
- unsigned int chklen = 0;
struct htx *htx = NULL;
struct buffer *csbuf;
+ unsigned int sent;
h2c->flags &= ~H2_CF_DEM_SFULL;
@@ -3783,11 +3724,10 @@
h2c->flags |= H2_CF_DEM_SALLOC;
goto fail;
}
+ htx = htx_from_buf(csbuf);
try_again:
flen = h2c->dfl - h2c->dpl;
- if (h2c->proxy->options2 & PR_O2_USE_HTX)
- htx = htx_from_buf(csbuf);
if (!flen)
goto end_transfer;
@@ -3797,140 +3737,47 @@
goto fail;
}
- if (htx) {
- unsigned int sent;
-
- block1 = htx_free_data_space(htx);
- if (!block1) {
- h2c->flags |= H2_CF_DEM_SFULL;
- goto fail;
- }
- if (flen > block1)
- flen = block1;
-
- /* here, flen is the max we can copy into the output buffer */
- block1 = b_contig_data(&h2c->dbuf, 0);
- if (flen > block1)
- flen = block1;
-
- sent = htx_add_data(htx, ist2(b_head(&h2c->dbuf), flen));
-
- b_del(&h2c->dbuf, sent);
- h2c->dfl -= sent;
- h2c->rcvd_c += sent;
- h2c->rcvd_s += sent; // warning, this can also affect the closed streams!
-
- if (h2s->flags & H2_SF_DATA_CLEN) {
- h2s->body_len -= sent;
- htx->extra = h2s->body_len;
- }
-
- if (sent < flen) {
- h2c->flags |= H2_CF_DEM_SFULL;
- goto fail;
- }
-
- goto try_again;
- }
- else if (unlikely(b_space_wraps(csbuf) &&
- flen + chklen <= b_room(csbuf) &&
- b_data(csbuf) <= MAX_DATA_REALIGN)) {
- /* it doesn't fit in a single block and the buffer is fragmented, if there are
- * not too many data in the buffer, let's defragment it and try
- * again.
- */
- b_slow_realign(csbuf, trash.area, 0);
- }
-
- /* chunked-encoding requires more room */
- if (h2s->flags & H2_SF_DATA_CHNK) {
- chklen = MIN(flen, b_room(csbuf));
- chklen = (chklen < 16) ? 1 : (chklen < 256) ? 2 :
- (chklen < 4096) ? 3 : (chklen < 65536) ? 4 :
- (chklen < 1048576) ? 4 : 8;
- chklen += 4; // CRLF, CRLF
- }
-
- /* does it fit in output buffer or should we wait ? */
- if (flen + chklen > b_room(csbuf)) {
- if (chklen >= b_room(csbuf)) {
- h2c->flags |= H2_CF_DEM_SFULL;
- goto fail;
- }
- flen = b_room(csbuf) - chklen;
+ block = htx_free_data_space(htx);
+ if (!block) {
+ h2c->flags |= H2_CF_DEM_SFULL;
+ goto fail;
}
+ if (flen > block)
+ flen = block;
- if (h2s->flags & H2_SF_DATA_CHNK) {
- /* emit the chunk size */
- unsigned int chksz = flen;
- char str[10];
- char *beg;
+ /* here, flen is the max we can copy into the output buffer */
+ block = b_contig_data(&h2c->dbuf, 0);
+ if (flen > block)
+ flen = block;
- beg = str + sizeof(str);
- *--beg = '\n';
- *--beg = '\r';
- do {
- *--beg = hextab[chksz & 0xF];
- } while (chksz >>= 4);
- b_putblk(csbuf, beg, str + sizeof(str) - beg);
- }
-
- /* Block1 is the length of the first block before the buffer wraps,
- * block2 is the optional second block to reach the end of the frame.
- */
- block1 = b_contig_data(&h2c->dbuf, 0);
- if (block1 > flen)
- block1 = flen;
- block2 = flen - block1;
+ sent = htx_add_data(htx, ist2(b_head(&h2c->dbuf), flen));
- if (block1)
- b_putblk(csbuf, b_head(&h2c->dbuf), block1);
+ b_del(&h2c->dbuf, sent);
+ h2c->dfl -= sent;
+ h2c->rcvd_c += sent;
+ h2c->rcvd_s += sent; // warning, this can also affect the closed streams!
- if (block2)
- b_putblk(csbuf, b_peek(&h2c->dbuf, block1), block2);
-
- if (h2s->flags & H2_SF_DATA_CHNK) {
- /* emit the CRLF */
- b_putblk(csbuf, "\r\n", 2);
+ if (h2s->flags & H2_SF_DATA_CLEN) {
+ h2s->body_len -= sent;
+ htx->extra = h2s->body_len;
}
- /* now mark the input data as consumed (will be deleted from the buffer
- * by the caller when seeing FRAME_A after sending the window update).
- */
- b_del(&h2c->dbuf, flen);
- h2c->dfl -= flen;
- h2c->rcvd_c += flen;
- h2c->rcvd_s += flen; // warning, this can also affect the closed streams!
-
- if (h2s->flags & H2_SF_DATA_CLEN)
- h2s->body_len -= flen;
-
- if (h2c->dfl > h2c->dpl) {
- /* more data available, transfer stalled on stream full */
+ if (sent < flen) {
h2c->flags |= H2_CF_DEM_SFULL;
goto fail;
}
+ goto try_again;
+
end_transfer:
/* here we're done with the frame, all the payload (except padding) was
* transferred.
*/
if (h2c->dff & H2_F_DATA_END_STREAM) {
- if (htx) {
- if (!htx_add_endof(htx, HTX_BLK_EOM)) {
- h2c->flags |= H2_CF_DEM_SFULL;
- goto fail;
- }
- }
- else if (h2s->flags & H2_SF_DATA_CHNK) {
- /* emit the trailing 0 CRLF CRLF */
- if (b_room(csbuf) < 5) {
- h2c->flags |= H2_CF_DEM_SFULL;
- goto fail;
- }
- chklen += 5;
- b_putblk(csbuf, "0\r\n\r\n", 5);
+ if (!htx_add_endof(htx, HTX_BLK_EOM)) {
+ h2c->flags |= H2_CF_DEM_SFULL;
+ goto fail;
}
}
@@ -3938,8 +3785,7 @@
h2c->rcvd_s += h2c->dpl;
h2c->dpl = 0;
h2c->st0 = H2_CS_FRAME_A; // send the corresponding window update
- if (htx)
- htx_to_buf(htx, csbuf);
+ htx_to_buf(htx, csbuf);
return 1;
fail:
if (htx)
@@ -3947,453 +3793,6 @@
return 0;
}
-/* Try to send a HEADERS frame matching HTTP/1 response present at offset <ofs>
- * and for <max> bytes in buffer <buf> for the H2 stream <h2s>. Returns the
- * number of bytes sent. The caller must check the stream's status to detect
- * any error which might have happened subsequently to a successful send.
- */
-static size_t h2s_frt_make_resp_headers(struct h2s *h2s, const struct buffer *buf, size_t ofs, size_t max)
-{
- struct http_hdr list[global.tune.max_http_hdr];
- struct h2c *h2c = h2s->h2c;
- struct h1m *h1m = &h2s->h1m;
- struct buffer outbuf;
- struct buffer *mbuf;
- union h1_sl sl;
- int es_now = 0;
- int ret = 0;
- int hdr;
-
- if (h2c_mux_busy(h2c, h2s)) {
- h2s->flags |= H2_SF_BLK_MBUSY;
- return 0;
- }
-
- /* First, try to parse the H1 response and index it into <list>.
- * NOTE! Since it comes from haproxy, we *know* that a response header
- * block does not wrap and we can safely read it this way without
- * having to realign the buffer.
- */
- ret = h1_headers_to_hdr_list(b_peek(buf, ofs), b_peek(buf, ofs) + max,
- list, sizeof(list)/sizeof(list[0]), h1m, &sl);
- if (ret <= 0) {
- /* incomplete or invalid response, this is abnormal coming from
- * haproxy and may only result in a bad errorfile or bad Lua code
- * so that won't be fixed, raise an error now.
- *
- * FIXME: we should instead add the ability to only return a
- * 502 bad gateway. But in theory this is not supposed to
- * happen.
- */
- h2s_error(h2s, H2_ERR_INTERNAL_ERROR);
- ret = 0;
- goto end;
- }
-
- h2s->status = sl.st.status;
-
- /* certain statuses have no body or an empty one, regardless of
- * what the headers say.
- */
- if (sl.st.status >= 100 && sl.st.status < 200) {
- h1m->flags &= ~(H1_MF_CLEN | H1_MF_CHNK);
- h1m->curr_len = h1m->body_len = 0;
- }
- else if (sl.st.status == 204 || sl.st.status == 304) {
- /* no contents, claim c-len is present and set to zero */
- h1m->flags &= ~H1_MF_CHNK;
- h1m->flags |= H1_MF_CLEN;
- h1m->curr_len = h1m->body_len = 0;
- }
-
- mbuf = br_tail(h2c->mbuf);
- retry:
- if (!h2_get_buf(h2c, mbuf)) {
- h2c->flags |= H2_CF_MUX_MALLOC;
- h2s->flags |= H2_SF_BLK_MROOM;
- return 0;
- }
-
- chunk_reset(&outbuf);
-
- while (1) {
- outbuf = b_make(b_tail(mbuf), b_contig_space(mbuf), 0, 0);
- if (outbuf.size >= 9 || !b_space_wraps(mbuf))
- break;
- realign_again:
- b_slow_realign(mbuf, trash.area, b_data(mbuf));
- }
-
- if (outbuf.size < 9)
- goto full;
-
- /* len: 0x000000 (fill later), type: 1(HEADERS), flags: ENDH=4 */
- memcpy(outbuf.area, "\x00\x00\x00\x01\x04", 5);
- write_n32(outbuf.area + 5, h2s->id); // 4 bytes
- outbuf.data = 9;
-
- /* encode status, which necessarily is the first one */
- if (unlikely(list[0].v.len != 3)) {
- /* this is an unparsable response */
- h2s_error(h2s, H2_ERR_INTERNAL_ERROR);
- ret = 0;
- goto end;
- }
-
- if (!hpack_encode_str_status(&outbuf, h2s->status, list[0].v)) {
- if (b_space_wraps(mbuf))
- goto realign_again;
- goto full;
- }
-
- /* encode all headers, stop at empty name */
- for (hdr = 1; hdr < sizeof(list)/sizeof(list[0]); hdr++) {
- /* these ones do not exist in H2 and must be dropped. */
- if (isteq(list[hdr].n, ist("connection")) ||
- isteq(list[hdr].n, ist("proxy-connection")) ||
- isteq(list[hdr].n, ist("keep-alive")) ||
- isteq(list[hdr].n, ist("upgrade")) ||
- isteq(list[hdr].n, ist("transfer-encoding")))
- continue;
-
- if (isteq(list[hdr].n, ist("")))
- break; // end
-
- if (!hpack_encode_header(&outbuf, list[hdr].n, list[hdr].v)) {
- /* output full */
- if (b_space_wraps(mbuf))
- goto realign_again;
- goto full;
- }
- }
-
- /* we may need to add END_STREAM */
- if (((h1m->flags & H1_MF_CLEN) && !h1m->body_len) || !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;
-
- /* consume incoming H1 response */
- max -= ret;
-
- /* commit the H2 response */
- b_add(mbuf, outbuf.data);
- h2s->flags |= H2_SF_HEADERS_SENT;
-
- if (es_now) {
- // trim any possibly pending data (eg: inconsistent content-length)
- ret += max;
-
- h1m->state = H1_MSG_DONE;
- h2s->flags |= H2_SF_ES_SENT;
- if (h2s->st == H2_SS_OPEN)
- h2s->st = H2_SS_HLOC;
- else
- h2s_close(h2s);
- }
- else if (h2s->status >= 100 && h2s->status < 200) {
- /* we'll let the caller check if it has more headers to send */
- h1m_init_res(h1m);
- h1m->err_pos = -1; // don't care about errors on the response path
- h2s->h1m.flags |= H1_MF_TOLOWER;
- goto end;
- }
-
- /* now the h1m state is either H1_MSG_CHUNK_SIZE or H1_MSG_DATA */
-
- end:
- //fprintf(stderr, "[%d] sent simple H2 response (sid=%d) = %d bytes (%d in, ep=%u, es=%s)\n", h2c->st0, h2s->id, outbuf.len, ret, h1m->err_pos, h1m_state_str(h1m->err_state));
- return ret;
- full:
- if ((mbuf = br_tail_add(h2c->mbuf)) != NULL)
- goto retry;
- h1m_init_res(h1m);
- h1m->err_pos = -1; // don't care about errors on the response path
- h2c->flags |= H2_CF_MUX_MFULL;
- h2s->flags |= H2_SF_BLK_MROOM;
- ret = 0;
- goto end;
-}
-
-/* Try to send a DATA frame matching HTTP/1 response present at offset <ofs>
- * for up to <max> bytes in response buffer <buf>, for stream <h2s>. Returns
- * the number of bytes sent. The caller must check the stream's status to
- * detect any error which might have happened subsequently to a successful send.
- */
-static size_t h2s_frt_make_resp_data(struct h2s *h2s, const struct buffer *buf, size_t ofs, size_t max)
-{
- struct h2c *h2c = h2s->h2c;
- struct h1m *h1m = &h2s->h1m;
- struct buffer outbuf;
- struct buffer *mbuf;
- int ret = 0;
- size_t total = 0;
- int es_now = 0;
- int size = 0;
- const char *blk1, *blk2;
- size_t len1, len2;
-
- if (h2c_mux_busy(h2c, h2s)) {
- h2s->flags |= H2_SF_BLK_MBUSY;
- goto end;
- }
-
- mbuf = br_tail(h2c->mbuf);
- retry:
- if (!h2_get_buf(h2c, mbuf)) {
- h2c->flags |= H2_CF_MUX_MALLOC;
- h2s->flags |= H2_SF_BLK_MROOM;
- goto end;
- }
-
- new_frame:
- if (!max)
- goto end;
-
- chunk_reset(&outbuf);
-
- while (1) {
- outbuf = b_make(b_tail(mbuf), b_contig_space(mbuf), 0, 0);
- if (outbuf.size >= 9 || !b_space_wraps(mbuf))
- break;
- realign_again:
- /* If there are pending data in the output buffer, and we have
- * less than 1/4 of the mbuf's size and everything fits, we'll
- * still perform a copy anyway. Otherwise we'll pretend the mbuf
- * is full and wait, to save some slow realign calls.
- */
- if ((max + 9 > b_room(mbuf) || max >= b_size(mbuf) / 4)) {
- if ((mbuf = br_tail_add(h2c->mbuf)) != NULL)
- goto retry;
- h2c->flags |= H2_CF_MUX_MFULL;
- h2s->flags |= H2_SF_BLK_MROOM;
- goto end;
- }
-
- b_slow_realign(mbuf, trash.area, b_data(mbuf));
- }
-
- if (outbuf.size < 9) {
- if ((mbuf = br_tail_add(h2c->mbuf)) != NULL)
- goto retry;
- h2c->flags |= H2_CF_MUX_MFULL;
- h2s->flags |= H2_SF_BLK_MROOM;
- goto end;
- }
-
- /* len: 0x000000 (fill later), type: 0(DATA), flags: none=0 */
- memcpy(outbuf.area, "\x00\x00\x00\x00\x00", 5);
- write_n32(outbuf.area + 5, h2s->id); // 4 bytes
- outbuf.data = 9;
-
- switch (h1m->flags & (H1_MF_CLEN|H1_MF_CHNK)) {
- case 0: /* no content length, read till SHUTW */
- size = max;
- h1m->curr_len = size;
- break;
- case H1_MF_CLEN: /* content-length: read only h2m->body_len */
- size = max;
- if ((long long)size > h1m->curr_len)
- size = h1m->curr_len;
- break;
- default: /* te:chunked : parse chunks */
- if (h1m->state == H1_MSG_CHUNK_CRLF) {
- ret = h1_skip_chunk_crlf(buf, ofs, ofs + max);
- if (!ret)
- goto end;
-
- if (ret < 0) {
- /* FIXME: bad contents. how to proceed here when we're in H2 ? */
- h1m->err_pos = ofs + max + ret;
- h2s_error(h2s, H2_ERR_INTERNAL_ERROR);
- goto end;
- }
- max -= ret;
- ofs += ret;
- total += ret;
- h1m->state = H1_MSG_CHUNK_SIZE;
- }
-
- if (h1m->state == H1_MSG_CHUNK_SIZE) {
- unsigned int chunk;
- ret = h1_parse_chunk_size(buf, ofs, ofs + max, &chunk);
- if (!ret)
- goto end;
-
- if (ret < 0) {
- /* FIXME: bad contents. how to proceed here when we're in H2 ? */
- h1m->err_pos = ofs + max + ret;
- h2s_error(h2s, H2_ERR_INTERNAL_ERROR);
- goto end;
- }
-
- size = chunk;
- h1m->curr_len = chunk;
- h1m->body_len += chunk;
- max -= ret;
- ofs += ret;
- total += ret;
- h1m->state = size ? H1_MSG_DATA : H1_MSG_TRAILERS;
- if (!size)
- goto send_empty;
- }
-
- /* in MSG_DATA state, continue below */
- size = h1m->curr_len;
- break;
- }
-
- /* we have in <size> the exact number of bytes we need to copy from
- * the H1 buffer. We need to check this against the connection's and
- * the stream's send windows, and to ensure that this fits in the max
- * frame size and in the buffer's available space minus 9 bytes (for
- * the frame header). The connection's flow control is applied last so
- * that we can use a separate list of streams which are immediately
- * unblocked on window opening. Note: we don't implement padding.
- */
-
- if (size > max)
- size = max;
-
- if (size > h2s->mws)
- size = h2s->mws;
-
- if (size <= 0) {
- h2s->flags |= H2_SF_BLK_SFCTL;
- if (LIST_ADDED(&h2s->list))
- LIST_DEL_INIT(&h2s->list);
- goto end;
- }
-
- if (h2c->mfs && size > h2c->mfs)
- size = h2c->mfs;
-
- if (size + 9 > outbuf.size) {
- /* It doesn't fit at once. If it at least fits once split and
- * the amount of data to move is low, let's defragment the
- * buffer now.
- */
- if (b_space_wraps(mbuf) &&
- (size + 9 <= b_room(mbuf)) &&
- b_data(mbuf) <= MAX_DATA_REALIGN)
- goto realign_again;
- size = outbuf.size - 9;
- }
-
- if (size <= 0) {
- if ((mbuf = br_tail_add(h2c->mbuf)) != NULL)
- goto retry;
- h2c->flags |= H2_CF_MUX_MFULL;
- h2s->flags |= H2_SF_BLK_MROOM;
- goto end;
- }
-
- if (size > h2c->mws)
- size = h2c->mws;
-
- if (size <= 0) {
- h2s->flags |= H2_SF_BLK_MFCTL;
- goto end;
- }
-
- /* copy whatever we can */
- blk1 = blk2 = NULL; // silence a maybe-uninitialized warning
- ret = b_getblk_nc(buf, &blk1, &len1, &blk2, &len2, ofs, max);
- if (ret == 1)
- len2 = 0;
-
- if (!ret || len1 + len2 < size) {
- /* FIXME: must normally never happen */
- h2s_error(h2s, H2_ERR_INTERNAL_ERROR);
- goto end;
- }
-
- /* limit len1/len2 to size */
- if (len1 + len2 > size) {
- int sub = len1 + len2 - size;
-
- if (len2 > sub)
- len2 -= sub;
- else {
- sub -= len2;
- len2 = 0;
- len1 -= sub;
- }
- }
-
- /* now let's copy this this into the output buffer */
- memcpy(outbuf.area + 9, blk1, len1);
- if (len2)
- memcpy(outbuf.area + 9 + len1, blk2, len2);
-
- send_empty:
- /* we may need to add END_STREAM */
- /* FIXME: we should also detect shutdown(w) below, but how ? Maybe we
- * could rely on the MSG_MORE flag as a hint for this ?
- *
- * FIXME: what we do here is not correct because we send end_stream
- * before knowing if we'll have to send a HEADERS frame for the
- * trailers. More importantly we're not consuming the trailing CRLF
- * after the end of trailers, so it will be left to the caller to
- * eat it. The right way to do it would be to measure trailers here
- * and to send ES only if there are no trailers.
- *
- */
- if (((h1m->flags & H1_MF_CLEN) && !(h1m->curr_len - size)) ||
- !h1m->curr_len || h1m->state >= H1_MSG_DONE)
- es_now = 1;
-
- /* update the frame's size */
- h2_set_frame_size(outbuf.area, size);
-
- if (es_now)
- outbuf.area[4] |= H2_F_DATA_END_STREAM;
-
- /* commit the H2 response */
- b_add(mbuf, size + 9);
-
- /* consume incoming H1 response */
- if (size > 0) {
- max -= size;
- ofs += size;
- total += size;
- h1m->curr_len -= size;
- h2s->mws -= size;
- h2c->mws -= size;
-
- if (size && !h1m->curr_len && (h1m->flags & H1_MF_CHNK)) {
- h1m->state = H1_MSG_CHUNK_CRLF;
- goto new_frame;
- }
- }
-
- if (es_now) {
- if (h2s->st == H2_SS_OPEN)
- h2s->st = H2_SS_HLOC;
- else
- h2s_close(h2s);
-
- if (!(h1m->flags & H1_MF_CHNK)) {
- // trim any possibly pending data (eg: inconsistent content-length)
- total += max;
- ofs += max;
- max = 0;
-
- h1m->state = H1_MSG_DONE;
- }
-
- h2s->flags |= H2_SF_ES_SENT;
- }
-
- end:
- trace("[%d] sent simple H2 DATA response (sid=%d) = %d bytes out (%u in, st=%s, ep=%u, es=%s, h2cws=%d h2sws=%d) data=%u", h2c->st0, h2s->id, size+9, (unsigned int)total, h1m_state_str(h1m->state), h1m->err_pos, h1m_state_str(h1m->err_state), h2c->mws, h2s->mws, (unsigned int)b_data(buf));
- return total;
-}
-
/* Try to send a HEADERS frame matching HTX response present in HTX message
* <htx> for the H2 stream <h2s>. Returns the number of bytes sent. The caller
* must check the stream's status to detect any error which might have happened
@@ -4403,7 +3802,7 @@
* header blocks and an end of header, otherwise an invalid frame could be
* emitted and the resulting htx message could be left in an inconsistent state.
*/
-static size_t h2s_htx_frt_make_resp_headers(struct h2s *h2s, struct htx *htx)
+static size_t h2s_frt_make_resp_headers(struct h2s *h2s, struct htx *htx)
{
struct http_hdr list[global.tune.max_http_hdr];
struct h2c *h2c = h2s->h2c;
@@ -4611,7 +4010,7 @@
* header blocks and an end of header, otherwise an invalid frame could be
* emitted and the resulting htx message could be left in an inconsistent state.
*/
-static size_t h2s_htx_bck_make_req_headers(struct h2s *h2s, struct htx *htx)
+static size_t h2s_bck_make_req_headers(struct h2s *h2s, struct htx *htx)
{
struct http_hdr list[global.tune.max_http_hdr];
struct h2c *h2c = h2s->h2c;
@@ -4862,7 +4261,7 @@
* happened subsequently to a successful send. Returns the number of data bytes
* consumed, or zero if nothing done. Note that EOM count for 1 byte.
*/
-static size_t h2s_htx_frt_make_resp_data(struct h2s *h2s, struct buffer *buf, size_t count)
+static size_t h2s_frt_make_resp_data(struct h2s *h2s, struct buffer *buf, size_t count)
{
struct h2c *h2c = h2s->h2c;
struct htx *htx;
@@ -5127,7 +4526,7 @@
* the EOM, which is *not* removed. All trailers are processed at once and sent
* as a single frame. The ES flag is always set.
*/
-static size_t h2s_htx_make_trailers(struct h2s *h2s, struct htx *htx)
+static size_t h2s_make_trailers(struct h2s *h2s, struct htx *htx)
{
struct http_hdr list[global.tune.max_http_hdr];
struct h2c *h2c = h2s->h2c;
@@ -5367,45 +4766,40 @@
size_t ret = 0;
/* transfer possibly pending data to the upper layer */
- if (h2c->proxy->options2 & PR_O2_USE_HTX) {
- h2s_htx = htx_from_buf(&h2s->rxbuf);
- if (htx_is_empty(h2s_htx)) {
- /* Here htx_to_buf() will set buffer data to 0 because
- * the HTX is empty.
- */
- htx_to_buf(h2s_htx, &h2s->rxbuf);
- goto end;
- }
-
- ret = h2s_htx->data;
- buf_htx = htx_from_buf(buf);
-
- /* <buf> is empty and the message is small enough, swap the
- * buffers. */
- if (htx_is_empty(buf_htx) && htx_used_space(h2s_htx) <= count) {
- htx_to_buf(buf_htx, buf);
- htx_to_buf(h2s_htx, &h2s->rxbuf);
- b_xfer(buf, &h2s->rxbuf, b_data(&h2s->rxbuf));
- goto end;
- }
-
- htx_xfer_blks(buf_htx, h2s_htx, count, HTX_BLK_EOM);
+ h2s_htx = htx_from_buf(&h2s->rxbuf);
+ if (htx_is_empty(h2s_htx)) {
+ /* Here htx_to_buf() will set buffer data to 0 because
+ * the HTX is empty.
+ */
+ htx_to_buf(h2s_htx, &h2s->rxbuf);
+ goto end;
+ }
- if (h2s_htx->flags & HTX_FL_PARSING_ERROR) {
- buf_htx->flags |= HTX_FL_PARSING_ERROR;
- if (htx_is_empty(buf_htx))
- cs->flags |= CS_FL_EOI;
- }
+ ret = h2s_htx->data;
+ buf_htx = htx_from_buf(buf);
- buf_htx->extra = (h2s_htx->extra ? (h2s_htx->data + h2s_htx->extra) : 0);
+ /* <buf> is empty and the message is small enough, swap the
+ * buffers. */
+ if (htx_is_empty(buf_htx) && htx_used_space(h2s_htx) <= count) {
htx_to_buf(buf_htx, buf);
htx_to_buf(h2s_htx, &h2s->rxbuf);
- ret -= h2s_htx->data;
+ b_xfer(buf, &h2s->rxbuf, b_data(&h2s->rxbuf));
+ goto end;
}
- else {
- ret = b_xfer(buf, &h2s->rxbuf, count);
+
+ htx_xfer_blks(buf_htx, h2s_htx, count, HTX_BLK_EOM);
+
+ if (h2s_htx->flags & HTX_FL_PARSING_ERROR) {
+ buf_htx->flags |= HTX_FL_PARSING_ERROR;
+ if (htx_is_empty(buf_htx))
+ cs->flags |= CS_FL_EOI;
}
+ buf_htx->extra = (h2s_htx->extra ? (h2s_htx->data + h2s_htx->extra) : 0);
+ htx_to_buf(buf_htx, buf);
+ htx_to_buf(h2s_htx, &h2s->rxbuf);
+ ret -= h2s_htx->data;
+
end:
if (b_data(&h2s->rxbuf))
cs->flags |= (CS_FL_RCV_MORE | CS_FL_WANT_ROOM);
@@ -5478,8 +4872,7 @@
if (h2s->h2c->st0 < H2_CS_FRAME_H)
return 0;
- /* htx will be enough to decide if we're using HTX or legacy */
- htx = (h2s->h2c->proxy->options2 & PR_O2_USE_HTX) ? htx_from_buf(buf) : NULL;
+ htx = htx_from_buf(buf);
if (!(h2s->flags & H2_SF_OUTGOING_DATA) && count)
h2s->flags |= H2_SF_OUTGOING_DATA;
@@ -5499,18 +4892,17 @@
eb32_insert(&h2s->h2c->streams_by_id, &h2s->by_id);
}
- if (htx) {
- while (h2s->st < H2_SS_HLOC && !(h2s->flags & H2_SF_BLK_ANY) &&
- count && !htx_is_empty(htx)) {
- idx = htx_get_head(htx);
- blk = htx_get_blk(htx, idx);
- btype = htx_get_blk_type(blk);
- bsize = htx_get_blksz(blk);
+ while (h2s->st < H2_SS_HLOC && !(h2s->flags & H2_SF_BLK_ANY) &&
+ count && !htx_is_empty(htx)) {
+ idx = htx_get_head(htx);
+ blk = htx_get_blk(htx, idx);
+ btype = htx_get_blk_type(blk);
+ bsize = htx_get_blksz(blk);
- switch (btype) {
+ switch (btype) {
case HTX_BLK_REQ_SL:
/* start-line before headers */
- ret = h2s_htx_bck_make_req_headers(h2s, htx);
+ ret = h2s_bck_make_req_headers(h2s, htx);
if (ret > 0) {
total += ret;
count -= ret;
@@ -5521,7 +4913,7 @@
case HTX_BLK_RES_SL:
/* start-line before headers */
- ret = h2s_htx_frt_make_resp_headers(h2s, htx);
+ ret = h2s_frt_make_resp_headers(h2s, htx);
if (ret > 0) {
total += ret;
count -= ret;
@@ -5536,7 +4928,7 @@
* This EOM necessarily is one before trailers, as the EOM following
* trailers would have been consumed by the trailers parser.
*/
- ret = h2s_htx_frt_make_resp_data(h2s, buf, count);
+ ret = h2s_frt_make_resp_data(h2s, buf, count);
if (ret > 0) {
htx = htx_from_buf(buf);
total += ret;
@@ -5551,7 +4943,7 @@
/* This is the first trailers block, all the subsequent ones AND
* the EOM will be swallowed by the parser.
*/
- ret = h2s_htx_make_trailers(h2s, htx);
+ ret = h2s_make_trailers(h2s, htx);
if (ret > 0) {
total += ret;
count -= ret;
@@ -5565,53 +4957,10 @@
total += bsize;
count -= bsize;
break;
- }
}
- goto done;
}
- /* legacy transfer mode */
- while (h2s->h1m.state < H1_MSG_DONE && count) {
- if (h2s->h1m.state <= H1_MSG_LAST_LF) {
- if (h2s->h2c->flags & H2_CF_IS_BACK)
- ret = -1;
- else
- ret = h2s_frt_make_resp_headers(h2s, buf, total, count);
- }
- else if (h2s->h1m.state < H1_MSG_TRAILERS) {
- ret = h2s_frt_make_resp_data(h2s, buf, total, count);
- }
- else if (h2s->h1m.state == H1_MSG_TRAILERS) {
- /* consume the trailers if any (we don't forward them for now) */
- ret = h1_measure_trailers(buf, total, count);
-
- if (unlikely((int)ret <= 0)) {
- if ((int)ret < 0)
- h2s_error(h2s, H2_ERR_INTERNAL_ERROR);
- break;
- }
- // trim any possibly pending data (eg: extra CR-LF, ...)
- total += count;
- count = 0;
- h2s->h1m.state = H1_MSG_DONE;
- break;
- }
- else {
- cs_set_error(cs);
- break;
- }
-
- total += ret;
- count -= ret;
-
- if (h2s->st >= H2_SS_HLOC)
- break;
-
- if (h2s->flags & H2_SF_BLK_ANY)
- break;
- }
-
- done:
+ done:
if (h2s->st >= H2_SS_HLOC) {
/* trim any possibly pending data after we close (extra CR-LF,
* unprocessed trailers, abnormal extra data, ...)
@@ -5627,11 +4976,7 @@
h2s_close(h2s);
}
- if (htx) {
- htx_to_buf(htx, buf);
- } else {
- b_del(buf, total);
- }
+ htx_to_buf(htx, buf);
/* The mux is full, cancel the pending tasks */
if ((h2s->h2c->flags & H2_CF_MUX_BLOCK_ANY) ||
@@ -5813,43 +5158,15 @@
.shutr = h2_shutr,
.shutw = h2_shutw,
.show_fd = h2_show_fd,
- .flags = MX_FL_CLEAN_ABRT,
+ .flags = MX_FL_CLEAN_ABRT|MX_FL_HTX,
.name = "H2",
};
-/* PROTO selection : this mux registers PROTO token "h2" */
static struct mux_proto_list mux_proto_h2 =
- { .token = IST("h2"), .mode = PROTO_MODE_HTTP, .side = PROTO_SIDE_FE, .mux = &h2_ops };
+ { .token = IST("h2"), .mode = PROTO_MODE_HTX, .side = PROTO_SIDE_BOTH, .mux = &h2_ops };
INITCALL1(STG_REGISTER, register_mux_proto, &mux_proto_h2);
-
-/* The mux operations */
-static const struct mux_ops h2_htx_ops = {
- .init = h2_init,
- .wake = h2_wake,
- .snd_buf = h2_snd_buf,
- .rcv_buf = h2_rcv_buf,
- .subscribe = h2_subscribe,
- .unsubscribe = h2_unsubscribe,
- .attach = h2_attach,
- .get_first_cs = h2_get_first_cs,
- .detach = h2_detach,
- .destroy = h2_destroy,
- .avail_streams = h2_avail_streams,
- .used_streams = h2_used_streams,
- .shutr = h2_shutr,
- .shutw = h2_shutw,
- .show_fd = h2_show_fd,
- .flags = MX_FL_CLEAN_ABRT|MX_FL_HTX,
- .name = "H2",
-};
-
-static struct mux_proto_list mux_proto_h2_htx =
- { .token = IST("h2"), .mode = PROTO_MODE_HTX, .side = PROTO_SIDE_BOTH, .mux = &h2_htx_ops };
-
-INITCALL1(STG_REGISTER, register_mux_proto, &mux_proto_h2_htx);
-
/* config keyword parsers */
static struct cfg_kw_list cfg_kws = {ILH, {
{ CFG_GLOBAL, "tune.h2.header-table-size", h2_parse_header_table_size },