]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: http-ana: Deal with L7 retries in HTTP analysers
authorChristopher Faulet <cfaulet@haproxy.com>
Mon, 12 Oct 2020 13:18:50 +0000 (15:18 +0200)
committerChristopher Faulet <cfaulet@haproxy.com>
Thu, 28 Jan 2021 15:37:14 +0000 (16:37 +0100)
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.

src/http_ana.c
src/stream.c
src/stream_interface.c

index 8f8ecce88c5e09a4f81ec44e32edd7044ee97015..cf1fb8c317747fc3f4c561779ff8278a6d0a7402 100644 (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)))) {
index 74dfa903c8435af3c415fceee7120b8b8fa52b78..5a2c0519f70803c9248bcf20318566eb75851c56 100644 (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 {
index 0e2f6819a88ed301bff84399c34c239f2c151776..59d2852e195049baeb774d0d6f7cebd149092958 100644 (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 */