summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristopher Faulet <cfaulet@haproxy.com>2020-10-12 15:18:50 +0200
committerChristopher Faulet <cfaulet@haproxy.com>2020-12-11 11:33:32 +0100
commit5f983a9a3b5119950ca6b102977b9d69817d64e4 (patch)
treec42ba58c354b3b54b5c39f742641ba4a97e803b1
parenta3150967632d6b65ad69e9325d9086e9ae5f9def (diff)
downloadhaproxy-20201208-mux_h1_refactoring-3.tar.gz
MEDIUM: http-ana: Deal with L7 retries in HTTP analysers20201208-mux_h1_refactoring-3
The code dealing with the copy of requests in the L7-buffer and the retransmits during L7 retries has been moved in the HTTP analysers. The copy is now performed in the REQ_HTTP_XFER_BODY analyser and the L7 retries is performed in the RES_WAIT_HTTP analyser. This way, si_cs_recv() and si_cs_send() don't care of it anymore. It is much more natural to deal with L7 retry in HTTP analysers.
-rw-r--r--src/http_ana.c70
-rw-r--r--src/stream.c4
-rw-r--r--src/stream_interface.c51
3 files changed, 55 insertions, 70 deletions
diff --git a/src/http_ana.c b/src/http_ana.c
index 7a9af9be1..8683f3a76 100644
--- a/src/http_ana.c
+++ b/src/http_ana.c
@@ -1100,6 +1100,29 @@ int http_request_forward_body(struct stream *s, struct channel *req, int an_bit)
else {
msg->msg_state = HTTP_MSG_DONE;
req->to_forward = 0;
+
+ if ((s->be->retry_type &~ PR_RE_CONN_FAILED) && !(s->si[1].flags & SI_FL_D_L7_RETRY)) {
+ struct stream_interface *si = &s->si[1];
+
+ /* If we want to be able to do L7 retries, copy the
+ * request, so that we are able to resend them if
+ * needed.
+ *
+ * Try to allocate a buffer if we had none. If it
+ * fails, the next test will just disable the l7
+ * retries.
+ */
+ DBG_TRACE_STATE("enable L7 retry, save the request", STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA, s, txn);
+ si->flags |= SI_FL_L7_RETRY;
+ if (b_is_null(&si->l7_buffer))
+ b_alloc(&si->l7_buffer);
+ if (b_is_null(&si->l7_buffer))
+ si->flags &= ~SI_FL_L7_RETRY;
+ else {
+ memcpy(b_orig(&si->l7_buffer), b_orig(&req->buf), b_size(&req->buf));
+ b_add(&si->l7_buffer, co_data(req));
+ }
+ }
}
done:
@@ -1118,6 +1141,7 @@ int http_request_forward_body(struct stream *s, struct channel *req, int an_bit)
}
goto return_bad_req;
}
+
DBG_TRACE_LEAVE(STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA, s, txn);
return 1;
}
@@ -1245,19 +1269,20 @@ int http_request_forward_body(struct stream *s, struct channel *req, int an_bit)
/* Returns 0 if we can attempt to retry, -1 otherwise */
static __inline int do_l7_retry(struct stream *s, struct stream_interface *si)
{
- struct channel *req, *res;
- int co_data;
+ struct channel *req = &s->req;
+ struct channel *res = &s->res;
si->conn_retries--;
if (si->conn_retries < 0)
- return -1;
+ goto no_retry;
+
+ if (b_is_null(&req->buf) && !channel_alloc_buffer(req, &s->buffer_wait))
+ goto no_retry;
if (objt_server(s->target))
_HA_ATOMIC_ADD(&__objt_server(s->target)->counters.retries, 1);
_HA_ATOMIC_ADD(&s->be->be_counters.retries, 1);
- req = &s->req;
- res = &s->res;
/* Remove any write error from the request, and read error from the response */
req->flags &= ~(CF_WRITE_ERROR | CF_WRITE_TIMEOUT | CF_SHUTW | CF_SHUTW_NOW);
res->flags &= ~(CF_READ_ERROR | CF_READ_TIMEOUT | CF_SHUTR | CF_EOI | CF_READ_NULL | CF_SHUTR_NOW);
@@ -1272,17 +1297,20 @@ static __inline int do_l7_retry(struct stream *s, struct stream_interface *si)
res->total = 0;
s->flags &= ~(SF_ERR_SRVTO | SF_ERR_SRVCL);
si_release_endpoint(&s->si[1]);
- b_free(&req->buf);
- /* Swap the L7 buffer with the channel buffer */
- /* We know we stored the co_data as b_data, so get it there */
- co_data = b_data(&si->l7_buffer);
- b_set_data(&si->l7_buffer, b_size(&si->l7_buffer));
- b_xfer(&req->buf, &si->l7_buffer, b_data(&si->l7_buffer));
-
- co_set_data(req, co_data);
+
+ b_reset(&req->buf);
+ memcpy(b_orig(&req->buf), b_orig(&si->l7_buffer), b_size(&si->l7_buffer));
+ b_set_data(&req->buf, b_size(&req->buf));
+ co_set_data(req, b_data(&si->l7_buffer));
+
+ DBG_TRACE_DEVEL("perfrom a L7 retry", STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA, s, s->txn);
b_reset(&res->buf);
co_set_data(res, 0);
return 0;
+
+ no_retry:
+ b_free(&si->l7_buffer);
+ return -1;
}
/* This stream analyser waits for a complete HTTP response. It returns 1 if the
@@ -1349,8 +1377,11 @@ int http_wait_for_response(struct stream *s, struct channel *rep, int an_bit)
* the SI_FL_L7_RETRY flag, so it's ok not
* to check s->be->retry_type.
*/
- if (co_data(rep) || do_l7_retry(s, si_b) == 0)
+ if (co_data(rep) || do_l7_retry(s, si_b) == 0) {
+ DBG_TRACE_DEVEL("leaving on L7 retry",
+ STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA, s, txn);
return 0;
+ }
}
if (txn->flags & TX_NOT_FIRST)
@@ -1511,10 +1542,19 @@ int http_wait_for_response(struct stream *s, struct channel *rep, int an_bit)
* response which at least looks like HTTP. We have an indicator
* of each header's length, so we can parse them quickly.
*/
- msg->msg_state = HTTP_MSG_BODY;
BUG_ON(htx_get_first_type(htx) != HTX_BLK_RES_SL);
sl = http_get_stline(htx);
+ if ((si_b->flags & SI_FL_L7_RETRY) &&
+ l7_status_match(s->be, sl->info.res.status) &&
+ do_l7_retry(s, si_b) == 0) {
+ DBG_TRACE_DEVEL("leaving on L7 retry", STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA, s, txn);
+ return 0;
+ }
+ b_free(&s->si[1].l7_buffer);
+
+ msg->msg_state = HTTP_MSG_BODY;
+
/* 0: we might have to print this header in debug mode */
if (unlikely((global.mode & MODE_DEBUG) &&
(!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)))) {
diff --git a/src/stream.c b/src/stream.c
index 690fe3e5a..72fee73bb 100644
--- a/src/stream.c
+++ b/src/stream.c
@@ -2026,10 +2026,6 @@ struct task *process_stream(struct task *t, void *context, unsigned short state)
*/
si_b->state = SI_ST_REQ; /* new connection requested */
si_b->conn_retries = s->be->conn_retries;
- if ((s->be->retry_type &~ PR_RE_CONN_FAILED) &&
- (s->be->mode == PR_MODE_HTTP) &&
- !(si_b->flags & SI_FL_D_L7_RETRY))
- si_b->flags |= SI_FL_L7_RETRY;
}
}
else {
diff --git a/src/stream_interface.c b/src/stream_interface.c
index 45bcea318..5f0dade9a 100644
--- a/src/stream_interface.c
+++ b/src/stream_interface.c
@@ -711,35 +711,6 @@ int si_cs_send(struct conn_stream *cs)
if (oc->flags & CF_STREAMER)
send_flag |= CO_SFL_STREAMER;
- if ((si->flags & SI_FL_L7_RETRY) && !b_data(&si->l7_buffer)) {
- struct stream *s = si_strm(si);
- /* If we want to be able to do L7 retries, copy
- * the data we're about to send, so that we are able
- * to resend them if needed
- */
- /* Try to allocate a buffer if we had none.
- * If it fails, the next test will just
- * disable the l7 retries by setting
- * l7_conn_retries to 0.
- */
- if (!s->txn || (s->txn->req.msg_state != HTTP_MSG_DONE))
- si->flags &= ~SI_FL_L7_RETRY;
- else {
- if (b_is_null(&si->l7_buffer))
- b_alloc(&si->l7_buffer);
- if (b_is_null(&si->l7_buffer))
- si->flags &= ~SI_FL_L7_RETRY;
- else {
- memcpy(b_orig(&si->l7_buffer),
- b_orig(&oc->buf),
- b_size(&oc->buf));
- si->l7_buffer.head = co_data(oc);
- b_add(&si->l7_buffer, co_data(oc));
- }
-
- }
- }
-
ret = cs->conn->mux->snd_buf(cs, &oc->buf, co_data(oc), send_flag);
if (ret > 0) {
did_send = 1;
@@ -1357,28 +1328,6 @@ int si_cs_recv(struct conn_stream *cs)
break;
}
- /* L7 retries enabled and maximum connection retries not reached */
- if ((si->flags & SI_FL_L7_RETRY) && si->conn_retries) {
- struct htx *htx;
- struct htx_sl *sl;
-
- htx = htxbuf(&ic->buf);
- if (htx) {
- sl = http_get_stline(htx);
- if (sl && l7_status_match(si_strm(si)->be,
- sl->info.res.status)) {
- /* If we got a status for which we would
- * like to retry the request, empty
- * the buffer and pretend there's an
- * error on the channel.
- */
- ic->flags |= CF_READ_ERROR;
- htx_reset(htx);
- return 1;
- }
- }
- si->flags &= ~SI_FL_L7_RETRY;
- }
cur_read += ret;
/* if we're allowed to directly forward data, we must update ->o */