MEDIUM: http: The redirect strings follows the log format rules.

We handle "http-request redirect" with a log-format string now, but we
leave "redirect" unaffected.

Note that the control of the special "/" case is move from the runtime
execution to the configuration parsing. If the format rule list is
empty, the build_logline() function does nothing.
This commit is contained in:
Thierry FOURNIER 2013-11-29 12:15:45 +01:00 committed by Willy Tarreau
parent 06d97f935c
commit d18cd0f110
9 changed files with 108 additions and 35 deletions

View File

@ -2768,7 +2768,8 @@ http-request { allow | deny | tarpit | auth [realm <realm>] | redirect <rule> |
- "redirect" : this performs an HTTP redirection based on a redirect rule.
This is exactly the same as the "redirect" statement except that it
inserts a redirect rule which can be processed in the middle of other
"http-request" rules. See the "redirect" keyword for the rule's syntax.
"http-request" rules and that these rules use the "log-format" strings.
See the "redirect" keyword for the rule's syntax.
- "add-header" appends an HTTP header field whose name is specified in
<name> and whose value is defined by <fmt> which follows the log-format
@ -4709,14 +4710,19 @@ redirect scheme <sch> [code <code>] <option> [{if | unless} <condition>]
Arguments :
<loc> With "redirect location", the exact value in <loc> is placed into
the HTTP "Location" header.
the HTTP "Location" header. When used in an "http-request" rule,
<loc> value follows the log-format rules and can include some
dynamic values (see Custom Log Format in section 8.2.4).
<pfx> With "redirect prefix", the "Location" header is built from the
concatenation of <pfx> and the complete URI path, including the
query string, unless the "drop-query" option is specified (see
below). As a special case, if <pfx> equals exactly "/", then
nothing is inserted before the original URI. It allows one to
redirect to the same URL (for instance, to insert a cookie).
redirect to the same URL (for instance, to insert a cookie). When
used in an "http-request" rule, <pfx> value follows the log-format
rules and can include some dynamic values (see Custom Log Format
in section 8.2.4).
<sch> With "redirect scheme", then the "Location" header is built by
concatenating <sch> with "://" then the first occurrence of the
@ -4726,7 +4732,9 @@ redirect scheme <sch> [code <code>] <option> [{if | unless} <condition>]
no "Host" header is found, then an empty host component will be
returned, which most recent browsers interprete as redirecting to
the same host. This directive is mostly used to redirect HTTP to
HTTPS.
HTTPS. When used in an "http-request" rule, <sch> value follows
the log-format rules and can include some dynamic values (see
Custom Log Format in section 8.2.4).
<code> The code is optional. It indicates which type of HTTP redirection
is desired. Only codes 301, 302, 303, 307 and 308 are supported,
@ -4790,6 +4798,10 @@ redirect scheme <sch> [code <code>] <option> [{if | unless} <condition>]
Example: redirect all HTTP traffic to HTTPS when SSL is handled by haproxy.
redirect scheme https if !{ ssl_fc }
Example: append 'www.' prefix in front of all hosts not having it
http-request redirect code 301 location www.%[hdr(host)]%[req.uri] \
unless { hdr_beg(host) -i www }
See section 7 about ACL usage.

View File

@ -117,8 +117,8 @@ struct http_req_rule *parse_http_req_cond(const char **args, const char *file, i
struct http_res_rule *parse_http_res_cond(const char **args, const char *file, int linenum, struct proxy *proxy);
void free_http_req_rules(struct list *r);
struct chunk *http_error_message(struct session *s, int msgnum);
struct redirect_rule *http_parse_redirect_rule(const char *file, int line, struct proxy *curproxy,
const char **args, char **errmsg);
struct redirect_rule *http_parse_redirect_rule(const char *file, int linenum, struct proxy *curproxy,
const char **args, char **errmsg, int use_fmt);
/* to be used when contents change in an HTTP message */
#define http_msg_move_end(msg, bytes) do { \

View File

@ -57,6 +57,7 @@ enum {
ARGC_HRQ, /* http-request */
ARGC_HRS, /* http-response */
ARGC_UIF, /* unique-id-format */
ARGC_RDR, /* redirect */
};
/* some types that are externally defined */

View File

@ -403,6 +403,7 @@ struct redirect_rule {
int type;
int rdr_len;
char *rdr_str;
struct list rdr_fmt;
int code;
unsigned int flags;
int cookie_len;

View File

@ -2770,7 +2770,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
goto out;
}
if ((rule = http_parse_redirect_rule(file, linenum, curproxy, (const char **)args + 1, &errmsg)) == NULL) {
if ((rule = http_parse_redirect_rule(file, linenum, curproxy, (const char **)args + 1, &errmsg, 0)) == NULL) {
Alert("parsing [%s:%d] : error detected in %s '%s' while parsing redirect rule : %s.\n",
file, linenum, proxy_type_str(curproxy), curproxy->id, errmsg);
err_code |= ERR_ALERT | ERR_FATAL;

View File

@ -1073,6 +1073,10 @@ void deinit(void)
free(rdr->cond);
}
free(rdr->rdr_str);
list_for_each_entry_safe(lf, lfb, &rdr->rdr_fmt, list) {
LIST_DEL(&lf->list);
free(lf);
}
free(rdr);
}

View File

@ -177,6 +177,8 @@ static inline const char *fmt_directive(const struct proxy *curproxy)
return "stick";
case ARGC_TRK:
return "track-sc"; break;
case ARGC_RDR:
return "redirect"; break;
case ARGC_ACL:
return "acl"; break;
default:

View File

@ -3340,6 +3340,7 @@ static int http_apply_redirect_rule(struct redirect_rule *rule, struct session *
{
struct http_msg *msg = &txn->req;
const char *msg_fmt;
const char *location;
/* build redirect message */
switch(rule->code) {
@ -3364,6 +3365,8 @@ static int http_apply_redirect_rule(struct redirect_rule *rule, struct session *
if (unlikely(!chunk_strcpy(&trash, msg_fmt)))
return 0;
location = trash.str + trash.len;
switch(rule->type) {
case REDIRECT_TYPE_SCHEME: {
const char *path;
@ -3399,6 +3402,7 @@ static int http_apply_redirect_rule(struct redirect_rule *rule, struct session *
pathlen = 1;
}
if (rule->rdr_str) { /* this is an old "redirect" rule */
/* check if we can add scheme + "://" + host + path */
if (trash.len + rule->rdr_len + 3 + hostlen + pathlen > trash.size - 4)
return 0;
@ -3406,7 +3410,15 @@ static int http_apply_redirect_rule(struct redirect_rule *rule, struct session *
/* add scheme */
memcpy(trash.str + trash.len, rule->rdr_str, rule->rdr_len);
trash.len += rule->rdr_len;
}
else {
/* add scheme with executing log format */
trash.len += build_logline(s, trash.str + trash.len, trash.size - trash.len, &rule->rdr_fmt);
/* check if we can add scheme + "://" + host + path */
if (trash.len + 3 + hostlen + pathlen > trash.size - 4)
return 0;
}
/* add "://" */
memcpy(trash.str + trash.len, "://", 3);
trash.len += 3;
@ -3419,7 +3431,7 @@ static int http_apply_redirect_rule(struct redirect_rule *rule, struct session *
memcpy(trash.str + trash.len, path, pathlen);
trash.len += pathlen;
/* append a slash at the end of the location is needed and missing */
/* append a slash at the end of the location if needed and missing */
if (trash.len && trash.str[trash.len - 1] != '/' &&
(rule->flags & REDIRECT_FLAG_APPEND_SLASH)) {
if (trash.len > trash.size - 5)
@ -3453,6 +3465,7 @@ static int http_apply_redirect_rule(struct redirect_rule *rule, struct session *
pathlen = 1;
}
if (rule->rdr_str) { /* this is an old "redirect" rule */
if (trash.len + rule->rdr_len + pathlen > trash.size - 4)
return 0;
@ -3464,12 +3477,21 @@ static int http_apply_redirect_rule(struct redirect_rule *rule, struct session *
memcpy(trash.str + trash.len, rule->rdr_str, rule->rdr_len);
trash.len += rule->rdr_len;
}
}
else {
/* add prefix with executing log format */
trash.len += build_logline(s, trash.str + trash.len, trash.size - trash.len, &rule->rdr_fmt);
/* Check length */
if (trash.len + pathlen > trash.size - 4)
return 0;
}
/* add path */
memcpy(trash.str + trash.len, path, pathlen);
trash.len += pathlen;
/* append a slash at the end of the location is needed and missing */
/* append a slash at the end of the location if needed and missing */
if (trash.len && trash.str[trash.len - 1] != '/' &&
(rule->flags & REDIRECT_FLAG_APPEND_SLASH)) {
if (trash.len > trash.size - 5)
@ -3482,12 +3504,22 @@ static int http_apply_redirect_rule(struct redirect_rule *rule, struct session *
}
case REDIRECT_TYPE_LOCATION:
default:
if (rule->rdr_str) { /* this is an old "redirect" rule */
if (trash.len + rule->rdr_len > trash.size - 4)
return 0;
/* add location */
memcpy(trash.str + trash.len, rule->rdr_str, rule->rdr_len);
trash.len += rule->rdr_len;
}
else {
/* add location with executing log format */
trash.len += build_logline(s, trash.str + trash.len, trash.size - trash.len, &rule->rdr_fmt);
/* Check left length */
if (trash.len > trash.size - 4)
return 0;
}
break;
}
@ -3509,7 +3541,7 @@ static int http_apply_redirect_rule(struct redirect_rule *rule, struct session *
/* let's log the request time */
s->logs.tv_request = now;
if (rule->rdr_len >= 1 && *rule->rdr_str == '/' &&
if (*location == '/' &&
(msg->flags & HTTP_MSGF_XFER_LEN) &&
!(msg->flags & HTTP_MSGF_TE_CHNK) && !txn->req.body_len &&
((txn->flags & TX_CON_WANT_MSK) == TX_CON_WANT_SCL ||
@ -8474,7 +8506,7 @@ struct http_req_rule *parse_http_req_cond(const char **args, const char *file, i
struct redirect_rule *redir;
char *errmsg = NULL;
if ((redir = http_parse_redirect_rule(file, linenum, proxy, (const char **)args + 1, &errmsg)) == NULL) {
if ((redir = http_parse_redirect_rule(file, linenum, proxy, (const char **)args + 1, &errmsg, 1)) == NULL) {
Alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-request %s' rule : %s.\n",
file, linenum, proxy_type_str(proxy), proxy->id, args[0], errmsg);
goto out_err;
@ -8670,10 +8702,11 @@ struct http_res_rule *parse_http_res_cond(const char **args, const char *file, i
}
/* Parses a redirect rule. Returns the redirect rule on success or NULL on error,
* with <err> filled with the error message.
* with <err> filled with the error message. If <use_fmt> is not null, builds a
* dynamic log-format rule instead of a static string.
*/
struct redirect_rule *http_parse_redirect_rule(const char *file, int linenum, struct proxy *curproxy,
const char **args, char **errmsg)
const char **args, char **errmsg, int use_fmt)
{
struct redirect_rule *rule;
int cur_arg;
@ -8771,8 +8804,27 @@ struct redirect_rule *http_parse_redirect_rule(const char *file, int linenum, st
rule = (struct redirect_rule *)calloc(1, sizeof(*rule));
rule->cond = cond;
if (!use_fmt) {
/* old-style static redirect rule */
rule->rdr_str = strdup(destination);
rule->rdr_len = strlen(destination);
}
else {
/* log-format based redirect rule */
LIST_INIT(&rule->rdr_fmt);
/* Parse destination. Note that in the REDIRECT_TYPE_PREFIX case,
* if prefix == "/", we don't want to add anything, otherwise it
* makes it hard for the user to configure a self-redirection.
*/
proxy->conf.args.ctx = ARGC_RDR;
if (!(type == REDIRECT_TYPE_PREFIX && destination[0] == '/' && destination[1] == '\0')) {
parse_logformat_string(destination, curproxy, &rule->rdr_fmt, 0,
(curproxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR);
}
}
if (cookie) {
/* depending on cookie_set, either we want to set the cookie, or to clear it.
* a clear consists in appending "; path=/; Max-Age=0;" at the end.

View File

@ -901,6 +901,7 @@ int smp_resolve_args(struct proxy *p)
case ARGC_HRQ: where = "in http-request header format string in"; break;
case ARGC_HRS: where = "in http-response header format string in"; break;
case ARGC_UIF: where = "in unique-id-format string in"; break;
case ARGC_RDR: where = "in redirect format string in"; break;
case ARGC_ACL: ctx = "ACL keyword"; break;
}