See also : "option tcp-check", "tcp-check send", "tcp-check expect"
-tcp-check expect [!] <match> <pattern>
+tcp-check expect [min-recv <int>] [!] <match> <pattern>
Specify data to be collected and analyzed during a generic health check
May be used in sections: defaults | frontend | listen | backend
no | no | yes | yes
Arguments :
+ min-recv is optional and can define the minimum amount of data required to
+ evaluate the current expect rule. If the number of received bytes
+ is under this limit, the check will wait for more data. This
+ option can be used to resolve some ambiguous matching rules or to
+ avoid executing costly regex matches on content known to be still
+ incomplete. If an exact string (string or binary) is used, the
+ minimum between the string length and this parameter is used.
+ This parameter is ignored if it is set to -1. If the expect rule
+ does not match, the check will wait for more data. If set to 0,
+ the evaluation result is always conclusive.
+
<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.
/* sent string is string */
char *string; /* sent or expected string */
int string_len; /* string length */
+ int min_recv; /* Minimum amount of data before an expect can be applied. (default: -1, ignored) */
struct my_regex *expect_regex; /* expected */
int inverse; /* 0 = regular match, 1 = inverse match */
unsigned short port; /* port to connect to */
--- /dev/null
+varnishtest "tcp-check negative bounded regex match"
+#EXCLUDE_TARGETS=freebsd,osx,generic
+#REGTEST_TYPE=slow
+#REQUIRE_VERSION=2.2
+# This test use a negative expect rule and verify that setting a required
+# minimum amount of data to match.
+feature ignore_unknown_macro
+
+syslog S1 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Proxy be1 started."
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be1/srv1 failed, reason: Layer7 timeout.*at step 2 of tcp-check"
+} -start
+
+syslog S2 -level notice {
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Proxy be2 started."
+ recv
+ expect ~ "[^:\\[ ]\\[${h1_pid}\\]: Health check for server be2/srv1 succeeded"
+} -start
+
+server s1 {
+ send "valid"
+ delay 0.2
+ expect_close
+} -start
+
+server s2 {
+ send "valid"
+ recv 10
+ send "valid"
+ delay 0.2
+ expect_close
+} -start
+
+haproxy h1 -conf {
+ defaults
+ mode tcp
+ timeout connect 200ms
+ timeout check 500ms
+ timeout server 5s
+ timeout client 5s
+
+ backend be1
+ log ${S1_addr}:${S1_port} len 2048 local0
+ option tcp-check
+ option log-health-checks
+ tcp-check connect
+ tcp-check expect !rstring "^error" comment "negative check"
+ tcp-check expect string "valid" comment "positive check"
+ tcp-check send "0123456789"
+ tcp-check expect string "valid" comment "positive check"
+ server srv1 ${s1_addr}:${s1_port} check inter 200ms rise 1 fall 1
+
+ backend be2
+ log ${S2_addr}:${S2_port} len 2048 local0
+ option tcp-check
+ option log-health-checks
+ tcp-check connect
+ tcp-check expect min-recv 5 !rstring "^error" comment "negative check"
+ tcp-check expect string "valid" comment "positive check"
+ tcp-check send "0123456789"
+ tcp-check expect string "valid" comment "positive check"
+ server srv1 ${s2_addr}:${s2_port} check inter 200ms rise 1 fall 1
+} -start
+
+syslog S1 -wait
+syslog S2 -wait
}
else if (strcmp(args[1], "expect") == 0) {
struct tcpcheck_rule *tcpcheck, *prev_check;
+ long min_recv = -1;
const char *ptr_arg;
int cur_arg;
int inverse = 0;
}
cur_arg = 2;
+
+ /* Parse potential the minimum amount of data
+ * required before proceeding with the match.
+ */
+ if (strcmp(args[cur_arg], "min-recv") == 0) {
+ if (!*(args[cur_arg + 1])) {
+ ha_alert("parsing [%s:%d] : '%s %s %s' expects an integer as an argument.\n",
+ file, linenum, args[0], args[1], args[2]);
+ err_code |= ERR_ALERT | ERR_FATAL;
+ goto out;
+ }
+
+ /* Use an signed integer here because of chksize */
+ min_recv = atol(args[cur_arg + 1]);
+ if (min_recv < -1 || min_recv > INT_MAX) {
+ ha_alert("parsing [%s:%d] : '%s %s %s' expects -1 or an integer from 0 to INT_MAX.\n",
+ file, linenum, args[0], args[1], args[2]);
+ 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 == '!') {
tcpcheck = calloc(1, sizeof(*tcpcheck));
tcpcheck->action = TCPCHK_ACT_EXPECT;
tcpcheck->inverse = inverse;
+ tcpcheck->min_recv = min_recv;
if (strcmp(ptr_arg, "binary") == 0) {
char *err = NULL;
}
tcpcheck_expect:
- if (!done && (check->current_step->string != NULL) && (b_data(&check->bi) < check->current_step->string_len) )
+
+ /* The current expect might need more data than the previous one, check again
+ * that the minimum amount data required to match is respected.
+ */
+ if (!done &&
+ (((check->current_step->string != NULL) && (b_data(&check->bi) < check->current_step->string_len)) ||
+ ((check->current_step->min_recv > 0 && (b_data(&check->bi) < check->current_step->min_recv)))))
continue; /* try to read more */
if (check->current_step->string != NULL)
ret = my_memmem(contentptr, b_data(&check->bi), check->current_step->string, check->current_step->string_len) != NULL;
else if (check->current_step->expect_regex != NULL)
ret = regex_exec(check->current_step->expect_regex, contentptr);
- if (!ret && !done)
+ /* Wait for more data on mismatch only if no minimum is defined (-1),
+ * otherwise the absence of match is already conclusive.
+ */
+ if (!ret && !done && (check->current_step->min_recv == -1))
continue; /* try to read more */
/* matched */