From e219db7a46e47b0cab99ca7ee110e1dcb349ac4e Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Mon, 3 Dec 2007 01:30:13 +0100 Subject: [PATCH] [MEDIUM] introduce the "timeout" keyword A new "timeout" keyword replaces old "{con|cli|srv}timeout", and provides the ability to independantly set the following timeouts : - client - tarpit - queue - connect - server - appsession Additionally, the "clitimeout", "contimeout" and "srvtimeout" values are supported but deprecated. No warning is emitted yet when they are used since the option is very new. Other timeouts should follow soon now. --- doc/configuration.txt | 15 ++++++-- include/proto/proxy.h | 2 + src/cfgparse.c | 82 ++++++++--------------------------------- src/proxy.c | 83 ++++++++++++++++++++++++++++++++++++++++++ tests/test-timeout.cfg | 26 +++++++++++++ 5 files changed, 138 insertions(+), 70 deletions(-) create mode 100644 tests/test-timeout.cfg 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