REORG: config: move the http redirect rule parser to proto_http.c

We'll have to use this elsewhere soon, let's move it to the proper
place.
diff --git a/include/proto/proto_http.h b/include/proto/proto_http.h
index dd8367e..a379909 100644
--- a/include/proto/proto_http.h
+++ b/include/proto/proto_http.h
@@ -110,6 +110,8 @@
 struct http_req_rule *parse_http_req_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);
 
 /* to be used when contents change in an HTTP message */
 #define http_msg_move_end(msg, bytes) do { \
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 4954e7a..abcb9c5 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -2648,13 +2648,6 @@
 	}
 	else if (!strcmp(args[0], "redirect")) {
 		struct redirect_rule *rule;
-		int cur_arg;
-		int type = REDIRECT_TYPE_NONE;
-		int code = 302;
-		char *destination = NULL;
-		char *cookie = NULL;
-		int cookie_set = 0;
-		unsigned int flags = REDIRECT_FLAG_NONE;
 
 		if (curproxy == &defproxy) {
 			Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
@@ -2662,142 +2655,13 @@
 			goto out;
 		}
 
-		cur_arg = 1;
-		while (*(args[cur_arg])) {
-			if (!strcmp(args[cur_arg], "location")) {
-				if (!*args[cur_arg + 1]) {
-					Alert("parsing [%s:%d] : '%s': missing argument for '%s'.\n",
-					      file, linenum, args[0], args[cur_arg]);
-					err_code |= ERR_ALERT | ERR_FATAL;
-					goto out;
-				}
-
-				type = REDIRECT_TYPE_LOCATION;
-				cur_arg++;
-				destination = args[cur_arg];
-			}
-			else if (!strcmp(args[cur_arg], "prefix")) {
-				if (!*args[cur_arg + 1]) {
-					Alert("parsing [%s:%d] : '%s': missing argument for '%s'.\n",
-					      file, linenum, args[0], args[cur_arg]);
-					err_code |= ERR_ALERT | ERR_FATAL;
-					goto out;
-				}
-
-				type = REDIRECT_TYPE_PREFIX;
-				cur_arg++;
-				destination = args[cur_arg];
-			}
-			else if (!strcmp(args[cur_arg], "scheme")) {
-				if (!*args[cur_arg + 1]) {
-					Alert("parsing [%s:%d] : '%s': missing argument for '%s'.\n",
-					      file, linenum, args[0], args[cur_arg]);
-					err_code |= ERR_ALERT | ERR_FATAL;
-					goto out;
-				}
-
-				type = REDIRECT_TYPE_SCHEME;
-				cur_arg++;
-				destination = args[cur_arg];
-			}
-			else if (!strcmp(args[cur_arg], "set-cookie")) {
-				if (!*args[cur_arg + 1]) {
-					Alert("parsing [%s:%d] : '%s': missing argument for '%s'.\n",
-					      file, linenum, args[0], args[cur_arg]);
-					err_code |= ERR_ALERT | ERR_FATAL;
-					goto out;
-				}
-
-				cur_arg++;
-				cookie = args[cur_arg];
-				cookie_set = 1;
-			}
-			else if (!strcmp(args[cur_arg], "clear-cookie")) {
-				if (!*args[cur_arg + 1]) {
-					Alert("parsing [%s:%d] : '%s': missing argument for '%s'.\n",
-					      file, linenum, args[0], args[cur_arg]);
-					err_code |= ERR_ALERT | ERR_FATAL;
-					goto out;
-				}
-
-				cur_arg++;
-				cookie = args[cur_arg];
-				cookie_set = 0;
-			}
-			else if (!strcmp(args[cur_arg],"code")) {
-				if (!*args[cur_arg + 1]) {
-					Alert("parsing [%s:%d] : '%s': missing HTTP code.\n",
-					      file, linenum, args[0]);
-					err_code |= ERR_ALERT | ERR_FATAL;
-					goto out;
-				}
-				cur_arg++;
-				code = atol(args[cur_arg]);
-				if (code < 301 || code > 303) {
-					Alert("parsing [%s:%d] : '%s': unsupported HTTP code '%d'.\n",
-					      file, linenum, args[0], code);
-					err_code |= ERR_ALERT | ERR_FATAL;
-					goto out;
-				}
-			}
-			else if (!strcmp(args[cur_arg],"drop-query")) {
-				flags |= REDIRECT_FLAG_DROP_QS;
-			}
-			else if (!strcmp(args[cur_arg],"append-slash")) {
-				flags |= REDIRECT_FLAG_APPEND_SLASH;
-			}
-			else if (strcmp(args[cur_arg], "if") == 0 ||
-				 strcmp(args[cur_arg], "unless") == 0) {
-				cond = build_acl_cond(file, linenum, curproxy, (const char **)args + cur_arg, &errmsg);
-				if (!cond) {
-					Alert("parsing [%s:%d] : '%s': error detected while parsing redirect condition : %s.\n",
-					      file, linenum, args[0], errmsg);
-					err_code |= ERR_ALERT | ERR_FATAL;
-					goto out;
-				}
-				break;
-			}
-			else {
-				Alert("parsing [%s:%d] : '%s' expects 'code', 'prefix', 'location', 'scheme', 'set-cookie', 'clear-cookie', 'drop-query' or 'append-slash' (was '%s').\n",
-				      file, linenum, args[0], args[cur_arg]);
-				err_code |= ERR_ALERT | ERR_FATAL;
-				goto out;
-			}
-			cur_arg++;
-		}
-
-		if (type == REDIRECT_TYPE_NONE) {
-			Alert("parsing [%s:%d] : '%s' expects a redirection type ('prefix' or 'location').\n",
-			      file, linenum, args[0]);
+		if ((rule = http_parse_redirect_rule(file, linenum, curproxy, (const char **)args + 1, &errmsg)) == 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;
 			goto out;
 		}
 
-		rule = (struct redirect_rule *)calloc(1, sizeof(*rule));
-		rule->cond = cond;
-		rule->rdr_str = strdup(destination);
-		rule->rdr_len = strlen(destination);
-		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.
-			 */
-			rule->cookie_len = strlen(cookie);
-			if (cookie_set) {
-				rule->cookie_str = malloc(rule->cookie_len + 10);
-				memcpy(rule->cookie_str, cookie, rule->cookie_len);
-				memcpy(rule->cookie_str + rule->cookie_len, "; path=/;", 10);
-				rule->cookie_len += 9;
-			} else {
-				rule->cookie_str = malloc(rule->cookie_len + 21);
-				memcpy(rule->cookie_str, cookie, rule->cookie_len);
-				memcpy(rule->cookie_str + rule->cookie_len, "; path=/; Max-Age=0;", 21);
-				rule->cookie_len += 20;
-			}
-		}
-		rule->type = type;
-		rule->code = code;
-		rule->flags = flags;
-		LIST_INIT(&rule->list);
 		LIST_ADDQ(&curproxy->redirect_rules, &rule->list);
 		err_code |= warnif_rule_after_use_backend(curproxy, file, linenum, args[0]);
 		err_code |= warnif_cond_requires_resp(cond, file, linenum);
diff --git a/src/proto_http.c b/src/proto_http.c
index 8b207a2..13a72c4 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -8104,6 +8104,138 @@
 	return rule;
 }
 
+/* Parses a redirect rule. Returns the redirect rule on success or NULL on error,
+ * with <err> filled with the error message.
+ */
+struct redirect_rule *http_parse_redirect_rule(const char *file, int linenum, struct proxy *curproxy,
+                                               const char **args, char **errmsg)
+{
+	struct redirect_rule *rule;
+	int cur_arg;
+	int type = REDIRECT_TYPE_NONE;
+	int code = 302;
+	const char *destination = NULL;
+	const char *cookie = NULL;
+	int cookie_set = 0;
+	unsigned int flags = REDIRECT_FLAG_NONE;
+	struct acl_cond *cond = NULL;
+
+	cur_arg = 0;
+	while (*(args[cur_arg])) {
+		if (strcmp(args[cur_arg], "location") == 0) {
+			if (!*args[cur_arg + 1])
+				goto missing_arg;
+
+			type = REDIRECT_TYPE_LOCATION;
+			cur_arg++;
+			destination = args[cur_arg];
+		}
+		else if (strcmp(args[cur_arg], "prefix") == 0) {
+			if (!*args[cur_arg + 1])
+				goto missing_arg;
+
+			type = REDIRECT_TYPE_PREFIX;
+			cur_arg++;
+			destination = args[cur_arg];
+		}
+		else if (strcmp(args[cur_arg], "scheme") == 0) {
+			if (!*args[cur_arg + 1])
+				goto missing_arg;
+
+			type = REDIRECT_TYPE_SCHEME;
+			cur_arg++;
+			destination = args[cur_arg];
+		}
+		else if (strcmp(args[cur_arg], "set-cookie") == 0) {
+			if (!*args[cur_arg + 1])
+				goto missing_arg;
+
+			cur_arg++;
+			cookie = args[cur_arg];
+			cookie_set = 1;
+		}
+		else if (strcmp(args[cur_arg], "clear-cookie") == 0) {
+			if (!*args[cur_arg + 1])
+				goto missing_arg;
+
+			cur_arg++;
+			cookie = args[cur_arg];
+			cookie_set = 0;
+		}
+		else if (strcmp(args[cur_arg], "code") == 0) {
+			if (!*args[cur_arg + 1])
+				goto missing_arg;
+
+			cur_arg++;
+			code = atol(args[cur_arg]);
+			if (code < 301 || code > 303) {
+				memprintf(errmsg,
+				          "'%s': unsupported HTTP code '%s' (must be a number between 301 and 303)",
+				          args[cur_arg - 1], args[cur_arg]);
+				return NULL;
+			}
+		}
+		else if (!strcmp(args[cur_arg],"drop-query")) {
+			flags |= REDIRECT_FLAG_DROP_QS;
+		}
+		else if (!strcmp(args[cur_arg],"append-slash")) {
+			flags |= REDIRECT_FLAG_APPEND_SLASH;
+		}
+		else if (strcmp(args[cur_arg], "if") == 0 ||
+			 strcmp(args[cur_arg], "unless") == 0) {
+			cond = build_acl_cond(file, linenum, curproxy, (const char **)args + cur_arg, errmsg);
+			if (!cond) {
+				memprintf(errmsg, "error in condition: %s", *errmsg);
+				return NULL;
+			}
+			break;
+		}
+		else {
+			memprintf(errmsg,
+			          "expects 'code', 'prefix', 'location', 'scheme', 'set-cookie', 'clear-cookie', 'drop-query' or 'append-slash' (was '%s')",
+			          args[cur_arg]);
+			return NULL;
+		}
+		cur_arg++;
+	}
+
+	if (type == REDIRECT_TYPE_NONE) {
+		memprintf(errmsg, "redirection type expected ('prefix', 'location', or 'scheme')");
+		return NULL;
+	}
+
+	rule = (struct redirect_rule *)calloc(1, sizeof(*rule));
+	rule->cond = cond;
+	rule->rdr_str = strdup(destination);
+	rule->rdr_len = strlen(destination);
+	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.
+		 */
+		rule->cookie_len = strlen(cookie);
+		if (cookie_set) {
+			rule->cookie_str = malloc(rule->cookie_len + 10);
+			memcpy(rule->cookie_str, cookie, rule->cookie_len);
+			memcpy(rule->cookie_str + rule->cookie_len, "; path=/;", 10);
+			rule->cookie_len += 9;
+		} else {
+			rule->cookie_str = malloc(rule->cookie_len + 21);
+			memcpy(rule->cookie_str, cookie, rule->cookie_len);
+			memcpy(rule->cookie_str + rule->cookie_len, "; path=/; Max-Age=0;", 21);
+			rule->cookie_len += 20;
+		}
+	}
+	rule->type = type;
+	rule->code = code;
+	rule->flags = flags;
+	LIST_INIT(&rule->list);
+	return rule;
+
+ missing_arg:
+	memprintf(errmsg, "missing argument for '%s'", args[cur_arg]);
+	return NULL;
+}
+
 /************************************************************************/
 /*        The code below is dedicated to ACL parsing and matching       */
 /************************************************************************/