From: Willy Tarreau Date: Wed, 25 Apr 2018 16:13:58 +0000 (+0200) Subject: MINOR: h2: detect presence of CONNECT and/or content-length X-Git-Tag: v1.9-dev1~291 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=174b06a572ef141f15d8b7ea64eb6b34ec4c9af1;p=thirdparty%2Fhaproxy.git MINOR: h2: detect presence of CONNECT and/or content-length We'll need this in order to support uploading chunks. The h2 to h1 converter checks for the presence of the content-length header field as well as the CONNECT method and returns these information to the caller. The caller indicates whether or not a body is detected for the message (presence of END_STREAM or not). No transfer-encoding header is emitted yet. --- diff --git a/include/common/h2.h b/include/common/h2.h index fdeafe67d3..d75a3f4653 100644 --- a/include/common/h2.h +++ b/include/common/h2.h @@ -145,9 +145,15 @@ enum h2_err { "\x0d\x0a\x53\x4d\x0d\x0a\x0d\x0a" +/* some flags related to protocol parsing */ +#define H2_MSGF_BODY 0x0001 // a body is present +#define H2_MSGF_BODY_CL 0x0002 // content-length is present +#define H2_MSGF_BODY_TUNNEL 0x0004 // a tunnel is in use (CONNECT) + + /* various protocol processing functions */ -int h2_make_h1_request(struct http_hdr *list, char *out, int osize); +int h2_make_h1_request(struct http_hdr *list, char *out, int osize, unsigned int *msgf); /* * Some helpful debugging functions. diff --git a/src/h2.c b/src/h2.c index 43ed7f3c8d..7d9ddd5026 100644 --- a/src/h2.c +++ b/src/h2.c @@ -36,9 +36,10 @@ * stored in . indicates what was found so far. This should be * called once at the detection of the first general header field or at the end * of the request if no general header field was found yet. Returns 0 on success - * or a negative error code on failure. + * or a negative error code on failure. Upon success, is updated with a + * few H2_MSGF_* flags indicating what was found while parsing. */ -static int h2_prepare_h1_reqline(uint32_t fields, struct ist *phdr, char **ptr, char *end) +static int h2_prepare_h1_reqline(uint32_t fields, struct ist *phdr, char **ptr, char *end, unsigned int *msgf) { char *out = *ptr; int uri_idx = H2_PHDR_IDX_PATH; @@ -62,6 +63,7 @@ static int h2_prepare_h1_reqline(uint32_t fields, struct ist *phdr, char **ptr, } // otherwise OK ; let's use the authority instead of the URI uri_idx = H2_PHDR_IDX_AUTH; + *msgf |= H2_MSGF_BODY_TUNNEL; } else if ((fields & (H2_PHDR_FND_METH|H2_PHDR_FND_SCHM|H2_PHDR_FND_PATH)) != (H2_PHDR_FND_METH|H2_PHDR_FND_SCHM|H2_PHDR_FND_PATH)) { @@ -113,6 +115,10 @@ static int h2_prepare_h1_reqline(uint32_t fields, struct ist *phdr, char **ptr, * for a max of bytes, and the amount of bytes emitted is returned. In * case of error, a negative error code is returned. * + * Upon success, is filled with a few H2_MSGF_* flags indicating what + * was found while parsing. The caller must set it to zero in or H2_MSGF_BODY + * if a body is detected (!ES). + * * The headers list must be composed of : * - n.name != NULL, n.len > 0 : literal header name * - n.name == NULL, n.len > 0 : indexed pseudo header name number @@ -124,7 +130,7 @@ static int h2_prepare_h1_reqline(uint32_t fields, struct ist *phdr, char **ptr, * The Cookie header will be reassembled at the end, and for this, the * will be used to create a linked list, so its contents may be destroyed. */ -int h2_make_h1_request(struct http_hdr *list, char *out, int osize) +int h2_make_h1_request(struct http_hdr *list, char *out, int osize, unsigned int *msgf) { struct ist phdr_val[H2_PHDR_NUM_ENTRIES]; char *out_end = out + osize; @@ -176,7 +182,7 @@ int h2_make_h1_request(struct http_hdr *list, char *out, int osize) /* regular header field in (name,value) */ if (!(fields & H2_PHDR_FND_NONE)) { /* no more pseudo-headers, time to build the request line */ - ret = h2_prepare_h1_reqline(fields, phdr_val, &out, out_end); + ret = h2_prepare_h1_reqline(fields, phdr_val, &out, out_end, msgf); if (ret != 0) goto leave; fields |= H2_PHDR_FND_NONE; @@ -185,6 +191,10 @@ int h2_make_h1_request(struct http_hdr *list, char *out, int osize) if (isteq(list[idx].n, ist("host"))) fields |= H2_PHDR_FND_HOST; + if ((*msgf & (H2_MSGF_BODY|H2_MSGF_BODY_TUNNEL|H2_MSGF_BODY_CL)) == H2_MSGF_BODY && + isteq(list[idx].n, ist("content-length"))) + *msgf |= H2_MSGF_BODY_CL; + /* these ones are forbidden in requests (RFC7540#8.1.2.2) */ if (isteq(list[idx].n, ist("connection")) || isteq(list[idx].n, ist("proxy-connection")) || @@ -232,7 +242,7 @@ int h2_make_h1_request(struct http_hdr *list, char *out, int osize) /* Let's dump the request now if not yet emitted. */ if (!(fields & H2_PHDR_FND_NONE)) { - ret = h2_prepare_h1_reqline(fields, phdr_val, &out, out_end); + ret = h2_prepare_h1_reqline(fields, phdr_val, &out, out_end, msgf); if (ret != 0) goto leave; } diff --git a/src/mux_h2.c b/src/mux_h2.c index f27131f7e3..5655bd8e0a 100644 --- a/src/mux_h2.c +++ b/src/mux_h2.c @@ -2586,6 +2586,7 @@ static int h2_frt_decode_headers(struct h2s *h2s, struct buffer *buf, int count) struct chunk *tmp = get_trash_chunk(); struct http_hdr list[MAX_HTTP_HDR * 2]; struct chunk *copy = NULL; + unsigned int msgf; int flen = h2c->dfl; int outlen = 0; int wrap; @@ -2687,13 +2688,22 @@ static int h2_frt_decode_headers(struct h2s *h2s, struct buffer *buf, int count) } /* OK now we have our header list in */ - outlen = h2_make_h1_request(list, bi_end(buf), try); + msgf = (h2c->dff & H2_F_DATA_END_STREAM) ? 0 : H2_MSGF_BODY; + outlen = h2_make_h1_request(list, bi_end(buf), try, &msgf); if (outlen < 0) { h2c_error(h2c, H2_ERR_COMPRESSION_ERROR); goto fail; } + if (msgf & H2_MSGF_BODY) { + /* a payload is present */ + if (msgf & H2_MSGF_BODY_CL) + h2s->flags |= H2_SF_DATA_CLEN; + else if (!(msgf & H2_MSGF_BODY_TUNNEL)) + h2s->flags |= H2_SF_DATA_CHNK; + } + /* now consume the input data */ bi_del(h2c->dbuf, h2c->dfl); h2c->st0 = H2_CS_FRAME_H;