diff --git a/doc/configuration.txt b/doc/configuration.txt index 9aaf22d3a..a4973ecb7 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -2606,7 +2606,7 @@ http-check send-state See also : "option httpchk", "http-check disable-on-404" -http-request { allow | deny | auth [realm ] | +http-request { allow | deny | auth [realm ] | redirect | add-header | set-header } [ { if | unless } ] Access control for Layer 7 requests @@ -2634,6 +2634,11 @@ http-request { allow | deny | auth [realm ] | optional "realm" parameter is supported, it sets the authentication realm that is returned with the response (typically the application's name). + - "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. + - "add-header" appends an HTTP header field whose name is specified in and whose value is defined by which follows the log-format rules (see Custom Log Format in section 8.2.4). This is particularly diff --git a/include/types/proto_http.h b/include/types/proto_http.h index 7c5a42a3f..ef81a12a6 100644 --- a/include/types/proto_http.h +++ b/include/types/proto_http.h @@ -243,6 +243,7 @@ enum { HTTP_REQ_ACT_AUTH, HTTP_REQ_ACT_ADD_HDR, HTTP_REQ_ACT_SET_HDR, + HTTP_REQ_ACT_REDIR, HTTP_REQ_ACT_MAX /* must always be last */ }; @@ -354,6 +355,7 @@ struct http_req_rule { int name_len; /* header name's length */ struct list fmt; /* log-format compatible expression */ } hdr_add; /* args used by "add-header" and "set-header" */ + struct redirect_rule *redir; /* redirect rule or "http-request redirect" */ } arg; /* arguments used by some actions */ }; diff --git a/src/proto_http.c b/src/proto_http.c index 13a72c45f..7fc2dce2b 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -3104,6 +3104,9 @@ http_req_get_intercept_rule(struct proxy *px, struct list *rules, struct session case HTTP_REQ_ACT_AUTH: return rule; + case HTTP_REQ_ACT_REDIR: + return rule; + case HTTP_REQ_ACT_SET_HDR: ctx.idx = 0; /* remove all occurrences of the header */ @@ -3552,6 +3555,13 @@ int http_process_req_common(struct session *s, struct channel *req, int an_bit, goto return_bad_req; } + if (http_req_last_rule && http_req_last_rule->action == HTTP_REQ_ACT_REDIR) { + if (!http_apply_redirect_rule(http_req_last_rule->arg.redir, s, txn)) + goto return_bad_req; + req->analyse_exp = TICK_ETERNITY; + return 1; + } + if (unlikely(do_stats)) { /* process the stats request now */ if (!http_handle_stats(s, req)) { @@ -8040,7 +8050,7 @@ struct http_req_rule *parse_http_req_cond(const char **args, const char *file, i rule = (struct http_req_rule*)calloc(1, sizeof(struct http_req_rule)); if (!rule) { Alert("parsing [%s:%d]: out of memory.\n", file, linenum); - return NULL; + goto out_err; } if (!strcmp(args[0], "allow")) { @@ -8068,7 +8078,7 @@ struct http_req_rule *parse_http_req_cond(const char **args, const char *file, i if (!*args[cur_arg] || !*args[cur_arg+1] || *args[cur_arg+2]) { Alert("parsing [%s:%d]: 'http-request %s' expects exactly 2 arguments.\n", file, linenum, args[0]); - return NULL; + goto out_err; } rule->arg.hdr_add.name = strdup(args[cur_arg]); @@ -8076,10 +8086,29 @@ struct http_req_rule *parse_http_req_cond(const char **args, const char *file, i LIST_INIT(&rule->arg.hdr_add.fmt); parse_logformat_string(args[cur_arg + 1], proxy, &rule->arg.hdr_add.fmt, PR_MODE_HTTP); cur_arg += 2; + } else if (strcmp(args[0], "redirect") == 0) { + struct redirect_rule *redir; + char *errmsg; + + if ((redir = http_parse_redirect_rule(file, linenum, proxy, (const char **)args + 1, &errmsg)) == 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; + } + + /* this redirect rule might already contain a parsed condition which + * we'll pass to the http-request rule. + */ + rule->action = HTTP_REQ_ACT_REDIR; + rule->arg.redir = redir; + rule->cond = redir->cond; + redir->cond = NULL; + cur_arg = 2; + return rule; } else { Alert("parsing [%s:%d]: 'http-request' expects 'allow', 'deny', 'auth', 'add-header', 'set-header', but got '%s'%s.\n", file, linenum, args[0], *args[0] ? "" : " (missing argument)"); - return NULL; + goto out_err; } if (strcmp(args[cur_arg], "if") == 0 || strcmp(args[cur_arg], "unless") == 0) { @@ -8090,7 +8119,7 @@ struct http_req_rule *parse_http_req_cond(const char **args, const char *file, i Alert("parsing [%s:%d] : error detected while parsing an 'http-request %s' condition : %s.\n", file, linenum, args[0], errmsg); free(errmsg); - return NULL; + goto out_err; } rule->cond = cond; } @@ -8098,10 +8127,13 @@ struct http_req_rule *parse_http_req_cond(const char **args, const char *file, i Alert("parsing [%s:%d]: 'http-request %s' expects 'realm' for 'auth' or" " either 'if' or 'unless' followed by a condition but found '%s'.\n", file, linenum, args[0], args[cur_arg]); - return NULL; + goto out_err; } return rule; + out_err: + free(rule); + return NULL; } /* Parses a redirect rule. Returns the redirect rule on success or NULL on error,