of a dynamic page, or to detect a failure when a specific
error appears on the check page (e.g. a stack trace).
+ string-lf <fmt> : test a log-format string match in the HTTP response body.
+ A health check response will be considered valid if the
+ response's body contains the string resulting of the
+ evaluation of <fmt>, which follows the log-format rules.
+ If prefixed with "!", then the response will be
+ considered invalid if the body contains the string.
+
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
will be considered invalid if the body matches the
expression.
+ string-lf <fmt> : test a log-format string match in the response's buffer.
+ A health check response will be considered valid if the
+ response's buffer contains the string resulting of the
+ evaluation of <fmt>, which follows the log-format rules.
+ If prefixed with "!", then the response will be
+ considered invalid if the buffer contains the string.
+
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
pattern should work on at-most half the response buffer
size.
+ binary-lf <hexfmt> : test a log-format string in its hexadecimal form
+ match in the response's buffer. A health check response
+ will be considered valid if the response's buffer
+ contains the hexadecimal string resulting of the
+ evaluation of <fmt>, which follows the log-format
+ rules. If prefixed with "!", then the response will be
+ considered invalid if the buffer contains the
+ hexadecimal string. The hexadecimal string is converted
+ in a binary string before matching the response's
+ buffer.
+
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
TCPCHK_EXPECT_UNDEF = 0, /* Match is not used. */
TCPCHK_EXPECT_STRING, /* Matches a string. */
TCPCHK_EXPECT_REGEX, /* Matches a regular pattern. */
+ TCPCHK_EXPECT_STRING_LF, /* Matches a log-format string. */
TCPCHK_EXPECT_REGEX_BINARY, /* Matches a regular pattern on a hex-encoded text. */
TCPCHK_EXPECT_BINARY, /* Matches a binary sequence on a hex-encoded text. */
+ TCPCHK_EXPECT_BINARY_LF, /* Matches a log-format binary sequence on a hex-encoded text. */
TCPCHK_EXPECT_CUSTOM, /* Execute a custom function. */
TCPCHK_EXPECT_HTTP_STATUS, /* Matches a list of codes on the HTTP status */
TCPCHK_EXPECT_HTTP_REGEX_STATUS, /* Matches a regular pattern on the HTTP status */
TCPCHK_EXPECT_HTTP_HEADER, /* Matches on HTTP headers */
TCPCHK_EXPECT_HTTP_BODY, /* Matches a string oa the HTTP payload */
TCPCHK_EXPECT_HTTP_REGEX_BODY, /* Matches a regular pattern on a HTTP payload */
+ TCPCHK_EXPECT_HTTP_BODY_LF, /* Matches a log-format string on the HTTP payload */
};
/* tcp-check expect flags */
#define TCPCHK_EXPT_FL_HTTP_HNAME_TYPE 0x003E /* Mask to get matching method on header name */
#define TCPCHK_EXPT_FL_HTTP_HVAL_TYPE 0x1F00 /* Mask to get matching method on header value */
+
struct tcpcheck_expect {
enum tcpcheck_expect_type type; /* Type of pattern used for matching. */
unsigned int flags; /* TCPCHK_EXPT_FL_* */
struct ist data; /* Matching a literal string / binary anywhere in the response. */
struct my_regex *regex; /* Matching a regex pattern. */
struct tcpcheck_codes codes; /* Matching a list of codes */
+ struct list fmt; /* Matching a log-format string / binary */
struct {
union {
struct ist name;
case TCPCHK_EXPECT_REGEX_BINARY:
chunk_appendf(chk, " (expect binary regex)");
break;
+ case TCPCHK_EXPECT_STRING_LF:
+ chunk_appendf(chk, " (expect log-format string)");
+ break;
+ case TCPCHK_EXPECT_BINARY_LF:
+ chunk_appendf(chk, " (expect log-format binary)");
+ break;
case TCPCHK_EXPECT_HTTP_STATUS:
chunk_appendf(chk, " (expect HTTP status codes)");
break;
case TCPCHK_EXPECT_HTTP_REGEX_BODY:
chunk_appendf(chk, " (expect HTTP body regex)");
break;
+ case TCPCHK_EXPECT_HTTP_BODY_LF:
+ chunk_appendf(chk, " (expect log-format HTTP body)");
+ break;
case TCPCHK_EXPECT_CUSTOM:
chunk_appendf(chk, " (expect custom function)");
break;
case TCPCHK_EXPECT_HTTP_REGEX_BODY:
regex_free(rule->expect.regex);
break;
+ case TCPCHK_EXPECT_STRING_LF:
+ case TCPCHK_EXPECT_BINARY_LF:
+ case TCPCHK_EXPECT_HTTP_BODY_LF:
+ free_tcpcheck_fmt(&rule->expect.fmt);
+ break;
case TCPCHK_EXPECT_HTTP_HEADER:
if (rule->expect.flags & TCPCHK_EXPT_FL_HTTP_HNAME_REG)
regex_free(rule->expect.hdr.name_re);
case TCPCHK_EXPECT_REGEX_BINARY:
chunk_appendf(msg, " (binary regex) at step %d", tcpcheck_get_step_id(check, rule));
break;
+ case TCPCHK_EXPECT_STRING_LF:
+ case TCPCHK_EXPECT_HTTP_BODY_LF:
+ chunk_appendf(msg, " (log-format string) at step %d", tcpcheck_get_step_id(check, rule));
+ break;
+ case TCPCHK_EXPECT_BINARY_LF:
+ chunk_appendf(msg, " (log-format binary) at step %d", tcpcheck_get_step_id(check, rule));
+ break;
case TCPCHK_EXPECT_CUSTOM:
chunk_appendf(msg, " (custom function) at step %d", tcpcheck_get_step_id(check, rule));
break;
struct htx_blk *blk;
enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
struct tcpcheck_expect *expect = &rule->expect;
- struct buffer *msg = NULL, *nbuf = NULL, *vbuf = NULL;
+ struct buffer *msg = NULL, *tmp = NULL, *nbuf = NULL, *vbuf = NULL;
enum healthcheck_status status = HCHK_STATUS_L7RSP;
struct ist desc = IST_NULL;
int i, match, inverse;
case TCPCHK_EXPECT_HTTP_BODY:
case TCPCHK_EXPECT_HTTP_REGEX_BODY:
+ case TCPCHK_EXPECT_HTTP_BODY_LF:
+ match = 0;
chunk_reset(&trash);
for (blk = htx_get_head_blk(htx); blk; blk = htx_get_next_blk(htx, blk)) {
enum htx_blk_type type = htx_get_blk_type(blk);
goto error;
}
+ if (expect->type == TCPCHK_EXPECT_HTTP_BODY_LF) {
+ tmp = alloc_trash_chunk();
+ if (!tmp) {
+ status = HCHK_STATUS_L7RSP;
+ desc = ist("Failed to allocate buffer to eval log-format string");
+ goto error;
+ }
+ tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &expect->fmt);
+ if (!b_data(tmp)) {
+ status = HCHK_STATUS_L7RSP;
+ desc = ist("log-format string evaluated to an empty string");
+ goto error;
+ }
+ }
+
if (!last_read &&
((expect->type == TCPCHK_EXPECT_HTTP_BODY && b_data(&trash) < istlen(expect->data)) ||
+ ((expect->type == TCPCHK_EXPECT_HTTP_BODY_LF && b_data(&trash) < b_data(tmp))) ||
(expect->min_recv > 0 && b_data(&trash) < expect->min_recv))) {
ret = TCPCHK_EVAL_WAIT;
goto out;
goto error;
out:
+ free_trash_chunk(tmp);
free_trash_chunk(nbuf);
free_trash_chunk(vbuf);
free_trash_chunk(msg);
{
enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
struct tcpcheck_expect *expect = &rule->expect;
- struct buffer *msg = NULL;
+ struct buffer *msg = NULL, *tmp = NULL;
struct ist desc = IST_NULL;
enum healthcheck_status status;
int match, inverse;
dump_binary(&trash, b_head(&check->bi), b_data(&check->bi));
match = regex_exec2(expect->regex, b_head(&trash), MIN(b_data(&trash), b_size(&trash)-1));
break;
+
+ case TCPCHK_EXPECT_STRING_LF:
+ case TCPCHK_EXPECT_BINARY_LF:
+ match = 0;
+ tmp = alloc_trash_chunk();
+ if (!tmp) {
+ status = HCHK_STATUS_L7RSP;
+ desc = ist("Failed to allocate buffer to eval format string");
+ goto error;
+ }
+ tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &expect->fmt);
+ if (!b_data(tmp)) {
+ status = HCHK_STATUS_L7RSP;
+ desc = ist("log-format string evaluated to an empty string");
+ goto error;
+ }
+ if (expect->type == TCPCHK_EXPECT_BINARY_LF) {
+ int len = tmp->data;
+ if (parse_binary(b_orig(tmp), &tmp->area, &len, NULL) == 0) {
+ status = HCHK_STATUS_L7RSP;
+ desc = ist("Failed to parse hexastring resulting of eval of a log-format string");
+ goto error;
+ }
+ tmp->data = len;
+ }
+ if (b_data(&check->bi) < tmp->data) {
+ if (!last_read) {
+ ret = TCPCHK_EVAL_WAIT;
+ goto out;
+ }
+ break;
+ }
+ match = my_memmem(b_head(&check->bi), b_data(&check->bi), b_orig(tmp), b_data(tmp)) != NULL;
+ break;
+
case TCPCHK_EXPECT_CUSTOM:
if (expect->custom)
ret = expect->custom(check, rule, last_read);
if (match ^ inverse)
goto out;
-
+ error:
/* From this point on, we matched something we did not want, this is an error state. */
ret = TCPCHK_EVAL_STOP;
msg = alloc_trash_chunk();
free_trash_chunk(msg);
out:
+ free_trash_chunk(tmp);
return ret;
}
cur_arg++;
pattern = args[cur_arg];
}
+ else if (strcmp(args[cur_arg], "string-lf") == 0 || strcmp(args[cur_arg], "binary-lf") == 0) {
+ if (type != TCPCHK_EXPECT_UNDEF) {
+ memprintf(errmsg, "only on pattern expected");
+ goto error;
+ }
+ if (proto != TCPCHK_RULES_HTTP_CHK)
+ type = ((*(args[cur_arg]) == 's') ? TCPCHK_EXPECT_STRING_LF : TCPCHK_EXPECT_BINARY_LF);
+ else {
+ if (*(args[cur_arg]) != 's')
+ goto bad_http_kw;
+ type = TCPCHK_EXPECT_HTTP_BODY_LF;
+ }
+
+ if (!*(args[cur_arg+1])) {
+ memprintf(errmsg, "'%s' expects a <pattern> as argument", args[cur_arg]);
+ goto error;
+ }
+ cur_arg++;
+ pattern = args[cur_arg];
+ }
else if (strcmp(args[cur_arg], "status") == 0 || strcmp(args[cur_arg], "rstatus") == 0) {
if (proto != TCPCHK_RULES_HTTP_CHK)
goto bad_tcp_kw;
else {
if (proto == TCPCHK_RULES_HTTP_CHK) {
bad_http_kw:
- memprintf(errmsg, "'only supports min-recv, [!]string', '[!]rstring', '[!]status', '[!]rstatus'"
- "[!]header or comment but got '%s' as argument.", args[cur_arg]);
+ memprintf(errmsg, "'only supports min-recv, [!]string', '[!]rstring', '[!]string-lf', '[!]status', "
+ "'[!]rstatus', [!]header or comment but got '%s' as argument.", args[cur_arg]);
}
else {
bad_tcp_kw:
- memprintf(errmsg, "'only supports min-recv, '[!]binary', '[!]string', '[!]rstring', '[!]rbinary'"
- " or comment but got '%s' as argument.", args[cur_arg]);
+ memprintf(errmsg, "'only supports min-recv, '[!]binary', '[!]string', '[!]rstring', '[!]string-lf'"
+ "'[!]rbinary', '[!]binary-lf' or comment but got '%s' as argument.", args[cur_arg]);
}
goto error;
}
if (!chk->expect.regex)
goto error;
break;
+
+ case TCPCHK_EXPECT_STRING_LF:
+ case TCPCHK_EXPECT_BINARY_LF:
+ case TCPCHK_EXPECT_HTTP_BODY_LF:
+ LIST_INIT(&chk->expect.fmt);
+ px->conf.args.ctx = ARGC_SRV;
+ if (!parse_logformat_string(pattern, px, &chk->expect.fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
+ memprintf(errmsg, "'%s' invalid log-format string (%s).\n", pattern, *errmsg);
+ goto error;
+ }
+ break;
+
case TCPCHK_EXPECT_HTTP_HEADER:
if (!npat) {
memprintf(errmsg, "unexpected error, undefined header name pattern");