]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
REORG: server: move keywords in srv_kws
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Wed, 10 Mar 2021 15:36:02 +0000 (16:36 +0100)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Thu, 18 Mar 2021 14:37:05 +0000 (15:37 +0100)
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.

src/server.c

index 7c08dd0ff10d5ef372ac2a3634a3e5b6f112aea3..9a55d967a00384aeaaf14310e7af5b9469d73789 100644 (file)
@@ -50,16 +50,12 @@ static void srv_update_status(struct server *s);
 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 @@ static const char *srv_find_best_kw(const char *word)
                }
        }
 
-       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 @@ static int srv_parse_enabled(char **args, int *cur_arg,
        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 @@ static int srv_parse_observe(char **args, int *cur_arg,
        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 @@ static int srv_parse_redir(char **args, int *cur_arg,
        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 @@ static int srv_parse_send_proxy_v2(char **args, int *cur_arg,
        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 @@ static int srv_parse_tfo(char **args, int *cur_arg, struct proxy *px, struct ser
        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 @@ static struct srv_kw_list srv_kws = { "ALL", { }, {
        { "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 @@ static struct srv_kw_list srv_kws = { "ALL", { }, {
        { "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 @@ int parse_server(const char *file, int linenum, char **args, struct proxy *curpr
        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 @@ int parse_server(const char *file, int linenum, char **args, struct proxy *curpr
                }
 
                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;
+                       struct srv_kw *kw;
+                       const char *best;
 
-                               for (p = args[cur_arg + 1]; *p; p = end) {
-                                       /* cut on next comma */
-                                       for (end = p; *end && *end != ','; end++);
-                                       if (*end)
-                                               *(end++) = 0;
+                       kw = srv_find_kw(args[cur_arg]);
+                       if (kw) {
+                               char *err = NULL;
+                               int code;
 
-                                       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);
-
-                               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;
-                               }
-
-                               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)