MEDIUM: http: add support for "http-request redirect" rules

These are exactly the same as the classic redirect rules except
that they can be interleaved with other http-request rules for
more flexibility.

The redirect parser should probably be changed to stop at the condition
so that the caller puts its own condition pointer. At the moment, the
redirect rule and condition are parsed at once by build_redirect_rule()
and the condition is assigned to the http_req_rule.
diff --git a/doc/configuration.txt b/doc/configuration.txt
index 9aaf22d..a4973ec 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -2606,7 +2606,7 @@
 
   See also : "option httpchk", "http-check disable-on-404"
 
-http-request { allow | deny | auth [realm <realm>] |
+http-request { allow | deny | auth [realm <realm>] | redirect <rule> |
               add-header <name> <fmt> | set-header <name> <fmt> }
              [ { if | unless } <condition> ]
   Access control for Layer 7 requests
@@ -2634,6 +2634,11 @@
       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
       <name> and whose value is defined by <fmt> 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 7c5a42a..ef81a12 100644
--- a/include/types/proto_http.h
+++ b/include/types/proto_http.h
@@ -243,6 +243,7 @@
 	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 @@
 			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 13a72c4..7fc2dce 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -3104,6 +3104,9 @@
 		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 */
@@ -3550,6 +3553,13 @@
 
 		if (unlikely(http_header_add_tail(&txn->req, &txn->hdr_idx, wl->s) < 0))
 			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)) {
@@ -8040,7 +8050,7 @@
 	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 @@
 		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 @@
 		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 @@
 			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 @@
 		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,