mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-08-07 07:37:02 +02:00
MINOR: checks: Add support of payload-based sample fetches
It is now possible to call check.payload(), check.payload_lv() and check.len() sample fetches from any sample expression or log-format string in a tcp-check based ruleset. In fact, check.payload() was already added. But instead of having a specific function to handle this sample fetch, we use the same than req.payload(). These sample fetches act on the check input buffer, containing data received for the server. So it should be part of or after an expect rule, but before any send rule. Because the input buffer is cleared at this stage.
This commit is contained in:
parent
16fff67e2e
commit
78f371e498
@ -17506,14 +17506,31 @@ placed in the dedicated scope "check". Other sample fetches may also be called
|
||||
when an health-check is performed if it makes sense and if the sample fetch was
|
||||
adapted to be called in this context.
|
||||
|
||||
check.payload(<offset>,<length>) : binary
|
||||
This extracts a binary block of <length> bytes and starting at byte <offset>
|
||||
in the check input buffer. As a special case, if the <length> argument is
|
||||
zero, then the whole buffer from <offset> to the end is extracted. This can
|
||||
be called from a tcp-check expect rule, or eventually from a set-var rule
|
||||
after an expect rule and before a send rule (check input buffer is filled on
|
||||
check.len : integer
|
||||
Returns an integer value corresponding to the number of bytes present in the
|
||||
check input buffer, containing the data received from the server. This can be
|
||||
called from a tcp-check expect rule, or eventually from a set-var rule after
|
||||
an expect rule and before a send rule (check input buffer is filled on
|
||||
tcp-check expect rules and reset on tcp-check send rules).
|
||||
|
||||
check.payload(<offset>,<length>) : binary
|
||||
This extracts a binary block of <length> bytes and starting at byte <offset>
|
||||
in the check input buffer, containing data received from the server. As a
|
||||
special case, if the <length> argument is zero, then the whole buffer from
|
||||
<offset> to the end is extracted. This can be called from a tcp-check expect
|
||||
rule, or eventually from a set-var rule after an expect rule and before a
|
||||
send rule (check input buffer is filled on tcp-check expect rules and reset
|
||||
on tcp-check send rules).
|
||||
|
||||
check.payload_lv(<offset1>,<length>[,<offset2>]) : binary
|
||||
This extracts a binary block whose size is specified at <offset1> for
|
||||
<length> bytes, and which starts at <offset2> if specified or just after the
|
||||
length in the check input buffer, containing data received from the
|
||||
server. The <offset2> parameter also supports relative offsets if prepended
|
||||
with a '+' or '-' sign. This can be called from a tcp-check expect rule, or
|
||||
eventually from a set-var rule after an expect rule and before a send rule
|
||||
(check input buffer is filled on tcp-check expect rules and reset on
|
||||
tcp-check send rules).
|
||||
|
||||
7.3.8. Fetching samples for developers
|
||||
---------------------------------------
|
||||
|
46
src/checks.c
46
src/checks.c
@ -1087,8 +1087,10 @@ static void tcpcheck_expect_onerror_message(struct buffer *msg, struct check *ch
|
||||
*/
|
||||
if (rule->expect.status_expr) {
|
||||
smp = sample_fetch_as_type(check->proxy, check->sess, NULL, SMP_OPT_DIR_RES | SMP_OPT_FINAL,
|
||||
rule->expect.status_expr, SMP_T_SINT);
|
||||
if (smp)
|
||||
rule->expect.status_expr, SMP_T_STR);
|
||||
|
||||
if (smp && sample_casts[smp->data.type][SMP_T_SINT] &&
|
||||
sample_casts[smp->data.type][SMP_T_SINT](smp))
|
||||
check->code = smp->data.u.sint;
|
||||
}
|
||||
|
||||
@ -1122,8 +1124,10 @@ static void tcpcheck_expect_onsuccess_message(struct buffer *msg, struct check *
|
||||
*/
|
||||
if (rule->expect.status_expr) {
|
||||
smp = sample_fetch_as_type(check->proxy, check->sess, NULL, SMP_OPT_DIR_RES | SMP_OPT_FINAL,
|
||||
rule->expect.status_expr, SMP_T_SINT);
|
||||
if (smp)
|
||||
rule->expect.status_expr, SMP_T_STR);
|
||||
|
||||
if (smp && sample_casts[smp->data.type][SMP_T_SINT] &&
|
||||
sample_casts[smp->data.type][SMP_T_SINT](smp))
|
||||
check->code = smp->data.u.sint;
|
||||
}
|
||||
|
||||
@ -5489,38 +5493,8 @@ void send_email_alert(struct server *s, int level, const char *format, ...)
|
||||
/**************************************************************************/
|
||||
/************************** Check sample fetches **************************/
|
||||
/**************************************************************************/
|
||||
/* extracts check payload at a fixed position and length */
|
||||
static int
|
||||
smp_fetch_chk_payload(const struct arg *arg_p, struct sample *smp, const char *kw, void *private)
|
||||
{
|
||||
unsigned int buf_offset = ((arg_p[0].type == ARGT_SINT) ? arg_p[0].data.sint : 0);
|
||||
unsigned int buf_size = ((arg_p[1].type == ARGT_SINT) ? arg_p[1].data.sint : 0);
|
||||
struct check *check = (smp->sess ? objt_check(smp->sess->origin) : NULL);
|
||||
struct buffer *buf;
|
||||
|
||||
if (!check)
|
||||
return 0;
|
||||
|
||||
buf = &check->bi;
|
||||
if (buf_offset > b_data(buf))
|
||||
goto no_match;
|
||||
if (buf_offset + buf_size > b_data(buf))
|
||||
buf_size = 0;
|
||||
|
||||
/* init chunk as read only */
|
||||
smp->data.type = SMP_T_STR;
|
||||
smp->flags = SMP_F_VOLATILE | SMP_F_CONST;
|
||||
chunk_initlen(&smp->data.u.str, b_head(buf) + buf_offset, 0, (buf_size ? buf_size : (b_data(buf) - buf_offset)));
|
||||
|
||||
return 1;
|
||||
|
||||
no_match:
|
||||
smp->flags = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct sample_fetch_kw_list smp_kws = {ILH, {
|
||||
{ "check.payload", smp_fetch_chk_payload, ARG2(0,SINT,SINT), NULL, SMP_T_STR, SMP_USE_INTRN },
|
||||
{ /* END */ },
|
||||
}};
|
||||
|
||||
@ -5873,7 +5847,7 @@ int proxy_parse_redis_check_opt(char **args, int cur_arg, struct proxy *curpx, s
|
||||
|
||||
chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "string", redis_res,
|
||||
"error-status", "L7STS",
|
||||
"on-error", "%[check.payload(),cut_crlf]",
|
||||
"on-error", "%[check.payload(0,0),cut_crlf]",
|
||||
"on-success", "Redis server is ok",
|
||||
""},
|
||||
1, curpx, &rs->rules, TCPCHK_RULES_REDIS_CHK, file, line, &errmsg);
|
||||
@ -6075,7 +6049,7 @@ int proxy_parse_smtpchk_opt(char **args, int cur_arg, struct proxy *curpx, struc
|
||||
chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^[0-9]{3}[ \r]",
|
||||
"min-recv", "4",
|
||||
"error-status", "L7RSP",
|
||||
"on-error", "%[check.payload(),cut_crlf]",
|
||||
"on-error", "%[check.payload(0,0),cut_crlf]",
|
||||
""},
|
||||
1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
|
||||
if (!chk) {
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include <proto/acl.h>
|
||||
#include <proto/arg.h>
|
||||
#include <proto/channel.h>
|
||||
#include <proto/connection.h>
|
||||
#include <proto/pattern.h>
|
||||
#include <proto/payload.h>
|
||||
#include <proto/sample.h>
|
||||
@ -48,19 +49,23 @@ smp_fetch_wait_end(const struct arg *args, struct sample *smp, const char *kw, v
|
||||
static int
|
||||
smp_fetch_len(const struct arg *args, struct sample *smp, const char *kw, void *private)
|
||||
{
|
||||
struct channel *chn;
|
||||
|
||||
if (!smp->strm)
|
||||
return 0;
|
||||
|
||||
chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
|
||||
smp->data.type = SMP_T_SINT;
|
||||
if (IS_HTX_STRM(smp->strm)) {
|
||||
struct htx *htx = htxbuf(&chn->buf);
|
||||
smp->data.u.sint = htx->data - co_data(chn);
|
||||
if (smp->strm) {
|
||||
struct channel *chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
|
||||
if (IS_HTX_STRM(smp->strm)) {
|
||||
struct htx *htx = htxbuf(&chn->buf);
|
||||
smp->data.u.sint = htx->data - co_data(chn);
|
||||
}
|
||||
else
|
||||
smp->data.u.sint = ci_data(chn);
|
||||
}
|
||||
else if (smp->sess && obj_type(smp->sess->origin) == OBJ_TYPE_CHECK) {
|
||||
struct check *check = __objt_check(smp->sess->origin);
|
||||
smp->data.u.sint = ((check->cs && IS_HTX_CS(check->cs)) ? (htxbuf(&check->bi))->data: b_data(&check->bi));
|
||||
}
|
||||
else
|
||||
smp->data.u.sint = ci_data(chn);
|
||||
return 0;
|
||||
|
||||
smp->data.type = SMP_T_SINT;
|
||||
smp->flags = SMP_F_VOLATILE | SMP_F_MAY_CHANGE;
|
||||
return 1;
|
||||
}
|
||||
@ -959,22 +964,35 @@ smp_fetch_payload_lv(const struct arg *arg_p, struct sample *smp, const char *kw
|
||||
unsigned int len_size = arg_p[1].data.sint;
|
||||
unsigned int buf_offset;
|
||||
unsigned int buf_size = 0;
|
||||
struct channel *chn;
|
||||
struct channel *chn = NULL;
|
||||
char *head = NULL;
|
||||
size_t max, data;
|
||||
int i;
|
||||
|
||||
/* Format is (len offset, len size, buf offset) or (len offset, len size) */
|
||||
/* by default buf offset == len offset + len size */
|
||||
/* buf offset could be absolute or relative to len offset + len size if prefixed by + or - */
|
||||
|
||||
if (!smp->strm)
|
||||
if (smp->strm) {
|
||||
chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
|
||||
head = ci_head(chn);
|
||||
data = ci_data(chn);
|
||||
max = global.tune.bufsize;
|
||||
}
|
||||
else if (smp->sess && obj_type(smp->sess->origin) == OBJ_TYPE_CHECK) {
|
||||
struct buffer *buf = &(__objt_check(smp->sess->origin)->bi);
|
||||
head = b_head(buf);
|
||||
data = b_data(buf);
|
||||
max = global.tune.chksize;
|
||||
}
|
||||
if (!head)
|
||||
return 0;
|
||||
|
||||
chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
|
||||
if (len_offset + len_size > ci_data(chn))
|
||||
if (len_offset + len_size > data)
|
||||
goto too_short;
|
||||
|
||||
for (i = 0; i < len_size; i++) {
|
||||
buf_size = (buf_size << 8) + ((unsigned char *)ci_head(chn))[i + len_offset];
|
||||
buf_size = (buf_size << 8) + ((unsigned char *)head)[i + len_offset];
|
||||
}
|
||||
|
||||
/* buf offset may be implicit, absolute or relative. If the LSB
|
||||
@ -988,19 +1006,19 @@ smp_fetch_payload_lv(const struct arg *arg_p, struct sample *smp, const char *kw
|
||||
buf_offset = arg_p[2].data.sint >> 1;
|
||||
}
|
||||
|
||||
if (!buf_size || buf_size > global.tune.bufsize || buf_offset + buf_size > global.tune.bufsize) {
|
||||
if (!buf_size || buf_size > max || buf_offset + buf_size > max) {
|
||||
/* will never match */
|
||||
smp->flags = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (buf_offset + buf_size > ci_data(chn))
|
||||
if (buf_offset + buf_size > data)
|
||||
goto too_short;
|
||||
|
||||
/* init chunk as read only */
|
||||
smp->data.type = SMP_T_BIN;
|
||||
smp->flags = SMP_F_VOLATILE | SMP_F_CONST;
|
||||
chunk_initlen(&smp->data.u.str, ci_head(chn) + buf_offset, 0, buf_size);
|
||||
chunk_initlen(&smp->data.u.str, head + buf_offset, 0, buf_size);
|
||||
return 1;
|
||||
|
||||
too_short:
|
||||
@ -1014,31 +1032,44 @@ smp_fetch_payload(const struct arg *arg_p, struct sample *smp, const char *kw, v
|
||||
{
|
||||
unsigned int buf_offset = arg_p[0].data.sint;
|
||||
unsigned int buf_size = arg_p[1].data.sint;
|
||||
struct channel *chn;
|
||||
struct channel *chn = NULL;
|
||||
char *head = NULL;
|
||||
size_t max, data;
|
||||
|
||||
if (!smp->strm)
|
||||
if (smp->strm) {
|
||||
chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
|
||||
head = ci_head(chn);
|
||||
data = ci_data(chn);
|
||||
max = global.tune.bufsize;
|
||||
}
|
||||
else if (smp->sess && obj_type(smp->sess->origin) == OBJ_TYPE_CHECK) {
|
||||
struct buffer *buf = &(__objt_check(smp->sess->origin)->bi);
|
||||
head = b_head(buf);
|
||||
data = b_data(buf);
|
||||
max = global.tune.chksize;
|
||||
}
|
||||
if (!head)
|
||||
return 0;
|
||||
|
||||
chn = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) ? &smp->strm->res : &smp->strm->req;
|
||||
if (buf_size > global.tune.bufsize || buf_offset + buf_size > global.tune.bufsize) {
|
||||
if (buf_size > max || buf_offset + buf_size > max) {
|
||||
/* will never match */
|
||||
smp->flags = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (buf_offset + buf_size > ci_data(chn))
|
||||
if (buf_offset + buf_size > data)
|
||||
goto too_short;
|
||||
|
||||
/* init chunk as read only */
|
||||
smp->data.type = SMP_T_BIN;
|
||||
smp->flags = SMP_F_VOLATILE | SMP_F_CONST;
|
||||
chunk_initlen(&smp->data.u.str, ci_head(chn) + buf_offset, 0, buf_size ? buf_size : (ci_data(chn) - buf_offset));
|
||||
if (!buf_size && channel_may_recv(chn) && !channel_input_closed(chn))
|
||||
chunk_initlen(&smp->data.u.str, head + buf_offset, 0, buf_size ? buf_size : (data - buf_offset));
|
||||
|
||||
if (!buf_size && chn && channel_may_recv(chn) && !channel_input_closed(chn))
|
||||
smp->flags |= SMP_F_MAY_CHANGE;
|
||||
|
||||
return 1;
|
||||
|
||||
too_short:
|
||||
too_short:
|
||||
smp->flags = SMP_F_MAY_CHANGE | SMP_F_CONST;
|
||||
return 0;
|
||||
}
|
||||
@ -1328,6 +1359,10 @@ static struct sample_fetch_kw_list smp_kws = {ILH, {
|
||||
{ "res.payload_lv", smp_fetch_payload_lv, ARG3(2,SINT,SINT,STR), val_payload_lv, SMP_T_BIN, SMP_USE_L6RES },
|
||||
{ "res.ssl_hello_type", smp_fetch_ssl_hello_type, 0, NULL, SMP_T_SINT, SMP_USE_L6RES },
|
||||
{ "wait_end", smp_fetch_wait_end, 0, NULL, SMP_T_BOOL, SMP_USE_INTRN },
|
||||
|
||||
{ "check.len", smp_fetch_len, 0, NULL, SMP_T_SINT, SMP_USE_INTRN },
|
||||
{ "check.payload", smp_fetch_payload, ARG2(2,SINT,SINT), NULL, SMP_T_BIN, SMP_USE_INTRN },
|
||||
{ "check.payload_lv", smp_fetch_payload_lv, ARG3(2,SINT,SINT,STR), val_payload_lv, SMP_T_BIN, SMP_USE_INTRN },
|
||||
{ /* END */ },
|
||||
}};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user