REORG: server: move keywords in srv_kws

Move server-keyword hardcoded in parse_server into the srv_kws list of
server.c. Now every server keywords is checked through srv_find_kw. This
has the effect to reduce the size of parse_server. As a side-effect,
common kw list can be reduced.

This change has been made to be able to quickly discard these keywords
in case of a dynamic server.
diff --git a/src/server.c b/src/server.c
index 7c08dd0..9a55d96 100644
--- a/src/server.c
+++ b/src/server.c
@@ -50,16 +50,12 @@
 static int srv_apply_lastaddr(struct server *srv, int *err_code);
 static void srv_cleanup_connections(struct server *srv);
 
-/* some keywords that are still being parsed using strcmp() and are not
- * registered anywhere. They are used as suggestions for mistyped words.
+/* extra keywords used as value for other arguments. They are used as
+ * suggestions for mistyped words.
  */
-static const char *common_kw_list[] = {
-	"init-addr", "resolvers", "resolve-opts", "resolve-prefer", "ipv4",
-	"ipv6", "resolve-net", "weight", "log-proto", "legacy", "octet-count",
-	"minconn", "maxconn", "maxqueue", "slowstart", "on-error", "fastinter",
-	"fail-check", "sudden-death", "mark-down", "on-marked-down",
-	"shutdown-sessions", "on-marked-up", "shutdown-backup-sessions",
-	"error-limit", "usesrc",
+static const char *extra_kw_list[] = {
+	"ipv4", "ipv6", "legacy", "octet-count",
+	"fastinter", "fail-check", "sudden-death", "mark-down",
 	NULL /* must be last */
 };
 
@@ -328,7 +324,7 @@
 		}
 	}
 
-	for (extra = common_kw_list; *extra; extra++) {
+	for (extra = extra_kw_list; *extra; extra++) {
 		make_word_fingerprint(list_sig, *extra);
 		dist = word_fingerprint_distance(word_sig, list_sig);
 		if (dist < best_dist) {
@@ -393,6 +389,119 @@
 	return 0;
 }
 
+/* Parse the "error-limit" server keyword */
+static int srv_parse_error_limit(char **args, int *cur_arg,
+                                 struct proxy *curproxy, struct server *newsrv, char **err)
+{
+	if (!*args[*cur_arg + 1]) {
+		memprintf(err, "'%s' expects an integer argument.",
+		          args[*cur_arg]);
+		return ERR_ALERT | ERR_FATAL;
+	}
+
+	newsrv->consecutive_errors_limit = atoi(args[*cur_arg + 1]);
+
+	if (newsrv->consecutive_errors_limit <= 0) {
+		memprintf(err, "%s has to be > 0.",
+		          args[*cur_arg]);
+		return ERR_ALERT | ERR_FATAL;
+	}
+
+	return 0;
+}
+
+/* Parse the "init-addr" server keyword */
+static int srv_parse_init_addr(char **args, int *cur_arg,
+                               struct proxy *curproxy, struct server *newsrv, char **err)
+{
+	char *p, *end;
+	int done;
+	struct sockaddr_storage sa;
+
+	newsrv->init_addr_methods = 0;
+	memset(&newsrv->init_addr, 0, sizeof(newsrv->init_addr));
+
+	for (p = args[*cur_arg + 1]; *p; p = end) {
+		/* cut on next comma */
+		for (end = p; *end && *end != ','; end++);
+		if (*end)
+			*(end++) = 0;
+
+		memset(&sa, 0, sizeof(sa));
+		if (strcmp(p, "libc") == 0) {
+			done = srv_append_initaddr(&newsrv->init_addr_methods, SRV_IADDR_LIBC);
+		}
+		else if (strcmp(p, "last") == 0) {
+			done = srv_append_initaddr(&newsrv->init_addr_methods, SRV_IADDR_LAST);
+		}
+		else if (strcmp(p, "none") == 0) {
+			done = srv_append_initaddr(&newsrv->init_addr_methods, SRV_IADDR_NONE);
+		}
+		else if (str2ip2(p, &sa, 0)) {
+			if (is_addr(&newsrv->init_addr)) {
+				memprintf(err, "'%s' : initial address already specified, cannot add '%s'.",
+				          args[*cur_arg], p);
+				return ERR_ALERT | ERR_FATAL;
+			}
+			newsrv->init_addr = sa;
+			done = srv_append_initaddr(&newsrv->init_addr_methods, SRV_IADDR_IP);
+		}
+		else {
+			memprintf(err, "'%s' : unknown init-addr method '%s', supported methods are 'libc', 'last', 'none'.",
+			          args[*cur_arg], p);
+			return ERR_ALERT | ERR_FATAL;
+		}
+		if (!done) {
+			memprintf(err, "'%s' : too many init-addr methods when trying to add '%s'",
+			          args[*cur_arg], p);
+			return ERR_ALERT | ERR_FATAL;
+		}
+	}
+
+	return 0;
+}
+
+/* Parse the "log-proto" server keyword */
+static int srv_parse_log_proto(char **args, int *cur_arg,
+                               struct proxy *curproxy, struct server *newsrv, char **err)
+{
+	if (strcmp(args[*cur_arg + 1], "legacy") == 0)
+		newsrv->log_proto = SRV_LOG_PROTO_LEGACY;
+	else if (strcmp(args[*cur_arg + 1], "octet-count") == 0)
+		newsrv->log_proto = SRV_LOG_PROTO_OCTET_COUNTING;
+	else {
+		memprintf(err, "'%s' expects one of 'legacy' or 'octet-count' but got '%s'",
+		          args[*cur_arg], args[*cur_arg + 1]);
+		return ERR_ALERT | ERR_FATAL;
+	}
+
+	return 0;
+}
+
+/* Parse the "maxconn" server keyword */
+static int srv_parse_maxconn(char **args, int *cur_arg,
+                             struct proxy *curproxy, struct server *newsrv, char **err)
+{
+	newsrv->maxconn = atol(args[*cur_arg + 1]);
+	return 0;
+}
+
+/* Parse the "maxqueue" server keyword */
+static int srv_parse_maxqueue(char **args, int *cur_arg,
+                              struct proxy *curproxy, struct server *newsrv, char **err)
+{
+	newsrv->maxqueue = atol(args[*cur_arg + 1]);
+	return 0;
+}
+
+/* Parse the "minconn" server keyword */
+static int srv_parse_minconn(char **args, int *cur_arg,
+                             struct proxy *curproxy, struct server *newsrv, char **err)
+{
+	newsrv->minconn = atol(args[*cur_arg + 1]);
+	return 0;
+}
+
 static int srv_parse_max_reuse(char **args, int *cur_arg, struct proxy *curproxy, struct server *newsrv, char **err)
 {
 	char *arg;
@@ -690,6 +799,58 @@
 	return 0;
 }
 
+/* Parse the "on-error" server keyword */
+static int srv_parse_on_error(char **args, int *cur_arg,
+                              struct proxy *curproxy, struct server *newsrv, char **err)
+{
+	if (strcmp(args[*cur_arg + 1], "fastinter") == 0)
+		newsrv->onerror = HANA_ONERR_FASTINTER;
+	else if (strcmp(args[*cur_arg + 1], "fail-check") == 0)
+		newsrv->onerror = HANA_ONERR_FAILCHK;
+	else if (strcmp(args[*cur_arg + 1], "sudden-death") == 0)
+		newsrv->onerror = HANA_ONERR_SUDDTH;
+	else if (strcmp(args[*cur_arg + 1], "mark-down") == 0)
+		newsrv->onerror = HANA_ONERR_MARKDWN;
+	else {
+		memprintf(err, "'%s' expects one of 'fastinter', "
+		          "'fail-check', 'sudden-death' or 'mark-down' but got '%s'",
+		          args[*cur_arg], args[*cur_arg + 1]);
+		return ERR_ALERT | ERR_FATAL;
+	}
+
+	return 0;
+}
+
+/* Parse the "on-marked-down" server keyword */
+static int srv_parse_on_marked_down(char **args, int *cur_arg,
+                                    struct proxy *curproxy, struct server *newsrv, char **err)
+{
+	if (strcmp(args[*cur_arg + 1], "shutdown-sessions") == 0)
+		newsrv->onmarkeddown = HANA_ONMARKEDDOWN_SHUTDOWNSESSIONS;
+	else {
+		memprintf(err, "'%s' expects 'shutdown-sessions' but got '%s'",
+		          args[*cur_arg], args[*cur_arg + 1]);
+		return ERR_ALERT | ERR_FATAL;
+	}
+
+	return 0;
+}
+
+/* Parse the "on-marked-up" server keyword */
+static int srv_parse_on_marked_up(char **args, int *cur_arg,
+                                  struct proxy *curproxy, struct server *newsrv, char **err)
+{
+	if (strcmp(args[*cur_arg + 1], "shutdown-backup-sessions") == 0)
+		newsrv->onmarkedup = HANA_ONMARKEDUP_SHUTDOWNBACKUPSESSIONS;
+	else {
+		memprintf(err, "'%s' expects 'shutdown-backup-sessions' but got '%s'",
+		          args[*cur_arg], args[*cur_arg + 1]);
+		return ERR_ALERT | ERR_FATAL;
+	}
+
+	return 0;
+}
+
 /* Parse the "redir" server keyword */
 static int srv_parse_redir(char **args, int *cur_arg,
                            struct proxy *curproxy, struct server *newsrv, char **err)
@@ -709,6 +870,120 @@
 	return 0;
 }
 
+/* Parse the "resolvers" server keyword */
+static int srv_parse_resolvers(char **args, int *cur_arg,
+                           struct proxy *curproxy, struct server *newsrv, char **err)
+{
+	free(newsrv->resolvers_id);
+	newsrv->resolvers_id = strdup(args[*cur_arg + 1]);
+	return 0;
+}
+
+/* Parse the "resolve-net" server keyword */
+static int srv_parse_resolve_net(char **args, int *cur_arg,
+                                 struct proxy *curproxy, struct server *newsrv, char **err)
+{
+	char *p, *e;
+	unsigned char mask;
+	struct resolv_options *opt;
+
+	if (!args[*cur_arg + 1] || args[*cur_arg + 1][0] == '\0') {
+		memprintf(err, "'%s' expects a list of networks.",
+		          args[*cur_arg]);
+		return ERR_ALERT | ERR_FATAL;
+	}
+
+	opt = &newsrv->resolv_opts;
+
+	/* Split arguments by comma, and convert it from ipv4 or ipv6
+	 * string network in in_addr or in6_addr.
+	 */
+	p = args[*cur_arg + 1];
+	e = p;
+	while (*p != '\0') {
+		/* If no room available, return error. */
+		if (opt->pref_net_nb >= SRV_MAX_PREF_NET) {
+			memprintf(err, "'%s' exceed %d networks.",
+			          args[*cur_arg], SRV_MAX_PREF_NET);
+			return ERR_ALERT | ERR_FATAL;
+		}
+		/* look for end or comma. */
+		while (*e != ',' && *e != '\0')
+			e++;
+		if (*e == ',') {
+			*e = '\0';
+			e++;
+		}
+		if (str2net(p, 0, &opt->pref_net[opt->pref_net_nb].addr.in4,
+		                  &opt->pref_net[opt->pref_net_nb].mask.in4)) {
+			/* Try to convert input string from ipv4 or ipv6 network. */
+			opt->pref_net[opt->pref_net_nb].family = AF_INET;
+		} else if (str62net(p, &opt->pref_net[opt->pref_net_nb].addr.in6,
+		                     &mask)) {
+			/* Try to convert input string from ipv6 network. */
+			len2mask6(mask, &opt->pref_net[opt->pref_net_nb].mask.in6);
+			opt->pref_net[opt->pref_net_nb].family = AF_INET6;
+		} else {
+			/* All network conversions fail, return error. */
+			memprintf(err, "'%s' invalid network '%s'.",
+			          args[*cur_arg], p);
+			return ERR_ALERT | ERR_FATAL;
+		}
+		opt->pref_net_nb++;
+		p = e;
+	}
+
+	return 0;
+}
+
+/* Parse the "resolve-opts" server keyword */
+static int srv_parse_resolve_opts(char **args, int *cur_arg,
+                                  struct proxy *curproxy, struct server *newsrv, char **err)
+{
+	char *p, *end;
+
+	for (p = args[*cur_arg + 1]; *p; p = end) {
+		/* cut on next comma */
+		for (end = p; *end && *end != ','; end++);
+		if (*end)
+			*(end++) = 0;
+
+		if (strcmp(p, "allow-dup-ip") == 0) {
+			newsrv->resolv_opts.accept_duplicate_ip = 1;
+		}
+		else if (strcmp(p, "ignore-weight") == 0) {
+			newsrv->resolv_opts.ignore_weight = 1;
+		}
+		else if (strcmp(p, "prevent-dup-ip") == 0) {
+			newsrv->resolv_opts.accept_duplicate_ip = 0;
+		}
+		else {
+			memprintf(err, "'%s' : unknown resolve-opts option '%s', supported methods are 'allow-dup-ip', 'ignore-weight', and 'prevent-dup-ip'.",
+			          args[*cur_arg], p);
+			return ERR_ALERT | ERR_FATAL;
+		}
+	}
+
+	return 0;
+}
+
+/* Parse the "resolve-prefer" server keyword */
+static int srv_parse_resolve_prefer(char **args, int *cur_arg,
+                                    struct proxy *curproxy, struct server *newsrv, char **err)
+{
+	if (strcmp(args[*cur_arg + 1], "ipv4") == 0)
+		newsrv->resolv_opts.family_prio = AF_INET;
+	else if (strcmp(args[*cur_arg + 1], "ipv6") == 0)
+		newsrv->resolv_opts.family_prio = AF_INET6;
+	else {
+		memprintf(err, "'%s' expects either ipv4 or ipv6 as argument.",
+		          args[*cur_arg]);
+		return ERR_ALERT | ERR_FATAL;
+	}
+
+	return 0;
+}
+
 /* Parse the "send-proxy" server keyword */
 static int srv_parse_send_proxy(char **args, int *cur_arg,
                                 struct proxy *curproxy, struct server *newsrv, char **err)
@@ -723,6 +998,33 @@
 	return srv_enable_pp_flags(newsrv, SRV_PP_V2);
 }
 
+/* Parse the "slowstart" server keyword */
+static int srv_parse_slowstart(char **args, int *cur_arg,
+                               struct proxy *curproxy, struct server *newsrv, char **err)
+{
+	/* slowstart is stored in seconds */
+	unsigned int val;
+	const char *time_err = parse_time_err(args[*cur_arg + 1], &val, TIME_UNIT_MS);
+
+	if (time_err == PARSE_TIME_OVER) {
+		memprintf(err, "overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
+		          args[*cur_arg+1], args[*cur_arg], newsrv->id);
+		return ERR_ALERT | ERR_FATAL;
+	}
+	else if (time_err == PARSE_TIME_UNDER) {
+		memprintf(err, "underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
+		          args[*cur_arg+1], args[*cur_arg], newsrv->id);
+		return ERR_ALERT | ERR_FATAL;
+	}
+	else if (time_err) {
+		memprintf(err, "unexpected character '%c' in 'slowstart' argument of server %s.",
+		          *time_err, newsrv->id);
+		return ERR_ALERT | ERR_FATAL;
+	}
+	newsrv->slowstart = (val + 999) / 1000;
+
+	return 0;
+}
 
 /* Parse the "source" server keyword */
 static int srv_parse_source(char **args, int *cur_arg,
@@ -937,6 +1239,30 @@
 	return 0;
 }
 
+/* parse the "usesrc" server keyword */
+static int srv_parse_usesrc(char **args, int *cur_arg, struct proxy *px, struct server *newsrv, char **err)
+{
+	memprintf(err, "'%s' only allowed after a '%s' statement.",
+	          "usesrc", "source");
+	return ERR_ALERT | ERR_FATAL;
+}
+
+/* parse the "weight" server keyword */
+static int srv_parse_weight(char **args, int *cur_arg, struct proxy *px, struct server *newsrv, char **err)
+{
+	int w;
+
+	w = atol(args[*cur_arg + 1]);
+	if (w < 0 || w > SRV_UWGHT_MAX) {
+		memprintf(err, "weight of server %s is not within 0 and %d (%d).",
+		          newsrv->id, SRV_UWGHT_MAX, w);
+		return ERR_ALERT | ERR_FATAL;
+	}
+	newsrv->uweight = newsrv->iweight = w;
+
+	return 0;
+}
+
 /* Shutdown all connections of a server. The caller must pass a termination
  * code in <why>, which must be one of SF_ERR_* indicating the reason for the
  * shutdown.
@@ -1304,8 +1630,14 @@
 	{ "cookie",              srv_parse_cookie,              1,  1 }, /* Assign a cookie to the server */
 	{ "disabled",            srv_parse_disabled,            0,  1 }, /* Start the server in 'disabled' state */
 	{ "enabled",             srv_parse_enabled,             0,  1 }, /* Start the server in 'enabled' state */
+	{ "error-limit",         srv_parse_error_limit,         1,  1 }, /* Configure the consecutive count of check failures to consider a server on error */
 	{ "id",                  srv_parse_id,                  1,  0 }, /* set id# of server */
+	{ "init-addr",           srv_parse_init_addr,           1,  1 }, /* */
+	{ "log-proto",           srv_parse_log_proto,           1,  1 }, /* Set the protocol for event messages, only relevant in a ring section */
+	{ "maxconn",             srv_parse_maxconn,             1,  1 }, /* Set the max number of concurrent connection */
+	{ "maxqueue",            srv_parse_maxqueue,            1,  1 }, /* Set the max number of connection to put in queue */
 	{ "max-reuse",           srv_parse_max_reuse,           1,  1 }, /* Set the max number of requests on a connection, -1 means unlimited */
+	{ "minconn",             srv_parse_minconn,             1,  1 }, /* Enable a dynamic maxconn limit */
 	{ "namespace",           srv_parse_namespace,           1,  1 }, /* Namespace the server socket belongs to (if supported) */
 	{ "no-backup",           srv_parse_no_backup,           0,  1 }, /* Flag as non-backup server */
 	{ "no-send-proxy",       srv_parse_no_send_proxy,       0,  1 }, /* Disable use of PROXY V1 protocol */
@@ -1313,19 +1645,29 @@
 	{ "no-tfo",              srv_parse_no_tfo,              0,  1 }, /* Disable use of TCP Fast Open */
 	{ "non-stick",           srv_parse_non_stick,           0,  1 }, /* Disable stick-table persistence */
 	{ "observe",             srv_parse_observe,             1,  1 }, /* Enables health adjusting based on observing communication with the server */
+	{ "on-error",            srv_parse_on_error,            1,  1 }, /* Configure the action on check failure */
+	{ "on-marked-down",      srv_parse_on_marked_down,      1,  1 }, /* Configure the action when a server is marked down */
+	{ "on-marked-up",        srv_parse_on_marked_up,        1,  1 }, /* Configure the action when a server is marked up */
 	{ "pool-low-conn",       srv_parse_pool_low_conn,       1,  1 }, /* Set the min number of orphan idle connecbefore being allowed to pick from other threads */
 	{ "pool-max-conn",       srv_parse_pool_max_conn,       1,  1 }, /* Set the max number of orphan idle connections, -1 means unlimited */
 	{ "pool-purge-delay",    srv_parse_pool_purge_delay,    1,  1 }, /* Set the time before we destroy orphan idle connections, defaults to 1s */
 	{ "proto",               srv_parse_proto,               1,  1 }, /* Set the proto to use for all outgoing connections */
 	{ "proxy-v2-options",    srv_parse_proxy_v2_options,    1,  1 }, /* options for send-proxy-v2 */
 	{ "redir",               srv_parse_redir,               1,  1 }, /* Enable redirection mode */
+	{ "resolve-net",         srv_parse_resolve_net,         1,  1 }, /* Set the prefered network range for name resolution */
+	{ "resolve-opts",        srv_parse_resolve_opts,        1,  1 }, /* Set options for name resolution */
+	{ "resolve-prefer",      srv_parse_resolve_prefer,      1,  1 }, /* Set the prefered family for name resolution */
+	{ "resolvers",           srv_parse_resolvers,           1,  1 }, /* Configure the resolver to use for name resolution */
 	{ "send-proxy",          srv_parse_send_proxy,          0,  1 }, /* Enforce use of PROXY V1 protocol */
 	{ "send-proxy-v2",       srv_parse_send_proxy_v2,       0,  1 }, /* Enforce use of PROXY V2 protocol */
+	{ "slowstart",           srv_parse_slowstart,           1,  1 }, /* Set the warm-up timer for a previously failed server */
 	{ "source",              srv_parse_source,             -1,  1 }, /* Set the source address to be used to connect to the server */
 	{ "stick",               srv_parse_stick,               0,  1 }, /* Enable stick-table persistence */
 	{ "tfo",                 srv_parse_tfo,                 0,  1 }, /* enable TCP Fast Open of server */
 	{ "track",               srv_parse_track,               1,  1 }, /* Set the current state of the server, tracking another one */
 	{ "socks4",              srv_parse_socks4,              1,  1 }, /* Set the socks4 proxy of the server*/
+	{ "usesrc",              srv_parse_usesrc,              0,  1 }, /* safe-guard against usesrc without preceding <source> keyword */
+	{ "weight",              srv_parse_weight,              1,  1 }, /* Set the load-balancing weight */
 	{ NULL, NULL, 0 },
 }};
 
@@ -2028,7 +2370,6 @@
 	const char *err = NULL;
 	char *errmsg = NULL;
 	int err_code = 0;
-	unsigned val;
 	char *fqdn = NULL;
 
 	if (strcmp(args[0], "server") == 0         ||
@@ -2198,343 +2539,60 @@
 		}
 
 		while (*args[cur_arg]) {
-			if (strcmp(args[cur_arg], "init-addr") == 0) {
-				char *p, *end;
-				int done;
-				struct sockaddr_storage sa;
-
-				newsrv->init_addr_methods = 0;
-				memset(&newsrv->init_addr, 0, sizeof(newsrv->init_addr));
-
-				for (p = args[cur_arg + 1]; *p; p = end) {
-					/* cut on next comma */
-					for (end = p; *end && *end != ','; end++);
-					if (*end)
-						*(end++) = 0;
-
-					memset(&sa, 0, sizeof(sa));
-					if (strcmp(p, "libc") == 0) {
-						done = srv_append_initaddr(&newsrv->init_addr_methods, SRV_IADDR_LIBC);
-					}
-					else if (strcmp(p, "last") == 0) {
-						done = srv_append_initaddr(&newsrv->init_addr_methods, SRV_IADDR_LAST);
-					}
-					else if (strcmp(p, "none") == 0) {
-						done = srv_append_initaddr(&newsrv->init_addr_methods, SRV_IADDR_NONE);
-					}
-					else if (str2ip2(p, &sa, 0)) {
-						if (is_addr(&newsrv->init_addr)) {
-							ha_alert("parsing [%s:%d]: '%s' : initial address already specified, cannot add '%s'.\n",
-							      file, linenum, args[cur_arg], p);
-							err_code |= ERR_ALERT | ERR_FATAL;
-							goto out;
-						}
-						newsrv->init_addr = sa;
-						done = srv_append_initaddr(&newsrv->init_addr_methods, SRV_IADDR_IP);
-					}
-					else {
-						ha_alert("parsing [%s:%d]: '%s' : unknown init-addr method '%s', supported methods are 'libc', 'last', 'none'.\n",
-							file, linenum, args[cur_arg], p);
-						err_code |= ERR_ALERT | ERR_FATAL;
-						goto out;
-					}
-					if (!done) {
-						ha_alert("parsing [%s:%d]: '%s' : too many init-addr methods when trying to add '%s'\n",
-							file, linenum, args[cur_arg], p);
-						err_code |= ERR_ALERT | ERR_FATAL;
-						goto out;
-					}
-				}
-				cur_arg += 2;
-			}
-			else if (strcmp(args[cur_arg], "resolvers") == 0) {
-				free(newsrv->resolvers_id);
-				newsrv->resolvers_id = strdup(args[cur_arg + 1]);
-				cur_arg += 2;
-			}
-			else if (strcmp(args[cur_arg], "resolve-opts") == 0) {
-				char *p, *end;
-
-				for (p = args[cur_arg + 1]; *p; p = end) {
-					/* cut on next comma */
-					for (end = p; *end && *end != ','; end++);
-					if (*end)
-						*(end++) = 0;
-
-					if (strcmp(p, "allow-dup-ip") == 0) {
-						newsrv->resolv_opts.accept_duplicate_ip = 1;
-					}
-					else if (strcmp(p, "ignore-weight") == 0) {
-						newsrv->resolv_opts.ignore_weight = 1;
-					}
-					else if (strcmp(p, "prevent-dup-ip") == 0) {
-						newsrv->resolv_opts.accept_duplicate_ip = 0;
-					}
-					else {
-						ha_alert("parsing [%s:%d]: '%s' : unknown resolve-opts option '%s', supported methods are 'allow-dup-ip', 'ignore-weight', and 'prevent-dup-ip'.\n",
-								file, linenum, args[cur_arg], p);
-						err_code |= ERR_ALERT | ERR_FATAL;
-						goto out;
-					}
-				}
-
-				cur_arg += 2;
-			}
-			else if (strcmp(args[cur_arg], "resolve-prefer") == 0) {
-				if (strcmp(args[cur_arg + 1], "ipv4") == 0)
-					newsrv->resolv_opts.family_prio = AF_INET;
-				else if (strcmp(args[cur_arg + 1], "ipv6") == 0)
-					newsrv->resolv_opts.family_prio = AF_INET6;
-				else {
-					ha_alert("parsing [%s:%d]: '%s' expects either ipv4 or ipv6 as argument.\n",
-						file, linenum, args[cur_arg]);
-					err_code |= ERR_ALERT | ERR_FATAL;
-					goto out;
-				}
-				cur_arg += 2;
-			}
-			else if (strcmp(args[cur_arg], "resolve-net") == 0) {
-				char *p, *e;
-				unsigned char mask;
-				struct resolv_options *opt;
-
-				if (!args[cur_arg + 1] || args[cur_arg + 1][0] == '\0') {
-					ha_alert("parsing [%s:%d]: '%s' expects a list of networks.\n",
-					      file, linenum, args[cur_arg]);
-					err_code |= ERR_ALERT | ERR_FATAL;
-					goto out;
-				}
-
-				opt = &newsrv->resolv_opts;
-
-				/* Split arguments by comma, and convert it from ipv4 or ipv6
-				 * string network in in_addr or in6_addr.
-				 */
-				p = args[cur_arg + 1];
-				e = p;
-				while (*p != '\0') {
-					/* If no room available, return error. */
-					if (opt->pref_net_nb >= SRV_MAX_PREF_NET) {
-						ha_alert("parsing [%s:%d]: '%s' exceed %d networks.\n",
-						      file, linenum, args[cur_arg], SRV_MAX_PREF_NET);
-						err_code |= ERR_ALERT | ERR_FATAL;
-						goto out;
-					}
-					/* look for end or comma. */
-					while (*e != ',' && *e != '\0')
-						e++;
-					if (*e == ',') {
-						*e = '\0';
-						e++;
-					}
-					if (str2net(p, 0, &opt->pref_net[opt->pref_net_nb].addr.in4,
-					                  &opt->pref_net[opt->pref_net_nb].mask.in4)) {
-						/* Try to convert input string from ipv4 or ipv6 network. */
-						opt->pref_net[opt->pref_net_nb].family = AF_INET;
-					} else if (str62net(p, &opt->pref_net[opt->pref_net_nb].addr.in6,
-					                     &mask)) {
-						/* Try to convert input string from ipv6 network. */
-						len2mask6(mask, &opt->pref_net[opt->pref_net_nb].mask.in6);
-						opt->pref_net[opt->pref_net_nb].family = AF_INET6;
-					} else {
-						/* All network conversions fail, return error. */
-						ha_alert("parsing [%s:%d]: '%s': invalid network '%s'.\n",
-						      file, linenum, args[cur_arg], p);
-						err_code |= ERR_ALERT | ERR_FATAL;
-						goto out;
-					}
-					opt->pref_net_nb++;
-					p = e;
-				}
-
-				cur_arg += 2;
-			}
-			else if (strcmp(args[cur_arg], "weight") == 0) {
-				int w;
-				w = atol(args[cur_arg + 1]);
-				if (w < 0 || w > SRV_UWGHT_MAX) {
-					ha_alert("parsing [%s:%d] : weight of server %s is not within 0 and %d (%d).\n",
-					      file, linenum, newsrv->id, SRV_UWGHT_MAX, w);
-					err_code |= ERR_ALERT | ERR_FATAL;
-					goto out;
-				}
-				newsrv->uweight = newsrv->iweight = w;
-				cur_arg += 2;
-			}
-			else if (strcmp(args[cur_arg], "log-proto") == 0) {
-				if (strcmp(args[cur_arg + 1], "legacy") == 0)
-					newsrv->log_proto = SRV_LOG_PROTO_LEGACY;
-				else if (strcmp(args[cur_arg + 1], "octet-count") == 0)
-					newsrv->log_proto = SRV_LOG_PROTO_OCTET_COUNTING;
-				else {
-					ha_alert("parsing [%s:%d]: '%s' expects one of 'legacy' or "
-						"'octet-count' but got '%s'\n",
-						file, linenum, args[cur_arg], args[cur_arg + 1]);
-					err_code |= ERR_ALERT | ERR_FATAL;
-					goto out;
-				}
-				cur_arg += 2;
-			}
-			else if (strcmp(args[cur_arg], "minconn") == 0) {
-				newsrv->minconn = atol(args[cur_arg + 1]);
-				cur_arg += 2;
-			}
-			else if (strcmp(args[cur_arg], "maxconn") == 0) {
-				newsrv->maxconn = atol(args[cur_arg + 1]);
-				cur_arg += 2;
-			}
-			else if (strcmp(args[cur_arg], "maxqueue") == 0) {
-				newsrv->maxqueue = atol(args[cur_arg + 1]);
-				cur_arg += 2;
-			}
-			else if (strcmp(args[cur_arg], "slowstart") == 0) {
-				/* slowstart is stored in seconds */
-				const char *err = parse_time_err(args[cur_arg + 1], &val, TIME_UNIT_MS);
+			struct srv_kw *kw;
+			const char *best;
 
-				if (err == PARSE_TIME_OVER) {
-					ha_alert("parsing [%s:%d]: timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).\n",
-						 file, linenum, args[cur_arg+1], args[cur_arg], newsrv->id);
-					err_code |= ERR_ALERT | ERR_FATAL;
-					goto out;
-				}
-				else if (err == PARSE_TIME_UNDER) {
-					ha_alert("parsing [%s:%d]: timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.\n",
-						 file, linenum, args[cur_arg+1], args[cur_arg], newsrv->id);
-					err_code |= ERR_ALERT | ERR_FATAL;
-					goto out;
-				}
-				else if (err) {
-					ha_alert("parsing [%s:%d] : unexpected character '%c' in 'slowstart' argument of server %s.\n",
-					      file, linenum, *err, newsrv->id);
-					err_code |= ERR_ALERT | ERR_FATAL;
-					goto out;
-				}
-				newsrv->slowstart = (val + 999) / 1000;
-				cur_arg += 2;
-			}
-			else if (strcmp(args[cur_arg], "on-error") == 0) {
-				if (strcmp(args[cur_arg + 1], "fastinter") == 0)
-					newsrv->onerror = HANA_ONERR_FASTINTER;
-				else if (strcmp(args[cur_arg + 1], "fail-check") == 0)
-					newsrv->onerror = HANA_ONERR_FAILCHK;
-				else if (strcmp(args[cur_arg + 1], "sudden-death") == 0)
-					newsrv->onerror = HANA_ONERR_SUDDTH;
-				else if (strcmp(args[cur_arg + 1], "mark-down") == 0)
-					newsrv->onerror = HANA_ONERR_MARKDWN;
-				else {
-					ha_alert("parsing [%s:%d]: '%s' expects one of 'fastinter', "
-						"'fail-check', 'sudden-death' or 'mark-down' but got '%s'\n",
-						file, linenum, args[cur_arg], args[cur_arg + 1]);
-					err_code |= ERR_ALERT | ERR_FATAL;
-					goto out;
-				}
-
-				cur_arg += 2;
-			}
-			else if (strcmp(args[cur_arg], "on-marked-down") == 0) {
-				if (strcmp(args[cur_arg + 1], "shutdown-sessions") == 0)
-					newsrv->onmarkeddown = HANA_ONMARKEDDOWN_SHUTDOWNSESSIONS;
-				else {
-					ha_alert("parsing [%s:%d]: '%s' expects 'shutdown-sessions' but got '%s'\n",
-						file, linenum, args[cur_arg], args[cur_arg + 1]);
-					err_code |= ERR_ALERT | ERR_FATAL;
-					goto out;
-				}
+			kw = srv_find_kw(args[cur_arg]);
+			if (kw) {
+				char *err = NULL;
+				int code;
 
-				cur_arg += 2;
-			}
-			else if (strcmp(args[cur_arg], "on-marked-up") == 0) {
-				if (strcmp(args[cur_arg + 1], "shutdown-backup-sessions") == 0)
-					newsrv->onmarkedup = HANA_ONMARKEDUP_SHUTDOWNBACKUPSESSIONS;
-				else {
-					ha_alert("parsing [%s:%d]: '%s' expects 'shutdown-backup-sessions' but got '%s'\n",
-						file, linenum, args[cur_arg], args[cur_arg + 1]);
-					err_code |= ERR_ALERT | ERR_FATAL;
-					goto out;
-				}
-
-				cur_arg += 2;
-			}
-			else if (strcmp(args[cur_arg], "error-limit") == 0) {
-				if (!*args[cur_arg + 1]) {
-					ha_alert("parsing [%s:%d]: '%s' expects an integer argument.\n",
-						file, linenum, args[cur_arg]);
+				if (!kw->parse) {
+					ha_alert("parsing [%s:%d] : '%s %s' : '%s' option is not implemented in this version (check build options).\n",
+					         file, linenum, args[0], args[1], args[cur_arg]);
+					if (kw->skip != -1)
+						cur_arg += 1 + kw->skip ;
 					err_code |= ERR_ALERT | ERR_FATAL;
 					goto out;
 				}
 
-				newsrv->consecutive_errors_limit = atoi(args[cur_arg + 1]);
-
-				if (newsrv->consecutive_errors_limit <= 0) {
-					ha_alert("parsing [%s:%d]: %s has to be > 0.\n",
-						file, linenum, args[cur_arg]);
-					err_code |= ERR_ALERT | ERR_FATAL;
-					goto out;
+				if (defsrv && !kw->default_ok) {
+					ha_alert("parsing [%s:%d] : '%s %s' : '%s' option is not accepted in default-server sections.\n",
+					         file, linenum, args[0], args[1], args[cur_arg]);
+					if (kw->skip != -1)
+						cur_arg += 1 + kw->skip;
+					err_code |= ERR_ALERT;
+					continue;
 				}
-				cur_arg += 2;
-			}
-			else if (strcmp(args[cur_arg], "usesrc") == 0) {  /* address to use outside: needs "source" first */
-				ha_alert("parsing [%s:%d] : '%s' only allowed after a '%s' statement.\n",
-				      file, linenum, "usesrc", "source");
-				err_code |= ERR_ALERT | ERR_FATAL;
-				goto out;
-			}
-			else {
-				struct srv_kw *kw;
-				const char *best;
 
-				kw = srv_find_kw(args[cur_arg]);
-				if (kw) {
-					char *err = NULL;
-					int code;
+				code = kw->parse(args, &cur_arg, curproxy, newsrv, &err);
+				err_code |= code;
 
-					if (!kw->parse) {
-						ha_alert("parsing [%s:%d] : '%s %s' : '%s' option is not implemented in this version (check build options).\n",
-						      file, linenum, args[0], args[1], args[cur_arg]);
+				if (code) {
+					display_parser_err(file, linenum, args, cur_arg, code, &err);
+					if (code & ERR_FATAL) {
+						free(err);
 						if (kw->skip != -1)
-							cur_arg += 1 + kw->skip ;
-						err_code |= ERR_ALERT | ERR_FATAL;
+							cur_arg += 1 + kw->skip;
 						goto out;
 					}
-
-					if (defsrv && !kw->default_ok) {
-						ha_alert("parsing [%s:%d] : '%s %s' : '%s' option is not accepted in default-server sections.\n",
-						      file, linenum, args[0], args[1], args[cur_arg]);
-						if (kw->skip != -1)
-							cur_arg += 1 + kw->skip ;
-						err_code |= ERR_ALERT;
-						continue;
-					}
-
-					code = kw->parse(args, &cur_arg, curproxy, newsrv, &err);
-					err_code |= code;
-
-					if (code) {
-						display_parser_err(file, linenum, args, cur_arg, code, &err);
-						if (code & ERR_FATAL) {
-							free(err);
-							if (kw->skip != -1)
-								cur_arg += 1 + kw->skip;
-							goto out;
-						}
-					}
-					free(err);
-					if (kw->skip != -1)
-						cur_arg += 1 + kw->skip;
-					continue;
 				}
+				free(err);
+				if (kw->skip != -1)
+					cur_arg += 1 + kw->skip;
+				continue;
+			}
 
-				best = srv_find_best_kw(args[cur_arg]);
-				if (best)
-					ha_alert("parsing [%s:%d] : '%s %s' unknown keyword '%s'; did you mean '%s' maybe ?\n",
-						 file, linenum, args[0], args[1], args[cur_arg], best);
-				else
-					ha_alert("parsing [%s:%d] : '%s %s' unknown keyword '%s'.\n",
-						 file, linenum, args[0], args[1], args[cur_arg]);
+			best = srv_find_best_kw(args[cur_arg]);
+			if (best)
+				ha_alert("parsing [%s:%d] : '%s %s' unknown keyword '%s'; did you mean '%s' maybe ?\n",
+				         file, linenum, args[0], args[1], args[cur_arg], best);
+			else
+				ha_alert("parsing [%s:%d] : '%s %s' unknown keyword '%s'.\n",
+				         file, linenum, args[0], args[1], args[cur_arg]);
 
-				err_code |= ERR_ALERT | ERR_FATAL;
-				goto out;
-			}
+			err_code |= ERR_ALERT | ERR_FATAL;
+			goto out;
 		}
 
 		if (!defsrv)