From 2c5a7ee3330dfad050942992d0431e4f5f881e7a Mon Sep 17 00:00:00 2001 From: Amaury Denoyelle Date: Wed, 17 Aug 2022 16:33:53 +0200 Subject: [PATCH] REORG: h2: extract cookies concat function in http_htx As specified by RFC 7540, multiple cookie headers are merged in a single entry before passing it to a HTTP/1.1 connection. This step is implemented during headers parsing in h2 module. Extract this code in the generic http_htx module. This will allow to reuse it quickly for HTTP/3 implementation which has the same requirement for cookie headers. --- include/haproxy/http_htx.h | 4 ++ src/h2.c | 40 +------------------ src/http_htx.c | 78 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+), 38 deletions(-) diff --git a/include/haproxy/http_htx.h b/include/haproxy/http_htx.h index f7becff26..71f56bc15 100644 --- a/include/haproxy/http_htx.h +++ b/include/haproxy/http_htx.h @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -68,6 +69,9 @@ struct http_reply *http_parse_http_reply(const char **args, int *orig_arg, struc int http_scheme_based_normalize(struct htx *htx); +void http_cookie_register(struct http_hdr *list, int idx, int *first, int *last); +int http_cookie_merge(struct htx *htx, struct http_hdr *list, int first); + struct buffer *http_load_errorfile(const char *file, char **errmsg); struct buffer *http_load_errormsg(const char *key, const struct ist msg, char **errmsg); struct buffer *http_parse_errorfile(int status, const char *file, char **errmsg); diff --git a/src/h2.c b/src/h2.c index 5c3a6aeaf..c191881f2 100644 --- a/src/h2.c +++ b/src/h2.c @@ -501,14 +501,7 @@ int h2_make_htx_request(struct http_hdr *list, struct htx *htx, unsigned int *ms /* cookie requires special processing at the end */ if (isteq(list[idx].n, ist("cookie"))) { - list[idx].n.len = -1; - - if (ck < 0) - ck = idx; - else - list[lck].n.len = idx; - - lck = idx; + http_cookie_register(list, idx, &ck, &lck); continue; } @@ -561,37 +554,8 @@ int h2_make_htx_request(struct http_hdr *list, struct htx *htx, unsigned int *ms * visited headers. */ if (ck >= 0) { - uint32_t fs; // free space - uint32_t bs; // block size - uint32_t vl; // value len - uint32_t tl; // total length - struct htx_blk *blk; - - blk = htx_add_header(htx, ist("cookie"), list[ck].v); - if (!blk) + if (http_cookie_merge(htx, list, ck)) goto fail; - - tl = list[ck].v.len; - fs = htx_free_data_space(htx); - bs = htx_get_blksz(blk); - - /* for each extra cookie, we'll extend the cookie's value and - * insert "; " before the new value. - */ - fs += tl; // first one is already counted - while ((ck = list[ck].n.len) >= 0) { - vl = list[ck].v.len; - tl += vl + 2; - if (tl > fs) - goto fail; - - htx_change_blk_value_len(htx, blk, tl); - *(char *)(htx_get_blk_ptr(htx, blk) + bs + 0) = ';'; - *(char *)(htx_get_blk_ptr(htx, blk) + bs + 1) = ' '; - memcpy(htx_get_blk_ptr(htx, blk) + bs + 2, list[ck].v.ptr, vl); - bs += vl + 2; - } - } /* now send the end of headers marker */ diff --git a/src/http_htx.c b/src/http_htx.c index fbd626068..9b7ef6fcb 100644 --- a/src/http_htx.c +++ b/src/http_htx.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -1799,6 +1800,83 @@ int http_scheme_based_normalize(struct htx *htx) return 1; } +/* First step function to merge multiple cookie headers in a single entry. + * + * Use it for each cookie header at index over HTTP headers in . + * and are state variables used internally and must be + * initialized to -1 before the first invocation. + */ +void http_cookie_register(struct http_hdr *list, int idx, int *first, int *last) +{ + /* Build a linked list of cookie headers. Use header length to point to + * the next one. The last entry will contains -1. + */ + + /* Mark the current end of cookie linked list. */ + list[idx].n.len = -1; + if (*first < 0) { + /* Save first found cookie for http_cookie_merge call. */ + *first = idx; + } + else { + /* Update linked list of cookies. */ + list[*last].n.len = idx; + } + + *last = idx; +} + +/* Second step to merge multiple cookie headers in a single entry. + * + * Use it when looping over HTTP headers is done and message is built. + * This will concatenate each cookie headers present from directly into + * message. is reused from previous http_cookie_register + * invocation. + * + * Returns 0 on success else non-zero. + */ +int http_cookie_merge(struct htx *htx, struct http_hdr *list, int first) +{ + uint32_t fs; /* free space */ + uint32_t bs; /* block size */ + uint32_t vl; /* value len */ + uint32_t tl; /* total length */ + struct htx_blk *blk; + + if (first < 0) + return 0; + + blk = htx_add_header(htx, ist("cookie"), list[first].v); + if (!blk) + return 1; + + tl = list[first].v.len; + fs = htx_free_data_space(htx); + bs = htx_get_blksz(blk); + + /* for each extra cookie, we'll extend the cookie's value and insert + * ";" before the new value. + */ + fs += tl; /* first one is already counted */ + + /* Loop over cookies linked list built from http_cookie_register. */ + while ((first = list[first].n.len) >= 0) { + vl = list[first].v.len; + tl += vl + 2; + if (tl > fs) + return 1; + + htx_change_blk_value_len(htx, blk, tl); + *(char *)(htx_get_blk_ptr(htx, blk) + bs + 0) = ';'; + *(char *)(htx_get_blk_ptr(htx, blk) + bs + 1) = ' '; + memcpy(htx_get_blk_ptr(htx, blk) + bs + 2, + list[first].v.ptr, vl); + bs += vl + 2; + } + + return 0; +} + /* Parses the "errorloc[302|303]" proxy keyword */ static int proxy_parse_errorloc(char **args, int section, struct proxy *curpx, const struct proxy *defpx, const char *file, int line,