MEDIUM: http-ana: Deal with L7 retries in HTTP analysers

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.
This commit is contained in:
Christopher Faulet 2020-10-12 15:18:50 +02:00
parent 991febdfe0
commit 5b82cc5b5c
3 changed files with 54 additions and 69 deletions

View File

@ -1109,6 +1109,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:
@ -1127,6 +1150,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;
}
@ -1254,19 +1278,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);
@ -1281,17 +1306,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
@ -1358,8 +1386,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)
@ -1520,10 +1551,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)))) {

View File

@ -2105,10 +2105,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 {

View File

@ -726,35 +726,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;
@ -1366,28 +1337,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 */