TCPCHK_EXPECT_STRING, /* Matches a string. */
TCPCHK_EXPECT_REGEX, /* Matches a regular pattern. */
TCPCHK_EXPECT_REGEX_BINARY, /* Matches a regular pattern on a hex-encoded text. */
- TCPCHK_EXPECT_BINARY, /* Matches a binary sequence. */
+ TCPCHK_EXPECT_BINARY, /* Matches a binary sequence on a hex-encoded text. */
TCPCHK_EXPECT_CUSTOM, /* Execute a custom function. */
- TCPCHK_EXPECT_HTTP_STATUS, /* Matches a string */
- TCPCHK_EXPECT_HTTP_REGEX_STATUS, /* Matches a regular pattern */
- TCPCHK_EXPECT_HTTP_BODY, /* Matches a string */
- TCPCHK_EXPECT_HTTP_REGEX_BODY, /* Matches a regular pattern */
+ 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 */
};
/* tcp-check expect flags */
-#define TCPCHK_EXPT_FL_INV 0x0001 /* Matching is inversed */
-
+#define TCPCHK_EXPT_FL_INV 0x0001 /* Matching is inversed */
+#define TCPCHK_EXPT_FL_HTTP_HNAME_STR 0x0002 /* Exact match on the HTTP header name */
+#define TCPCHK_EXPT_FL_HTTP_HNAME_BEG 0x0004 /* Prefix match on the HTTP header name */
+#define TCPCHK_EXPT_FL_HTTP_HNAME_END 0x0008 /* Suffix match on the HTTP header name */
+#define TCPCHK_EXPT_FL_HTTP_HNAME_SUB 0x0010 /* Substring match on the HTTP header name */
+#define TCPCHK_EXPT_FL_HTTP_HNAME_REG 0x0020 /* Regex match on the HTTP header name */
+#define TCPCHK_EXPT_FL_HTTP_HNAME_FMT 0x0040 /* The HTTP header name is a log-format string */
+#define TCPCHK_EXPT_FL_HTTP_HVAL_NONE 0x0080 /* No match on the HTTP header value */
+#define TCPCHK_EXPT_FL_HTTP_HVAL_STR 0x0100 /* Exact match on the HTTP header value */
+#define TCPCHK_EXPT_FL_HTTP_HVAL_BEG 0x0200 /* Prefix match on the HTTP header value */
+#define TCPCHK_EXPT_FL_HTTP_HVAL_END 0x0400 /* Suffix match on the HTTP header value */
+#define TCPCHK_EXPT_FL_HTTP_HVAL_SUB 0x0800 /* Substring match on the HTTP header value */
+#define TCPCHK_EXPT_FL_HTTP_HVAL_REG 0x1000 /* Regex match on the HTTP header value*/
+#define TCPCHK_EXPT_FL_HTTP_HVAL_FMT 0x2000 /* The HTTP header value is a log-format string */
+#define TCPCHK_EXPT_FL_HTTP_HVAL_FULL 0x4000 /* Match the full header value ( no stop on commas ) */
+
+#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 {
+ union {
+ struct ist name;
+ struct list name_fmt;
+ struct my_regex *name_re;
+ };
+ union {
+ struct ist value;
+ struct list value_fmt;
+ struct my_regex *value_re;
+ };
+ } hdr; /* Matching a header pattern */
+
/* custom function to eval epxect rule */
enum tcpcheck_eval_ret (*custom)(struct check *, struct tcpcheck_rule *, int);
case TCPCHK_EXPECT_HTTP_REGEX_STATUS:
chunk_appendf(chk, " (expect HTTP status regex)");
break;
+ case TCPCHK_EXPECT_HTTP_HEADER:
+ chunk_appendf(chk, " (expect HTTP header pattern)");
+ break;
case TCPCHK_EXPECT_HTTP_BODY:
chunk_appendf(chk, " (expect HTTP body content '%.*s')", (unsigned int)istlen(expect->data), istptr(expect->data));
break;
case TCPCHK_EXPECT_HTTP_REGEX_BODY:
regex_free(rule->expect.regex);
break;
+ case TCPCHK_EXPECT_HTTP_HEADER:
+ if (rule->expect.flags & TCPCHK_EXPT_FL_HTTP_HNAME_REG)
+ regex_free(rule->expect.hdr.name_re);
+ else if (rule->expect.flags & TCPCHK_EXPT_FL_HTTP_HNAME_FMT)
+ free_tcpcheck_fmt(&rule->expect.hdr.name_fmt);
+ else
+ istfree(&rule->expect.hdr.name);
+
+ if (rule->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_REG)
+ regex_free(rule->expect.hdr.value_re);
+ else if (rule->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_FMT)
+ free_tcpcheck_fmt(&rule->expect.hdr.value_fmt);
+ else if (!(rule->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_NONE))
+ istfree(&rule->expect.hdr.value);
+ break;
case TCPCHK_EXPECT_CUSTOM:
case TCPCHK_EXPECT_UNDEF:
break;
case TCPCHK_EXPECT_CUSTOM:
chunk_appendf(msg, " (custom function) at step %d", tcpcheck_get_step_id(check, rule));
break;
+ case TCPCHK_EXPECT_HTTP_HEADER:
+ chunk_appendf(msg, " (header pattern) at step %d", tcpcheck_get_step_id(check, rule));
case TCPCHK_EXPECT_UNDEF:
/* Should never happen. */
return;
struct htx_blk *blk;
enum tcpcheck_eval_ret ret = TCPCHK_EVAL_CONTINUE;
struct tcpcheck_expect *expect = &rule->expect;
- struct buffer *msg = NULL;
- enum healthcheck_status status;
+ struct buffer *msg = NULL, *nbuf = NULL, *vbuf = NULL;
+ enum healthcheck_status status = HCHK_STATUS_L7RSP;
struct ist desc = IST_NULL;
int i, match, inverse;
desc = htx_sl_res_reason(sl);
break;
+ case TCPCHK_EXPECT_HTTP_HEADER: {
+ struct http_hdr_ctx ctx;
+ struct ist npat, vpat, value;
+ int full = (expect->flags & (TCPCHK_EXPT_FL_HTTP_HVAL_NONE|TCPCHK_EXPT_FL_HTTP_HVAL_FULL));
+
+ if (expect->flags & TCPCHK_EXPT_FL_HTTP_HNAME_FMT) {
+ nbuf = alloc_trash_chunk();
+ if (!nbuf)
+ goto error;
+ nbuf->data = sess_build_logline(check->sess, NULL, b_orig(nbuf), b_size(nbuf), &expect->hdr.name_fmt);
+ npat = ist2(b_orig(nbuf), b_data(nbuf));
+ }
+ else if (!(expect->flags & TCPCHK_EXPT_FL_HTTP_HNAME_REG))
+ npat = expect->hdr.name;
+
+ if (expect->flags & TCPCHK_EXPT_FL_HTTP_HVAL_FMT) {
+ vbuf = alloc_trash_chunk();
+ if (!vbuf)
+ goto error;
+ vbuf->data = sess_build_logline(check->sess, NULL, b_orig(vbuf), b_size(vbuf), &expect->hdr.value_fmt);
+ vpat = ist2(b_orig(vbuf), b_data(vbuf));
+ }
+ else if (!(expect->flags & TCPCHK_EXPT_FL_HTTP_HVAL_REG))
+ vpat = expect->hdr.value;
+
+ match = 0;
+ ctx.blk = NULL;
+ while (1) {
+ switch (expect->flags & TCPCHK_EXPT_FL_HTTP_HNAME_TYPE) {
+ case TCPCHK_EXPT_FL_HTTP_HNAME_STR:
+ if (!http_find_str_header(htx, npat, &ctx, full))
+ goto end_of_match;
+ break;
+ case TCPCHK_EXPT_FL_HTTP_HNAME_BEG:
+ if (!http_find_pfx_header(htx, npat, &ctx, full))
+ goto end_of_match;
+ break;
+ case TCPCHK_EXPT_FL_HTTP_HNAME_END:
+ if (!http_find_sfx_header(htx, npat, &ctx, full))
+ goto end_of_match;
+ break;
+ case TCPCHK_EXPT_FL_HTTP_HNAME_SUB:
+ if (!http_find_sub_header(htx, npat, &ctx, full))
+ goto end_of_match;
+ break;
+ case TCPCHK_EXPT_FL_HTTP_HNAME_REG:
+ if (!http_match_header(htx, expect->hdr.name_re, &ctx, full))
+ goto end_of_match;
+ break;
+ }
+
+ if (expect->flags & TCPCHK_EXPT_FL_HTTP_HVAL_NONE) {
+ match = 1;
+ goto end_of_match;
+ }
+
+ value = ctx.value;
+ switch (expect->flags & TCPCHK_EXPT_FL_HTTP_HVAL_TYPE) {
+ case TCPCHK_EXPT_FL_HTTP_HVAL_STR:
+ if (isteq(value, vpat)) {
+ match = 1;
+ goto end_of_match;
+ }
+ break;
+ case TCPCHK_EXPT_FL_HTTP_HVAL_BEG:
+ if (istlen(value) < istlen(vpat))
+ break;
+ value = ist2(istptr(value), istlen(vpat));
+ if (isteq(value, vpat)) {
+ match = 1;
+ goto end_of_match;
+ }
+ break;
+ case TCPCHK_EXPT_FL_HTTP_HVAL_END:
+ if (istlen(value) < istlen(vpat))
+ break;
+ value = ist2(istptr(value) + istlen(value) - istlen(vpat), istlen(vpat));
+ if (isteq(value, vpat)) {
+ match = 1;
+ goto end_of_match;
+ }
+ break;
+ case TCPCHK_EXPT_FL_HTTP_HVAL_SUB:
+ if (isttest(istist(value, vpat))) {
+ match = 1;
+ goto end_of_match;
+ }
+ break;
+ case TCPCHK_EXPT_FL_HTTP_HVAL_REG:
+ if (regex_exec2(expect->hdr.value_re, istptr(value), istlen(value))) {
+ match = 1;
+ goto end_of_match;
+ }
+ break;
+ }
+ }
+
+ end_of_match:
+ status = ((rule->expect.err_status != HCHK_STATUS_UNKNOWN) ? rule->expect.err_status : HCHK_STATUS_L7STS);
+ if (LIST_ISEMPTY(&expect->onerror_fmt))
+ desc = htx_sl_res_reason(sl);
+ break;
+ }
+
case TCPCHK_EXPECT_HTTP_BODY:
case TCPCHK_EXPECT_HTTP_REGEX_BODY:
chunk_reset(&trash);
goto error;
out:
+ free_trash_chunk(nbuf);
+ free_trash_chunk(vbuf);
free_trash_chunk(msg);
return ret;
{
struct tcpcheck_rule *prev_check, *chk = NULL;
struct sample_expr *status_expr = NULL;
- char *on_success_msg, *on_error_msg, *comment, *pattern;
+ char *on_success_msg, *on_error_msg, *comment, *pattern, *npat, *vpat;
enum tcpcheck_expect_type type = TCPCHK_EXPECT_UNDEF;
enum healthcheck_status ok_st = HCHK_STATUS_UNKNOWN;
enum healthcheck_status err_st = HCHK_STATUS_UNKNOWN;
enum healthcheck_status tout_st = HCHK_STATUS_UNKNOWN;
+ unsigned int flags = 0;
long min_recv = -1;
int inverse = 0;
- on_success_msg = on_error_msg = comment = pattern = NULL;
+ on_success_msg = on_error_msg = comment = pattern = npat = vpat = NULL;
if (!*(args[cur_arg+1])) {
memprintf(errmsg, "expects at least a matching pattern as arguments");
goto error;
}
type = TCPCHK_EXPECT_CUSTOM;
}
+ else if (strcmp(args[cur_arg], "header") == 0) {
+ int orig_arg = cur_arg;
+
+ if (proto != TCPCHK_RULES_HTTP_CHK)
+ goto bad_tcp_kw;
+ if (type != TCPCHK_EXPECT_UNDEF) {
+ memprintf(errmsg, "only on pattern expected");
+ goto error;
+ }
+ type = TCPCHK_EXPECT_HTTP_HEADER;
+
+ /* Parse the name pattern, mandatory */
+ if (!*(args[cur_arg+1]) || !*(args[cur_arg+2]) || strcmp(args[cur_arg+1], "name") != 0) {
+ memprintf(errmsg, "'%s' expects at the keyword name as first argument followed by a pattern",
+ args[orig_arg]);
+ goto error;
+ }
+ cur_arg += 2;
+ if (strcmp(args[cur_arg], "-m") == 0) {
+ if (!*(args[cur_arg+1])) {
+ memprintf(errmsg, "'%s' : '%s' expects at a matching pattern ('str', 'beg', 'end', 'sub' or 'reg')",
+ args[orig_arg], args[cur_arg]);
+ goto error;
+ }
+ if (strcmp(args[cur_arg+1], "str") == 0)
+ flags |= TCPCHK_EXPT_FL_HTTP_HNAME_STR;
+ else if (strcmp(args[cur_arg+1], "beg") == 0)
+ flags |= TCPCHK_EXPT_FL_HTTP_HNAME_BEG;
+ else if (strcmp(args[cur_arg+1], "end") == 0)
+ flags |= TCPCHK_EXPT_FL_HTTP_HNAME_END;
+ else if (strcmp(args[cur_arg+1], "sub") == 0)
+ flags |= TCPCHK_EXPT_FL_HTTP_HNAME_SUB;
+ else if (strcmp(args[cur_arg+1], "reg") == 0)
+ flags |= TCPCHK_EXPT_FL_HTTP_HNAME_REG;
+ else {
+ memprintf(errmsg, "'%s' : '%s' only supports 'str', 'beg', 'end', 'sub' or 'reg' (got '%s')",
+ args[orig_arg], args[cur_arg], args[cur_arg+1]);
+ goto error;
+ }
+ cur_arg += 2;
+ }
+ else
+ flags |= TCPCHK_EXPT_FL_HTTP_HNAME_STR;
+ npat = args[cur_arg];
+
+ if (!(*args[cur_arg+1])) {
+ flags |= TCPCHK_EXPT_FL_HTTP_HVAL_NONE;
+ goto next;
+ }
+
+ if (strcmp(args[cur_arg+1], "log-format") == 0) {
+ if (flags & TCPCHK_EXPT_FL_HTTP_HNAME_REG) {
+ memprintf(errmsg, "'%s': '%s' cannot be used with a regex matching pattern",
+ args[orig_arg], args[cur_arg+1]);
+ goto error;
+ }
+ flags |= TCPCHK_EXPT_FL_HTTP_HNAME_FMT;
+ cur_arg++;
+ }
+
+ if (!(*args[cur_arg+1]) || strcmp(args[cur_arg+1], "value") != 0) {
+ flags |= TCPCHK_EXPT_FL_HTTP_HVAL_NONE;
+ goto next;
+ }
+
+ /* Parse the value pattern, optionnal */
+ cur_arg += 2;
+ if (strcmp(args[cur_arg], "-m") == 0) {
+ if (!*(args[cur_arg+1])) {
+ memprintf(errmsg, "'%s' : '%s' expects at a matching pattern ('str', 'beg', 'end', 'sub' or 'reg')",
+ args[orig_arg], args[cur_arg]);
+ goto error;
+ }
+ if (strcmp(args[cur_arg+1], "str") == 0)
+ flags |= TCPCHK_EXPT_FL_HTTP_HVAL_STR;
+ else if (strcmp(args[cur_arg+1], "beg") == 0)
+ flags |= TCPCHK_EXPT_FL_HTTP_HVAL_BEG;
+ else if (strcmp(args[cur_arg+1], "end") == 0)
+ flags |= TCPCHK_EXPT_FL_HTTP_HVAL_END;
+ else if (strcmp(args[cur_arg+1], "sub") == 0)
+ flags |= TCPCHK_EXPT_FL_HTTP_HVAL_SUB;
+ else if (strcmp(args[cur_arg+1], "reg") == 0)
+ flags |= TCPCHK_EXPT_FL_HTTP_HVAL_REG;
+ else {
+ memprintf(errmsg, "'%s' : '%s' only supports 'str', 'beg', 'end', 'sub' or 'reg' (got '%s')",
+ args[orig_arg], args[cur_arg], args[cur_arg+1]);
+ goto error;
+ }
+ cur_arg += 2;
+ }
+ else
+ flags |= TCPCHK_EXPT_FL_HTTP_HVAL_STR;
+ vpat = args[cur_arg];
+
+ while (*args[cur_arg+1]) {
+ if (strcmp(args[cur_arg+1], "log-format") == 0) {
+ if (flags & TCPCHK_EXPT_FL_HTTP_HVAL_REG) {
+ memprintf(errmsg, "'%s': '%s' cannot be used with a regex matching pattern",
+ args[orig_arg], args[cur_arg+1]);
+ goto error;
+ }
+ flags |= TCPCHK_EXPT_FL_HTTP_HVAL_FMT;
+ }
+ else if (strcmp(args[cur_arg+1], "full") == 0)
+ flags |= TCPCHK_EXPT_FL_HTTP_HVAL_FULL;
+ else
+ break;
+ cur_arg++;
+ }
+ }
else if (strcmp(args[cur_arg], "comment") == 0) {
if (in_pattern) {
memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
if (proto == TCPCHK_RULES_HTTP_CHK) {
bad_http_kw:
memprintf(errmsg, "'only supports min-recv, [!]string', '[!]rstring', '[!]status', '[!]rstatus'"
- " or comment but got '%s' as argument.", args[cur_arg]);
+ "[!]header or comment but got '%s' as argument.", args[cur_arg]);
}
else {
bad_tcp_kw:
}
goto error;
}
-
+ next:
cur_arg++;
}
chk->comment = comment; comment = NULL;
chk->expect.type = type;
chk->expect.min_recv = min_recv;
- chk->expect.flags |= (inverse ? TCPCHK_EXPT_FL_INV : 0);
+ chk->expect.flags = flags | (inverse ? TCPCHK_EXPT_FL_INV : 0);
chk->expect.ok_status = ok_st;
chk->expect.err_status = err_st;
chk->expect.tout_status = tout_st;
if (!chk->expect.regex)
goto error;
break;
+ case TCPCHK_EXPECT_HTTP_HEADER:
+ if (!npat) {
+ memprintf(errmsg, "unexpected error, undefined header name pattern");
+ goto error;
+ }
+ if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HNAME_REG) {
+ chk->expect.hdr.name_re = regex_comp(npat, 0, 0, errmsg);
+ if (!chk->expect.hdr.name_re)
+ goto error;
+ }
+ else if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HNAME_FMT) {
+ px->conf.args.ctx = ARGC_SRV;
+ LIST_INIT(&chk->expect.hdr.name_fmt);
+ if (!parse_logformat_string(npat, px, &chk->expect.hdr.name_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
+ memprintf(errmsg, "'%s' invalid log-format string (%s).\n", npat, *errmsg);
+ goto error;
+ }
+ }
+ else {
+ chk->expect.hdr.name = ist2(strdup(npat), strlen(npat));
+ if (!isttest(chk->expect.hdr.name)) {
+ memprintf(errmsg, "out of memory");
+ goto error;
+ }
+ }
+
+ if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_NONE) {
+ chk->expect.hdr.value = IST_NULL;
+ break;
+ }
+
+ if (!vpat) {
+ memprintf(errmsg, "unexpected error, undefined header value pattern");
+ goto error;
+ }
+ else if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_REG) {
+ chk->expect.hdr.value_re = regex_comp(vpat, 1, 0, errmsg);
+ if (!chk->expect.hdr.value_re)
+ goto error;
+ }
+ else if (chk->expect.flags & TCPCHK_EXPT_FL_HTTP_HVAL_FMT) {
+ px->conf.args.ctx = ARGC_SRV;
+ LIST_INIT(&chk->expect.hdr.value_fmt);
+ if (!parse_logformat_string(vpat, px, &chk->expect.hdr.value_fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
+ memprintf(errmsg, "'%s' invalid log-format string (%s).\n", npat, *errmsg);
+ goto error;
+ }
+ }
+ else {
+ chk->expect.hdr.value = ist2(strdup(vpat), strlen(vpat));
+ if (!isttest(chk->expect.hdr.value)) {
+ memprintf(errmsg, "out of memory");
+ goto error;
+ }
+ }
+
+ break;
case TCPCHK_EXPECT_CUSTOM:
chk->expect.custom = NULL; /* Must be defined by the caller ! */
break;