]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: h2: detect presence of CONNECT and/or content-length
authorWilly Tarreau <w@1wt.eu>
Wed, 25 Apr 2018 16:13:58 +0000 (18:13 +0200)
committerWilly Tarreau <w@1wt.eu>
Thu, 26 Apr 2018 08:15:14 +0000 (10:15 +0200)
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.

include/common/h2.h
src/h2.c
src/mux_h2.c

index fdeafe67d34506c7de530b469d89089ea98feb10..d75a3f465379147e0a546e0ecbcf02cf07620ec9 100644 (file)
@@ -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.
index 43ed7f3c8dd234ef291c45e36950ab6f5acc6f3f..7d9ddd5026025f489dd523638f274ab0ce504c8c 100644 (file)
--- a/src/h2.c
+++ b/src/h2.c
  * stored in <phdr[]>. <fields> 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, <msgf> 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 <osize> bytes, and the amount of bytes emitted is returned. In
  * case of error, a negative error code is returned.
  *
+ * Upon success, <msgf> 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 <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 <n.len>
@@ -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 <list>
  * 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;
        }
index f27131f7e3627543b3222d54759694482c527893..5655bd8e0a67c1e900ce3872d7457fdd8d2e1e14 100644 (file)
@@ -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 <list> */
-       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;