From ab813a4b053c73df13b7980d37a782ebff772a9b Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Mon, 10 Sep 2018 18:41:28 +0200 Subject: [PATCH] REORG: http: move some header value processing functions to http.c The following functions only deal with header field values and are agnostic to the HTTP version so they were moved to http.c : http_header_match2(), find_hdr_value_end(), find_cookie_value_end(), extract_cookie_value(), parse_qvalue(), http_find_url_param_pos(), http_find_next_url_param(). Those lacking the "http_" prefix were modified to have it. --- include/common/http.h | 36 +++ include/common/standard.h | 18 ++ src/flt_http_comp.c | 2 +- src/http.c | 390 +++++++++++++++++++++++++++++++ src/proto_http.c | 471 ++------------------------------------ 5 files changed, 462 insertions(+), 455 deletions(-) diff --git a/include/common/http.h b/include/common/http.h index 90d914ad1..3377db475 100644 --- a/include/common/http.h +++ b/include/common/http.h @@ -125,6 +125,42 @@ enum http_meth_t find_http_meth(const char *str, const int len); const int http_get_status_idx(unsigned int status); const char *http_get_reason(unsigned int status); struct ist http_get_path(const struct ist uri); +int http_header_match2(const char *hdr, const char *end, + const char *name, int len); +char *http_find_hdr_value_end(char *s, const char *e); +char *http_find_cookie_value_end(char *s, const char *e); +char *http_extract_cookie_value(char *hdr, const char *hdr_end, + char *cookie_name, size_t cookie_name_l, + int list, char **value, size_t *value_l); +int http_parse_qvalue(const char *qvalue, const char **end); +const char *http_find_url_param_pos(const char **chunks, + const char* url_param_name, + size_t url_param_name_l, char delim); +int http_find_next_url_param(const char **chunks, + const char* url_param_name, size_t url_param_name_l, + const char **vstart, const char **vend, char delim); + +/* + * Given a path string and its length, find the position of beginning of the + * query string. Returns NULL if no query string is found in the path. + * + * Example: if path = "/foo/bar/fubar?yo=mama;ye=daddy", and n = 22: + * + * find_query_string(path, n, '?') points to "yo=mama;ye=daddy" string. + */ +static inline char *http_find_param_list(char *path, size_t path_l, char delim) +{ + char *p; + + p = memchr(path, delim, path_l); + return p ? p + 1 : NULL; +} + +static inline int http_is_param_delimiter(char c, char delim) +{ + return c == '&' || c == ';' || c == delim; +} + #endif /* _COMMON_HTTP_H */ diff --git a/include/common/standard.h b/include/common/standard.h index 7af9c25ce..4a7b2a333 100644 --- a/include/common/standard.h +++ b/include/common/standard.h @@ -1190,6 +1190,24 @@ static inline void shut_your_big_mouth_gcc(int r) /* same as strstr() but case-insensitive */ const char *strnistr(const char *str1, int len_str1, const char *str2, int len_str2); +/* after increasing a pointer value, it can exceed the first buffer + * size. This function transform the value of according with + * the expected position. is an array of the one or two + * avalaible chunks. The first value is the start of the first chunk, + * the second value if the end+1 of the first chunks. The third value + * is NULL or the start of the second chunk and the fourth value is + * the end+1 of the second chunk. The function returns 1 if does a + * wrap, else returns 0. + */ +static inline int fix_pointer_if_wrap(const char **chunks, const char **ptr) +{ + if (*ptr < chunks[1]) + return 0; + if (!chunks[2]) + return 0; + *ptr = chunks[2] + ( *ptr - chunks[1] ); + return 1; +} /************************* Composite address manipulation ********************* * Composite addresses are simply unsigned long data in which the higher bits diff --git a/src/flt_http_comp.c b/src/flt_http_comp.c index 81e1b4244..6092dfc7b 100644 --- a/src/flt_http_comp.c +++ b/src/flt_http_comp.c @@ -414,7 +414,7 @@ select_compression_request_header(struct comp_state *st, struct stream *s, } /* here we have qval pointing to the first "q=" attribute or NULL if not found */ - q = qval ? parse_qvalue(qval + 2, NULL) : 1000; + q = qval ? http_parse_qvalue(qval + 2, NULL) : 1000; if (q <= best_q) continue; diff --git a/src/http.c b/src/http.c index 202f7a468..932f3cf70 100644 --- a/src/http.c +++ b/src/http.c @@ -515,6 +515,396 @@ struct ist http_get_path(const struct ist uri) return ist2(NULL, 0); } +/* + * Checks if is exactly for chars, and ends with a colon. + * If so, returns the position of the first non-space character relative to + * , or - if not found before. If no value is found, it tries + * to return a pointer to the place after the first space. Returns 0 if the + * header name does not match. Checks are case-insensitive. + */ +int http_header_match2(const char *hdr, const char *end, + const char *name, int len) +{ + const char *val; + + if (hdr + len >= end) + return 0; + if (hdr[len] != ':') + return 0; + if (strncasecmp(hdr, name, len) != 0) + return 0; + val = hdr + len + 1; + while (val < end && HTTP_IS_SPHT(*val)) + val++; + if ((val >= end) && (len + 2 <= end - hdr)) + return len + 2; /* we may replace starting from second space */ + return val - hdr; +} + +/* Find the end of the header value contained between and . See RFC7230, + * par 3.2 for more information. Note that it requires a valid header to return + * a valid result. This works for headers defined as comma-separated lists. + */ +char *http_find_hdr_value_end(char *s, const char *e) +{ + int quoted, qdpair; + + quoted = qdpair = 0; + +#if defined(__x86_64__) || \ + defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__) || \ + defined(__ARM_ARCH_7A__) + /* speedup: skip everything not a comma nor a double quote */ + for (; s <= e - sizeof(int); s += sizeof(int)) { + unsigned int c = *(int *)s; // comma + unsigned int q = c; // quote + + c ^= 0x2c2c2c2c; // contains one zero on a comma + q ^= 0x22222222; // contains one zero on a quote + + c = (c - 0x01010101) & ~c; // contains 0x80 below a comma + q = (q - 0x01010101) & ~q; // contains 0x80 below a quote + + if ((c | q) & 0x80808080) + break; // found a comma or a quote + } +#endif + for (; s < e; s++) { + if (qdpair) qdpair = 0; + else if (quoted) { + if (*s == '\\') qdpair = 1; + else if (*s == '"') quoted = 0; + } + else if (*s == '"') quoted = 1; + else if (*s == ',') return s; + } + return s; +} + +/* Find the end of a cookie value contained between and . It works the + * same way as with headers above except that the semi-colon also ends a token. + * See RFC2965 for more information. Note that it requires a valid header to + * return a valid result. + */ +char *http_find_cookie_value_end(char *s, const char *e) +{ + int quoted, qdpair; + + quoted = qdpair = 0; + for (; s < e; s++) { + if (qdpair) qdpair = 0; + else if (quoted) { + if (*s == '\\') qdpair = 1; + else if (*s == '"') quoted = 0; + } + else if (*s == '"') quoted = 1; + else if (*s == ',' || *s == ';') return s; + } + return s; +} + +/* Try to find the next occurrence of a cookie name in a cookie header value. + * The lookup begins at . The pointer and size of the next occurrence of + * the cookie value is returned into *value and *value_l, and the function + * returns a pointer to the next pointer to search from if the value was found. + * Otherwise if the cookie was not found, NULL is returned and neither value + * nor value_l are touched. The input string should first point to the + * header's value, and the pointer must point to the first character + * not part of the value. must be non-zero if value may represent a list + * of values (cookie headers). This makes it faster to abort parsing when no + * list is expected. + */ +char *http_extract_cookie_value(char *hdr, const char *hdr_end, + char *cookie_name, size_t cookie_name_l, + int list, char **value, size_t *value_l) +{ + char *equal, *att_end, *att_beg, *val_beg, *val_end; + char *next; + + /* we search at least a cookie name followed by an equal, and more + * generally something like this : + * Cookie: NAME1 = VALUE 1 ; NAME2 = VALUE2 ; NAME3 = VALUE3\r\n + */ + for (att_beg = hdr; att_beg + cookie_name_l + 1 < hdr_end; att_beg = next + 1) { + /* Iterate through all cookies on this line */ + + while (att_beg < hdr_end && HTTP_IS_SPHT(*att_beg)) + att_beg++; + + /* find att_end : this is the first character after the last non + * space before the equal. It may be equal to hdr_end. + */ + equal = att_end = att_beg; + + while (equal < hdr_end) { + if (*equal == '=' || *equal == ';' || (list && *equal == ',')) + break; + if (HTTP_IS_SPHT(*equal++)) + continue; + att_end = equal; + } + + /* here, points to '=', a delimitor or the end. + * is between and , both may be identical. + */ + + /* look for end of cookie if there is an equal sign */ + if (equal < hdr_end && *equal == '=') { + /* look for the beginning of the value */ + val_beg = equal + 1; + while (val_beg < hdr_end && HTTP_IS_SPHT(*val_beg)) + val_beg++; + + /* find the end of the value, respecting quotes */ + next = http_find_cookie_value_end(val_beg, hdr_end); + + /* make val_end point to the first white space or delimitor after the value */ + val_end = next; + while (val_end > val_beg && HTTP_IS_SPHT(*(val_end - 1))) + val_end--; + } else { + val_beg = val_end = next = equal; + } + + /* We have nothing to do with attributes beginning with '$'. However, + * they will automatically be removed if a header before them is removed, + * since they're supposed to be linked together. + */ + if (*att_beg == '$') + continue; + + /* Ignore cookies with no equal sign */ + if (equal == next) + continue; + + /* Now we have the cookie name between att_beg and att_end, and + * its value between val_beg and val_end. + */ + + if (att_end - att_beg == cookie_name_l && + memcmp(att_beg, cookie_name, cookie_name_l) == 0) { + /* let's return this value and indicate where to go on from */ + *value = val_beg; + *value_l = val_end - val_beg; + return next + 1; + } + + /* Set-Cookie headers only have the name in the first attr=value part */ + if (!list) + break; + } + + return NULL; +} + +/* Parses a qvalue and returns it multipled by 1000, from 0 to 1000. If the + * value is larger than 1000, it is bound to 1000. The parser consumes up to + * 1 digit, one dot and 3 digits and stops on the first invalid character. + * Unparsable qvalues return 1000 as "q=1.000". + */ +int http_parse_qvalue(const char *qvalue, const char **end) +{ + int q = 1000; + + if (!isdigit((unsigned char)*qvalue)) + goto out; + q = (*qvalue++ - '0') * 1000; + + if (*qvalue++ != '.') + goto out; + + if (!isdigit((unsigned char)*qvalue)) + goto out; + q += (*qvalue++ - '0') * 100; + + if (!isdigit((unsigned char)*qvalue)) + goto out; + q += (*qvalue++ - '0') * 10; + + if (!isdigit((unsigned char)*qvalue)) + goto out; + q += (*qvalue++ - '0') * 1; + out: + if (q > 1000) + q = 1000; + if (end) + *end = qvalue; + return q; +} + +/* + * Given a url parameter, find the starting position of the first occurence, + * or NULL if the parameter is not found. + * + * Example: if query_string is "yo=mama;ye=daddy" and url_param_name is "ye", + * the function will return query_string+8. + * + * Warning: this function returns a pointer that can point to the first chunk + * or the second chunk. The caller must be check the position before using the + * result. + */ +const char *http_find_url_param_pos(const char **chunks, + const char* url_param_name, size_t url_param_name_l, + char delim) +{ + const char *pos, *last, *equal; + const char **bufs = chunks; + int l1, l2; + + + pos = bufs[0]; + last = bufs[1]; + while (pos < last) { + /* Check the equal. */ + equal = pos + url_param_name_l; + if (fix_pointer_if_wrap(chunks, &equal)) { + if (equal >= chunks[3]) + return NULL; + } else { + if (equal >= chunks[1]) + return NULL; + } + if (*equal == '=') { + if (pos + url_param_name_l > last) { + /* process wrap case, we detect a wrap. In this case, the + * comparison is performed in two parts. + */ + + /* This is the end, we dont have any other chunk. */ + if (bufs != chunks || !bufs[2]) + return NULL; + + /* Compute the length of each part of the comparison. */ + l1 = last - pos; + l2 = url_param_name_l - l1; + + /* The second buffer is too short to contain the compared string. */ + if (bufs[2] + l2 > bufs[3]) + return NULL; + + if (memcmp(pos, url_param_name, l1) == 0 && + memcmp(bufs[2], url_param_name+l1, l2) == 0) + return pos; + + /* Perform wrapping and jump the string who fail the comparison. */ + bufs += 2; + pos = bufs[0] + l2; + last = bufs[1]; + + } else { + /* process a simple comparison. */ + if (memcmp(pos, url_param_name, url_param_name_l) == 0) + return pos; + pos += url_param_name_l + 1; + if (fix_pointer_if_wrap(chunks, &pos)) + last = bufs[2]; + } + } + + while (1) { + /* Look for the next delimiter. */ + while (pos < last && !http_is_param_delimiter(*pos, delim)) + pos++; + if (pos < last) + break; + /* process buffer wrapping. */ + if (bufs != chunks || !bufs[2]) + return NULL; + bufs += 2; + pos = bufs[0]; + last = bufs[1]; + } + pos++; + } + return NULL; +} + +/* + * Given a url parameter name and a query string, find the next value. + * An empty url_param_name matches the first available parameter. + * If the parameter is found, 1 is returned and *vstart / *vend are updated to + * respectively provide a pointer to the value and its end. + * Otherwise, 0 is returned and vstart/vend are not modified. + */ +int http_find_next_url_param(const char **chunks, + const char* url_param_name, size_t url_param_name_l, + const char **vstart, const char **vend, char delim) +{ + const char *arg_start, *qs_end; + const char *value_start, *value_end; + + arg_start = chunks[0]; + qs_end = chunks[1]; + if (url_param_name_l) { + /* Looks for an argument name. */ + arg_start = http_find_url_param_pos(chunks, + url_param_name, url_param_name_l, + delim); + /* Check for wrapping. */ + if (arg_start >= qs_end) + qs_end = chunks[3]; + } + if (!arg_start) + return 0; + + if (!url_param_name_l) { + while (1) { + /* looks for the first argument. */ + value_start = memchr(arg_start, '=', qs_end - arg_start); + if (!value_start) { + /* Check for wrapping. */ + if (arg_start >= chunks[0] && + arg_start < chunks[1] && + chunks[2]) { + arg_start = chunks[2]; + qs_end = chunks[3]; + continue; + } + return 0; + } + break; + } + value_start++; + } + else { + /* Jump the argument length. */ + value_start = arg_start + url_param_name_l + 1; + + /* Check for pointer wrapping. */ + if (fix_pointer_if_wrap(chunks, &value_start)) { + /* Update the end pointer. */ + qs_end = chunks[3]; + + /* Check for overflow. */ + if (value_start >= qs_end) + return 0; + } + } + + value_end = value_start; + + while (1) { + while ((value_end < qs_end) && !http_is_param_delimiter(*value_end, delim)) + value_end++; + if (value_end < qs_end) + break; + /* process buffer wrapping. */ + if (value_end >= chunks[0] && + value_end < chunks[1] && + chunks[2]) { + value_end = chunks[2]; + qs_end = chunks[3]; + continue; + } + break; + } + + *vstart = value_start; + *vend = value_end; + return 1; +} + + /* post-initializes the HTTP parts. Returns non-zero on error, with * pointing to the error message. */ diff --git a/src/proto_http.c b/src/proto_http.c index 0c2acacea..22eab8b30 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -144,32 +144,6 @@ int http_header_add_tail2(struct http_msg *msg, return hdr_idx_add(len, 1, hdr_idx, hdr_idx->tail); } -/* - * Checks if is exactly for chars, and ends with a colon. - * If so, returns the position of the first non-space character relative to - * , or - if not found before. If no value is found, it tries - * to return a pointer to the place after the first space. Returns 0 if the - * header name does not match. Checks are case-insensitive. - */ -int http_header_match2(const char *hdr, const char *end, - const char *name, int len) -{ - const char *val; - - if (hdr + len >= end) - return 0; - if (hdr[len] != ':') - return 0; - if (strncasecmp(hdr, name, len) != 0) - return 0; - val = hdr + len + 1; - while (val < end && HTTP_IS_SPHT(*val)) - val++; - if ((val >= end) && (len + 2 <= end - hdr)) - return len + 2; /* we may replace starting from second space */ - return val - hdr; -} - /* Find the first or next occurrence of header in message buffer * using headers index , and return it in the structure. This * structure holds everything necessary to use the header and find next @@ -303,46 +277,6 @@ int http_find_next_header(char *sol, struct hdr_idx *idx, struct hdr_ctx *ctx) return 0; } -/* Find the end of the header value contained between and . See RFC7230, - * par 3.2 for more information. Note that it requires a valid header to return - * a valid result. This works for headers defined as comma-separated lists. - */ -char *find_hdr_value_end(char *s, const char *e) -{ - int quoted, qdpair; - - quoted = qdpair = 0; - -#if defined(__x86_64__) || \ - defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__) || \ - defined(__ARM_ARCH_7A__) - /* speedup: skip everything not a comma nor a double quote */ - for (; s <= e - sizeof(int); s += sizeof(int)) { - unsigned int c = *(int *)s; // comma - unsigned int q = c; // quote - - c ^= 0x2c2c2c2c; // contains one zero on a comma - q ^= 0x22222222; // contains one zero on a quote - - c = (c - 0x01010101) & ~c; // contains 0x80 below a comma - q = (q - 0x01010101) & ~q; // contains 0x80 below a quote - - if ((c | q) & 0x80808080) - break; // found a comma or a quote - } -#endif - for (; s < e; s++) { - if (qdpair) qdpair = 0; - else if (quoted) { - if (*s == '\\') qdpair = 1; - else if (*s == '"') quoted = 0; - } - else if (*s == '"') quoted = 1; - else if (*s == ',') return s; - } - return s; -} - /* Find the first or next occurrence of header in message buffer * using headers index , and return it in the structure. This * structure holds everything necessary to use the header and find next @@ -413,7 +347,7 @@ int http_find_header2(const char *name, int len, ctx->idx = cur_idx; ctx->val = sov - sol; - eol = find_hdr_value_end(sov, eol); + eol = http_find_hdr_value_end(sov, eol); ctx->tws = 0; while (eol > sov && HTTP_IS_LWS(*(eol - 1))) { eol--; @@ -969,41 +903,6 @@ void http_change_connection_header(struct http_txn *txn, struct http_msg *msg, i return; } -/* Parses a qvalue and returns it multipled by 1000, from 0 to 1000. If the - * value is larger than 1000, it is bound to 1000. The parser consumes up to - * 1 digit, one dot and 3 digits and stops on the first invalid character. - * Unparsable qvalues return 1000 as "q=1.000". - */ -int parse_qvalue(const char *qvalue, const char **end) -{ - int q = 1000; - - if (!isdigit((unsigned char)*qvalue)) - goto out; - q = (*qvalue++ - '0') * 1000; - - if (*qvalue++ != '.') - goto out; - - if (!isdigit((unsigned char)*qvalue)) - goto out; - q += (*qvalue++ - '0') * 100; - - if (!isdigit((unsigned char)*qvalue)) - goto out; - q += (*qvalue++ - '0') * 10; - - if (!isdigit((unsigned char)*qvalue)) - goto out; - q += (*qvalue++ - '0') * 1; - out: - if (q > 1000) - q = 1000; - if (end) - *end = qvalue; - return q; -} - void http_adjust_conn_mode(struct stream *s, struct http_txn *txn, struct http_msg *msg) { struct proxy *fe = strm_fe(s); @@ -6237,28 +6136,6 @@ int apply_filters_to_request(struct stream *s, struct channel *req, struct proxy } -/* Find the end of a cookie value contained between and . It works the - * same way as with headers above except that the semi-colon also ends a token. - * See RFC2965 for more information. Note that it requires a valid header to - * return a valid result. - */ -char *find_cookie_value_end(char *s, const char *e) -{ - int quoted, qdpair; - - quoted = qdpair = 0; - for (; s < e; s++) { - if (qdpair) qdpair = 0; - else if (quoted) { - if (*s == '\\') qdpair = 1; - else if (*s == '"') quoted = 0; - } - else if (*s == '"') quoted = 1; - else if (*s == ',' || *s == ';') return s; - } - return s; -} - /* Delete a value in a header between delimiters and in buffer * . The number of characters displaced is returned, and the pointer to * the first delimiter is updated if required. The function tries as much as @@ -6432,7 +6309,7 @@ void manage_client_side_cookies(struct stream *s, struct channel *req) val_beg++; /* find the end of the value, respecting quotes */ - next = find_cookie_value_end(val_beg, hdr_end); + next = http_find_cookie_value_end(val_beg, hdr_end); /* make val_end point to the first white space or delimitor after the value */ val_end = next; @@ -7098,7 +6975,7 @@ void manage_server_side_cookies(struct stream *s, struct channel *res) val_beg++; /* find the end of the value, respecting quotes */ - next = find_cookie_value_end(val_beg, hdr_end); + next = http_find_cookie_value_end(val_beg, hdr_end); /* make val_end point to the first white space or delimitor after the value */ val_end = next; @@ -7116,7 +6993,7 @@ void manage_server_side_cookies(struct stream *s, struct channel *res) * commas are permitted in values, skip to the end. */ if (is_cookie2) - next = find_hdr_value_end(next, hdr_end); + next = http_find_hdr_value_end(next, hdr_end); else next = hdr_end; } @@ -10132,101 +10009,6 @@ smp_fetch_http_auth_grp(const struct arg *args, struct sample *smp, const char * return 1; } -/* Try to find the next occurrence of a cookie name in a cookie header value. - * The lookup begins at . The pointer and size of the next occurrence of - * the cookie value is returned into *value and *value_l, and the function - * returns a pointer to the next pointer to search from if the value was found. - * Otherwise if the cookie was not found, NULL is returned and neither value - * nor value_l are touched. The input string should first point to the - * header's value, and the pointer must point to the first character - * not part of the value. must be non-zero if value may represent a list - * of values (cookie headers). This makes it faster to abort parsing when no - * list is expected. - */ -char * -extract_cookie_value(char *hdr, const char *hdr_end, - char *cookie_name, size_t cookie_name_l, int list, - char **value, size_t *value_l) -{ - char *equal, *att_end, *att_beg, *val_beg, *val_end; - char *next; - - /* we search at least a cookie name followed by an equal, and more - * generally something like this : - * Cookie: NAME1 = VALUE 1 ; NAME2 = VALUE2 ; NAME3 = VALUE3\r\n - */ - for (att_beg = hdr; att_beg + cookie_name_l + 1 < hdr_end; att_beg = next + 1) { - /* Iterate through all cookies on this line */ - - while (att_beg < hdr_end && HTTP_IS_SPHT(*att_beg)) - att_beg++; - - /* find att_end : this is the first character after the last non - * space before the equal. It may be equal to hdr_end. - */ - equal = att_end = att_beg; - - while (equal < hdr_end) { - if (*equal == '=' || *equal == ';' || (list && *equal == ',')) - break; - if (HTTP_IS_SPHT(*equal++)) - continue; - att_end = equal; - } - - /* here, points to '=', a delimitor or the end. - * is between and , both may be identical. - */ - - /* look for end of cookie if there is an equal sign */ - if (equal < hdr_end && *equal == '=') { - /* look for the beginning of the value */ - val_beg = equal + 1; - while (val_beg < hdr_end && HTTP_IS_SPHT(*val_beg)) - val_beg++; - - /* find the end of the value, respecting quotes */ - next = find_cookie_value_end(val_beg, hdr_end); - - /* make val_end point to the first white space or delimitor after the value */ - val_end = next; - while (val_end > val_beg && HTTP_IS_SPHT(*(val_end - 1))) - val_end--; - } else { - val_beg = val_end = next = equal; - } - - /* We have nothing to do with attributes beginning with '$'. However, - * they will automatically be removed if a header before them is removed, - * since they're supposed to be linked together. - */ - if (*att_beg == '$') - continue; - - /* Ignore cookies with no equal sign */ - if (equal == next) - continue; - - /* Now we have the cookie name between att_beg and att_end, and - * its value between val_beg and val_end. - */ - - if (att_end - att_beg == cookie_name_l && - memcmp(att_beg, cookie_name, cookie_name_l) == 0) { - /* let's return this value and indicate where to go on from */ - *value = val_beg; - *value_l = val_end - val_beg; - return next + 1; - } - - /* Set-Cookie headers only have the name in the first attr=value part */ - if (!list) - break; - } - - return NULL; -} - /* Fetch a captured HTTP request header. The index is the position of * the "capture" option in the configuration file */ @@ -10466,12 +10248,10 @@ int smp_fetch_cookie(const struct arg *args, struct sample *smp, const char *kw, smp->data.type = SMP_T_STR; smp->flags |= SMP_F_CONST; - smp->ctx.a[0] = extract_cookie_value(smp->ctx.a[0], smp->ctx.a[1], - args->data.str.area, - args->data.str.data, - (smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ, - &smp->data.u.str.area, - &smp->data.u.str.data); + smp->ctx.a[0] = http_extract_cookie_value(smp->ctx.a[0], smp->ctx.a[1], + args->data.str.area, args->data.str.data, + (smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ, + &smp->data.u.str.area, &smp->data.u.str.data); if (smp->ctx.a[0]) { found = 1; if (occ >= 0) { @@ -10546,11 +10326,10 @@ smp_fetch_cookie_cnt(const struct arg *args, struct sample *smp, const char *kw, smp->data.type = SMP_T_STR; smp->flags |= SMP_F_CONST; - while ((val_beg = extract_cookie_value(val_beg, val_end, - args->data.str.area, args->data.str.data, - (smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ, - &smp->data.u.str.area, - &smp->data.u.str.data))) { + while ((val_beg = http_extract_cookie_value(val_beg, val_end, + args->data.str.area, args->data.str.data, + (smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ, + &smp->data.u.str.area, &smp->data.u.str.data))) { cnt++; } } @@ -10582,220 +10361,6 @@ smp_fetch_cookie_val(const struct arg *args, struct sample *smp, const char *kw, /* The code below is dedicated to sample fetches */ /************************************************************************/ -/* - * Given a path string and its length, find the position of beginning of the - * query string. Returns NULL if no query string is found in the path. - * - * Example: if path = "/foo/bar/fubar?yo=mama;ye=daddy", and n = 22: - * - * find_query_string(path, n, '?') points to "yo=mama;ye=daddy" string. - */ -static inline char *find_param_list(char *path, size_t path_l, char delim) -{ - char *p; - - p = memchr(path, delim, path_l); - return p ? p + 1 : NULL; -} - -static inline int is_param_delimiter(char c, char delim) -{ - return c == '&' || c == ';' || c == delim; -} - -/* after increasing a pointer value, it can exceed the first buffer - * size. This function transform the value of according with - * the expected position. is an array of the one or two - * avalaible chunks. The first value is the start of the first chunk, - * the second value if the end+1 of the first chunks. The third value - * is NULL or the start of the second chunk and the fourth value is - * the end+1 of the second chunk. The function returns 1 if does a - * wrap, else returns 0. - */ -static inline int fix_pointer_if_wrap(const char **chunks, const char **ptr) -{ - if (*ptr < chunks[1]) - return 0; - if (!chunks[2]) - return 0; - *ptr = chunks[2] + ( *ptr - chunks[1] ); - return 1; -} - -/* - * Given a url parameter, find the starting position of the first occurence, - * or NULL if the parameter is not found. - * - * Example: if query_string is "yo=mama;ye=daddy" and url_param_name is "ye", - * the function will return query_string+8. - * - * Warning: this function returns a pointer that can point to the first chunk - * or the second chunk. The caller must be check the position before using the - * result. - */ -static const char * -find_url_param_pos(const char **chunks, - const char* url_param_name, size_t url_param_name_l, - char delim) -{ - const char *pos, *last, *equal; - const char **bufs = chunks; - int l1, l2; - - - pos = bufs[0]; - last = bufs[1]; - while (pos < last) { - /* Check the equal. */ - equal = pos + url_param_name_l; - if (fix_pointer_if_wrap(chunks, &equal)) { - if (equal >= chunks[3]) - return NULL; - } else { - if (equal >= chunks[1]) - return NULL; - } - if (*equal == '=') { - if (pos + url_param_name_l > last) { - /* process wrap case, we detect a wrap. In this case, the - * comparison is performed in two parts. - */ - - /* This is the end, we dont have any other chunk. */ - if (bufs != chunks || !bufs[2]) - return NULL; - - /* Compute the length of each part of the comparison. */ - l1 = last - pos; - l2 = url_param_name_l - l1; - - /* The second buffer is too short to contain the compared string. */ - if (bufs[2] + l2 > bufs[3]) - return NULL; - - if (memcmp(pos, url_param_name, l1) == 0 && - memcmp(bufs[2], url_param_name+l1, l2) == 0) - return pos; - - /* Perform wrapping and jump the string who fail the comparison. */ - bufs += 2; - pos = bufs[0] + l2; - last = bufs[1]; - - } else { - /* process a simple comparison. */ - if (memcmp(pos, url_param_name, url_param_name_l) == 0) - return pos; - pos += url_param_name_l + 1; - if (fix_pointer_if_wrap(chunks, &pos)) - last = bufs[2]; - } - } - - while (1) { - /* Look for the next delimiter. */ - while (pos < last && !is_param_delimiter(*pos, delim)) - pos++; - if (pos < last) - break; - /* process buffer wrapping. */ - if (bufs != chunks || !bufs[2]) - return NULL; - bufs += 2; - pos = bufs[0]; - last = bufs[1]; - } - pos++; - } - return NULL; -} - -/* - * Given a url parameter name and a query string, find the next value. - * An empty url_param_name matches the first available parameter. - * If the parameter is found, 1 is returned and *vstart / *vend are updated to - * respectively provide a pointer to the value and its end. - * Otherwise, 0 is returned and vstart/vend are not modified. - */ -static int -find_next_url_param(const char **chunks, - const char* url_param_name, size_t url_param_name_l, - const char **vstart, const char **vend, char delim) -{ - const char *arg_start, *qs_end; - const char *value_start, *value_end; - - arg_start = chunks[0]; - qs_end = chunks[1]; - if (url_param_name_l) { - /* Looks for an argument name. */ - arg_start = find_url_param_pos(chunks, - url_param_name, url_param_name_l, - delim); - /* Check for wrapping. */ - if (arg_start >= qs_end) - qs_end = chunks[3]; - } - if (!arg_start) - return 0; - - if (!url_param_name_l) { - while (1) { - /* looks for the first argument. */ - value_start = memchr(arg_start, '=', qs_end - arg_start); - if (!value_start) { - /* Check for wrapping. */ - if (arg_start >= chunks[0] && - arg_start < chunks[1] && - chunks[2]) { - arg_start = chunks[2]; - qs_end = chunks[3]; - continue; - } - return 0; - } - break; - } - value_start++; - } - else { - /* Jump the argument length. */ - value_start = arg_start + url_param_name_l + 1; - - /* Check for pointer wrapping. */ - if (fix_pointer_if_wrap(chunks, &value_start)) { - /* Update the end pointer. */ - qs_end = chunks[3]; - - /* Check for overflow. */ - if (value_start >= qs_end) - return 0; - } - } - - value_end = value_start; - - while (1) { - while ((value_end < qs_end) && !is_param_delimiter(*value_end, delim)) - value_end++; - if (value_end < qs_end) - break; - /* process buffer wrapping. */ - if (value_end >= chunks[0] && - value_end < chunks[1] && - chunks[2]) { - value_end = chunks[2]; - qs_end = chunks[3]; - continue; - } - break; - } - - *vstart = value_start; - *vend = value_end; - return 1; -} - /* This scans a URL-encoded query string. It takes an optionally wrapping * string whose first contigous chunk has its beginning in ctx->a[0] and end * in ctx->a[1], and the optional second part in (ctx->a[2]..ctx->a[3]). The @@ -10808,10 +10373,8 @@ smp_fetch_param(char delim, const char *name, int name_len, const struct arg *ar struct buffer *temp; const char **chunks = (const char **)smp->ctx.a; - if (!find_next_url_param(chunks, - name, name_len, - &vstart, &vend, - delim)) + if (!http_find_next_url_param(chunks, name, name_len, + &vstart, &vend, delim)) return 0; /* Create sample. If the value is contiguous, return the pointer as CONST, @@ -10883,8 +10446,8 @@ smp_fetch_url_param(const struct arg *args, struct sample *smp, const char *kw, msg = &smp->strm->txn->req; - smp->ctx.a[0] = find_param_list(ci_head(msg->chn) + msg->sl.rq.u, - msg->sl.rq.u_l, delim); + smp->ctx.a[0] = http_find_param_list(ci_head(msg->chn) + msg->sl.rq.u, + msg->sl.rq.u_l, delim); if (!smp->ctx.a[0]) return 0; @@ -11261,7 +10824,7 @@ look_for_q: goto process_value; /* Parse the q value. */ - qvalue = parse_qvalue(al, &al); + qvalue = http_parse_qvalue(al, &al); process_value: