MEDIUM: checks: Support log-format strings for tcp-check send rules
An extra parameter for tcp-check send rules can be specified to handle the
string or the hexa string as a log-format one. Using "log-format" option,
instead of considering the data to send as raw data, it is parsed as a
log-format string. Thus it is possible to call sample fetches to customize data
sent to a server. Of course, because we have no stream attached to healthchecks,
not all sample fetches are available. So be careful.
tcp-check set-var(check.port) int(8000)
tcp-check set-var(check.uri) str(/status)
tcp-check connect port var(check.port)
tcp-check send "GET %[check.uri] HTTP/1.0\r\n" log-format
tcp-check send "Host: %[srv_name]\r\n" log-format
tcp-check send "\r\n"
diff --git a/include/types/checks.h b/include/types/checks.h
index 49e4ed6..10e0b59 100644
--- a/include/types/checks.h
+++ b/include/types/checks.h
@@ -230,15 +230,19 @@
};
enum tcpcheck_send_type {
- TCPCHK_SEND_UNDEF = 0, /* Send is not parsed. */
- TCPCHK_SEND_STRING, /* Send an ASCII string. */
- TCPCHK_SEND_BINARY, /* Send a binary sequence. */
+ TCPCHK_SEND_UNDEF = 0, /* Send is not parsed. */
+ TCPCHK_SEND_STRING, /* Send an ASCII string. */
+ TCPCHK_SEND_BINARY, /* Send a binary sequence. */
+ TCPCHK_SEND_STRING_LF, /* Send an ASCII log-format string. */
+ TCPCHK_SEND_BINARY_LF, /* Send a binary log-format sequence. */
};
struct tcpcheck_send {
enum tcpcheck_send_type type;
- char *string; /* Sending an ASCII string or a binary sequence. */
- int length; /* Size in bytes of the sequence referenced by string / binary. */
+ union {
+ struct ist data; /* an ASCII string or a binary sequence */
+ struct list fmt; /* an ASCII or hexa log-format string */
+ };
};
enum tcpcheck_expect_type {
diff --git a/src/checks.c b/src/checks.c
index 9e0edaf..d7c6647 100644
--- a/src/checks.c
+++ b/src/checks.c
@@ -3028,24 +3028,41 @@
struct tcpcheck_send *send = &rule->send;
struct conn_stream *cs = check->cs;
struct connection *conn = cs_conn(cs);
+ struct buffer *tmp = NULL;
/* reset the read & write buffer */
b_reset(&check->bi);
b_reset(&check->bo);
- if (send->length >= b_size(&check->bo)) {
- chunk_printf(&trash, "tcp-check send : string too large (%d) for buffer size (%u) at step %d",
- send->length, (unsigned int)b_size(&check->bo),
- tcpcheck_get_step_id(check, rule));
- set_server_check_status(check, HCHK_STATUS_L7RSP, trash.area);
- ret = TCPCHK_EVAL_STOP;
- goto out;
- }
-
switch (send->type) {
case TCPCHK_SEND_STRING:
case TCPCHK_SEND_BINARY:
- b_putblk(&check->bo, send->string, send->length);
+ if (istlen(send->data) >= b_size(&check->bo)) {
+ chunk_printf(&trash, "tcp-check send : string too large (%u) for buffer size (%u) at step %d",
+ (unsigned int)istlen(send->data), (unsigned int)b_size(&check->bo),
+ tcpcheck_get_step_id(check, rule));
+ set_server_check_status(check, HCHK_STATUS_L7RSP, trash.area);
+ ret = TCPCHK_EVAL_STOP;
+ goto out;
+ }
+ b_putist(&check->bo, send->data);
+ break;
+ case TCPCHK_SEND_STRING_LF:
+ check->bo.data = sess_build_logline(check->sess, NULL, b_orig(&check->bo), b_size(&check->bo), &rule->send.fmt);
+ if (!b_data(&check->bo))
+ goto out;
+ break;
+ case TCPCHK_SEND_BINARY_LF:
+ tmp = alloc_trash_chunk();
+ if (!tmp)
+ goto error_lf;
+ tmp->data = sess_build_logline(check->sess, NULL, b_orig(tmp), b_size(tmp), &rule->send.fmt);
+ if (!b_data(tmp))
+ goto out;
+ tmp->area[tmp->data] = '\0';
+ b_set_data(&check->bo, b_size(&check->bo));
+ if (parse_binary(b_orig(tmp), &check->bo.area, (int *)&check->bo.data, NULL) == 0)
+ goto error_lf;
break;
case TCPCHK_SEND_UNDEF:
/* Should never happen. */
@@ -3066,7 +3083,16 @@
}
out:
+ free_trash_chunk(tmp);
return ret;
+
+ error_lf:
+ chunk_printf(&trash, "tcp-check send : failed to build log-format string at step %d",
+ tcpcheck_get_step_id(check, rule));
+ set_server_check_status(check, HCHK_STATUS_L7RSP, trash.area);
+ ret = TCPCHK_EVAL_STOP;
+ goto out;
+
}
/* Evaluate a TCPCHK_ACT_EXPECT rule. It returns 1 to evaluate the next rule, 0
@@ -3443,6 +3469,8 @@
static void free_tcpcheck(struct tcpcheck_rule *rule, int in_pool)
{
+ struct logformat_node *lf, *lfb;
+
if (!rule)
return;
@@ -3452,7 +3480,16 @@
switch (rule->send.type) {
case TCPCHK_SEND_STRING:
case TCPCHK_SEND_BINARY:
- free(rule->send.string);
+ free(rule->send.data.ptr);
+ break;
+ case TCPCHK_SEND_STRING_LF:
+ case TCPCHK_SEND_BINARY_LF:
+ list_for_each_entry_safe(lf, lfb, &rule->send.fmt, list) {
+ LIST_DEL(&lf->list);
+ release_sample_expr(lf->expr);
+ free(lf->arg);
+ free(lf);
+ }
break;
case TCPCHK_SEND_UNDEF:
break;
@@ -3669,15 +3706,15 @@
send->type = TCPCHK_SEND_STRING;
for (i = 0; strs[i]; i++)
- send->length += strlen(strs[i]);
+ send->data.len += strlen(strs[i]);
- send->string = malloc(send->length + 1);
- if (!send->string) {
+ send->data.ptr = malloc(send->data.len + 1);
+ if (!isttest(send->data)) {
pool_free(pool_head_tcpcheck_rule, tcpcheck);
return 0;
}
- dst = send->string;
+ dst = send->data.ptr;
for (i = 0; strs[i]; i++)
for (in = strs[i]; (*dst = *in++); dst++);
*dst = 0;
@@ -4283,12 +4320,12 @@
return NULL;
}
-static struct tcpcheck_rule *parse_tcpcheck_send(char **args, int cur_arg, struct list *rules, char **errmsg)
+static struct tcpcheck_rule *parse_tcpcheck_send(char **args, int cur_arg, struct proxy *px, struct list *rules,
+ char **errmsg)
{
struct tcpcheck_rule *chk = NULL;
- char *str = NULL, *comment = NULL;
+ char *comment = NULL, *data = NULL;
enum tcpcheck_send_type type = TCPCHK_SEND_UNDEF;
- int len;
type = ((strcmp(args[cur_arg], "send-binary") == 0) ? TCPCHK_SEND_BINARY : TCPCHK_SEND_STRING);
if (!*(args[cur_arg+1])) {
@@ -4297,33 +4334,35 @@
goto error;
}
- if (type == TCPCHK_SEND_BINARY) {
- if (parse_binary(args[cur_arg+1], &str, &len, errmsg) == 0) {
- memprintf(errmsg, "'%s' invalid binary string (%s).\n", args[cur_arg], *errmsg);
- goto error;
+ data = args[cur_arg+1];
+
+ cur_arg += 2;
+ while (*(args[cur_arg])) {
+ if (strcmp(args[cur_arg], "comment") == 0) {
+ if (!*(args[cur_arg+1])) {
+ memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
+ goto error;
+ }
+ cur_arg++;
+ free(comment);
+ comment = strdup(args[cur_arg]);
+ if (!comment) {
+ memprintf(errmsg, "out of memory");
+ goto error;
+ }
}
- }
- else {
- str = strdup(args[cur_arg+1]);
- len = strlen(args[cur_arg+1]);
- if (!str) {
- memprintf(errmsg, "out of memory");
- goto error;
+ else if (strcmp(args[cur_arg], "log-format") == 0) {
+ if (type == TCPCHK_SEND_BINARY)
+ type = TCPCHK_SEND_BINARY_LF;
+ else if (type == TCPCHK_SEND_STRING)
+ type = TCPCHK_SEND_STRING_LF;
}
- }
- cur_arg++;
-
- if (strcmp(args[cur_arg], "comment") == 0) {
- if (!*(args[cur_arg+1])) {
- memprintf(errmsg, "'%s' expects a string as argument.", args[cur_arg]);
+ else {
+ memprintf(errmsg, "expects 'comment', 'log-format' but got '%s' as argument.",
+ args[cur_arg]);
goto error;
}
cur_arg++;
- comment = strdup(args[cur_arg]);
- if (!comment) {
- memprintf(errmsg, "out of memory");
- goto error;
- }
}
chk = calloc(1, sizeof(*chk));
@@ -4334,12 +4373,38 @@
chk->action = TCPCHK_ACT_SEND;
chk->comment = comment;
chk->send.type = type;
- chk->send.string = str;
- chk->send.length = len;
+
+ switch (chk->send.type) {
+ case TCPCHK_SEND_STRING:
+ chk->send.data = ist2(strdup(data), strlen(data));
+ if (!isttest(chk->send.data)) {
+ memprintf(errmsg, "out of memory");
+ goto error;
+ }
+ break;
+ case TCPCHK_SEND_BINARY:
+ if (parse_binary(data, &chk->send.data.ptr, (int *)&chk->send.data.len, errmsg) == 0) {
+ memprintf(errmsg, "'%s' invalid binary string (%s).\n", data, *errmsg);
+ goto error;
+ }
+ break;
+ case TCPCHK_SEND_STRING_LF:
+ case TCPCHK_SEND_BINARY_LF:
+ LIST_INIT(&chk->send.fmt);
+ px->conf.args.ctx = ARGC_SRV;
+ if (!parse_logformat_string(data, px, &chk->send.fmt, 0, SMP_VAL_BE_CHK_RUL, errmsg)) {
+ memprintf(errmsg, "'%s' invalid log-format string (%s).\n", data, *errmsg);
+ goto error;
+ }
+ break;
+ case TCPCHK_SEND_UNDEF:
+ goto error;
+ }
+
return chk;
error:
- free(str);
+ free(chk);
free(comment);
return NULL;
}
@@ -4578,7 +4643,7 @@
if (strcmp(args[cur_arg], "connect") == 0)
chk = parse_tcpcheck_connect(args, cur_arg, curpx, rules, file, line, errmsg);
else if (strcmp(args[cur_arg], "send") == 0 || strcmp(args[cur_arg], "send-binary") == 0)
- chk = parse_tcpcheck_send(args, cur_arg, rules, errmsg);
+ chk = parse_tcpcheck_send(args, cur_arg, curpx, rules, errmsg);
else if (strcmp(args[cur_arg], "expect") == 0)
chk = parse_tcpcheck_expect(args, cur_arg, rules, errmsg);
else if (strcmp(args[cur_arg], "comment") == 0)