REORG: tcpcheck: Move check option parsing functions based on tcp-check

The parsing of the check options based on tcp-check rules (redis, spop,
smtp, http...) are moved aways from check.c. Now, these functions are placed
in tcpcheck.c. These functions are only related to the tcpcheck ruleset
configured on a proxy and not to the health-check attached to a server.
diff --git a/include/haproxy/check.h b/include/haproxy/check.h
index 5e55da1..2e697ee 100644
--- a/include/haproxy/check.h
+++ b/include/haproxy/check.h
@@ -50,25 +50,6 @@
 int spoe_prepare_healthcheck_request(char **req, int *len);
 int spoe_handle_healthcheck_response(char *frame, size_t size, char *err, int errlen);
 
-int proxy_parse_tcp_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
-			      const char *file, int line);
-int proxy_parse_redis_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
-				const char *file, int line);
-int proxy_parse_ssl_hello_chk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
-				  const char *file, int line);
-int proxy_parse_smtpchk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
-			const char *file, int line);
-int proxy_parse_pgsql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
-				const char *file, int line);
-int proxy_parse_mysql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
-			    const char *file, int line);
-int proxy_parse_ldap_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
-			       const char *file, int line);
-int proxy_parse_spop_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
-			       const char *file, int line);
-int proxy_parse_httpchk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
-			    const char *file, int line);
-
 int set_srv_agent_send(struct server *srv, const char *send);
 
 /* Use this one only. This inline version only ensures that we don't
diff --git a/include/haproxy/tcpcheck.h b/include/haproxy/tcpcheck.h
index 8befe3b..a8a36a7 100644
--- a/include/haproxy/tcpcheck.h
+++ b/include/haproxy/tcpcheck.h
@@ -83,6 +83,24 @@
                                             struct list *rules, unsigned int proto,
                                             const char *file, int line, char **errmsg);
 
+int proxy_parse_tcp_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
+			      const char *file, int line);
+int proxy_parse_redis_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
+				const char *file, int line);
+int proxy_parse_ssl_hello_chk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
+				  const char *file, int line);
+int proxy_parse_smtpchk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
+			const char *file, int line);
+int proxy_parse_pgsql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
+				const char *file, int line);
+int proxy_parse_mysql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
+			    const char *file, int line);
+int proxy_parse_ldap_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
+			       const char *file, int line);
+int proxy_parse_spop_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
+			       const char *file, int line);
+int proxy_parse_httpchk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
+			    const char *file, int line);
 
 
 /* Return the struct action_kw associated to a keyword */
diff --git a/src/check.c b/src/check.c
index 1453970..bedcff1 100644
--- a/src/check.c
+++ b/src/check.c
@@ -1492,1212 +1492,6 @@
 /**************************************************************************/
 /************************ Check's parsing functions ***********************/
 /**************************************************************************/
-/* Parses the "http-check" proxy keyword */
-static int proxy_parse_httpcheck(char **args, int section, struct proxy *curpx,
-				 struct proxy *defpx, const char *file, int line,
-				 char **errmsg)
-{
-	struct tcpcheck_ruleset *rs = NULL;
-	struct tcpcheck_rule *chk = NULL;
-	int index, cur_arg, ret = 0;
-
-	if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[0], NULL))
-		ret = 1;
-
-	cur_arg = 1;
-	if (strcmp(args[cur_arg], "disable-on-404") == 0) {
-		/* enable a graceful server shutdown on an HTTP 404 response */
-		curpx->options |= PR_O_DISABLE404;
-		if (too_many_args(1, args, errmsg, NULL))
-			goto error;
-		goto out;
-	}
-	else if (strcmp(args[cur_arg], "send-state") == 0) {
-		/* enable emission of the apparent state of a server in HTTP checks */
-		curpx->options2 |= PR_O2_CHK_SNDST;
-		if (too_many_args(1, args, errmsg, NULL))
-			goto error;
-		goto out;
-	}
-
-	/* Deduce the ruleset name from the proxy info */
-	chunk_printf(&trash, "*http-check-%s_%s-%d",
-		     ((curpx == defpx) ? "defaults" : curpx->id),
-		     curpx->conf.file, curpx->conf.line);
-
-	rs = find_tcpcheck_ruleset(b_orig(&trash));
-	if (rs == NULL) {
-		rs = create_tcpcheck_ruleset(b_orig(&trash));
-		if (rs == NULL) {
-			memprintf(errmsg, "out of memory.\n");
-			goto error;
-		}
-	}
-
-	index = 0;
-	if (!LIST_ISEMPTY(&rs->rules)) {
-		chk = LIST_PREV(&rs->rules, typeof(chk), list);
-		if (chk->action != TCPCHK_ACT_SEND || !(chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT))
-			index = chk->index + 1;
-	}
-
-	if (strcmp(args[cur_arg], "connect") == 0)
-		chk = parse_tcpcheck_connect(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
-	else if (strcmp(args[cur_arg], "send") == 0)
-		chk = parse_tcpcheck_send_http(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
-	else if (strcmp(args[cur_arg], "expect") == 0)
-		chk = parse_tcpcheck_expect(args, cur_arg, curpx, &rs->rules, TCPCHK_RULES_HTTP_CHK,
-					    file, line, errmsg);
-	else if (strcmp(args[cur_arg], "comment") == 0)
-		chk = parse_tcpcheck_comment(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
-	else {
-		struct action_kw *kw = action_kw_tcp_check_lookup(args[cur_arg]);
-
-		if (!kw) {
-			action_kw_tcp_check_build_list(&trash);
-			memprintf(errmsg, "'%s' only supports 'disable-on-404', 'send-state', 'comment', 'connect',"
-				  " 'send', 'expect'%s%s. but got '%s'",
-				  args[0], (*trash.area ? ", " : ""), trash.area, args[1]);
-			goto error;
-		}
-		chk = parse_tcpcheck_action(args, cur_arg, curpx, &rs->rules, kw, file, line, errmsg);
-	}
-
-	if (!chk) {
-		memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
-		goto error;
-	}
-	ret = (*errmsg != NULL); /* Handle warning */
-
-	chk->index = index;
-	if ((curpx->options2 & PR_O2_CHK_ANY) == PR_O2_TCPCHK_CHK &&
-	    (curpx->tcpcheck_rules.flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK) {
-		/* Use this ruleset if the proxy already has http-check enabled */
-		curpx->tcpcheck_rules.list = &rs->rules;
-		curpx->tcpcheck_rules.flags &= ~TCPCHK_RULES_UNUSED_HTTP_RS;
-		if (!tcpcheck_add_http_rule(chk, &curpx->tcpcheck_rules, errmsg)) {
-			memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
-			curpx->tcpcheck_rules.list = NULL;
-			goto error;
-		}
-	}
-	else {
-		/* mark this ruleset as unused for now */
-		curpx->tcpcheck_rules.flags |= TCPCHK_RULES_UNUSED_HTTP_RS;
-		LIST_ADDQ(&rs->rules, &chk->list);
-	}
-
-  out:
-	return ret;
-
-  error:
-	free_tcpcheck(chk, 0);
-	free_tcpcheck_ruleset(rs);
-	return -1;
-}
-
-/* Parses the "option tcp-check" proxy keyword */
-int proxy_parse_tcp_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
-			      const char *file, int line)
-{
-	struct tcpcheck_ruleset *rs = NULL;
-	struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
-	int err_code = 0;
-
-	if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
-		err_code |= ERR_WARN;
-
-	if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
-		goto out;
-
-	curpx->options2 &= ~PR_O2_CHK_ANY;
-	curpx->options2 |= PR_O2_TCPCHK_CHK;
-
-	if ((rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK) {
-		/* If a tcp-check rulesset is already set, do nothing */
-		if (rules->list)
-			goto out;
-
-		/* If a tcp-check ruleset is waiting to be used for the current proxy,
-		 * get it.
-		 */
-		if (rules->flags & TCPCHK_RULES_UNUSED_TCP_RS)
-			goto curpx_ruleset;
-
-		/* Otherwise, try to get the tcp-check ruleset of the default proxy */
-		chunk_printf(&trash, "*tcp-check-defaults_%s-%d", defpx->conf.file, defpx->conf.line);
-		rs = find_tcpcheck_ruleset(b_orig(&trash));
-		if (rs)
-			goto ruleset_found;
-	}
-
-  curpx_ruleset:
-	/* Deduce the ruleset name from the proxy info */
-	chunk_printf(&trash, "*tcp-check-%s_%s-%d",
-		     ((curpx == defpx) ? "defaults" : curpx->id),
-		     curpx->conf.file, curpx->conf.line);
-
-	rs = find_tcpcheck_ruleset(b_orig(&trash));
-	if (rs == NULL) {
-		rs = create_tcpcheck_ruleset(b_orig(&trash));
-		if (rs == NULL) {
-			ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
-			goto error;
-		}
-	}
-
-  ruleset_found:
-	free_tcpcheck_vars(&rules->preset_vars);
-	rules->list = &rs->rules;
-	rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
-	rules->flags |= TCPCHK_RULES_TCP_CHK;
-
-  out:
-	return err_code;
-
-  error:
-	err_code |= ERR_ALERT | ERR_FATAL;
-	goto out;
-}
-
-/* Parses the "option redis-check" proxy keyword */
-int proxy_parse_redis_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
-				const char *file, int line)
-{
-	static char *redis_req = "*1\r\n$4\r\nPING\r\n";
-	static char *redis_res = "+PONG\r\n";
-
-	struct tcpcheck_ruleset *rs = NULL;
-	struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
-	struct tcpcheck_rule *chk;
-	char *errmsg = NULL;
-	int err_code = 0;
-
-	if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
-		err_code |= ERR_WARN;
-
-	if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
-		goto out;
-
-	curpx->options2 &= ~PR_O2_CHK_ANY;
-	curpx->options2 |= PR_O2_TCPCHK_CHK;
-
-	free_tcpcheck_vars(&rules->preset_vars);
-	rules->list  = NULL;
-	rules->flags = 0;
-
-	rs = find_tcpcheck_ruleset("*redis-check");
-	if (rs)
-		goto ruleset_found;
-
-	rs = create_tcpcheck_ruleset("*redis-check");
-	if (rs == NULL) {
-		ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
-		goto error;
-	}
-
-	chk = parse_tcpcheck_send((char *[]){"tcp-check", "send", redis_req, ""},
-				  1, curpx, &rs->rules, file, line, &errmsg);
-	if (!chk) {
-		ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
-		goto error;
-	}
-	chk->index = 0;
-	LIST_ADDQ(&rs->rules, &chk->list);
-
-	chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "string", redis_res,
-				               "error-status", "L7STS",
-				               "on-error", "%[res.payload(0,0),cut_crlf]",
-				               "on-success", "Redis server is ok",
-				               ""},
-				    1, curpx, &rs->rules, TCPCHK_RULES_REDIS_CHK, file, line, &errmsg);
-	if (!chk) {
-		ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
-		goto error;
-	}
-	chk->index = 1;
-	LIST_ADDQ(&rs->rules, &chk->list);
-
-  ruleset_found:
-	rules->list = &rs->rules;
-	rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
-	rules->flags |= TCPCHK_RULES_REDIS_CHK;
-
-  out:
-	free(errmsg);
-	return err_code;
-
-  error:
-	free_tcpcheck_ruleset(rs);
-	err_code |= ERR_ALERT | ERR_FATAL;
-	goto out;
-}
-
-
-/* Parses the "option ssl-hello-chk" proxy keyword */
-int proxy_parse_ssl_hello_chk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
-				  const char *file, int line)
-{
-	/* This is the SSLv3 CLIENT HELLO packet used in conjunction with the
-	 * ssl-hello-chk option to ensure that the remote server speaks SSL.
-	 *
-	 * Check RFC 2246 (TLSv1.0) sections A.3 and A.4 for details.
-	 */
-	static char sslv3_client_hello[] = {
-		"16"                        /* ContentType         : 0x16 = Handshake          */
-		"0300"                      /* ProtocolVersion     : 0x0300 = SSLv3            */
-		"0079"                      /* ContentLength       : 0x79 bytes after this one */
-		"01"                        /* HanshakeType        : 0x01 = CLIENT HELLO       */
-		"000075"                    /* HandshakeLength     : 0x75 bytes after this one */
-		"0300"                      /* Hello Version       : 0x0300 = v3               */
-		"%[date(),htonl,hex]"       /* Unix GMT Time (s)   : filled with <now> (@0x0B) */
-		"%[str(HAPROXYSSLCHK\nHAPROXYSSLCHK\n),hex]" /* Random   : must be exactly 28 bytes  */
-		"00"                        /* Session ID length   : empty (no session ID)     */
-		"004E"                      /* Cipher Suite Length : 78 bytes after this one   */
-		"0001" "0002" "0003" "0004" /* 39 most common ciphers :  */
-		"0005" "0006" "0007" "0008" /* 0x01...0x1B, 0x2F...0x3A  */
-		"0009" "000A" "000B" "000C" /* This covers RSA/DH,       */
-		"000D" "000E" "000F" "0010" /* various bit lengths,      */
-		"0011" "0012" "0013" "0014" /* SHA1/MD5, DES/3DES/AES... */
-		"0015" "0016" "0017" "0018"
-		"0019" "001A" "001B" "002F"
-		"0030" "0031" "0032" "0033"
-		"0034" "0035" "0036" "0037"
-		"0038" "0039" "003A"
-		"01"                       /* Compression Length  : 0x01 = 1 byte for types   */
-		"00"                       /* Compression Type    : 0x00 = NULL compression   */
-	};
-
-	struct tcpcheck_ruleset *rs = NULL;
-	struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
-	struct tcpcheck_rule *chk;
-	char *errmsg = NULL;
-	int err_code = 0;
-
-	if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
-		err_code |= ERR_WARN;
-
-	if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
-		goto out;
-
-	curpx->options2 &= ~PR_O2_CHK_ANY;
-	curpx->options2 |= PR_O2_TCPCHK_CHK;
-
-	free_tcpcheck_vars(&rules->preset_vars);
-	rules->list  = NULL;
-	rules->flags = 0;
-
-	rs = find_tcpcheck_ruleset("*ssl-hello-check");
-	if (rs)
-		goto ruleset_found;
-
-	rs = create_tcpcheck_ruleset("*ssl-hello-check");
-	if (rs == NULL) {
-		ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
-		goto error;
-	}
-
-	chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", sslv3_client_hello, ""},
-				  1, curpx, &rs->rules, file, line, &errmsg);
-	if (!chk) {
-		ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
-		goto error;
-	}
-	chk->index = 0;
-	LIST_ADDQ(&rs->rules, &chk->list);
-
-	chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^1[56]",
-				                "min-recv", "5", "ok-status", "L6OK",
-				                "error-status", "L6RSP", "tout-status", "L6TOUT",
-				                ""},
-		                    1, curpx, &rs->rules, TCPCHK_RULES_SSL3_CHK, file, line, &errmsg);
-	if (!chk) {
-		ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
-		goto error;
-	}
-	chk->index = 1;
-	LIST_ADDQ(&rs->rules, &chk->list);
-
-  ruleset_found:
-	rules->list = &rs->rules;
-	rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
-	rules->flags |= TCPCHK_RULES_SSL3_CHK;
-
-  out:
-	free(errmsg);
-	return err_code;
-
-  error:
-	free_tcpcheck_ruleset(rs);
-	err_code |= ERR_ALERT | ERR_FATAL;
-	goto out;
-}
-
-/* Parses the "option smtpchk" proxy keyword */
-int proxy_parse_smtpchk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
-			    const char *file, int line)
-{
-	static char *smtp_req = "%[var(check.smtp_cmd)]\r\n";
-
-	struct tcpcheck_ruleset *rs = NULL;
-	struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
-	struct tcpcheck_rule *chk;
-	struct tcpcheck_var *var = NULL;
-	char *cmd = NULL, *errmsg = NULL;
-	int err_code = 0;
-
-	if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
-		err_code |= ERR_WARN;
-
-	if (alertif_too_many_args_idx(2, 1, file, line, args, &err_code))
-		goto out;
-
-	curpx->options2 &= ~PR_O2_CHK_ANY;
-	curpx->options2 |= PR_O2_TCPCHK_CHK;
-
-	free_tcpcheck_vars(&rules->preset_vars);
-	rules->list  = NULL;
-	rules->flags = 0;
-
-	cur_arg += 2;
-	if (*args[cur_arg] && *args[cur_arg+1] &&
-	    (strcmp(args[cur_arg], "EHLO") == 0 || strcmp(args[cur_arg], "HELO") == 0)) {
-		/* <EHLO|HELO> + space (1) + <host> + null byte (1) */
-		cmd = calloc(strlen(args[cur_arg]) + 1 + strlen(args[cur_arg+1]) + 1, sizeof(*cmd));
-		if (cmd)
-			sprintf(cmd, "%s %s", args[cur_arg], args[cur_arg+1]);
-	}
-	else {
-		/* this just hits the default for now, but you could potentially expand it to allow for other stuff
-		   though, it's unlikely you'd want to send anything other than an EHLO or HELO */
-		cmd = strdup("HELO localhost");
-	}
-
-	var = create_tcpcheck_var(ist("check.smtp_cmd"));
-	if (cmd == NULL || var == NULL) {
-		ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
-		goto error;
-	}
-	var->data.type = SMP_T_STR;
-	var->data.u.str.area = cmd;
-	var->data.u.str.data = strlen(cmd);
-	LIST_INIT(&var->list);
-	LIST_ADDQ(&rules->preset_vars, &var->list);
-	cmd = NULL;
-	var = NULL;
-
-	rs = find_tcpcheck_ruleset("*smtp-check");
-	if (rs)
-		goto ruleset_found;
-
-	rs = create_tcpcheck_ruleset("*smtp-check");
-	if (rs == NULL) {
-		ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
-		goto error;
-	}
-
-	chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
-				     1, curpx, &rs->rules, file, line, &errmsg);
-	if (!chk) {
-		ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
-		goto error;
-	}
-	chk->index = 0;
-	LIST_ADDQ(&rs->rules, &chk->list);
-
-	chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^[0-9]{3}[ \r]",
-				               "min-recv", "4",
-				               "error-status", "L7RSP",
-				               "on-error", "%[res.payload(0,0),cut_crlf]",
-				               ""},
-		                    1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
-	if (!chk) {
-		ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
-		goto error;
-	}
-	chk->index = 1;
-	LIST_ADDQ(&rs->rules, &chk->list);
-
-	chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^2[0-9]{2}[ \r]",
-				               "min-recv", "4",
-				               "error-status", "L7STS",
-				               "on-error", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
-				               "status-code", "res.payload(0,3)",
-				               ""},
-		                    1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
-	if (!chk) {
-		ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
-		goto error;
-	}
-	chk->index = 2;
-	LIST_ADDQ(&rs->rules, &chk->list);
-
-	chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-lf", smtp_req, ""},
-				  1, curpx, &rs->rules, file, line, &errmsg);
-	if (!chk) {
-		ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
-		goto error;
-	}
-	chk->index = 3;
-	LIST_ADDQ(&rs->rules, &chk->list);
-
-	chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^2[0-9]{2}[- \r]",
-				               "min-recv", "4",
-				               "error-status", "L7STS",
-				               "on-error", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
-				               "on-success", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
-				               "status-code", "res.payload(0,3)",
-				               ""},
-		                    1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
-	if (!chk) {
-		ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
-		goto error;
-	}
-	chk->index = 4;
-	LIST_ADDQ(&rs->rules, &chk->list);
-
-  ruleset_found:
-	rules->list = &rs->rules;
-	rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
-	rules->flags |= TCPCHK_RULES_SMTP_CHK;
-
-  out:
-	free(errmsg);
-	return err_code;
-
-  error:
-	free(cmd);
-	free(var);
-	free_tcpcheck_vars(&rules->preset_vars);
-	free_tcpcheck_ruleset(rs);
-	err_code |= ERR_ALERT | ERR_FATAL;
-	goto out;
-}
-
-/* Parses the "option pgsql-check" proxy keyword */
-int proxy_parse_pgsql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
-				const char *file, int line)
-{
-	static char pgsql_req[] = {
-		"%[var(check.plen),htonl,hex]" /* The packet length*/
-		"00030000"                     /* the version 3.0 */
-		"7573657200"                   /* "user" key */
-		"%[var(check.username),hex]00" /* the username */
-		"00"
-	};
-
-	struct tcpcheck_ruleset *rs = NULL;
-	struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
-	struct tcpcheck_rule *chk;
-	struct tcpcheck_var *var = NULL;
-	char *user = NULL, *errmsg = NULL;
-	size_t packetlen = 0;
-	int err_code = 0;
-
-	if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
-		err_code |= ERR_WARN;
-
-	if (alertif_too_many_args_idx(2, 1, file, line, args, &err_code))
-		goto out;
-
-	curpx->options2 &= ~PR_O2_CHK_ANY;
-	curpx->options2 |= PR_O2_TCPCHK_CHK;
-
-	free_tcpcheck_vars(&rules->preset_vars);
-	rules->list  = NULL;
-	rules->flags = 0;
-
-	cur_arg += 2;
-	if (!*args[cur_arg] || !*args[cur_arg+1]) {
-		ha_alert("parsing [%s:%d] : '%s %s' expects 'user <username>' as argument.\n",
-			 file, line, args[0], args[1]);
-		goto error;
-	}
-	if (strcmp(args[cur_arg], "user") == 0) {
-		packetlen = 15 + strlen(args[cur_arg+1]);
-		user = strdup(args[cur_arg+1]);
-
-		var = create_tcpcheck_var(ist("check.username"));
-		if (user == NULL || var == NULL) {
-			ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
-			goto error;
-		}
-		var->data.type = SMP_T_STR;
-		var->data.u.str.area = user;
-		var->data.u.str.data = strlen(user);
-		LIST_INIT(&var->list);
-		LIST_ADDQ(&rules->preset_vars, &var->list);
-		user = NULL;
-		var = NULL;
-
-		var = create_tcpcheck_var(ist("check.plen"));
-		if (var == NULL) {
-			ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
-			goto error;
-		}
-		var->data.type = SMP_T_SINT;
-		var->data.u.sint = packetlen;
-		LIST_INIT(&var->list);
-		LIST_ADDQ(&rules->preset_vars, &var->list);
-		var = NULL;
-	}
-	else {
-		ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'user'.\n",
-			 file, line, args[0], args[1]);
-		goto error;
-	}
-
-	rs = find_tcpcheck_ruleset("*pgsql-check");
-	if (rs)
-		goto ruleset_found;
-
-	rs = create_tcpcheck_ruleset("*pgsql-check");
-	if (rs == NULL) {
-		ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
-		goto error;
-	}
-
-	chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
-				     1, curpx, &rs->rules, file, line, &errmsg);
-	if (!chk) {
-		ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
-		goto error;
-	}
-	chk->index = 0;
-	LIST_ADDQ(&rs->rules, &chk->list);
-
-	chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", pgsql_req, ""},
-				  1, curpx, &rs->rules, file, line, &errmsg);
-	if (!chk) {
-		ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
-		goto error;
-	}
-	chk->index = 1;
-	LIST_ADDQ(&rs->rules, &chk->list);
-
-	chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "!rstring", "^E",
-				               "min-recv", "5",
-				               "error-status", "L7RSP",
-				               "on-error", "%[res.payload(6,0)]",
-				               ""},
-		                    1, curpx, &rs->rules, TCPCHK_RULES_PGSQL_CHK, file, line, &errmsg);
-	if (!chk) {
-		ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
-		goto error;
-	}
-	chk->index = 2;
-	LIST_ADDQ(&rs->rules, &chk->list);
-
-	chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^52000000(08|0A|0C)000000(00|02|03|04|05|06)",
-				               "min-recv", "9",
-				               "error-status", "L7STS",
-				               "on-success", "PostgreSQL server is ok",
-				               "on-error",   "PostgreSQL unknown error",
-				               ""},
-		                    1, curpx, &rs->rules, TCPCHK_RULES_PGSQL_CHK, file, line, &errmsg);
-	if (!chk) {
-		ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
-		goto error;
-	}
-	chk->index = 3;
-	LIST_ADDQ(&rs->rules, &chk->list);
-
-  ruleset_found:
-	rules->list = &rs->rules;
-	rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
-	rules->flags |= TCPCHK_RULES_PGSQL_CHK;
-
-  out:
-	free(errmsg);
-	return err_code;
-
-  error:
-	free(user);
-	free(var);
-	free_tcpcheck_vars(&rules->preset_vars);
-	free_tcpcheck_ruleset(rs);
-	err_code |= ERR_ALERT | ERR_FATAL;
-	goto out;
-}
-
-
-/* Parses the "option mysql-check" proxy keyword */
-int proxy_parse_mysql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
-				const char *file, int line)
-{
-	/* This is an example of a MySQL >=4.0 client Authentication packet kindly provided by Cyril Bonte.
-	 * const char mysql40_client_auth_pkt[] = {
-	 * 	"\x0e\x00\x00"	// packet length
-	 * 	"\x01"		// packet number
-	 * 	"\x00\x00"	// client capabilities
-	 * 	"\x00\x00\x01"	// max packet
-	 * 	"haproxy\x00"	// username (null terminated string)
-	 * 	"\x00"		// filler (always 0x00)
-	 * 	"\x01\x00\x00"	// packet length
-	 * 	"\x00"		// packet number
-	 * 	"\x01"		// COM_QUIT command
-	 * };
-	 */
-	static char mysql40_rsname[] = "*mysql40-check";
-	static char mysql40_req[] = {
-		"%[var(check.header),hex]"     /* 3 bytes for the packet length and 1 byte for the sequence ID */
-		"0080"                         /* client capabilities */
-		"000001"                       /* max packet */
-		"%[var(check.username),hex]00" /* the username */
-		"00"                           /* filler (always 0x00) */
-		"010000"                       /* packet length*/
-		"00"                           /* sequence ID */
-		"01"                           /* COM_QUIT command */
-	};
-
-	/* This is an example of a MySQL >=4.1  client Authentication packet provided by Nenad Merdanovic.
-	 * const char mysql41_client_auth_pkt[] = {
-	 * 	"\x0e\x00\x00\"		// packet length
-	 * 	"\x01"			// packet number
-	 * 	"\x00\x00\x00\x00"	// client capabilities
-	 * 	"\x00\x00\x00\x01"	// max packet
-	 *	"\x21"			// character set (UTF-8)
-	 *	char[23]		// All zeroes
-	 * 	"haproxy\x00"		// username (null terminated string)
-	 * 	"\x00"			// filler (always 0x00)
-	 * 	"\x01\x00\x00"		// packet length
-	 * 	"\x00"			// packet number
-	 * 	"\x01"			// COM_QUIT command
-	 * };
-	 */
-	static char mysql41_rsname[] = "*mysql41-check";
-	static char mysql41_req[] = {
-		"%[var(check.header),hex]"     /* 3 bytes for the packet length and 1 byte for the sequence ID */
-		"00820000"                     /* client capabilities */
-		"00800001"                     /* max packet */
-		"21"                           /* character set (UTF-8) */
-		"000000000000000000000000"     /* 23 bytes, al zeroes */
-		"0000000000000000000000"
-		"%[var(check.username),hex]00" /* the username */
-		"00"                           /* filler (always 0x00) */
-		"010000"                       /* packet length*/
-		"00"                           /* sequence ID */
-		"01"                           /* COM_QUIT command */
-	};
-
-	struct tcpcheck_ruleset *rs = NULL;
-	struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
-	struct tcpcheck_rule *chk;
-	struct tcpcheck_var *var = NULL;
-	char *mysql_rsname = "*mysql-check";
-	char *mysql_req = NULL, *hdr = NULL, *user = NULL, *errmsg = NULL;
-	int index = 0, err_code = 0;
-
-	if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
-		err_code |= ERR_WARN;
-
-	if (alertif_too_many_args_idx(3, 1, file, line, args, &err_code))
-		goto out;
-
-	curpx->options2 &= ~PR_O2_CHK_ANY;
-	curpx->options2 |= PR_O2_TCPCHK_CHK;
-
-	free_tcpcheck_vars(&rules->preset_vars);
-	rules->list  = NULL;
-	rules->flags = 0;
-
-	cur_arg += 2;
-	if (*args[cur_arg]) {
-		int packetlen, userlen;
-
-		if (strcmp(args[cur_arg], "user") != 0) {
-			ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'user' (got '%s').\n",
-				 file, line, args[0], args[1], args[cur_arg]);
-			goto error;
-		}
-
-		if (*(args[cur_arg+1]) == 0) {
-			ha_alert("parsing [%s:%d] : '%s %s %s' expects <username> as argument.\n",
-				 file, line, args[0], args[1], args[cur_arg]);
-			goto error;
-		}
-
-		hdr     = calloc(4, sizeof(*hdr));
-		user    = strdup(args[cur_arg+1]);
-		userlen = strlen(args[cur_arg+1]);
-
-		if (hdr == NULL || user == NULL) {
-			ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
-			goto error;
-		}
-
-		if (!*args[cur_arg+2] || strcmp(args[cur_arg+2], "post-41") == 0) {
-			packetlen = userlen + 7 + 27;
-			mysql_req = mysql41_req;
-			mysql_rsname  = mysql41_rsname;
-		}
-		else if (strcmp(args[cur_arg+2], "pre-41") == 0) {
-			packetlen = userlen + 7;
-			mysql_req = mysql40_req;
-			mysql_rsname  = mysql40_rsname;
-		}
-		else  {
-			ha_alert("parsing [%s:%d] : keyword '%s' only supports 'post-41' and 'pre-41' (got '%s').\n",
-				 file, line, args[cur_arg], args[cur_arg+2]);
-			goto error;
-		}
-
-		hdr[0] = (unsigned char)(packetlen & 0xff);
-		hdr[1] = (unsigned char)((packetlen >> 8) & 0xff);
-		hdr[2] = (unsigned char)((packetlen >> 16) & 0xff);
-		hdr[3] = 1;
-
-		var = create_tcpcheck_var(ist("check.header"));
-		if (var == NULL) {
-			ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
-			goto error;
-		}
-		var->data.type = SMP_T_STR;
-		var->data.u.str.area = hdr;
-		var->data.u.str.data = 4;
-		LIST_INIT(&var->list);
-		LIST_ADDQ(&rules->preset_vars, &var->list);
-		hdr = NULL;
-		var = NULL;
-
-		var = create_tcpcheck_var(ist("check.username"));
-		if (var == NULL) {
-			ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
-			goto error;
-		}
-		var->data.type = SMP_T_STR;
-		var->data.u.str.area = user;
-		var->data.u.str.data = strlen(user);
-		LIST_INIT(&var->list);
-		LIST_ADDQ(&rules->preset_vars, &var->list);
-		user = NULL;
-		var = NULL;
-	}
-
-	rs = find_tcpcheck_ruleset(mysql_rsname);
-	if (rs)
-		goto ruleset_found;
-
-	rs = create_tcpcheck_ruleset(mysql_rsname);
-	if (rs == NULL) {
-		ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
-		goto error;
-	}
-
-	chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
-				     1, curpx, &rs->rules, file, line, &errmsg);
-	if (!chk) {
-		ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
-		goto error;
-	}
-	chk->index = index++;
-	LIST_ADDQ(&rs->rules, &chk->list);
-
-	if (mysql_req) {
-		chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", mysql_req, ""},
-					  1, curpx, &rs->rules, file, line, &errmsg);
-		if (!chk) {
-			ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
-			goto error;
-		}
-		chk->index = index++;
-		LIST_ADDQ(&rs->rules, &chk->list);
-	}
-
-	chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
-		                    1, curpx, &rs->rules, TCPCHK_RULES_MYSQL_CHK, file, line, &errmsg);
-	if (!chk) {
-		ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
-		goto error;
-	}
-	chk->expect.custom = tcpcheck_mysql_expect_iniths;
-	chk->index = index++;
-	LIST_ADDQ(&rs->rules, &chk->list);
-
-	if (mysql_req) {
-		chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
-					    1, curpx, &rs->rules, TCPCHK_RULES_MYSQL_CHK, file, line, &errmsg);
-		if (!chk) {
-			ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
-			goto error;
-		}
-		chk->expect.custom = tcpcheck_mysql_expect_ok;
-		chk->index = index++;
-		LIST_ADDQ(&rs->rules, &chk->list);
-	}
-
-  ruleset_found:
-	rules->list = &rs->rules;
-	rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
-	rules->flags |= TCPCHK_RULES_MYSQL_CHK;
-
-  out:
-	free(errmsg);
-	return err_code;
-
-  error:
-	free(hdr);
-	free(user);
-	free(var);
-	free_tcpcheck_vars(&rules->preset_vars);
-	free_tcpcheck_ruleset(rs);
-	err_code |= ERR_ALERT | ERR_FATAL;
-	goto out;
-}
-
-int proxy_parse_ldap_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
-			       const char *file, int line)
-{
-	static char *ldap_req = "300C020101600702010304008000";
-
-	struct tcpcheck_ruleset *rs = NULL;
-	struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
-	struct tcpcheck_rule *chk;
-	char *errmsg = NULL;
-	int err_code = 0;
-
-	if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
-		err_code |= ERR_WARN;
-
-	if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
-		goto out;
-
-	curpx->options2 &= ~PR_O2_CHK_ANY;
-	curpx->options2 |= PR_O2_TCPCHK_CHK;
-
-	free_tcpcheck_vars(&rules->preset_vars);
-	rules->list  = NULL;
-	rules->flags = 0;
-
-	rs = find_tcpcheck_ruleset("*ldap-check");
-	if (rs)
-		goto ruleset_found;
-
-	rs = create_tcpcheck_ruleset("*ldap-check");
-	if (rs == NULL) {
-		ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
-		goto error;
-	}
-
-	chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", ldap_req, ""},
-				  1, curpx, &rs->rules, file, line, &errmsg);
-	if (!chk) {
-		ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
-		goto error;
-	}
-	chk->index = 0;
-	LIST_ADDQ(&rs->rules, &chk->list);
-
-	chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^30",
-				               "min-recv", "14",
-				               "on-error", "Not LDAPv3 protocol",
-				               ""},
-		                    1, curpx, &rs->rules, TCPCHK_RULES_LDAP_CHK, file, line, &errmsg);
-	if (!chk) {
-		ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
-		goto error;
-	}
-	chk->index = 1;
-	LIST_ADDQ(&rs->rules, &chk->list);
-
-	chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
-		                    1, curpx, &rs->rules, TCPCHK_RULES_LDAP_CHK, file, line, &errmsg);
-	if (!chk) {
-		ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
-		goto error;
-	}
-	chk->expect.custom = tcpcheck_ldap_expect_bindrsp;
-	chk->index = 2;
-	LIST_ADDQ(&rs->rules, &chk->list);
-
-  ruleset_found:
-	rules->list = &rs->rules;
-	rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
-	rules->flags |= TCPCHK_RULES_LDAP_CHK;
-
-  out:
-	free(errmsg);
-	return err_code;
-
-  error:
-	free_tcpcheck_ruleset(rs);
-	err_code |= ERR_ALERT | ERR_FATAL;
-	goto out;
-}
-
-int proxy_parse_spop_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
-			       const char *file, int line)
-{
-	struct tcpcheck_ruleset *rs = NULL;
-	struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
-	struct tcpcheck_rule *chk;
-	char *spop_req = NULL;
-	char *errmsg = NULL;
-	int spop_len = 0, err_code = 0;
-
-	if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
-		err_code |= ERR_WARN;
-
-	if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
-		goto out;
-
-	curpx->options2 &= ~PR_O2_CHK_ANY;
-	curpx->options2 |= PR_O2_TCPCHK_CHK;
-
-	free_tcpcheck_vars(&rules->preset_vars);
-	rules->list  = NULL;
-	rules->flags = 0;
-
-
-	rs = find_tcpcheck_ruleset("*spop-check");
-	if (rs)
-		goto ruleset_found;
-
-	rs = create_tcpcheck_ruleset("*spop-check");
-	if (rs == NULL) {
-		ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
-		goto error;
-	}
-
-	if (spoe_prepare_healthcheck_request(&spop_req, &spop_len) == -1) {
-		ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
-		goto error;
-	}
-	chunk_reset(&trash);
-	dump_binary(&trash, spop_req, spop_len);
-	trash.area[trash.data] = '\0';
-
-	chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", b_head(&trash), ""},
-				  1, curpx, &rs->rules, file, line, &errmsg);
-	if (!chk) {
-		ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
-		goto error;
-	}
-	chk->index = 0;
-	LIST_ADDQ(&rs->rules, &chk->list);
-
-	chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", "min-recv", "4", ""},
-		                    1, curpx, &rs->rules, TCPCHK_RULES_SPOP_CHK, file, line, &errmsg);
-	if (!chk) {
-		ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
-		goto error;
-	}
-	chk->expect.custom = tcpcheck_spop_expect_agenthello;
-	chk->index = 1;
-	LIST_ADDQ(&rs->rules, &chk->list);
-
-  ruleset_found:
-	rules->list = &rs->rules;
-	rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
-	rules->flags |= TCPCHK_RULES_SPOP_CHK;
-
-  out:
-	free(spop_req);
-	free(errmsg);
-	return err_code;
-
-  error:
-	free_tcpcheck_ruleset(rs);
-	err_code |= ERR_ALERT | ERR_FATAL;
-	goto out;
-}
-
-
-struct tcpcheck_rule *proxy_parse_httpchk_req(char **args, int cur_arg, struct proxy *px, char **errmsg)
-{
-	struct tcpcheck_rule *chk = NULL;
-	struct tcpcheck_http_hdr *hdr = NULL;
-	char *meth = NULL, *uri = NULL, *vsn = NULL;
-	char *hdrs, *body;
-
-	hdrs = (*args[cur_arg+2] ? strstr(args[cur_arg+2], "\r\n") : NULL);
-	body = (*args[cur_arg+2] ? strstr(args[cur_arg+2], "\r\n\r\n") : NULL);
-	if (hdrs == body)
-		hdrs = NULL;
-	if (hdrs) {
-		*hdrs = '\0';
-		hdrs +=2;
-	}
-	if (body) {
-		*body = '\0';
-		body += 4;
-	}
-	if (hdrs || body) {
-		memprintf(errmsg, "hiding headers or body at the end of the version string is deprecated."
-			  " Please, consider to use 'http-check send' directive instead.");
-	}
-
-	chk = calloc(1, sizeof(*chk));
-	if (!chk) {
-		memprintf(errmsg, "out of memory");
-		goto error;
-	}
-	chk->action    = TCPCHK_ACT_SEND;
-	chk->send.type = TCPCHK_SEND_HTTP;
-	chk->send.http.flags |= TCPCHK_SND_HTTP_FROM_OPT;
-	chk->send.http.meth.meth = HTTP_METH_OPTIONS;
-	LIST_INIT(&chk->send.http.hdrs);
-
-	/* Copy the method, uri and version */
-	if (*args[cur_arg]) {
-		if (!*args[cur_arg+1])
-			uri = args[cur_arg];
-		else
-			meth = args[cur_arg];
-	}
-	if (*args[cur_arg+1])
-		uri = args[cur_arg+1];
-	if (*args[cur_arg+2])
-		vsn = args[cur_arg+2];
-
-	if (meth) {
-		chk->send.http.meth.meth = find_http_meth(meth, strlen(meth));
-		chk->send.http.meth.str.area = strdup(meth);
-		chk->send.http.meth.str.data = strlen(meth);
-		if (!chk->send.http.meth.str.area) {
-			memprintf(errmsg, "out of memory");
-			goto error;
-		}
-	}
-	if (uri) {
-		chk->send.http.uri = ist2(strdup(uri), strlen(uri));
-		if (!isttest(chk->send.http.uri)) {
-			memprintf(errmsg, "out of memory");
-			goto error;
-		}
-	}
-	if (vsn) {
-		chk->send.http.vsn = ist2(strdup(vsn), strlen(vsn));
-		if (!isttest(chk->send.http.vsn)) {
-			memprintf(errmsg, "out of memory");
-			goto error;
-		}
-	}
-
-	/* Copy the header */
-	if (hdrs) {
-		struct http_hdr tmp_hdrs[global.tune.max_http_hdr];
-		struct h1m h1m;
-		int i, ret;
-
-		/* Build and parse the request */
-		chunk_printf(&trash, "%s\r\n\r\n", hdrs);
-
-		h1m.flags = H1_MF_HDRS_ONLY;
-		ret = h1_headers_to_hdr_list(b_orig(&trash), b_tail(&trash),
-					     tmp_hdrs, sizeof(tmp_hdrs)/sizeof(tmp_hdrs[0]),
-					     &h1m, NULL);
-		if (ret <= 0) {
-			memprintf(errmsg, "unable to parse the request '%s'.", b_orig(&trash));
-			goto error;
-		}
-
-		for (i = 0; istlen(tmp_hdrs[i].n); i++) {
-			hdr = calloc(1, sizeof(*hdr));
-			if (!hdr) {
-				memprintf(errmsg, "out of memory");
-				goto error;
-			}
-			LIST_INIT(&hdr->value);
-			hdr->name = istdup(tmp_hdrs[i].n);
-			if (!hdr->name.ptr) {
-				memprintf(errmsg, "out of memory");
-				goto error;
-			}
-
-			ist0(tmp_hdrs[i].v);
-			if (!parse_logformat_string(istptr(tmp_hdrs[i].v), px, &hdr->value, 0, SMP_VAL_BE_CHK_RUL, errmsg))
-				goto error;
-			LIST_ADDQ(&chk->send.http.hdrs, &hdr->list);
-		}
-	}
-
-	/* Copy the body */
-	if (body) {
-		chk->send.http.body = ist2(strdup(body), strlen(body));
-		if (!isttest(chk->send.http.body)) {
-			memprintf(errmsg, "out of memory");
-			goto error;
-		}
-	}
-
-	return chk;
-
-  error:
-	free_tcpcheck_http_hdr(hdr);
-	free_tcpcheck(chk, 0);
-	return NULL;
-}
-
-int proxy_parse_httpchk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
-			    const char *file, int line)
-{
-	struct tcpcheck_ruleset *rs = NULL;
-	struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
-	struct tcpcheck_rule *chk;
-	char *errmsg = NULL;
-	int err_code = 0;
-
-	if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
-		err_code |= ERR_WARN;
-
-	if (alertif_too_many_args_idx(3, 1, file, line, args, &err_code))
-		goto out;
-
-	chk = proxy_parse_httpchk_req(args, cur_arg+2, curpx, &errmsg);
-	if (!chk) {
-		ha_alert("parsing [%s:%d] : '%s %s' : %s.\n", file, line, args[0], args[1], errmsg);
-		goto error;
-	}
-	if (errmsg) {
-		ha_warning("parsing [%s:%d]: '%s %s' : %s\n", file, line, args[0], args[1], errmsg);
-		err_code |= ERR_WARN;
-		free(errmsg);
-		errmsg = NULL;
-	}
-
-  no_request:
-	curpx->options2 &= ~PR_O2_CHK_ANY;
-	curpx->options2 |= PR_O2_TCPCHK_CHK;
-
-	free_tcpcheck_vars(&rules->preset_vars);
-	rules->list = NULL;
-	rules->flags |= TCPCHK_SND_HTTP_FROM_OPT;
-
-	/* Deduce the ruleset name from the proxy info */
-	chunk_printf(&trash, "*http-check-%s_%s-%d",
-		     ((curpx == defpx) ? "defaults" : curpx->id),
-		     curpx->conf.file, curpx->conf.line);
-
-	rs = find_tcpcheck_ruleset(b_orig(&trash));
-	if (rs == NULL) {
-		rs = create_tcpcheck_ruleset(b_orig(&trash));
-		if (rs == NULL) {
-			ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
-			goto error;
-		}
-	}
-
-	rules->list = &rs->rules;
-	rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
-	rules->flags |= TCPCHK_RULES_HTTP_CHK;
-	if (!tcpcheck_add_http_rule(chk, rules, &errmsg)) {
-		ha_alert("parsing [%s:%d] : '%s %s' : %s.\n", file, line, args[0], args[1], errmsg);
-		rules->list = NULL;
-		goto error;
-	}
-
-  out:
-	free(errmsg);
-	return err_code;
-
-  error:
-	free_tcpcheck_ruleset(rs);
-	free_tcpcheck(chk, 0);
-	err_code |= ERR_ALERT | ERR_FATAL;
-	goto out;
-}
-
 /* Parse the "addr" server keyword */
 static int srv_parse_addr(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
 			  char **errmsg)
@@ -3240,11 +2034,6 @@
 	goto out;
 }
 
-static struct cfg_kw_list cfg_kws = {ILH, {
-        { CFG_LISTEN, "http-check",     proxy_parse_httpcheck },
-        { 0, NULL, NULL },
-}};
-
 static struct srv_kw_list srv_kws = { "CHK", { }, {
 	{ "addr",                srv_parse_addr,                1,  1 }, /* IP address to send health to or to probe from agent-check */
 	{ "agent-addr",          srv_parse_agent_addr,          1,  1 }, /* Enable an auxiliary agent check */
@@ -3268,7 +2057,6 @@
 	{ NULL, NULL, 0 },
 }};
 
-INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
 INITCALL1(STG_REGISTER, srv_register_keywords, &srv_kws);
 
 /*
diff --git a/src/tcpcheck.c b/src/tcpcheck.c
index f5eb8e9..282588c 100644
--- a/src/tcpcheck.c
+++ b/src/tcpcheck.c
@@ -3765,7 +3765,1215 @@
 	return -1;
 }
 
+/* Parses the "http-check" proxy keyword */
+static int proxy_parse_httpcheck(char **args, int section, struct proxy *curpx,
+				 struct proxy *defpx, const char *file, int line,
+				 char **errmsg)
+{
+	struct tcpcheck_ruleset *rs = NULL;
+	struct tcpcheck_rule *chk = NULL;
+	int index, cur_arg, ret = 0;
+
+	if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[0], NULL))
+		ret = 1;
+
+	cur_arg = 1;
+	if (strcmp(args[cur_arg], "disable-on-404") == 0) {
+		/* enable a graceful server shutdown on an HTTP 404 response */
+		curpx->options |= PR_O_DISABLE404;
+		if (too_many_args(1, args, errmsg, NULL))
+			goto error;
+		goto out;
+	}
+	else if (strcmp(args[cur_arg], "send-state") == 0) {
+		/* enable emission of the apparent state of a server in HTTP checks */
+		curpx->options2 |= PR_O2_CHK_SNDST;
+		if (too_many_args(1, args, errmsg, NULL))
+			goto error;
+		goto out;
+	}
+
+	/* Deduce the ruleset name from the proxy info */
+	chunk_printf(&trash, "*http-check-%s_%s-%d",
+		     ((curpx == defpx) ? "defaults" : curpx->id),
+		     curpx->conf.file, curpx->conf.line);
+
+	rs = find_tcpcheck_ruleset(b_orig(&trash));
+	if (rs == NULL) {
+		rs = create_tcpcheck_ruleset(b_orig(&trash));
+		if (rs == NULL) {
+			memprintf(errmsg, "out of memory.\n");
+			goto error;
+		}
+	}
+
+	index = 0;
+	if (!LIST_ISEMPTY(&rs->rules)) {
+		chk = LIST_PREV(&rs->rules, typeof(chk), list);
+		if (chk->action != TCPCHK_ACT_SEND || !(chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT))
+			index = chk->index + 1;
+	}
+
+	if (strcmp(args[cur_arg], "connect") == 0)
+		chk = parse_tcpcheck_connect(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
+	else if (strcmp(args[cur_arg], "send") == 0)
+		chk = parse_tcpcheck_send_http(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
+	else if (strcmp(args[cur_arg], "expect") == 0)
+		chk = parse_tcpcheck_expect(args, cur_arg, curpx, &rs->rules, TCPCHK_RULES_HTTP_CHK,
+					    file, line, errmsg);
+	else if (strcmp(args[cur_arg], "comment") == 0)
+		chk = parse_tcpcheck_comment(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
+	else {
+		struct action_kw *kw = action_kw_tcp_check_lookup(args[cur_arg]);
+
+		if (!kw) {
+			action_kw_tcp_check_build_list(&trash);
+			memprintf(errmsg, "'%s' only supports 'disable-on-404', 'send-state', 'comment', 'connect',"
+				  " 'send', 'expect'%s%s. but got '%s'",
+				  args[0], (*trash.area ? ", " : ""), trash.area, args[1]);
+			goto error;
+		}
+		chk = parse_tcpcheck_action(args, cur_arg, curpx, &rs->rules, kw, file, line, errmsg);
+	}
+
+	if (!chk) {
+		memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
+		goto error;
+	}
+	ret = (*errmsg != NULL); /* Handle warning */
+
+	chk->index = index;
+	if ((curpx->options2 & PR_O2_CHK_ANY) == PR_O2_TCPCHK_CHK &&
+	    (curpx->tcpcheck_rules.flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK) {
+		/* Use this ruleset if the proxy already has http-check enabled */
+		curpx->tcpcheck_rules.list = &rs->rules;
+		curpx->tcpcheck_rules.flags &= ~TCPCHK_RULES_UNUSED_HTTP_RS;
+		if (!tcpcheck_add_http_rule(chk, &curpx->tcpcheck_rules, errmsg)) {
+			memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
+			curpx->tcpcheck_rules.list = NULL;
+			goto error;
+		}
+	}
+	else {
+		/* mark this ruleset as unused for now */
+		curpx->tcpcheck_rules.flags |= TCPCHK_RULES_UNUSED_HTTP_RS;
+		LIST_ADDQ(&rs->rules, &chk->list);
+	}
+
+  out:
+	return ret;
+
+  error:
+	free_tcpcheck(chk, 0);
+	free_tcpcheck_ruleset(rs);
+	return -1;
+}
+
+/* Parses the "option redis-check" proxy keyword */
+int proxy_parse_redis_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
+				const char *file, int line)
+{
+	static char *redis_req = "*1\r\n$4\r\nPING\r\n";
+	static char *redis_res = "+PONG\r\n";
+
+	struct tcpcheck_ruleset *rs = NULL;
+	struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
+	struct tcpcheck_rule *chk;
+	char *errmsg = NULL;
+	int err_code = 0;
+
+	if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
+		err_code |= ERR_WARN;
+
+	if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
+		goto out;
+
+	curpx->options2 &= ~PR_O2_CHK_ANY;
+	curpx->options2 |= PR_O2_TCPCHK_CHK;
+
+	free_tcpcheck_vars(&rules->preset_vars);
+	rules->list  = NULL;
+	rules->flags = 0;
+
+	rs = find_tcpcheck_ruleset("*redis-check");
+	if (rs)
+		goto ruleset_found;
+
+	rs = create_tcpcheck_ruleset("*redis-check");
+	if (rs == NULL) {
+		ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
+		goto error;
+	}
+
+	chk = parse_tcpcheck_send((char *[]){"tcp-check", "send", redis_req, ""},
+				  1, curpx, &rs->rules, file, line, &errmsg);
+	if (!chk) {
+		ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
+		goto error;
+	}
+	chk->index = 0;
+	LIST_ADDQ(&rs->rules, &chk->list);
+
+	chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "string", redis_res,
+				               "error-status", "L7STS",
+				               "on-error", "%[res.payload(0,0),cut_crlf]",
+				               "on-success", "Redis server is ok",
+				               ""},
+				    1, curpx, &rs->rules, TCPCHK_RULES_REDIS_CHK, file, line, &errmsg);
+	if (!chk) {
+		ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
+		goto error;
+	}
+	chk->index = 1;
+	LIST_ADDQ(&rs->rules, &chk->list);
+
+  ruleset_found:
+	rules->list = &rs->rules;
+	rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
+	rules->flags |= TCPCHK_RULES_REDIS_CHK;
+
+  out:
+	free(errmsg);
+	return err_code;
+
+  error:
+	free_tcpcheck_ruleset(rs);
+	err_code |= ERR_ALERT | ERR_FATAL;
+	goto out;
+}
+
+
+/* Parses the "option ssl-hello-chk" proxy keyword */
+int proxy_parse_ssl_hello_chk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
+				  const char *file, int line)
+{
+	/* This is the SSLv3 CLIENT HELLO packet used in conjunction with the
+	 * ssl-hello-chk option to ensure that the remote server speaks SSL.
+	 *
+	 * Check RFC 2246 (TLSv1.0) sections A.3 and A.4 for details.
+	 */
+	static char sslv3_client_hello[] = {
+		"16"                        /* ContentType         : 0x16 = Handshake          */
+		"0300"                      /* ProtocolVersion     : 0x0300 = SSLv3            */
+		"0079"                      /* ContentLength       : 0x79 bytes after this one */
+		"01"                        /* HanshakeType        : 0x01 = CLIENT HELLO       */
+		"000075"                    /* HandshakeLength     : 0x75 bytes after this one */
+		"0300"                      /* Hello Version       : 0x0300 = v3               */
+		"%[date(),htonl,hex]"       /* Unix GMT Time (s)   : filled with <now> (@0x0B) */
+		"%[str(HAPROXYSSLCHK\nHAPROXYSSLCHK\n),hex]" /* Random   : must be exactly 28 bytes  */
+		"00"                        /* Session ID length   : empty (no session ID)     */
+		"004E"                      /* Cipher Suite Length : 78 bytes after this one   */
+		"0001" "0002" "0003" "0004" /* 39 most common ciphers :  */
+		"0005" "0006" "0007" "0008" /* 0x01...0x1B, 0x2F...0x3A  */
+		"0009" "000A" "000B" "000C" /* This covers RSA/DH,       */
+		"000D" "000E" "000F" "0010" /* various bit lengths,      */
+		"0011" "0012" "0013" "0014" /* SHA1/MD5, DES/3DES/AES... */
+		"0015" "0016" "0017" "0018"
+		"0019" "001A" "001B" "002F"
+		"0030" "0031" "0032" "0033"
+		"0034" "0035" "0036" "0037"
+		"0038" "0039" "003A"
+		"01"                       /* Compression Length  : 0x01 = 1 byte for types   */
+		"00"                       /* Compression Type    : 0x00 = NULL compression   */
+	};
+
+	struct tcpcheck_ruleset *rs = NULL;
+	struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
+	struct tcpcheck_rule *chk;
+	char *errmsg = NULL;
+	int err_code = 0;
+
+	if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
+		err_code |= ERR_WARN;
+
+	if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
+		goto out;
+
+	curpx->options2 &= ~PR_O2_CHK_ANY;
+	curpx->options2 |= PR_O2_TCPCHK_CHK;
+
+	free_tcpcheck_vars(&rules->preset_vars);
+	rules->list  = NULL;
+	rules->flags = 0;
+
+	rs = find_tcpcheck_ruleset("*ssl-hello-check");
+	if (rs)
+		goto ruleset_found;
+
+	rs = create_tcpcheck_ruleset("*ssl-hello-check");
+	if (rs == NULL) {
+		ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
+		goto error;
+	}
+
+	chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", sslv3_client_hello, ""},
+				  1, curpx, &rs->rules, file, line, &errmsg);
+	if (!chk) {
+		ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
+		goto error;
+	}
+	chk->index = 0;
+	LIST_ADDQ(&rs->rules, &chk->list);
+
+	chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^1[56]",
+				                "min-recv", "5", "ok-status", "L6OK",
+				                "error-status", "L6RSP", "tout-status", "L6TOUT",
+				                ""},
+		                    1, curpx, &rs->rules, TCPCHK_RULES_SSL3_CHK, file, line, &errmsg);
+	if (!chk) {
+		ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
+		goto error;
+	}
+	chk->index = 1;
+	LIST_ADDQ(&rs->rules, &chk->list);
+
+  ruleset_found:
+	rules->list = &rs->rules;
+	rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
+	rules->flags |= TCPCHK_RULES_SSL3_CHK;
+
+  out:
+	free(errmsg);
+	return err_code;
+
+  error:
+	free_tcpcheck_ruleset(rs);
+	err_code |= ERR_ALERT | ERR_FATAL;
+	goto out;
+}
+
+/* Parses the "option smtpchk" proxy keyword */
+int proxy_parse_smtpchk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
+			    const char *file, int line)
+{
+	static char *smtp_req = "%[var(check.smtp_cmd)]\r\n";
+
+	struct tcpcheck_ruleset *rs = NULL;
+	struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
+	struct tcpcheck_rule *chk;
+	struct tcpcheck_var *var = NULL;
+	char *cmd = NULL, *errmsg = NULL;
+	int err_code = 0;
+
+	if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
+		err_code |= ERR_WARN;
+
+	if (alertif_too_many_args_idx(2, 1, file, line, args, &err_code))
+		goto out;
+
+	curpx->options2 &= ~PR_O2_CHK_ANY;
+	curpx->options2 |= PR_O2_TCPCHK_CHK;
+
+	free_tcpcheck_vars(&rules->preset_vars);
+	rules->list  = NULL;
+	rules->flags = 0;
+
+	cur_arg += 2;
+	if (*args[cur_arg] && *args[cur_arg+1] &&
+	    (strcmp(args[cur_arg], "EHLO") == 0 || strcmp(args[cur_arg], "HELO") == 0)) {
+		/* <EHLO|HELO> + space (1) + <host> + null byte (1) */
+		cmd = calloc(strlen(args[cur_arg]) + 1 + strlen(args[cur_arg+1]) + 1, sizeof(*cmd));
+		if (cmd)
+			sprintf(cmd, "%s %s", args[cur_arg], args[cur_arg+1]);
+	}
+	else {
+		/* this just hits the default for now, but you could potentially expand it to allow for other stuff
+		   though, it's unlikely you'd want to send anything other than an EHLO or HELO */
+		cmd = strdup("HELO localhost");
+	}
+
+	var = create_tcpcheck_var(ist("check.smtp_cmd"));
+	if (cmd == NULL || var == NULL) {
+		ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
+		goto error;
+	}
+	var->data.type = SMP_T_STR;
+	var->data.u.str.area = cmd;
+	var->data.u.str.data = strlen(cmd);
+	LIST_INIT(&var->list);
+	LIST_ADDQ(&rules->preset_vars, &var->list);
+	cmd = NULL;
+	var = NULL;
+
+	rs = find_tcpcheck_ruleset("*smtp-check");
+	if (rs)
+		goto ruleset_found;
+
+	rs = create_tcpcheck_ruleset("*smtp-check");
+	if (rs == NULL) {
+		ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
+		goto error;
+	}
+
+	chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
+				     1, curpx, &rs->rules, file, line, &errmsg);
+	if (!chk) {
+		ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
+		goto error;
+	}
+	chk->index = 0;
+	LIST_ADDQ(&rs->rules, &chk->list);
+
+	chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^[0-9]{3}[ \r]",
+				               "min-recv", "4",
+				               "error-status", "L7RSP",
+				               "on-error", "%[res.payload(0,0),cut_crlf]",
+				               ""},
+		                    1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
+	if (!chk) {
+		ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
+		goto error;
+	}
+	chk->index = 1;
+	LIST_ADDQ(&rs->rules, &chk->list);
+
+	chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^2[0-9]{2}[ \r]",
+				               "min-recv", "4",
+				               "error-status", "L7STS",
+				               "on-error", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
+				               "status-code", "res.payload(0,3)",
+				               ""},
+		                    1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
+	if (!chk) {
+		ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
+		goto error;
+	}
+	chk->index = 2;
+	LIST_ADDQ(&rs->rules, &chk->list);
+
+	chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-lf", smtp_req, ""},
+				  1, curpx, &rs->rules, file, line, &errmsg);
+	if (!chk) {
+		ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
+		goto error;
+	}
+	chk->index = 3;
+	LIST_ADDQ(&rs->rules, &chk->list);
+
+	chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^2[0-9]{2}[- \r]",
+				               "min-recv", "4",
+				               "error-status", "L7STS",
+				               "on-error", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
+				               "on-success", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
+				               "status-code", "res.payload(0,3)",
+				               ""},
+		                    1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
+	if (!chk) {
+		ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
+		goto error;
+	}
+	chk->index = 4;
+	LIST_ADDQ(&rs->rules, &chk->list);
+
+  ruleset_found:
+	rules->list = &rs->rules;
+	rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
+	rules->flags |= TCPCHK_RULES_SMTP_CHK;
+
+  out:
+	free(errmsg);
+	return err_code;
+
+  error:
+	free(cmd);
+	free(var);
+	free_tcpcheck_vars(&rules->preset_vars);
+	free_tcpcheck_ruleset(rs);
+	err_code |= ERR_ALERT | ERR_FATAL;
+	goto out;
+}
+
+/* Parses the "option pgsql-check" proxy keyword */
+int proxy_parse_pgsql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
+				const char *file, int line)
+{
+	static char pgsql_req[] = {
+		"%[var(check.plen),htonl,hex]" /* The packet length*/
+		"00030000"                     /* the version 3.0 */
+		"7573657200"                   /* "user" key */
+		"%[var(check.username),hex]00" /* the username */
+		"00"
+	};
+
+	struct tcpcheck_ruleset *rs = NULL;
+	struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
+	struct tcpcheck_rule *chk;
+	struct tcpcheck_var *var = NULL;
+	char *user = NULL, *errmsg = NULL;
+	size_t packetlen = 0;
+	int err_code = 0;
+
+	if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
+		err_code |= ERR_WARN;
+
+	if (alertif_too_many_args_idx(2, 1, file, line, args, &err_code))
+		goto out;
+
+	curpx->options2 &= ~PR_O2_CHK_ANY;
+	curpx->options2 |= PR_O2_TCPCHK_CHK;
+
+	free_tcpcheck_vars(&rules->preset_vars);
+	rules->list  = NULL;
+	rules->flags = 0;
+
+	cur_arg += 2;
+	if (!*args[cur_arg] || !*args[cur_arg+1]) {
+		ha_alert("parsing [%s:%d] : '%s %s' expects 'user <username>' as argument.\n",
+			 file, line, args[0], args[1]);
+		goto error;
+	}
+	if (strcmp(args[cur_arg], "user") == 0) {
+		packetlen = 15 + strlen(args[cur_arg+1]);
+		user = strdup(args[cur_arg+1]);
+
+		var = create_tcpcheck_var(ist("check.username"));
+		if (user == NULL || var == NULL) {
+			ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
+			goto error;
+		}
+		var->data.type = SMP_T_STR;
+		var->data.u.str.area = user;
+		var->data.u.str.data = strlen(user);
+		LIST_INIT(&var->list);
+		LIST_ADDQ(&rules->preset_vars, &var->list);
+		user = NULL;
+		var = NULL;
+
+		var = create_tcpcheck_var(ist("check.plen"));
+		if (var == NULL) {
+			ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
+			goto error;
+		}
+		var->data.type = SMP_T_SINT;
+		var->data.u.sint = packetlen;
+		LIST_INIT(&var->list);
+		LIST_ADDQ(&rules->preset_vars, &var->list);
+		var = NULL;
+	}
+	else {
+		ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'user'.\n",
+			 file, line, args[0], args[1]);
+		goto error;
+	}
+
+	rs = find_tcpcheck_ruleset("*pgsql-check");
+	if (rs)
+		goto ruleset_found;
+
+	rs = create_tcpcheck_ruleset("*pgsql-check");
+	if (rs == NULL) {
+		ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
+		goto error;
+	}
+
+	chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
+				     1, curpx, &rs->rules, file, line, &errmsg);
+	if (!chk) {
+		ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
+		goto error;
+	}
+	chk->index = 0;
+	LIST_ADDQ(&rs->rules, &chk->list);
+
+	chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", pgsql_req, ""},
+				  1, curpx, &rs->rules, file, line, &errmsg);
+	if (!chk) {
+		ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
+		goto error;
+	}
+	chk->index = 1;
+	LIST_ADDQ(&rs->rules, &chk->list);
+
+	chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "!rstring", "^E",
+				               "min-recv", "5",
+				               "error-status", "L7RSP",
+				               "on-error", "%[res.payload(6,0)]",
+				               ""},
+		                    1, curpx, &rs->rules, TCPCHK_RULES_PGSQL_CHK, file, line, &errmsg);
+	if (!chk) {
+		ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
+		goto error;
+	}
+	chk->index = 2;
+	LIST_ADDQ(&rs->rules, &chk->list);
+
+	chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^52000000(08|0A|0C)000000(00|02|03|04|05|06)",
+				               "min-recv", "9",
+				               "error-status", "L7STS",
+				               "on-success", "PostgreSQL server is ok",
+				               "on-error",   "PostgreSQL unknown error",
+				               ""},
+		                    1, curpx, &rs->rules, TCPCHK_RULES_PGSQL_CHK, file, line, &errmsg);
+	if (!chk) {
+		ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
+		goto error;
+	}
+	chk->index = 3;
+	LIST_ADDQ(&rs->rules, &chk->list);
+
+  ruleset_found:
+	rules->list = &rs->rules;
+	rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
+	rules->flags |= TCPCHK_RULES_PGSQL_CHK;
+
+  out:
+	free(errmsg);
+	return err_code;
+
+  error:
+	free(user);
+	free(var);
+	free_tcpcheck_vars(&rules->preset_vars);
+	free_tcpcheck_ruleset(rs);
+	err_code |= ERR_ALERT | ERR_FATAL;
+	goto out;
+}
+
+
+/* Parses the "option mysql-check" proxy keyword */
+int proxy_parse_mysql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
+				const char *file, int line)
+{
+	/* This is an example of a MySQL >=4.0 client Authentication packet kindly provided by Cyril Bonte.
+	 * const char mysql40_client_auth_pkt[] = {
+	 * 	"\x0e\x00\x00"	// packet length
+	 * 	"\x01"		// packet number
+	 * 	"\x00\x00"	// client capabilities
+	 * 	"\x00\x00\x01"	// max packet
+	 * 	"haproxy\x00"	// username (null terminated string)
+	 * 	"\x00"		// filler (always 0x00)
+	 * 	"\x01\x00\x00"	// packet length
+	 * 	"\x00"		// packet number
+	 * 	"\x01"		// COM_QUIT command
+	 * };
+	 */
+	static char mysql40_rsname[] = "*mysql40-check";
+	static char mysql40_req[] = {
+		"%[var(check.header),hex]"     /* 3 bytes for the packet length and 1 byte for the sequence ID */
+		"0080"                         /* client capabilities */
+		"000001"                       /* max packet */
+		"%[var(check.username),hex]00" /* the username */
+		"00"                           /* filler (always 0x00) */
+		"010000"                       /* packet length*/
+		"00"                           /* sequence ID */
+		"01"                           /* COM_QUIT command */
+	};
+
+	/* This is an example of a MySQL >=4.1  client Authentication packet provided by Nenad Merdanovic.
+	 * const char mysql41_client_auth_pkt[] = {
+	 * 	"\x0e\x00\x00\"		// packet length
+	 * 	"\x01"			// packet number
+	 * 	"\x00\x00\x00\x00"	// client capabilities
+	 * 	"\x00\x00\x00\x01"	// max packet
+	 *	"\x21"			// character set (UTF-8)
+	 *	char[23]		// All zeroes
+	 * 	"haproxy\x00"		// username (null terminated string)
+	 * 	"\x00"			// filler (always 0x00)
+	 * 	"\x01\x00\x00"		// packet length
+	 * 	"\x00"			// packet number
+	 * 	"\x01"			// COM_QUIT command
+	 * };
+	 */
+	static char mysql41_rsname[] = "*mysql41-check";
+	static char mysql41_req[] = {
+		"%[var(check.header),hex]"     /* 3 bytes for the packet length and 1 byte for the sequence ID */
+		"00820000"                     /* client capabilities */
+		"00800001"                     /* max packet */
+		"21"                           /* character set (UTF-8) */
+		"000000000000000000000000"     /* 23 bytes, al zeroes */
+		"0000000000000000000000"
+		"%[var(check.username),hex]00" /* the username */
+		"00"                           /* filler (always 0x00) */
+		"010000"                       /* packet length*/
+		"00"                           /* sequence ID */
+		"01"                           /* COM_QUIT command */
+	};
+
+	struct tcpcheck_ruleset *rs = NULL;
+	struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
+	struct tcpcheck_rule *chk;
+	struct tcpcheck_var *var = NULL;
+	char *mysql_rsname = "*mysql-check";
+	char *mysql_req = NULL, *hdr = NULL, *user = NULL, *errmsg = NULL;
+	int index = 0, err_code = 0;
+
+	if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
+		err_code |= ERR_WARN;
+
+	if (alertif_too_many_args_idx(3, 1, file, line, args, &err_code))
+		goto out;
+
+	curpx->options2 &= ~PR_O2_CHK_ANY;
+	curpx->options2 |= PR_O2_TCPCHK_CHK;
+
+	free_tcpcheck_vars(&rules->preset_vars);
+	rules->list  = NULL;
+	rules->flags = 0;
+
+	cur_arg += 2;
+	if (*args[cur_arg]) {
+		int packetlen, userlen;
+
+		if (strcmp(args[cur_arg], "user") != 0) {
+			ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'user' (got '%s').\n",
+				 file, line, args[0], args[1], args[cur_arg]);
+			goto error;
+		}
+
+		if (*(args[cur_arg+1]) == 0) {
+			ha_alert("parsing [%s:%d] : '%s %s %s' expects <username> as argument.\n",
+				 file, line, args[0], args[1], args[cur_arg]);
+			goto error;
+		}
+
+		hdr     = calloc(4, sizeof(*hdr));
+		user    = strdup(args[cur_arg+1]);
+		userlen = strlen(args[cur_arg+1]);
+
+		if (hdr == NULL || user == NULL) {
+			ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
+			goto error;
+		}
+
+		if (!*args[cur_arg+2] || strcmp(args[cur_arg+2], "post-41") == 0) {
+			packetlen = userlen + 7 + 27;
+			mysql_req = mysql41_req;
+			mysql_rsname  = mysql41_rsname;
+		}
+		else if (strcmp(args[cur_arg+2], "pre-41") == 0) {
+			packetlen = userlen + 7;
+			mysql_req = mysql40_req;
+			mysql_rsname  = mysql40_rsname;
+		}
+		else  {
+			ha_alert("parsing [%s:%d] : keyword '%s' only supports 'post-41' and 'pre-41' (got '%s').\n",
+				 file, line, args[cur_arg], args[cur_arg+2]);
+			goto error;
+		}
+
+		hdr[0] = (unsigned char)(packetlen & 0xff);
+		hdr[1] = (unsigned char)((packetlen >> 8) & 0xff);
+		hdr[2] = (unsigned char)((packetlen >> 16) & 0xff);
+		hdr[3] = 1;
+
+		var = create_tcpcheck_var(ist("check.header"));
+		if (var == NULL) {
+			ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
+			goto error;
+		}
+		var->data.type = SMP_T_STR;
+		var->data.u.str.area = hdr;
+		var->data.u.str.data = 4;
+		LIST_INIT(&var->list);
+		LIST_ADDQ(&rules->preset_vars, &var->list);
+		hdr = NULL;
+		var = NULL;
+
+		var = create_tcpcheck_var(ist("check.username"));
+		if (var == NULL) {
+			ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
+			goto error;
+		}
+		var->data.type = SMP_T_STR;
+		var->data.u.str.area = user;
+		var->data.u.str.data = strlen(user);
+		LIST_INIT(&var->list);
+		LIST_ADDQ(&rules->preset_vars, &var->list);
+		user = NULL;
+		var = NULL;
+	}
+
+	rs = find_tcpcheck_ruleset(mysql_rsname);
+	if (rs)
+		goto ruleset_found;
+
+	rs = create_tcpcheck_ruleset(mysql_rsname);
+	if (rs == NULL) {
+		ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
+		goto error;
+	}
+
+	chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
+				     1, curpx, &rs->rules, file, line, &errmsg);
+	if (!chk) {
+		ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
+		goto error;
+	}
+	chk->index = index++;
+	LIST_ADDQ(&rs->rules, &chk->list);
+
+	if (mysql_req) {
+		chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", mysql_req, ""},
+					  1, curpx, &rs->rules, file, line, &errmsg);
+		if (!chk) {
+			ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
+			goto error;
+		}
+		chk->index = index++;
+		LIST_ADDQ(&rs->rules, &chk->list);
+	}
+
+	chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
+		                    1, curpx, &rs->rules, TCPCHK_RULES_MYSQL_CHK, file, line, &errmsg);
+	if (!chk) {
+		ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
+		goto error;
+	}
+	chk->expect.custom = tcpcheck_mysql_expect_iniths;
+	chk->index = index++;
+	LIST_ADDQ(&rs->rules, &chk->list);
+
+	if (mysql_req) {
+		chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
+					    1, curpx, &rs->rules, TCPCHK_RULES_MYSQL_CHK, file, line, &errmsg);
+		if (!chk) {
+			ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
+			goto error;
+		}
+		chk->expect.custom = tcpcheck_mysql_expect_ok;
+		chk->index = index++;
+		LIST_ADDQ(&rs->rules, &chk->list);
+	}
+
+  ruleset_found:
+	rules->list = &rs->rules;
+	rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
+	rules->flags |= TCPCHK_RULES_MYSQL_CHK;
+
+  out:
+	free(errmsg);
+	return err_code;
+
+  error:
+	free(hdr);
+	free(user);
+	free(var);
+	free_tcpcheck_vars(&rules->preset_vars);
+	free_tcpcheck_ruleset(rs);
+	err_code |= ERR_ALERT | ERR_FATAL;
+	goto out;
+}
+
+int proxy_parse_ldap_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
+			       const char *file, int line)
+{
+	static char *ldap_req = "300C020101600702010304008000";
+
+	struct tcpcheck_ruleset *rs = NULL;
+	struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
+	struct tcpcheck_rule *chk;
+	char *errmsg = NULL;
+	int err_code = 0;
+
+	if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
+		err_code |= ERR_WARN;
+
+	if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
+		goto out;
+
+	curpx->options2 &= ~PR_O2_CHK_ANY;
+	curpx->options2 |= PR_O2_TCPCHK_CHK;
+
+	free_tcpcheck_vars(&rules->preset_vars);
+	rules->list  = NULL;
+	rules->flags = 0;
+
+	rs = find_tcpcheck_ruleset("*ldap-check");
+	if (rs)
+		goto ruleset_found;
+
+	rs = create_tcpcheck_ruleset("*ldap-check");
+	if (rs == NULL) {
+		ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
+		goto error;
+	}
+
+	chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", ldap_req, ""},
+				  1, curpx, &rs->rules, file, line, &errmsg);
+	if (!chk) {
+		ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
+		goto error;
+	}
+	chk->index = 0;
+	LIST_ADDQ(&rs->rules, &chk->list);
+
+	chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^30",
+				               "min-recv", "14",
+				               "on-error", "Not LDAPv3 protocol",
+				               ""},
+		                    1, curpx, &rs->rules, TCPCHK_RULES_LDAP_CHK, file, line, &errmsg);
+	if (!chk) {
+		ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
+		goto error;
+	}
+	chk->index = 1;
+	LIST_ADDQ(&rs->rules, &chk->list);
+
+	chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
+		                    1, curpx, &rs->rules, TCPCHK_RULES_LDAP_CHK, file, line, &errmsg);
+	if (!chk) {
+		ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
+		goto error;
+	}
+	chk->expect.custom = tcpcheck_ldap_expect_bindrsp;
+	chk->index = 2;
+	LIST_ADDQ(&rs->rules, &chk->list);
+
+  ruleset_found:
+	rules->list = &rs->rules;
+	rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
+	rules->flags |= TCPCHK_RULES_LDAP_CHK;
+
+  out:
+	free(errmsg);
+	return err_code;
+
+  error:
+	free_tcpcheck_ruleset(rs);
+	err_code |= ERR_ALERT | ERR_FATAL;
+	goto out;
+}
+
+int proxy_parse_spop_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
+			       const char *file, int line)
+{
+	struct tcpcheck_ruleset *rs = NULL;
+	struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
+	struct tcpcheck_rule *chk;
+	char *spop_req = NULL;
+	char *errmsg = NULL;
+	int spop_len = 0, err_code = 0;
+
+	if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
+		err_code |= ERR_WARN;
+
+	if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
+		goto out;
+
+	curpx->options2 &= ~PR_O2_CHK_ANY;
+	curpx->options2 |= PR_O2_TCPCHK_CHK;
+
+	free_tcpcheck_vars(&rules->preset_vars);
+	rules->list  = NULL;
+	rules->flags = 0;
+
+
+	rs = find_tcpcheck_ruleset("*spop-check");
+	if (rs)
+		goto ruleset_found;
+
+	rs = create_tcpcheck_ruleset("*spop-check");
+	if (rs == NULL) {
+		ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
+		goto error;
+	}
+
+	if (spoe_prepare_healthcheck_request(&spop_req, &spop_len) == -1) {
+		ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
+		goto error;
+	}
+	chunk_reset(&trash);
+	dump_binary(&trash, spop_req, spop_len);
+	trash.area[trash.data] = '\0';
+
+	chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", b_head(&trash), ""},
+				  1, curpx, &rs->rules, file, line, &errmsg);
+	if (!chk) {
+		ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
+		goto error;
+	}
+	chk->index = 0;
+	LIST_ADDQ(&rs->rules, &chk->list);
+
+	chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", "min-recv", "4", ""},
+		                    1, curpx, &rs->rules, TCPCHK_RULES_SPOP_CHK, file, line, &errmsg);
+	if (!chk) {
+		ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
+		goto error;
+	}
+	chk->expect.custom = tcpcheck_spop_expect_agenthello;
+	chk->index = 1;
+	LIST_ADDQ(&rs->rules, &chk->list);
+
+  ruleset_found:
+	rules->list = &rs->rules;
+	rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
+	rules->flags |= TCPCHK_RULES_SPOP_CHK;
+
+  out:
+	free(spop_req);
+	free(errmsg);
+	return err_code;
+
+  error:
+	free_tcpcheck_ruleset(rs);
+	err_code |= ERR_ALERT | ERR_FATAL;
+	goto out;
+}
+
+
+static struct tcpcheck_rule *proxy_parse_httpchk_req(char **args, int cur_arg, struct proxy *px, char **errmsg)
+{
+	struct tcpcheck_rule *chk = NULL;
+	struct tcpcheck_http_hdr *hdr = NULL;
+	char *meth = NULL, *uri = NULL, *vsn = NULL;
+	char *hdrs, *body;
+
+	hdrs = (*args[cur_arg+2] ? strstr(args[cur_arg+2], "\r\n") : NULL);
+	body = (*args[cur_arg+2] ? strstr(args[cur_arg+2], "\r\n\r\n") : NULL);
+	if (hdrs == body)
+		hdrs = NULL;
+	if (hdrs) {
+		*hdrs = '\0';
+		hdrs +=2;
+	}
+	if (body) {
+		*body = '\0';
+		body += 4;
+	}
+	if (hdrs || body) {
+		memprintf(errmsg, "hiding headers or body at the end of the version string is deprecated."
+			  " Please, consider to use 'http-check send' directive instead.");
+	}
+
+	chk = calloc(1, sizeof(*chk));
+	if (!chk) {
+		memprintf(errmsg, "out of memory");
+		goto error;
+	}
+	chk->action    = TCPCHK_ACT_SEND;
+	chk->send.type = TCPCHK_SEND_HTTP;
+	chk->send.http.flags |= TCPCHK_SND_HTTP_FROM_OPT;
+	chk->send.http.meth.meth = HTTP_METH_OPTIONS;
+	LIST_INIT(&chk->send.http.hdrs);
+
+	/* Copy the method, uri and version */
+	if (*args[cur_arg]) {
+		if (!*args[cur_arg+1])
+			uri = args[cur_arg];
+		else
+			meth = args[cur_arg];
+	}
+	if (*args[cur_arg+1])
+		uri = args[cur_arg+1];
+	if (*args[cur_arg+2])
+		vsn = args[cur_arg+2];
+
+	if (meth) {
+		chk->send.http.meth.meth = find_http_meth(meth, strlen(meth));
+		chk->send.http.meth.str.area = strdup(meth);
+		chk->send.http.meth.str.data = strlen(meth);
+		if (!chk->send.http.meth.str.area) {
+			memprintf(errmsg, "out of memory");
+			goto error;
+		}
+	}
+	if (uri) {
+		chk->send.http.uri = ist2(strdup(uri), strlen(uri));
+		if (!isttest(chk->send.http.uri)) {
+			memprintf(errmsg, "out of memory");
+			goto error;
+		}
+	}
+	if (vsn) {
+		chk->send.http.vsn = ist2(strdup(vsn), strlen(vsn));
+		if (!isttest(chk->send.http.vsn)) {
+			memprintf(errmsg, "out of memory");
+			goto error;
+		}
+	}
+
+	/* Copy the header */
+	if (hdrs) {
+		struct http_hdr tmp_hdrs[global.tune.max_http_hdr];
+		struct h1m h1m;
+		int i, ret;
+
+		/* Build and parse the request */
+		chunk_printf(&trash, "%s\r\n\r\n", hdrs);
+
+		h1m.flags = H1_MF_HDRS_ONLY;
+		ret = h1_headers_to_hdr_list(b_orig(&trash), b_tail(&trash),
+					     tmp_hdrs, sizeof(tmp_hdrs)/sizeof(tmp_hdrs[0]),
+					     &h1m, NULL);
+		if (ret <= 0) {
+			memprintf(errmsg, "unable to parse the request '%s'.", b_orig(&trash));
+			goto error;
+		}
+
+		for (i = 0; istlen(tmp_hdrs[i].n); i++) {
+			hdr = calloc(1, sizeof(*hdr));
+			if (!hdr) {
+				memprintf(errmsg, "out of memory");
+				goto error;
+			}
+			LIST_INIT(&hdr->value);
+			hdr->name = istdup(tmp_hdrs[i].n);
+			if (!hdr->name.ptr) {
+				memprintf(errmsg, "out of memory");
+				goto error;
+			}
+
+			ist0(tmp_hdrs[i].v);
+			if (!parse_logformat_string(istptr(tmp_hdrs[i].v), px, &hdr->value, 0, SMP_VAL_BE_CHK_RUL, errmsg))
+				goto error;
+			LIST_ADDQ(&chk->send.http.hdrs, &hdr->list);
+		}
+	}
+
+	/* Copy the body */
+	if (body) {
+		chk->send.http.body = ist2(strdup(body), strlen(body));
+		if (!isttest(chk->send.http.body)) {
+			memprintf(errmsg, "out of memory");
+			goto error;
+		}
+	}
+
+	return chk;
+
+  error:
+	free_tcpcheck_http_hdr(hdr);
+	free_tcpcheck(chk, 0);
+	return NULL;
+}
+
+/* Parses the "option httpchck" proxy keyword */
+int proxy_parse_httpchk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
+			    const char *file, int line)
+{
+	struct tcpcheck_ruleset *rs = NULL;
+	struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
+	struct tcpcheck_rule *chk;
+	char *errmsg = NULL;
+	int err_code = 0;
+
+	if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
+		err_code |= ERR_WARN;
+
+	if (alertif_too_many_args_idx(3, 1, file, line, args, &err_code))
+		goto out;
+
+	chk = proxy_parse_httpchk_req(args, cur_arg+2, curpx, &errmsg);
+	if (!chk) {
+		ha_alert("parsing [%s:%d] : '%s %s' : %s.\n", file, line, args[0], args[1], errmsg);
+		goto error;
+	}
+	if (errmsg) {
+		ha_warning("parsing [%s:%d]: '%s %s' : %s\n", file, line, args[0], args[1], errmsg);
+		err_code |= ERR_WARN;
+		free(errmsg);
+		errmsg = NULL;
+	}
+
+  no_request:
+	curpx->options2 &= ~PR_O2_CHK_ANY;
+	curpx->options2 |= PR_O2_TCPCHK_CHK;
+
+	free_tcpcheck_vars(&rules->preset_vars);
+	rules->list = NULL;
+	rules->flags |= TCPCHK_SND_HTTP_FROM_OPT;
+
+	/* Deduce the ruleset name from the proxy info */
+	chunk_printf(&trash, "*http-check-%s_%s-%d",
+		     ((curpx == defpx) ? "defaults" : curpx->id),
+		     curpx->conf.file, curpx->conf.line);
+
+	rs = find_tcpcheck_ruleset(b_orig(&trash));
+	if (rs == NULL) {
+		rs = create_tcpcheck_ruleset(b_orig(&trash));
+		if (rs == NULL) {
+			ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
+			goto error;
+		}
+	}
+
+	rules->list = &rs->rules;
+	rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
+	rules->flags |= TCPCHK_RULES_HTTP_CHK;
+	if (!tcpcheck_add_http_rule(chk, rules, &errmsg)) {
+		ha_alert("parsing [%s:%d] : '%s %s' : %s.\n", file, line, args[0], args[1], errmsg);
+		rules->list = NULL;
+		goto error;
+	}
+
+  out:
+	free(errmsg);
+	return err_code;
+
+  error:
+	free_tcpcheck_ruleset(rs);
+	free_tcpcheck(chk, 0);
+	err_code |= ERR_ALERT | ERR_FATAL;
+	goto out;
+}
+
+/* Parses the "option tcp-check" proxy keyword */
+int proxy_parse_tcp_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
+			      const char *file, int line)
+{
+	struct tcpcheck_ruleset *rs = NULL;
+	struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
+	int err_code = 0;
+
+	if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
+		err_code |= ERR_WARN;
+
+	if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
+		goto out;
+
+	curpx->options2 &= ~PR_O2_CHK_ANY;
+	curpx->options2 |= PR_O2_TCPCHK_CHK;
+
+	if ((rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK) {
+		/* If a tcp-check rulesset is already set, do nothing */
+		if (rules->list)
+			goto out;
+
+		/* If a tcp-check ruleset is waiting to be used for the current proxy,
+		 * get it.
+		 */
+		if (rules->flags & TCPCHK_RULES_UNUSED_TCP_RS)
+			goto curpx_ruleset;
+
+		/* Otherwise, try to get the tcp-check ruleset of the default proxy */
+		chunk_printf(&trash, "*tcp-check-defaults_%s-%d", defpx->conf.file, defpx->conf.line);
+		rs = find_tcpcheck_ruleset(b_orig(&trash));
+		if (rs)
+			goto ruleset_found;
+	}
+
+  curpx_ruleset:
+	/* Deduce the ruleset name from the proxy info */
+	chunk_printf(&trash, "*tcp-check-%s_%s-%d",
+		     ((curpx == defpx) ? "defaults" : curpx->id),
+		     curpx->conf.file, curpx->conf.line);
+
+	rs = find_tcpcheck_ruleset(b_orig(&trash));
+	if (rs == NULL) {
+		rs = create_tcpcheck_ruleset(b_orig(&trash));
+		if (rs == NULL) {
+			ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
+			goto error;
+		}
+	}
+
+  ruleset_found:
+	free_tcpcheck_vars(&rules->preset_vars);
+	rules->list = &rs->rules;
+	rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
+	rules->flags |= TCPCHK_RULES_TCP_CHK;
+
+  out:
+	return err_code;
+
+  error:
+	err_code |= ERR_ALERT | ERR_FATAL;
+	goto out;
+}
+
 static struct cfg_kw_list cfg_kws = {ILH, {
+        { CFG_LISTEN, "http-check",     proxy_parse_httpcheck },
         { CFG_LISTEN, "tcp-check",      proxy_parse_tcpcheck },
         { 0, NULL, NULL },
 }};