diff --git a/doc/configuration.txt b/doc/configuration.txt index 6a0da4e16..c9ba70dcb 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -244,8 +244,8 @@ block - X X X capture cookie X X X X capture request header X X X X capture response header X X X X -clitimeout X X X - -contimeout X X X X +clitimeout X X X - (deprecated) +contimeout X X X X (deprecated) cookie X - X X default_backend - X X - disabled - X X X @@ -314,7 +314,7 @@ rspirep - X X X rsprep - X X X server - - X X source X - X X -srvtimeout X - X X +srvtimeout X - X X (deprecated) stats auth X - X X stats enable X - X X stats realm X - X X @@ -322,6 +322,15 @@ stats refresh X - X X stats scope X - X X stats uri X - X X stats hide-version X - X X +timeout appsession X - X X +timeout client X X X - +timeout clitimeout X X X - (deprecated) +timeout connect X - X X +timeout contimeout X - X X (deprecated) +timeout queue X - X X +timeout server X - X X +timeout srvtimeout X - X X (deprecated) +timeout tarpit X X X - transparent X X X - use_backend - X X - usesrc X - X X diff --git a/include/proto/proxy.h b/include/proto/proxy.h index a0c86cb87..f00f64697 100644 --- a/include/proto/proxy.h +++ b/include/proto/proxy.h @@ -36,6 +36,8 @@ void listen_proxies(void); const char *proxy_cap_str(int cap); const char *proxy_mode_str(int mode); struct proxy *findproxy(const char *name, int mode, int cap); +int proxy_parse_timeout(const char **args, struct proxy *proxy, + struct proxy *defpx, char *err, int errlen); /* * This function returns a string containing the type of the proxy in a format diff --git a/src/cfgparse.c b/src/cfgparse.c index 91b9f3ccd..8bb787e88 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -976,78 +976,26 @@ int cfg_parse_listen(const char *file, int linenum, char **args) return -1; } } - else if (!strcmp(args[0], "contimeout")) { /* connect timeout */ - if (!__tv_iseq(&curproxy->contimeout, &defproxy.contimeout)) { - Alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]); - return 0; - } - else if (warnifnotcap(curproxy, PR_CAP_BE, file, linenum, args[0], NULL)) - return 0; + else if (!strcmp(args[0], "contimeout") || !strcmp(args[0], "clitimeout") || + !strcmp(args[0], "srvtimeout") || !strcmp(args[0], "timeout")) { - if (*(args[1]) == 0) { - Alert("parsing [%s:%d] : '%s' expects an integer as argument.\n", - file, linenum, args[0]); - return -1; - } - err = parse_time_err(args[1], &val, TIME_UNIT_MS); - if (err) { - Alert("parsing [%s:%d] : unexpected character '%c' in %s.\n", - file, linenum, *err, args[0]); - return -1; - } - if (val > 0) - __tv_from_ms(&curproxy->contimeout, val); - else - tv_eternity(&curproxy->contimeout); - } - else if (!strcmp(args[0], "clitimeout")) { /* client timeout */ - if (!__tv_iseq(&curproxy->clitimeout, &defproxy.clitimeout)) { - Alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", - file, linenum, args[0]); - return 0; - } - else if (warnifnotcap(curproxy, PR_CAP_FE, file, linenum, args[0], NULL)) - return 0; + /* either we have {con|srv|cli}timeout or we have the + * new form: timeout . The parser needs the word + * preceeding the value. + */ + const char **start_arg = (const char **)args; - if (*(args[1]) == 0) { - Alert("parsing [%s:%d] : '%s' expects an integer as argument.\n", - file, linenum, args[0]); - return -1; - } - err = parse_time_err(args[1], &val, TIME_UNIT_MS); - if (err) { - Alert("parsing [%s:%d] : unexpected character '%c' in %s.\n", - file, linenum, *err, args[0]); - return -1; - } - if (val > 0) - __tv_from_ms(&curproxy->clitimeout, val); - else - tv_eternity(&curproxy->clitimeout); - } - else if (!strcmp(args[0], "srvtimeout")) { /* server timeout */ - if (!__tv_iseq(&curproxy->srvtimeout, &defproxy.srvtimeout)) { - Alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]); - return 0; - } - else if (warnifnotcap(curproxy, PR_CAP_BE, file, linenum, args[0], NULL)) - return 0; + if (strcmp(args[0], "timeout") == 0) + start_arg++; - if (*(args[1]) == 0) { - Alert("parsing [%s:%d] : '%s' expects an integer as argument.\n", - file, linenum, args[0]); + snprintf(trash, sizeof(trash), "error near '%s'", args[0]); + rc = proxy_parse_timeout(start_arg, curproxy, &defproxy, trash, sizeof(trash)); + if (rc < 0) { + Alert("parsing [%s:%d] : %s\n", file, linenum, trash); return -1; } - err = parse_time_err(args[1], &val, TIME_UNIT_MS); - if (err) { - Alert("parsing [%s:%d] : unexpected character '%c' in %s.\n", - file, linenum, *err, args[0]); - return -1; - } - if (val > 0) - __tv_from_ms(&curproxy->srvtimeout, val); - else - tv_eternity(&curproxy->srvtimeout); + if (rc > 0) + Warning("parsing [%s:%d] : %s\n", file, linenum, trash); } else if (!strcmp(args[0], "retries")) { /* connection retries */ if (warnifnotcap(curproxy, PR_CAP_BE, file, linenum, args[0], NULL)) diff --git a/src/proxy.c b/src/proxy.c index 06424c62c..826396497 100644 --- a/src/proxy.c +++ b/src/proxy.c @@ -75,6 +75,89 @@ const char *proxy_mode_str(int mode) { return "unknown"; } +/* This function parses a "timeout" statement in a proxy section. It returns + * -1 if there is any error, 1 for a warning, otherwise zero. If it does not + * return zero, it may write an error message into the buffer, for at + * most bytes, trailing zero included. The trailing '\n' must not + * be written. The function must be called with pointing to the first + * word after "timeout", with pointing to the proxy being parsed, and + * to the default proxy or NULL. As a special case for compatibility + * with older configs, it also accepts "{cli|srv|con}timeout" in args[0]. + */ +int proxy_parse_timeout(const char **args, struct proxy *proxy, + struct proxy *defpx, char *err, int errlen) +{ + unsigned timeout; + int retval, cap; + const char *res, *name; + struct timeval *tv = NULL; + struct timeval *td = NULL; + + retval = 0; + name = args[0]; + if (!strcmp(args[0], "client") || !strcmp(args[0], "clitimeout")) { + name = "client"; + tv = &proxy->clitimeout; + td = &defpx->clitimeout; + cap = PR_CAP_FE; + } else if (!strcmp(args[0], "tarpit")) { + tv = &proxy->timeout.tarpit; + td = &defpx->timeout.tarpit; + cap = PR_CAP_FE; + } else if (!strcmp(args[0], "server") || !strcmp(args[0], "srvtimeout")) { + name = "server"; + tv = &proxy->srvtimeout; + td = &defpx->srvtimeout; + cap = PR_CAP_BE; + } else if (!strcmp(args[0], "connect") || !strcmp(args[0], "contimeout")) { + name = "connect"; + tv = &proxy->contimeout; + td = &defpx->contimeout; + cap = PR_CAP_BE; + } else if (!strcmp(args[0], "appsession")) { + tv = &proxy->appsession_timeout; + td = &defpx->appsession_timeout; + cap = PR_CAP_BE; + } else if (!strcmp(args[0], "queue")) { + tv = &proxy->timeout.queue; + td = &defpx->timeout.queue; + cap = PR_CAP_BE; + } else { + snprintf(err, errlen, "timeout '%s': must be 'client', 'server', 'connect', 'appsession', 'queue', or 'tarpit'", + args[0]); + return -1; + } + + if (*args[1] == 0) { + snprintf(err, errlen, "%s timeout expects an integer value (in milliseconds)", name); + return -1; + } + + res = parse_time_err(args[1], &timeout, TIME_UNIT_MS); + if (res) { + snprintf(err, errlen, "unexpected character '%c' in %s timeout", *err, name); + return -1; + } + + if (!(proxy->cap & cap)) { + snprintf(err, errlen, "%s timeout will be ignored because %s '%s' has no %s capability", + name, proxy_type_str(proxy), proxy->id, + (cap & PR_CAP_BE) ? "backend" : "frontend"); + retval = 1; + } + else if (defpx && !__tv_iseq(tv, td)) { + snprintf(err, errlen, "overwriting %s timeout which was already specified", name); + retval = 1; + } + + if (timeout) + __tv_from_ms(tv, timeout); + else + tv_eternity(tv); + + return retval; +} + /* * This function finds a proxy with matching name, mode and with satisfying * capabilities. It also checks if there are more matching proxies with diff --git a/tests/test-timeout.cfg b/tests/test-timeout.cfg new file mode 100644 index 000000000..7053f5c28 --- /dev/null +++ b/tests/test-timeout.cfg @@ -0,0 +1,26 @@ +# This is a test configuration. +# It is used to check that time units are correctly parsed. + +global + maxconn 1000 + stats timeout 3s + +listen sample1 + mode http + retries 1 + redispatch + timeout client 15m + timeout tarpit 20s + timeout queue 60s + timeout connect 5s + timeout server 15m + maxconn 40000 + bind :8000 + balance roundrobin + option allbackups + server act1 127.0.0.1:80 weight 10 check port 81 inter 500ms fall 1 + server act2 127.0.0.2:80 weight 20 check port 81 inter 500ms fall 1 + server act3 127.0.0.3:80 weight 30 check port 81 inter 500ms fall 1 + option httpclose + stats uri /stats + stats refresh 5000ms