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.
This commit is contained in:
Amaury Denoyelle 2022-08-17 16:33:53 +02:00
parent 704675656b
commit 2c5a7ee333
3 changed files with 84 additions and 38 deletions

View File

@ -25,6 +25,7 @@
#include <import/ist.h>
#include <haproxy/buf-t.h>
#include <haproxy/http-hdr-t.h>
#include <haproxy/http_htx-t.h>
#include <haproxy/proxy-t.h>
#include <haproxy/regex-t.h>
@ -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);

View File

@ -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 */

View File

@ -21,6 +21,7 @@
#include <haproxy/global.h>
#include <haproxy/h1.h>
#include <haproxy/http.h>
#include <haproxy/http-hdr.h>
#include <haproxy/http_fetch.h>
#include <haproxy/http_htx.h>
#include <haproxy/htx.h>
@ -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 <idx> index over HTTP headers in <list>.
* <first> and <last> 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 <htx> message is built.
* This will concatenate each cookie headers present from <list> directly into
* <htx> message. <first> 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,