]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: checks: Add support of payload-based sample fetches
authorChristopher Faulet <cfaulet@haproxy.com>
Thu, 30 Apr 2020 07:38:08 +0000 (09:38 +0200)
committerChristopher Faulet <cfaulet@haproxy.com>
Tue, 5 May 2020 09:06:43 +0000 (11:06 +0200)
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.

doc/configuration.txt
src/checks.c
src/payload.c

index 0a320642340d41b7657c07fea4d14882ba3dbbe5..763e4ad7adb4c3f65acd281715390a68cef198c3 100644 (file)
@@ -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
 ---------------------------------------
index 7c75a1d84290e015b50f678bab92fbd9189d0a89..1249b2410441d34c2b0d67faa32cf950b90c69e2 100644 (file)
@@ -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) {
index 5665794bc92a34f827c7401dc3f9f3a82eed1986..43cf2fb7fd7cfe5e6ed2bb5debf307c55008f1d0 100644 (file)
@@ -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)
+       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
                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);
-       }
-       else
-               smp->data.u.sint = ci_data(chn);
        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;
-
-       if (!smp->strm)
+       struct channel *chn = NULL;
+       char *head = NULL;
+       size_t max, data;
+
+       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 */ },
 }};