MEDIUM: http: implement http-request set-{method,path,query,uri}
This commit implements the following new actions : - "set-method" rewrites the request method with the result of the evaluation of format string <fmt>. There should be very few valid reasons for having to do so as this is more likely to break something than to fix it. - "set-path" rewrites the request path with the result of the evaluation of format string <fmt>. The query string, if any, is left intact. If a scheme and authority is found before the path, they are left intact as well. If the request doesn't have a path ("*"), this one is replaced with the format. This can be used to prepend a directory component in front of a path for example. See also "set-query" and "set-uri". Example : # prepend the host name before the path http-request set-path /%[hdr(host)]%[path] - "set-query" rewrites the request's query string which appears after the first question mark ("?") with the result of the evaluation of format string <fmt>. The part prior to the question mark is left intact. If the request doesn't contain a question mark and the new value is not empty, then one is added at the end of the URI, followed by the new value. If a question mark was present, it will never be removed even if the value is empty. This can be used to add or remove parameters from the query string. See also "set-query" and "set-uri". Example : # replace "%3D" with "=" in the query string http-request set-query %[query,regsub(%3D,=,g)] - "set-uri" rewrites the request URI with the result of the evaluation of format string <fmt>. The scheme, authority, path and query string are all replaced at once. This can be used to rewrite hosts in front of proxies, or to perform complex modifications to the URI such as moving parts between the path and the query string. See also "set-path" and "set-query". All of them are handled by the same parser and the same exec function, which is why they're merged all together. For once, instead of adding even more entries to the huge switch/case, we used the new facility to register action keywords. A number of the existing ones should probably move there as well.
This commit is contained in:
parent
d817e468bf
commit
a0dc23f093
@ -2971,7 +2971,8 @@ http-request { allow | deny | tarpit | auth [realm <realm>] | redirect <rule> |
|
|||||||
del-header <name> | set-nice <nice> | set-log-level <level> |
|
del-header <name> | set-nice <nice> | set-log-level <level> |
|
||||||
replace-header <name> <match-regex> <replace-fmt> |
|
replace-header <name> <match-regex> <replace-fmt> |
|
||||||
replace-value <name> <match-regex> <replace-fmt> |
|
replace-value <name> <match-regex> <replace-fmt> |
|
||||||
set-tos <tos> | set-mark <mark> |
|
set-method <fmt> | set-path <fmt> | set-query <fmt> |
|
||||||
|
set-uri <fmt> | set-tos <tos> | set-mark <mark> |
|
||||||
add-acl(<file name>) <key fmt> |
|
add-acl(<file name>) <key fmt> |
|
||||||
del-acl(<file name>) <key fmt> |
|
del-acl(<file name>) <key fmt> |
|
||||||
del-map(<file name>) <key fmt> |
|
del-map(<file name>) <key fmt> |
|
||||||
@ -3081,6 +3082,42 @@ http-request { allow | deny | tarpit | auth [realm <realm>] | redirect <rule> |
|
|||||||
|
|
||||||
X-Forwarded-For: 172.16.10.1, 172.16.13.24, 10.0.0.37
|
X-Forwarded-For: 172.16.10.1, 172.16.13.24, 10.0.0.37
|
||||||
|
|
||||||
|
- "set-method" rewrites the request method with the result of the
|
||||||
|
evaluation of format string <fmt>. There should be very few valid reasons
|
||||||
|
for having to do so as this is more likely to break something than to fix
|
||||||
|
it.
|
||||||
|
|
||||||
|
- "set-path" rewrites the request path with the result of the evaluation of
|
||||||
|
format string <fmt>. The query string, if any, is left intact. If a
|
||||||
|
scheme and authority is found before the path, they are left intact as
|
||||||
|
well. If the request doesn't have a path ("*"), this one is replaced with
|
||||||
|
the format. This can be used to prepend a directory component in front of
|
||||||
|
a path for example. See also "set-query" and "set-uri".
|
||||||
|
|
||||||
|
Example :
|
||||||
|
# prepend the host name before the path
|
||||||
|
http-request set-path /%[hdr(host)]%[path]
|
||||||
|
|
||||||
|
- "set-query" rewrites the request's query string which appears after the
|
||||||
|
first question mark ("?") with the result of the evaluation of format
|
||||||
|
string <fmt>. The part prior to the question mark is left intact. If the
|
||||||
|
request doesn't contain a question mark and the new value is not empty,
|
||||||
|
then one is added at the end of the URI, followed by the new value. If
|
||||||
|
a question mark was present, it will never be removed even if the value
|
||||||
|
is empty. This can be used to add or remove parameters from the query
|
||||||
|
string. See also "set-query" and "set-uri".
|
||||||
|
|
||||||
|
Example :
|
||||||
|
# replace "%3D" with "=" in the query string
|
||||||
|
http-request set-query %[query,regsub(%3D,=,g)]
|
||||||
|
|
||||||
|
- "set-uri" rewrites the request URI with the result of the evaluation of
|
||||||
|
format string <fmt>. The scheme, authority, path and query string are all
|
||||||
|
replaced at once. This can be used to rewrite hosts in front of proxies,
|
||||||
|
or to perform complex modifications to the URI such as moving parts
|
||||||
|
between the path and the query string. See also "set-path" and
|
||||||
|
"set-query".
|
||||||
|
|
||||||
- "set-nice" sets the "nice" factor of the current request being processed.
|
- "set-nice" sets the "nice" factor of the current request being processed.
|
||||||
It only has effect against the other requests being processed at the same
|
It only has effect against the other requests being processed at the same
|
||||||
time. The default value is 0, unless altered by the "nice" setting on the
|
time. The default value is 0, unless altered by the "nice" setting on the
|
||||||
|
@ -438,6 +438,9 @@ struct http_req_rule {
|
|||||||
struct list key; /* pattern to retrieve MAP or ACL key */
|
struct list key; /* pattern to retrieve MAP or ACL key */
|
||||||
struct list value; /* pattern to retrieve MAP value */
|
struct list value; /* pattern to retrieve MAP value */
|
||||||
} map;
|
} map;
|
||||||
|
struct {
|
||||||
|
void *p[4];
|
||||||
|
} act; /* generic pointers to be used by custom actions */
|
||||||
} arg; /* arguments used by some actions */
|
} arg; /* arguments used by some actions */
|
||||||
|
|
||||||
union {
|
union {
|
||||||
|
160
src/proto_http.c
160
src/proto_http.c
@ -11476,6 +11476,149 @@ expect_comma:
|
|||||||
return smp->data.str.len != 0;
|
return smp->data.str.len != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* This function executes one of the set-{method,path,query,uri} actions. It
|
||||||
|
* builds a string in the trash from the specified format string, then modifies
|
||||||
|
* the relevant part of the request line accordingly. Then it updates various
|
||||||
|
* pointers to the next elements which were moved, and the total buffer length.
|
||||||
|
* It finds the action to be performed in p[2], previously filled by function
|
||||||
|
* parse_set_req_line(). It returns 0 in case of success, -1 in case of internal
|
||||||
|
* error, though this can be revisited when this code is finally exploited.
|
||||||
|
*/
|
||||||
|
int http_action_set_req_line(struct http_req_rule *rule, struct proxy *px, struct session *s, struct http_txn *txn)
|
||||||
|
{
|
||||||
|
char *cur_ptr, *cur_end;
|
||||||
|
int offset;
|
||||||
|
int delta;
|
||||||
|
|
||||||
|
chunk_reset(&trash);
|
||||||
|
|
||||||
|
/* prepare a '?' just in case we have to create a query string */
|
||||||
|
trash.str[trash.len++] = '?';
|
||||||
|
offset = 1;
|
||||||
|
trash.len += build_logline(s, trash.str + trash.len, trash.size - trash.len, (struct list *)&rule->arg.act.p[0]);
|
||||||
|
|
||||||
|
switch (*(int *)&rule->arg.act.p[2]) {
|
||||||
|
case 0: // method
|
||||||
|
cur_ptr = s->req->buf->p;
|
||||||
|
cur_end = cur_ptr + txn->req.sl.rq.m_l;
|
||||||
|
|
||||||
|
/* adjust req line offsets and lengths */
|
||||||
|
delta = trash.len - offset - (cur_end - cur_ptr);
|
||||||
|
txn->req.sl.rq.m_l += delta;
|
||||||
|
txn->req.sl.rq.u += delta;
|
||||||
|
txn->req.sl.rq.v += delta;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1: // path
|
||||||
|
cur_ptr = http_get_path(txn);
|
||||||
|
if (!cur_ptr)
|
||||||
|
cur_ptr = s->req->buf->p + txn->req.sl.rq.u;
|
||||||
|
|
||||||
|
cur_end = cur_ptr;
|
||||||
|
while (cur_end < s->req->buf->p + txn->req.sl.rq.u + txn->req.sl.rq.u_l && *cur_end != '?')
|
||||||
|
cur_end++;
|
||||||
|
|
||||||
|
/* adjust req line offsets and lengths */
|
||||||
|
delta = trash.len - offset - (cur_end - cur_ptr);
|
||||||
|
txn->req.sl.rq.u_l += delta;
|
||||||
|
txn->req.sl.rq.v += delta;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2: // query
|
||||||
|
cur_ptr = s->req->buf->p + txn->req.sl.rq.u;
|
||||||
|
cur_end = cur_ptr + txn->req.sl.rq.u_l;
|
||||||
|
while (cur_ptr < cur_end && *cur_ptr != '?')
|
||||||
|
cur_ptr++;
|
||||||
|
|
||||||
|
/* skip the question mark or indicate that we must insert it
|
||||||
|
* (but only if the format string is not empty then).
|
||||||
|
*/
|
||||||
|
if (cur_ptr < cur_end)
|
||||||
|
cur_ptr++;
|
||||||
|
else if (trash.len > 1)
|
||||||
|
offset = 0;
|
||||||
|
|
||||||
|
/* adjust req line offsets and lengths */
|
||||||
|
delta = trash.len - offset - (cur_end - cur_ptr);
|
||||||
|
txn->req.sl.rq.u_l += delta;
|
||||||
|
txn->req.sl.rq.v += delta;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3: // uri
|
||||||
|
cur_ptr = s->req->buf->p + txn->req.sl.rq.u;
|
||||||
|
cur_end = cur_ptr + txn->req.sl.rq.u_l;
|
||||||
|
|
||||||
|
/* adjust req line offsets and lengths */
|
||||||
|
delta = trash.len - offset - (cur_end - cur_ptr);
|
||||||
|
txn->req.sl.rq.u_l += delta;
|
||||||
|
txn->req.sl.rq.v += delta;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* commit changes and adjust end of message */
|
||||||
|
delta = buffer_replace2(s->req->buf, cur_ptr, cur_end, trash.str + offset, trash.len - offset);
|
||||||
|
http_msg_move_end(&txn->req, delta);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* parse an http-request action among :
|
||||||
|
* set-method
|
||||||
|
* set-path
|
||||||
|
* set-query
|
||||||
|
* set-uri
|
||||||
|
*
|
||||||
|
* All of them accept a single argument of type string representing a log-format.
|
||||||
|
* The resulting rule makes use of arg->act.p[0..1] to store the log-format list
|
||||||
|
* head, and p[2] to store the action as an int (0=method, 1=path, 2=query, 3=uri).
|
||||||
|
* It returns 0 on success, < 0 on error.
|
||||||
|
*/
|
||||||
|
int parse_set_req_line(const char **args, int *orig_arg, struct proxy *px, struct http_req_rule *rule, char **err)
|
||||||
|
{
|
||||||
|
int cur_arg = *orig_arg;
|
||||||
|
|
||||||
|
rule->action = HTTP_REQ_ACT_CUSTOM_CONT;
|
||||||
|
|
||||||
|
switch (args[0][4]) {
|
||||||
|
case 'm' :
|
||||||
|
*(int *)&rule->arg.act.p[2] = 0;
|
||||||
|
rule->action_ptr = http_action_set_req_line;
|
||||||
|
break;
|
||||||
|
case 'p' :
|
||||||
|
*(int *)&rule->arg.act.p[2] = 1;
|
||||||
|
rule->action_ptr = http_action_set_req_line;
|
||||||
|
break;
|
||||||
|
case 'q' :
|
||||||
|
*(int *)&rule->arg.act.p[2] = 2;
|
||||||
|
rule->action_ptr = http_action_set_req_line;
|
||||||
|
break;
|
||||||
|
case 'u' :
|
||||||
|
*(int *)&rule->arg.act.p[2] = 3;
|
||||||
|
rule->action_ptr = http_action_set_req_line;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
memprintf(err, "internal error: unhandled action '%s'", args[0]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!*args[cur_arg] ||
|
||||||
|
(*args[cur_arg + 1] && strcmp(args[cur_arg + 1], "if") != 0 && strcmp(args[cur_arg + 1], "unless") != 0)) {
|
||||||
|
memprintf(err, "expects exactly 1 argument <format>");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
LIST_INIT((struct list *)&rule->arg.act.p[0]);
|
||||||
|
proxy->conf.args.ctx = ARGC_HRQ;
|
||||||
|
parse_logformat_string(args[cur_arg], proxy, (struct list *)&rule->arg.act.p[0], LOG_OPT_HTTP,
|
||||||
|
(proxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR,
|
||||||
|
proxy->conf.args.file, proxy->conf.args.line);
|
||||||
|
|
||||||
|
(*orig_arg)++;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Return the struct http_req_action_kw associated to a keyword.
|
* Return the struct http_req_action_kw associated to a keyword.
|
||||||
*/
|
*/
|
||||||
@ -11715,6 +11858,9 @@ static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, {
|
|||||||
}};
|
}};
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************/
|
||||||
|
/* All supported converter keywords must be declared here. */
|
||||||
|
/************************************************************************/
|
||||||
/* Note: must not be declared <const> as its list will be overwritten */
|
/* Note: must not be declared <const> as its list will be overwritten */
|
||||||
static struct sample_conv_kw_list sample_conv_kws = {ILH, {
|
static struct sample_conv_kw_list sample_conv_kws = {ILH, {
|
||||||
{ "http_date", sample_conv_http_date, ARG1(0,SINT), NULL, SMP_T_UINT, SMP_T_STR},
|
{ "http_date", sample_conv_http_date, ARG1(0,SINT), NULL, SMP_T_UINT, SMP_T_STR},
|
||||||
@ -11722,12 +11868,26 @@ static struct sample_conv_kw_list sample_conv_kws = {ILH, {
|
|||||||
{ NULL, NULL, 0, 0, 0 },
|
{ NULL, NULL, 0, 0, 0 },
|
||||||
}};
|
}};
|
||||||
|
|
||||||
|
/************************************************************************/
|
||||||
|
/* All supported http-request action keywords must be declared here. */
|
||||||
|
/************************************************************************/
|
||||||
|
struct http_req_action_kw_list http_req_actions = {
|
||||||
|
.scope = "http",
|
||||||
|
.kw = {
|
||||||
|
{ "set-method", parse_set_req_line },
|
||||||
|
{ "set-path", parse_set_req_line },
|
||||||
|
{ "set-query", parse_set_req_line },
|
||||||
|
{ "set-uri", parse_set_req_line },
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
__attribute__((constructor))
|
__attribute__((constructor))
|
||||||
static void __http_protocol_init(void)
|
static void __http_protocol_init(void)
|
||||||
{
|
{
|
||||||
acl_register_keywords(&acl_kws);
|
acl_register_keywords(&acl_kws);
|
||||||
sample_register_fetches(&sample_fetch_keywords);
|
sample_register_fetches(&sample_fetch_keywords);
|
||||||
sample_register_convs(&sample_conv_kws);
|
sample_register_convs(&sample_conv_kws);
|
||||||
|
http_req_keywords_register(&http_req_actions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user