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.
This commit is contained in:
parent
e10cd48a83
commit
ab813a4b05
@ -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 */
|
||||
|
||||
|
@ -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 <ptr> according with
|
||||
* the expected position. <chunks> 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
|
||||
|
@ -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;
|
||||
|
390
src/http.c
390
src/http.c
@ -515,6 +515,396 @@ struct ist http_get_path(const struct ist uri)
|
||||
return ist2(NULL, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks if <hdr> is exactly <name> for <len> chars, and ends with a colon.
|
||||
* If so, returns the position of the first non-space character relative to
|
||||
* <hdr>, or <end>-<hdr> 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 <s> and <e>. 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 <s> and <e>. 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 <hdr>. 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 <hdr> string should first point to the
|
||||
* header's value, and the <hdr_end> pointer must point to the first character
|
||||
* not part of the value. <list> 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, <equal> points to '=', a delimitor or the end. <att_end>
|
||||
* is between <att_beg> and <equal>, 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 <err>
|
||||
* pointing to the error message.
|
||||
*/
|
||||
|
471
src/proto_http.c
471
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 <hdr> is exactly <name> for <len> chars, and ends with a colon.
|
||||
* If so, returns the position of the first non-space character relative to
|
||||
* <hdr>, or <end>-<hdr> 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 <name> in message buffer <sol>
|
||||
* using headers index <idx>, and return it in the <ctx> 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 <s> and <e>. 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 <name> in message buffer <sol>
|
||||
* using headers index <idx>, and return it in the <ctx> 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 <s> and <e>. 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 <from> and <next> in buffer
|
||||
* <buf>. 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 <hdr>. 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 <hdr> string should first point to the
|
||||
* header's value, and the <hdr_end> pointer must point to the first character
|
||||
* not part of the value. <list> 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, <equal> points to '=', a delimitor or the end. <att_end>
|
||||
* is between <att_beg> and <equal>, 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 <ptr> according with
|
||||
* the expected position. <chunks> 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:
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user