mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2025-05-11 04:08:11 +00:00
MEDIUM: checks: add send/expect tcp based check
This is a generic health check which can be used to match a banner or send a request and analyse a server response. It works in a send/expect ways and many exchange can be done between HAProxy and a server to decide the server status, making HAProxy able to speak the server's protocol. It can send arbitrary regular or binary strings and match content as a regular or binary string or a regex. Signed-off-by: Baptiste Assmann <bedis9@gmail.com>
This commit is contained in:
parent
bb77c8e26d
commit
5ecb77f4c7
@ -1144,6 +1144,9 @@ http-check expect - - X X
|
|||||||
http-check send-state X - X X
|
http-check send-state X - X X
|
||||||
http-request - X X X
|
http-request - X X X
|
||||||
http-response - X X X
|
http-response - X X X
|
||||||
|
tcp-check expect - - X X
|
||||||
|
tcp-check send - - X X
|
||||||
|
tcp-check send-binary - - X X
|
||||||
http-send-name-header - - X X
|
http-send-name-header - - X X
|
||||||
id - X X X
|
id - X X X
|
||||||
ignore-persist - X X X
|
ignore-persist - X X X
|
||||||
@ -1165,6 +1168,7 @@ option dontlognull (*) X X X -
|
|||||||
option forceclose (*) X X X X
|
option forceclose (*) X X X X
|
||||||
-- keyword -------------------------- defaults - frontend - listen -- backend -
|
-- keyword -------------------------- defaults - frontend - listen -- backend -
|
||||||
option forwardfor X X X X
|
option forwardfor X X X X
|
||||||
|
option tcp-check X - X X
|
||||||
option http-no-delay (*) X X X X
|
option http-no-delay (*) X X X X
|
||||||
option http-pretend-keepalive (*) X X X X
|
option http-pretend-keepalive (*) X X X X
|
||||||
option http-server-close (*) X X X X
|
option http-server-close (*) X X X X
|
||||||
@ -2936,6 +2940,132 @@ http-response { allow | deny | add-header <name> <fmt> | set-nice <nice> |
|
|||||||
See also : "http-request", section 3.4 about userlists and section 7 about
|
See also : "http-request", section 3.4 about userlists and section 7 about
|
||||||
ACL usage.
|
ACL usage.
|
||||||
|
|
||||||
|
|
||||||
|
tcp-check expect [!] <match> <pattern>
|
||||||
|
Specify data to be collected and analysed during a generic health check
|
||||||
|
May be used in sections: defaults | frontend | listen | backend
|
||||||
|
no | no | yes | yes
|
||||||
|
|
||||||
|
Arguments :
|
||||||
|
<match> is a keyword indicating how to look for a specific pattern in the
|
||||||
|
response. The keyword may be one of "string", "rstring" or
|
||||||
|
binary.
|
||||||
|
The keyword may be preceded by an exclamation mark ("!") to negate
|
||||||
|
the match. Spaces are allowed between the exclamation mark and the
|
||||||
|
keyword. See below for more details on the supported keywords.
|
||||||
|
|
||||||
|
<pattern> is the pattern to look for. It may be a string or a regular
|
||||||
|
expression. If the pattern contains spaces, they must be escaped
|
||||||
|
with the usual backslash ('\').
|
||||||
|
If the match is set to binary, then the pattern must be passed as
|
||||||
|
a serie of hexadecimal digits in an even number. Each sequence of
|
||||||
|
two digits will represent a byte. The hexadecimal digits may be
|
||||||
|
used upper or lower case.
|
||||||
|
|
||||||
|
|
||||||
|
The available matches are intentionally similar to their http-check cousins :
|
||||||
|
|
||||||
|
string <string> : test the exact string matches in the response buffer.
|
||||||
|
A health check response will be considered valid if the
|
||||||
|
response's buffer contains this exact string. If the
|
||||||
|
"string" keyword is prefixed with "!", then the response
|
||||||
|
will be considered invalid if the body contains this
|
||||||
|
string. This can be used to look for a mandatory pattern
|
||||||
|
in a protocol response, or to detect a failure when a
|
||||||
|
specific error appears in a protocol banner.
|
||||||
|
|
||||||
|
rstring <regex> : test a regular expression on the response buffer.
|
||||||
|
A health check response will be considered valid if the
|
||||||
|
response's buffer matches this expression. If the
|
||||||
|
"rstring" keyword is prefixed with "!", then the response
|
||||||
|
will be considered invalid if the body matches the
|
||||||
|
expression.
|
||||||
|
|
||||||
|
binary <hexstring> : test the exact string in its hexadecimal form matches
|
||||||
|
in the response buffer. A health check response will
|
||||||
|
be considered valid if the response's buffer contains
|
||||||
|
this exact hexadecimal string.
|
||||||
|
Purpose is to match data on binary protocols.
|
||||||
|
|
||||||
|
It is important to note that the responses will be limited to a certain size
|
||||||
|
defined by the global "tune.chksize" option, which defaults to 16384 bytes.
|
||||||
|
Thus, too large responses may not contain the mandatory pattern when using
|
||||||
|
"string", "rstring" or binary. If a large response is absolutely required, it
|
||||||
|
is possible to change the default max size by setting the global variable.
|
||||||
|
However, it is worth keeping in mind that parsing very large responses can
|
||||||
|
waste some CPU cycles, especially when regular expressions are used, and that
|
||||||
|
it is always better to focus the checks on smaller resources. Also, in its
|
||||||
|
current state, the check will not find any string nor regex past a null
|
||||||
|
character in the response. Similarly it is not possible to request matching
|
||||||
|
the null character.
|
||||||
|
|
||||||
|
Examples :
|
||||||
|
# perform a POP check
|
||||||
|
option tcp-check
|
||||||
|
tcp-check expect string +OK\ POP3\ ready
|
||||||
|
|
||||||
|
# perform an IMAP check
|
||||||
|
option tcp-check
|
||||||
|
tcp-check expect string *\ OK\ IMAP4\ ready
|
||||||
|
|
||||||
|
# look for the redis master server
|
||||||
|
option tcp-check
|
||||||
|
tcp-check send PING\r\n
|
||||||
|
tcp-check expect +PONG
|
||||||
|
tcp-check send info\ replication\r\n
|
||||||
|
tcp-check expect string role:master
|
||||||
|
tcp-check send QUIT\r\n
|
||||||
|
tcp-check expect string +OK
|
||||||
|
|
||||||
|
|
||||||
|
See also : "option tcp-check", "tcp-check send", "http-check expect",
|
||||||
|
tune.chksize
|
||||||
|
|
||||||
|
|
||||||
|
tcp-check send <data>
|
||||||
|
Specify a string to be sent as a question during a generic health check
|
||||||
|
May be used in sections: defaults | frontend | listen | backend
|
||||||
|
no | no | yes | yes
|
||||||
|
|
||||||
|
<data> : the data to be sent as a question during a generic health check
|
||||||
|
session. For now, <data> must be a string.
|
||||||
|
|
||||||
|
Examples :
|
||||||
|
# look for the redis master server
|
||||||
|
option tcp-check
|
||||||
|
tcp-check send info\ replication\r\n
|
||||||
|
tcp-check expect string role:master
|
||||||
|
|
||||||
|
See also : "option tcp-check", "tcp-check expect", "tcp-check send-binary",
|
||||||
|
tune.chksize
|
||||||
|
|
||||||
|
|
||||||
|
tcp-check send-binary <hexastring>
|
||||||
|
Specify an hexa digits string to be sent as a binary question during a raw
|
||||||
|
tcp health check
|
||||||
|
May be used in sections: defaults | frontend | listen | backend
|
||||||
|
no | no | yes | yes
|
||||||
|
|
||||||
|
<data> : the data to be sent as a question during a generic health check
|
||||||
|
session. For now, <data> must be a string.
|
||||||
|
<hexastring> : test the exact string in its hexadecimal form matches in the
|
||||||
|
response buffer. A health check response will be considered
|
||||||
|
valid if the response's buffer contains this exact
|
||||||
|
hexadecimal string.
|
||||||
|
Purpose is to send binary data to ask on binary protocols.
|
||||||
|
|
||||||
|
Examples :
|
||||||
|
# redis check in binary
|
||||||
|
option tcp-check
|
||||||
|
tcp-check send-binary 50494e470d0a # PING\r\n
|
||||||
|
tcp-check expect binary 2b504F4e47 # +PONG
|
||||||
|
|
||||||
|
|
||||||
|
See also : "option tcp-check", "tcp-check expect", "tcp-check send",
|
||||||
|
tune.chksize
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
http-send-name-header [<header>]
|
http-send-name-header [<header>]
|
||||||
Add the server name to a request. Use the header string given by <header>
|
Add the server name to a request. Use the header string given by <header>
|
||||||
|
|
||||||
@ -3631,6 +3761,75 @@ option forwardfor [ except <network> ] [ header <name> ] [ if-none ]
|
|||||||
"option forceclose"
|
"option forceclose"
|
||||||
|
|
||||||
|
|
||||||
|
option tcp-check
|
||||||
|
Perform health checks using tcp-check send/expect sequences
|
||||||
|
May be used in sections: defaults | frontend | listen | backend
|
||||||
|
yes | no | yes | yes
|
||||||
|
|
||||||
|
This health check method is intended to be combined with "tcp-check" command
|
||||||
|
lists in order to support send/expect types of health check sequences.
|
||||||
|
|
||||||
|
TCP checks currently support 4 modes of operations :
|
||||||
|
- no "tcp-check" directive : the health check only consists in a connection
|
||||||
|
attempt, which remains the default mode.
|
||||||
|
|
||||||
|
- "tcp-check send" or "tcp-check send-binary" only is mentionned : this is
|
||||||
|
used to send a string along with a connection opening. With some
|
||||||
|
protocols, it helps sending a "QUIT" message for example that prevents
|
||||||
|
the server from logging a connection error for each health check. The
|
||||||
|
check result will still be based on the ability to open the connection
|
||||||
|
only.
|
||||||
|
|
||||||
|
- "tcp-check expect" only is mentionned : this is used to test a banner.
|
||||||
|
The connection is opened and haproxy waits for the server to present some
|
||||||
|
contents which must validate some rules. The check result will be based
|
||||||
|
on the matching between the contents and the rules. This is suited for
|
||||||
|
POP, IMAP, SMTP, FTP, SSH, TELNET.
|
||||||
|
|
||||||
|
- both "tcp-check send" and "tcp-check expect" are mentionned : this is
|
||||||
|
used to test a hello-type protocol. Haproxy sends a message, the server
|
||||||
|
responds and its response is analysed. the check result will be based on
|
||||||
|
the maching between the response contents and the rules. This is often
|
||||||
|
suited for protocols which require a binding or a request/response model.
|
||||||
|
LDAP, MySQL, Redis and SSL are example of such protocols, though they
|
||||||
|
already all have their dedicated checks with a deeper understanding of
|
||||||
|
the respective protocols.
|
||||||
|
In this mode, many questions may be sent and many answers may be
|
||||||
|
analysed.
|
||||||
|
|
||||||
|
Examples :
|
||||||
|
# perform a POP check (analyse only server's banner)
|
||||||
|
option tcp-check
|
||||||
|
tcp-check expect string +OK\ POP3\ ready
|
||||||
|
|
||||||
|
# perform an IMAP check (analyse only server's banner)
|
||||||
|
option tcp-check
|
||||||
|
tcp-check expect string *\ OK\ IMAP4\ ready
|
||||||
|
|
||||||
|
# look for the redis master server after ensuring it speaks well
|
||||||
|
# redis protocol, then it exits properly.
|
||||||
|
# (send a command then analyse the response 3 tims)
|
||||||
|
option tcp-check
|
||||||
|
tcp-check send PING\r\n
|
||||||
|
tcp-check expect +PONG
|
||||||
|
tcp-check send info\ replication\r\n
|
||||||
|
tcp-check expect string role:master
|
||||||
|
tcp-check send QUIT\r\n
|
||||||
|
tcp-check expect string +OK
|
||||||
|
|
||||||
|
forge a HTTP request, then analyse the response
|
||||||
|
(send many headers before analyzing)
|
||||||
|
option tcp-check
|
||||||
|
tcp-check send HEAD\ /\ HTTP/1.1\r\n
|
||||||
|
tcp-check send Host:\ www.mydomain.com\r\n
|
||||||
|
tcp-check send User-Agent:\ HAProxy\ tcpcheck\r\n
|
||||||
|
tcp-check send \r\n
|
||||||
|
tcp-check expect rstring HTTP/1\..\ (2..|3..)
|
||||||
|
|
||||||
|
|
||||||
|
See also : "tcp-check expect", "tcp-check send"
|
||||||
|
|
||||||
|
|
||||||
option http-no-delay
|
option http-no-delay
|
||||||
no option http-no-delay
|
no option http-no-delay
|
||||||
Instruct the system to favor low interactive delays over performance in HTTP
|
Instruct the system to favor low interactive delays over performance in HTTP
|
||||||
|
@ -106,4 +106,21 @@ struct analyze_status {
|
|||||||
unsigned char lr[HANA_OBS_SIZE]; /* result for l4/l7: 0 = ignore, 1 - error, 2 - OK */
|
unsigned char lr[HANA_OBS_SIZE]; /* result for l4/l7: 0 = ignore, 1 - error, 2 - OK */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* bits for tcpcheck_rule->action */
|
||||||
|
enum {
|
||||||
|
TCPCHK_ACT_SEND = 1, /* send action, regular string format */
|
||||||
|
TCPCHK_ACT_EXPECT, /* expect action, either regular or binary string */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct tcpcheck_rule {
|
||||||
|
struct list list; /* list linked to from the proxy */
|
||||||
|
int action; /* action: send or expect */
|
||||||
|
/* match type uses NON-NULL pointer from either string or expect_regex below */
|
||||||
|
/* sent string is string */
|
||||||
|
char *string; /* sent or expected string */
|
||||||
|
int string_len; /* string lenght */
|
||||||
|
regex_t *expect_regex; /* expected */
|
||||||
|
int inverse; /* 0 = regular match, 1 = inverse match */
|
||||||
|
};
|
||||||
|
|
||||||
#endif /* _TYPES_CHECKS_H */
|
#endif /* _TYPES_CHECKS_H */
|
||||||
|
@ -152,7 +152,8 @@ enum {
|
|||||||
#define PR_O2_LDAP_CHK 0x60000000 /* use LDAP check for server health */
|
#define PR_O2_LDAP_CHK 0x60000000 /* use LDAP check for server health */
|
||||||
#define PR_O2_SSL3_CHK 0x70000000 /* use SSLv3 CLIENT_HELLO packets for server health */
|
#define PR_O2_SSL3_CHK 0x70000000 /* use SSLv3 CLIENT_HELLO packets for server health */
|
||||||
#define PR_O2_LB_AGENT_CHK 0x80000000 /* use a TCP connection to obtain a metric of server health */
|
#define PR_O2_LB_AGENT_CHK 0x80000000 /* use a TCP connection to obtain a metric of server health */
|
||||||
/* unused: 0x90000000 to 0xF000000, reserved for health checks */
|
#define PR_O2_TCPCHK_CHK 0x90000000 /* use TCPCHK check for server health */
|
||||||
|
/* unused: 0xA0000000 to 0xF000000, reserved for health checks */
|
||||||
#define PR_O2_CHK_ANY 0xF0000000 /* Mask to cover any check */
|
#define PR_O2_CHK_ANY 0xF0000000 /* Mask to cover any check */
|
||||||
/* end of proxy->options2 */
|
/* end of proxy->options2 */
|
||||||
|
|
||||||
@ -324,6 +325,7 @@ struct proxy {
|
|||||||
|
|
||||||
struct task *task; /* the associated task, mandatory to manage rate limiting, stopping and resource shortage, NULL if disabled */
|
struct task *task; /* the associated task, mandatory to manage rate limiting, stopping and resource shortage, NULL if disabled */
|
||||||
int grace; /* grace time after stop request */
|
int grace; /* grace time after stop request */
|
||||||
|
struct list tcpcheck_rules; /* tcp-check send / expect rules */
|
||||||
char *check_req; /* HTTP or SSL request to use for PR_O_HTTP_CHK|PR_O_SSL3_CHK */
|
char *check_req; /* HTTP or SSL request to use for PR_O_HTTP_CHK|PR_O_SSL3_CHK */
|
||||||
int check_len; /* Length of the HTTP or SSL3 request */
|
int check_len; /* Length of the HTTP or SSL3 request */
|
||||||
char *expect_str; /* http-check expected content : string or text version of the regex */
|
char *expect_str; /* http-check expected content : string or text version of the regex */
|
||||||
|
@ -122,6 +122,7 @@ struct check {
|
|||||||
char desc[HCHK_DESC_LEN]; /* health check descritpion */
|
char desc[HCHK_DESC_LEN]; /* health check descritpion */
|
||||||
int use_ssl; /* use SSL for health checks */
|
int use_ssl; /* use SSL for health checks */
|
||||||
int send_proxy; /* send a PROXY protocol header with checks */
|
int send_proxy; /* send a PROXY protocol header with checks */
|
||||||
|
struct tcpcheck_rule *current_step; /* current step when using tcpcheck */
|
||||||
int inter, fastinter, downinter; /* checks: time in milliseconds */
|
int inter, fastinter, downinter; /* checks: time in milliseconds */
|
||||||
int result; /* health-check result : SRV_CHK_* */
|
int result; /* health-check result : SRV_CHK_* */
|
||||||
int state; /* health-check result : CHK_* */
|
int state; /* health-check result : CHK_* */
|
||||||
|
165
src/cfgparse.c
165
src/cfgparse.c
@ -3732,6 +3732,16 @@ stats_error_parsing:
|
|||||||
memcpy(curproxy->check_req, DEF_LDAP_CHECK_REQ, sizeof(DEF_LDAP_CHECK_REQ) - 1);
|
memcpy(curproxy->check_req, DEF_LDAP_CHECK_REQ, sizeof(DEF_LDAP_CHECK_REQ) - 1);
|
||||||
curproxy->check_len = sizeof(DEF_LDAP_CHECK_REQ) - 1;
|
curproxy->check_len = sizeof(DEF_LDAP_CHECK_REQ) - 1;
|
||||||
}
|
}
|
||||||
|
else if (!strcmp(args[1], "tcp-check")) {
|
||||||
|
/* use raw TCPCHK send/expect to check servers' health */
|
||||||
|
if (warnifnotcap(curproxy, PR_CAP_BE, file, linenum, args[1], NULL))
|
||||||
|
err_code |= ERR_WARN;
|
||||||
|
|
||||||
|
free(curproxy->check_req);
|
||||||
|
curproxy->check_req = NULL;
|
||||||
|
curproxy->options2 &= ~PR_O2_CHK_ANY;
|
||||||
|
curproxy->options2 |= PR_O2_TCPCHK_CHK;
|
||||||
|
}
|
||||||
else if (!strcmp(args[1], "forwardfor")) {
|
else if (!strcmp(args[1], "forwardfor")) {
|
||||||
int cur_arg;
|
int cur_arg;
|
||||||
|
|
||||||
@ -3969,6 +3979,161 @@ stats_error_parsing:
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (!strcmp(args[0], "tcp-check")) {
|
||||||
|
if (warnifnotcap(curproxy, PR_CAP_BE, file, linenum, args[0], NULL))
|
||||||
|
err_code |= ERR_WARN;
|
||||||
|
|
||||||
|
if (strcmp(args[1], "send") == 0) {
|
||||||
|
if (! *(args[2]) ) {
|
||||||
|
/* SEND string expected */
|
||||||
|
Alert("parsing [%s:%d] : '%s %s %s' expects <STRING> as argument.\n",
|
||||||
|
file, linenum, args[0], args[1], args[2]);
|
||||||
|
err_code |= ERR_ALERT | ERR_FATAL;
|
||||||
|
goto out;
|
||||||
|
} else {
|
||||||
|
struct tcpcheck_rule *tcpcheck;
|
||||||
|
|
||||||
|
tcpcheck = (struct tcpcheck_rule *)calloc(1, sizeof(*tcpcheck));
|
||||||
|
|
||||||
|
tcpcheck->action = TCPCHK_ACT_SEND;
|
||||||
|
tcpcheck->string_len = strlen(args[2]);
|
||||||
|
tcpcheck->string = strdup(args[2]);
|
||||||
|
tcpcheck->expect_regex = NULL;
|
||||||
|
|
||||||
|
LIST_ADDQ(&curproxy->tcpcheck_rules, &tcpcheck->list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (strcmp(args[1], "send-binary") == 0) {
|
||||||
|
if (! *(args[2]) ) {
|
||||||
|
/* SEND binary string expected */
|
||||||
|
Alert("parsing [%s:%d] : '%s %s %s' expects <BINARY STRING> as argument.\n",
|
||||||
|
file, linenum, args[0], args[1], args[2]);
|
||||||
|
err_code |= ERR_ALERT | ERR_FATAL;
|
||||||
|
goto out;
|
||||||
|
} else {
|
||||||
|
struct tcpcheck_rule *tcpcheck;
|
||||||
|
char *err = NULL;
|
||||||
|
|
||||||
|
tcpcheck = (struct tcpcheck_rule *)calloc(1, sizeof(*tcpcheck));
|
||||||
|
|
||||||
|
tcpcheck->action = TCPCHK_ACT_SEND;
|
||||||
|
if (parse_binary(args[2], &tcpcheck->string, &tcpcheck->string_len, &err) == 0) {
|
||||||
|
Alert("parsing [%s:%d] : '%s %s %s' expects <BINARY STRING> as argument, but %s\n",
|
||||||
|
file, linenum, args[0], args[1], args[2], err);
|
||||||
|
err_code |= ERR_ALERT | ERR_FATAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
tcpcheck->expect_regex = NULL;
|
||||||
|
|
||||||
|
LIST_ADDQ(&curproxy->tcpcheck_rules, &tcpcheck->list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (strcmp(args[1], "expect") == 0) {
|
||||||
|
const char *ptr_arg;
|
||||||
|
int cur_arg;
|
||||||
|
int inverse = 0;
|
||||||
|
|
||||||
|
if (curproxy->options2 & PR_O2_EXP_TYPE) {
|
||||||
|
Alert("parsing [%s:%d] : '%s %s' already specified.\n", file, linenum, args[0], args[1]);
|
||||||
|
err_code |= ERR_ALERT | ERR_FATAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
cur_arg = 2;
|
||||||
|
/* consider exclamation marks, sole or at the beginning of a word */
|
||||||
|
while (*(ptr_arg = args[cur_arg])) {
|
||||||
|
while (*ptr_arg == '!') {
|
||||||
|
inverse = !inverse;
|
||||||
|
ptr_arg++;
|
||||||
|
}
|
||||||
|
if (*ptr_arg)
|
||||||
|
break;
|
||||||
|
cur_arg++;
|
||||||
|
}
|
||||||
|
/* now ptr_arg points to the beginning of a word past any possible
|
||||||
|
* exclamation mark, and cur_arg is the argument which holds this word.
|
||||||
|
*/
|
||||||
|
if (strcmp(ptr_arg, "binary") == 0) {
|
||||||
|
if (!*(args[cur_arg + 1])) {
|
||||||
|
Alert("parsing [%s:%d] : '%s %s %s' expects <binary string> as an argument.\n",
|
||||||
|
file, linenum, args[0], args[1], ptr_arg);
|
||||||
|
err_code |= ERR_ALERT | ERR_FATAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
struct tcpcheck_rule *tcpcheck;
|
||||||
|
char *err = NULL;
|
||||||
|
|
||||||
|
tcpcheck = (struct tcpcheck_rule *)calloc(1, sizeof(*tcpcheck));
|
||||||
|
|
||||||
|
tcpcheck->action = TCPCHK_ACT_EXPECT;
|
||||||
|
if (parse_binary(args[cur_arg + 1], &tcpcheck->string, &tcpcheck->string_len, &err) == 0) {
|
||||||
|
Alert("parsing [%s:%d] : '%s %s %s' expects <BINARY STRING> as argument, but %s\n",
|
||||||
|
file, linenum, args[0], args[1], args[2], err);
|
||||||
|
err_code |= ERR_ALERT | ERR_FATAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
tcpcheck->expect_regex = NULL;
|
||||||
|
tcpcheck->inverse = inverse;
|
||||||
|
|
||||||
|
LIST_ADDQ(&curproxy->tcpcheck_rules, &tcpcheck->list);
|
||||||
|
}
|
||||||
|
else if (strcmp(ptr_arg, "string") == 0) {
|
||||||
|
if (!*(args[cur_arg + 1])) {
|
||||||
|
Alert("parsing [%s:%d] : '%s %s %s' expects <string> as an argument.\n",
|
||||||
|
file, linenum, args[0], args[1], ptr_arg);
|
||||||
|
err_code |= ERR_ALERT | ERR_FATAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
struct tcpcheck_rule *tcpcheck;
|
||||||
|
|
||||||
|
tcpcheck = (struct tcpcheck_rule *)calloc(1, sizeof(*tcpcheck));
|
||||||
|
|
||||||
|
tcpcheck->action = TCPCHK_ACT_EXPECT;
|
||||||
|
tcpcheck->string_len = strlen(args[cur_arg + 1]);
|
||||||
|
tcpcheck->string = strdup(args[cur_arg + 1]);
|
||||||
|
tcpcheck->expect_regex = NULL;
|
||||||
|
tcpcheck->inverse = inverse;
|
||||||
|
|
||||||
|
LIST_ADDQ(&curproxy->tcpcheck_rules, &tcpcheck->list);
|
||||||
|
}
|
||||||
|
else if (strcmp(ptr_arg, "rstring") == 0) {
|
||||||
|
if (!*(args[cur_arg + 1])) {
|
||||||
|
Alert("parsing [%s:%d] : '%s %s %s' expects <regex> as an argument.\n",
|
||||||
|
file, linenum, args[0], args[1], ptr_arg);
|
||||||
|
err_code |= ERR_ALERT | ERR_FATAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
struct tcpcheck_rule *tcpcheck;
|
||||||
|
|
||||||
|
tcpcheck = (struct tcpcheck_rule *)calloc(1, sizeof(*tcpcheck));
|
||||||
|
|
||||||
|
tcpcheck->action = TCPCHK_ACT_EXPECT;
|
||||||
|
tcpcheck->string_len = 0;
|
||||||
|
tcpcheck->string = NULL;
|
||||||
|
tcpcheck->expect_regex = calloc(1, sizeof(regex_t));
|
||||||
|
if (regcomp(tcpcheck->expect_regex, args[cur_arg + 1], REG_EXTENDED) != 0) {
|
||||||
|
Alert("parsing [%s:%d] : '%s %s %s' : bad regular expression '%s'.\n",
|
||||||
|
file, linenum, args[0], args[1], ptr_arg, args[cur_arg + 1]);
|
||||||
|
err_code |= ERR_ALERT | ERR_FATAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
tcpcheck->inverse = inverse;
|
||||||
|
|
||||||
|
LIST_ADDQ(&curproxy->tcpcheck_rules, &tcpcheck->list);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Alert("parsing [%s:%d] : '%s %s' only supports [!] 'binary', 'string', 'rstring', found '%s'.\n",
|
||||||
|
file, linenum, args[0], args[1], ptr_arg);
|
||||||
|
err_code |= ERR_ALERT | ERR_FATAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Alert("parsing [%s:%d] : '%s' only supports 'send' or 'expect'.\n", file, linenum, args[0]);
|
||||||
|
err_code |= ERR_ALERT | ERR_FATAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
else if (!strcmp(args[0], "monitor")) {
|
else if (!strcmp(args[0], "monitor")) {
|
||||||
if (curproxy == &defproxy) {
|
if (curproxy == &defproxy) {
|
||||||
Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
|
Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
|
||||||
|
325
src/checks.c
325
src/checks.c
@ -52,6 +52,8 @@
|
|||||||
#include <proto/task.h>
|
#include <proto/task.h>
|
||||||
|
|
||||||
static int httpchk_expect(struct server *s, int done);
|
static int httpchk_expect(struct server *s, int done);
|
||||||
|
static int tcpcheck_get_step_id(struct server *);
|
||||||
|
static void tcpcheck_main(struct connection *);
|
||||||
|
|
||||||
static const struct check_status check_statuses[HCHK_STATUS_SIZE] = {
|
static const struct check_status check_statuses[HCHK_STATUS_SIZE] = {
|
||||||
[HCHK_STATUS_UNKNOWN] = { SRV_CHK_UNKNOWN, "UNK", "Unknown" },
|
[HCHK_STATUS_UNKNOWN] = { SRV_CHK_UNKNOWN, "UNK", "Unknown" },
|
||||||
@ -834,6 +836,7 @@ static void chk_report_conn_err(struct connection *conn, int errno_bck, int expi
|
|||||||
{
|
{
|
||||||
struct check *check = conn->owner;
|
struct check *check = conn->owner;
|
||||||
const char *err_msg;
|
const char *err_msg;
|
||||||
|
struct chunk *chk;
|
||||||
|
|
||||||
if (check->result != SRV_CHK_UNKNOWN)
|
if (check->result != SRV_CHK_UNKNOWN)
|
||||||
return;
|
return;
|
||||||
@ -850,20 +853,36 @@ static void chk_report_conn_err(struct connection *conn, int errno_bck, int expi
|
|||||||
* socket error possibly collected above. This is useful to know the
|
* socket error possibly collected above. This is useful to know the
|
||||||
* exact step of the L6 layer (eg: SSL handshake).
|
* exact step of the L6 layer (eg: SSL handshake).
|
||||||
*/
|
*/
|
||||||
|
chk = get_trash_chunk();
|
||||||
|
|
||||||
|
if (check->type == PR_O2_TCPCHK_CHK) {
|
||||||
|
chunk_printf(chk, " at step %d of tcp-check", tcpcheck_get_step_id(check->server));
|
||||||
|
/* we were looking for a string */
|
||||||
|
if (check->current_step && check->current_step->action == TCPCHK_ACT_EXPECT) {
|
||||||
|
if (check->current_step->string)
|
||||||
|
chunk_appendf(chk, " (string '%s')", check->current_step->string);
|
||||||
|
else if (check->current_step->expect_regex)
|
||||||
|
chunk_appendf(chk, " (expect regex)");
|
||||||
|
}
|
||||||
|
else if (check->current_step && check->current_step->action == TCPCHK_ACT_SEND) {
|
||||||
|
chunk_appendf(chk, " (send)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (conn->err_code) {
|
if (conn->err_code) {
|
||||||
if (errno && errno != EAGAIN)
|
if (errno && errno != EAGAIN)
|
||||||
chunk_printf(&trash, "%s (%s)", conn_err_code_str(conn), strerror(errno));
|
chunk_printf(&trash, "%s (%s)%s", conn_err_code_str(conn), strerror(errno), chk->str);
|
||||||
else
|
else
|
||||||
chunk_printf(&trash, "%s", conn_err_code_str(conn));
|
chunk_printf(&trash, "%s%s", conn_err_code_str(conn), chk->str);
|
||||||
err_msg = trash.str;
|
err_msg = trash.str;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (errno && errno != EAGAIN) {
|
if (errno && errno != EAGAIN) {
|
||||||
chunk_printf(&trash, "%s", strerror(errno));
|
chunk_printf(&trash, "%s%s", strerror(errno), chk->str);
|
||||||
err_msg = trash.str;
|
err_msg = trash.str;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
err_msg = NULL;
|
err_msg = chk->str;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -933,6 +952,11 @@ static void event_srv_chk_w(struct connection *conn)
|
|||||||
if (!check->type)
|
if (!check->type)
|
||||||
goto out_wakeup;
|
goto out_wakeup;
|
||||||
|
|
||||||
|
if (check->type == PR_O2_TCPCHK_CHK) {
|
||||||
|
tcpcheck_main(conn);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (check->bo->o) {
|
if (check->bo->o) {
|
||||||
conn->xprt->snd_buf(conn, check->bo, MSG_DONTWAIT | MSG_NOSIGNAL);
|
conn->xprt->snd_buf(conn, check->bo, MSG_DONTWAIT | MSG_NOSIGNAL);
|
||||||
if (conn->flags & CO_FL_ERROR) {
|
if (conn->flags & CO_FL_ERROR) {
|
||||||
@ -986,6 +1010,11 @@ static void event_srv_chk_r(struct connection *conn)
|
|||||||
if (conn->flags & (CO_FL_HANDSHAKE | CO_FL_WAIT_RD))
|
if (conn->flags & (CO_FL_HANDSHAKE | CO_FL_WAIT_RD))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (check->type == PR_O2_TCPCHK_CHK) {
|
||||||
|
tcpcheck_main(conn);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* Warning! Linux returns EAGAIN on SO_ERROR if data are still available
|
/* Warning! Linux returns EAGAIN on SO_ERROR if data are still available
|
||||||
* but the connection was closed on the remote end. Fortunately, recv still
|
* but the connection was closed on the remote end. Fortunately, recv still
|
||||||
* works correctly and we don't need to do the getsockopt() on linux.
|
* works correctly and we don't need to do the getsockopt() on linux.
|
||||||
@ -1481,11 +1510,17 @@ static struct task *process_chk(struct task *t)
|
|||||||
check->bo->p = check->bo->data;
|
check->bo->p = check->bo->data;
|
||||||
check->bo->o = 0;
|
check->bo->o = 0;
|
||||||
|
|
||||||
/* prepare the check buffer
|
/* tcpcheck send/expect initialisation */
|
||||||
* This should not be used if check is the secondary agent check
|
if (check->type == PR_O2_TCPCHK_CHK)
|
||||||
* of a server as s->proxy->check_req will relate to the
|
check->current_step = NULL;
|
||||||
* configuration of the primary check */
|
|
||||||
if (check->type && check != &s->agent) {
|
/* prepare the check buffer.
|
||||||
|
* This should not be used if check is the secondary agent check
|
||||||
|
* of a server as s->proxy->check_req will relate to the
|
||||||
|
* configuration of the primary check. Similarly, tcp-check uses
|
||||||
|
* its own strings.
|
||||||
|
*/
|
||||||
|
if (check->type && check->type != PR_O2_TCPCHK_CHK && check != &s->agent) {
|
||||||
bo_putblk(check->bo, s->proxy->check_req, s->proxy->check_len);
|
bo_putblk(check->bo, s->proxy->check_req, s->proxy->check_len);
|
||||||
|
|
||||||
/* we want to check if this host replies to HTTP or SSLv3 requests
|
/* we want to check if this host replies to HTTP or SSLv3 requests
|
||||||
@ -1863,6 +1898,278 @@ static int httpchk_expect(struct server *s, int done)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* return the id of a step in a send/expect session
|
||||||
|
*/
|
||||||
|
static int tcpcheck_get_step_id(struct server *s)
|
||||||
|
{
|
||||||
|
struct tcpcheck_rule *cur = NULL, *next = NULL;
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
cur = s->check.current_step;
|
||||||
|
|
||||||
|
/* no step => first step */
|
||||||
|
if (cur == NULL)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
/* increment i until current step */
|
||||||
|
list_for_each_entry(next, &s->proxy->tcpcheck_rules, list) {
|
||||||
|
if (next->list.p == &cur->list)
|
||||||
|
break;
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tcpcheck_main(struct connection *conn)
|
||||||
|
{
|
||||||
|
char *contentptr;
|
||||||
|
unsigned int contentlen;
|
||||||
|
struct list *head = NULL;
|
||||||
|
struct tcpcheck_rule *cur = NULL;
|
||||||
|
int done = 0, ret = 0;
|
||||||
|
|
||||||
|
struct check *check = conn->owner;
|
||||||
|
struct server *s = check->server;
|
||||||
|
struct task *t = check->task;
|
||||||
|
|
||||||
|
/* don't do anything until the connection is established */
|
||||||
|
if (!(conn->flags & CO_FL_CONNECTED)) {
|
||||||
|
/* update expire time, should be done by process_chk */
|
||||||
|
/* we allow up to min(inter, timeout.connect) for a connection
|
||||||
|
* to establish but only when timeout.check is set
|
||||||
|
* as it may be to short for a full check otherwise
|
||||||
|
*/
|
||||||
|
while (tick_is_expired(t->expire, now_ms)) {
|
||||||
|
int t_con;
|
||||||
|
|
||||||
|
t_con = tick_add(t->expire, s->proxy->timeout.connect);
|
||||||
|
t->expire = tick_add(t->expire, MS_TO_TICKS(check->inter));
|
||||||
|
|
||||||
|
if (s->proxy->timeout.check)
|
||||||
|
t->expire = tick_first(t->expire, t_con);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* here, we know that the connection is established */
|
||||||
|
if (check->result & (SRV_CHK_FAILED | SRV_CHK_PASSED)) {
|
||||||
|
goto out_end_tcpcheck;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* head is be the first element of the double chained list */
|
||||||
|
head = &s->proxy->tcpcheck_rules;
|
||||||
|
|
||||||
|
/* no step means first step
|
||||||
|
* initialisation */
|
||||||
|
if (check->current_step == NULL) {
|
||||||
|
check->bo->p = check->bo->data;
|
||||||
|
check->bo->o = 0;
|
||||||
|
check->bi->p = check->bi->data;
|
||||||
|
check->bi->i = 0;
|
||||||
|
cur = check->current_step = LIST_ELEM(head->n, struct tcpcheck_rule *, list);
|
||||||
|
t->expire = tick_add(now_ms, MS_TO_TICKS(check->inter));
|
||||||
|
if (s->proxy->timeout.check)
|
||||||
|
t->expire = tick_add_ifset(now_ms, s->proxy->timeout.check);
|
||||||
|
}
|
||||||
|
/* keep on processing step */
|
||||||
|
else {
|
||||||
|
cur = check->current_step;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (&cur->list != head) {
|
||||||
|
if (check->current_step->action & TCPCHK_ACT_SEND) {
|
||||||
|
/* reset the read buffer */
|
||||||
|
if (*check->bi->data != '\0') {
|
||||||
|
*check->bi->data = '\0';
|
||||||
|
check->bi->i = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conn->flags & (CO_FL_SOCK_WR_SH | CO_FL_DATA_WR_SH)) {
|
||||||
|
conn->flags |= CO_FL_ERROR;
|
||||||
|
chk_report_conn_err(conn, 0, 0);
|
||||||
|
goto out_end_tcpcheck;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conn->flags & (CO_FL_HANDSHAKE | CO_FL_WAIT_WR))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* disable reading for now */
|
||||||
|
//if (conn->flags & (CO_FL_WAIT_RD | CO_FL_DATA_RD_ENA))
|
||||||
|
__conn_data_stop_recv(conn);
|
||||||
|
|
||||||
|
bo_putblk(check->bo, check->current_step->string, check->current_step->string_len);
|
||||||
|
*check->bo->p = '\0'; /* to make gdb output easier to read */
|
||||||
|
|
||||||
|
if (check->bo->o) {
|
||||||
|
conn->xprt->snd_buf(conn, check->bo, MSG_DONTWAIT | MSG_NOSIGNAL);
|
||||||
|
if (conn->flags & CO_FL_ERROR) {
|
||||||
|
chk_report_conn_err(conn, errno, 0);
|
||||||
|
__conn_data_stop_both(conn);
|
||||||
|
goto out_end_tcpcheck;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cur = (struct tcpcheck_rule *)cur->list.n;
|
||||||
|
check->current_step = cur;
|
||||||
|
|
||||||
|
if (check->bo->o)
|
||||||
|
goto out_incomplete;
|
||||||
|
|
||||||
|
__conn_data_stop_send(conn); /* nothing more to write */
|
||||||
|
} /* end 'send' */
|
||||||
|
else if (check->current_step->action & TCPCHK_ACT_EXPECT) {
|
||||||
|
if (unlikely(check->result & SRV_CHK_FAILED))
|
||||||
|
goto out_end_tcpcheck;
|
||||||
|
|
||||||
|
if (conn->flags & (CO_FL_HANDSHAKE | CO_FL_WAIT_RD))
|
||||||
|
return;
|
||||||
|
|
||||||
|
conn->xprt->rcv_buf(conn, check->bi, buffer_total_space(check->bi));
|
||||||
|
|
||||||
|
if (conn->flags & (CO_FL_ERROR | CO_FL_SOCK_RD_SH | CO_FL_DATA_RD_SH)) {
|
||||||
|
done = 1;
|
||||||
|
if ((conn->flags & CO_FL_ERROR) && !check->bi->i) {
|
||||||
|
/* Report network errors only if we got no other data. Otherwise
|
||||||
|
* we'll let the upper layers decide whether the response is OK
|
||||||
|
* or not. It is very common that an RST sent by the server is
|
||||||
|
* reported as an error just after the last data chunk.
|
||||||
|
*/
|
||||||
|
chk_report_conn_err(conn, errno, 0);
|
||||||
|
goto out_end_tcpcheck;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Intermediate or complete response received.
|
||||||
|
* Terminate string in check->bi->data buffer.
|
||||||
|
*/
|
||||||
|
if (check->bi->i < check->bi->size) {
|
||||||
|
check->bi->data[check->bi->i] = '\0';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
check->bi->data[check->bi->i - 1] = '\0';
|
||||||
|
done = 1; /* buffer full, don't wait for more data */
|
||||||
|
}
|
||||||
|
|
||||||
|
contentptr = check->bi->data;
|
||||||
|
contentlen = check->bi->i;
|
||||||
|
|
||||||
|
/* Check that response body is not empty... */
|
||||||
|
if (*contentptr == '\0') {
|
||||||
|
if (!done)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* empty response */
|
||||||
|
chunk_printf(&trash, "TCPCHK got an empty response at step %d",
|
||||||
|
tcpcheck_get_step_id(s));
|
||||||
|
set_server_check_status(check, HCHK_STATUS_L7RSP, trash.str);
|
||||||
|
|
||||||
|
goto out_end_tcpcheck;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!done && (cur->string != NULL) && (check->bi->i < cur->string_len) )
|
||||||
|
goto wait_more_data;
|
||||||
|
|
||||||
|
tcpcheck_expect:
|
||||||
|
if (cur->string != NULL)
|
||||||
|
ret = my_memmem(contentptr, contentlen, cur->string, cur->string_len) != NULL;
|
||||||
|
else if (cur->expect_regex != NULL)
|
||||||
|
ret = regexec(cur->expect_regex, contentptr, MAX_MATCH, pmatch, 0) == 0;
|
||||||
|
|
||||||
|
if (!ret && !done)
|
||||||
|
goto wait_more_data;
|
||||||
|
|
||||||
|
/* matched */
|
||||||
|
if (ret) {
|
||||||
|
/* matched but we did not want to => ERROR */
|
||||||
|
if (cur->inverse) {
|
||||||
|
/* we were looking for a string */
|
||||||
|
if (cur->string != NULL) {
|
||||||
|
chunk_printf(&trash, "TCPCHK matched unwanted content '%s' at step %d",
|
||||||
|
cur->string, tcpcheck_get_step_id(s));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* we were looking for a regex */
|
||||||
|
chunk_printf(&trash, "TCPCHK matched unwanted content (regex) at step %d",
|
||||||
|
tcpcheck_get_step_id(s));
|
||||||
|
}
|
||||||
|
set_server_check_status(check, HCHK_STATUS_L7RSP, trash.str);
|
||||||
|
goto out_end_tcpcheck;
|
||||||
|
}
|
||||||
|
/* matched and was supposed to => OK, next step */
|
||||||
|
else {
|
||||||
|
cur = (struct tcpcheck_rule*)cur->list.n;
|
||||||
|
check->current_step = cur;
|
||||||
|
if (check->current_step->action & TCPCHK_ACT_EXPECT)
|
||||||
|
goto tcpcheck_expect;
|
||||||
|
__conn_data_stop_recv(conn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* not matched */
|
||||||
|
/* not matched and was not supposed to => OK, next step */
|
||||||
|
if (cur->inverse) {
|
||||||
|
cur = (struct tcpcheck_rule*)cur->list.n;
|
||||||
|
check->current_step = cur;
|
||||||
|
if (check->current_step->action & TCPCHK_ACT_EXPECT)
|
||||||
|
goto tcpcheck_expect;
|
||||||
|
__conn_data_stop_recv(conn);
|
||||||
|
}
|
||||||
|
/* not matched but was supposed to => ERROR */
|
||||||
|
else {
|
||||||
|
/* we were looking for a string */
|
||||||
|
if (cur->string != NULL) {
|
||||||
|
chunk_printf(&trash, "TCPCHK did not match content '%s' at step %d",
|
||||||
|
cur->string, tcpcheck_get_step_id(s));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* we were looking for a regex */
|
||||||
|
chunk_printf(&trash, "TCPCHK did not match content (regex) at step %d",
|
||||||
|
tcpcheck_get_step_id(s));
|
||||||
|
}
|
||||||
|
set_server_check_status(check, HCHK_STATUS_L7RSP, trash.str);
|
||||||
|
goto out_end_tcpcheck;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} /* end expect */
|
||||||
|
} /* end loop over double chained step list */
|
||||||
|
|
||||||
|
set_server_check_status(check, HCHK_STATUS_L7OKD, "(tcp-check)");
|
||||||
|
goto out_end_tcpcheck;
|
||||||
|
|
||||||
|
wait_more_data:
|
||||||
|
__conn_data_poll_recv(conn);
|
||||||
|
return;
|
||||||
|
|
||||||
|
out_incomplete:
|
||||||
|
return;
|
||||||
|
|
||||||
|
out_end_tcpcheck:
|
||||||
|
/* collect possible new errors */
|
||||||
|
if (conn->flags & CO_FL_ERROR)
|
||||||
|
chk_report_conn_err(conn, 0, 0);
|
||||||
|
|
||||||
|
/* Close the connection... We absolutely want to perform a hard close
|
||||||
|
* and reset the connection if some data are pending, otherwise we end
|
||||||
|
* up with many TIME_WAITs and eat all the source port range quickly.
|
||||||
|
* To avoid sending RSTs all the time, we first try to drain pending
|
||||||
|
* data.
|
||||||
|
*/
|
||||||
|
if (conn->xprt && conn->xprt->shutw)
|
||||||
|
conn->xprt->shutw(conn, 0);
|
||||||
|
|
||||||
|
check->current_step = NULL;
|
||||||
|
|
||||||
|
if (check->result & SRV_CHK_FAILED)
|
||||||
|
conn->flags |= CO_FL_ERROR;
|
||||||
|
|
||||||
|
__conn_data_stop_both(conn);
|
||||||
|
task_wakeup(t, TASK_WOKEN_IO);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Local variables:
|
* Local variables:
|
||||||
* c-indent-level: 8
|
* c-indent-level: 8
|
||||||
|
@ -454,6 +454,7 @@ void init_new_proxy(struct proxy *p)
|
|||||||
LIST_INIT(&p->conf.bind);
|
LIST_INIT(&p->conf.bind);
|
||||||
LIST_INIT(&p->conf.listeners);
|
LIST_INIT(&p->conf.listeners);
|
||||||
LIST_INIT(&p->conf.args.list);
|
LIST_INIT(&p->conf.args.list);
|
||||||
|
LIST_INIT(&p->tcpcheck_rules);
|
||||||
|
|
||||||
/* Timeouts are defined as -1 */
|
/* Timeouts are defined as -1 */
|
||||||
proxy_reset_timeouts(p);
|
proxy_reset_timeouts(p);
|
||||||
|
Loading…
Reference in New Issue
Block a user