From b2f82b2b51ffa90ae0463f5fb2fe60b24d856bb3 Mon Sep 17 00:00:00 2001 From: Christopher Faulet Date: Mon, 27 Nov 2023 18:12:13 +0100 Subject: [PATCH] MINOR: http-fetch: Add a sample to retrieve the server status code The code returned by the "status" sample fetch is the one in the HTTP response at the moment the sample is evaluated. It may be the status code in the server response or the one of the HAProxy reply in case of error, deny, redirect... However, it could be handy to retrieve the status code returned by the server, when a HTTP response was really received from it. It is the purpose of the "server_status" sample fetch. The server status code itself is stored in the HTTP txn. --- doc/configuration.txt | 4 ++++ include/haproxy/http_ana-t.h | 3 ++- src/http_ana.c | 5 +++-- src/http_fetch.c | 32 ++++++++++++++++++++++++++++++-- 4 files changed, 39 insertions(+), 5 deletions(-) diff --git a/doc/configuration.txt b/doc/configuration.txt index 8b7f4aa19..76647e5e3 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -22822,6 +22822,10 @@ resp_ver : string (deprecated) ACL derivatives : resp.ver : exact string match +server_status : integer + Return an integer containing the HTTP status code as received from the + server. If no response was received from the server, the sample fetch fails. + set-cookie([]) : string (deprecated) This extracts the last occurrence of the cookie name on a "Set-Cookie" header line from the response and uses the corresponding value to match. This diff --git a/include/haproxy/http_ana-t.h b/include/haproxy/http_ana-t.h index 41887a995..5b7342f4d 100644 --- a/include/haproxy/http_ana-t.h +++ b/include/haproxy/http_ana-t.h @@ -239,7 +239,8 @@ struct http_txn { unsigned int flags; /* transaction flags */ enum http_meth_t meth; /* HTTP method */ /* 1 unused byte here */ - short status; /* HTTP status from the server, negative if from proxy */ + short status; /* HTTP status sent to the client, negative if not set */ + short server_status; /* HTTP status received from the server, negative if not received */ struct http_reply *http_reply; /* The HTTP reply to use as reply */ struct buffer l7_buffer; /* To store the data, in case we have to retry */ char cache_hash[20]; /* Store the cache hash */ diff --git a/src/http_ana.c b/src/http_ana.c index 37034ad2f..bbf01ff85 100644 --- a/src/http_ana.c +++ b/src/http_ana.c @@ -1429,7 +1429,7 @@ int http_wait_for_response(struct stream *s, struct channel *rep, int an_bit) } /* 1: get the status code and the version. Also set HTTP flags */ - txn->status = sl->info.res.status; + txn->server_status = txn->status = sl->info.res.status; if (sl->flags & HTX_SL_F_VER_11) msg->flags |= HTTP_MSGF_VER_11; if (sl->flags & HTX_SL_F_XFER_LEN) { @@ -1488,7 +1488,7 @@ int http_wait_for_response(struct stream *s, struct channel *rep, int an_bit) htx->first = channel_htx_fwd_headers(rep, htx); msg->msg_state = HTTP_MSG_RPBEFORE; msg->flags = 0; - txn->status = 0; + txn->server_status = txn->status = 0; s->logs.t_data = -1; /* was not a response yet */ s->scf->flags |= SC_FL_SND_ASAP; /* Send ASAP informational messages */ goto next_one; @@ -5036,6 +5036,7 @@ struct http_txn *http_create_txn(struct stream *s) txn->meth = HTTP_METH_OTHER; txn->flags = ((sc && sc_ep_test(sc, SE_FL_NOT_FIRST)) ? TX_NOT_FIRST : 0); txn->status = -1; + txn->server_status = -1; txn->http_reply = NULL; txn->l7_buffer = BUF_NULL; write_u32(txn->cache_hash, 0); diff --git a/src/http_fetch.c b/src/http_fetch.c index f5ff9f304..38b1e0b08 100644 --- a/src/http_fetch.c +++ b/src/http_fetch.c @@ -311,8 +311,12 @@ struct htx *smp_prefetch_htx(struct sample *smp, struct channel *chn, struct che if (txn->meth == HTTP_METH_GET || txn->meth == HTTP_METH_HEAD) s->flags |= SF_REDIRECTABLE; } - else if (txn->status == -1) - txn->status = sl->info.res.status; + else { + if (txn->status == -1) + txn->status = sl->info.res.status; + if (!(htx->flags & HTX_FL_PROXY_RESP) && txn->server_status == -1) + txn->server_status = sl->info.res.status; + } if (sl->flags & HTX_SL_F_VER_11) msg->flags |= HTTP_MSGF_VER_11; } @@ -441,6 +445,28 @@ static int smp_fetch_stcode(const struct arg *args, struct sample *smp, const ch return 1; } +/* It returns the server status code */ +static int smp_fetch_srv_status(const struct arg *args, struct sample *smp, const char *kw, void *private) +{ + struct http_txn *txn; + + txn = (smp->strm ? smp->strm->txn : NULL); + if (!txn) + return 0; + + if (txn->server_status == -1) { + struct channel *chn = SMP_RES_CHN(smp); + struct htx *htx = smp_prefetch_htx(smp, chn, NULL, 1); + + if (!htx) + return 0; + } + + smp->data.type = SMP_T_SINT; + smp->data.u.sint = txn->server_status; + return 1; +} + static int smp_fetch_uniqueid(const struct arg *args, struct sample *smp, const char *kw, void *private) { struct ist unique_id; @@ -2298,6 +2324,8 @@ static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, { { "res.hdr_names", smp_fetch_hdr_names, ARG1(0,STR), NULL, SMP_T_STR, SMP_USE_HRSHV }, { "res.hdr_val", smp_fetch_hdr_val, ARG2(0,STR,SINT), val_hdr, SMP_T_SINT, SMP_USE_HRSHV }, + { "server_status", smp_fetch_srv_status, 0, NULL, SMP_T_SINT, SMP_USE_HRSHP }, + /* scook is valid only on the response and is used for ACL compatibility */ { "scook", smp_fetch_cookie, ARG1(0,STR), NULL, SMP_T_STR, SMP_USE_HRSHV }, { "scook_cnt", smp_fetch_cookie_cnt, ARG1(0,STR), NULL, SMP_T_SINT, SMP_USE_HRSHV },