]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
BUG/MINOR: cache: Fully consume large requests in the cache applet
authorChristopher Faulet <cfaulet@haproxy.com>
Mon, 25 Feb 2019 10:40:49 +0000 (11:40 +0100)
committerWilly Tarreau <w@1wt.eu>
Tue, 19 Mar 2019 08:49:08 +0000 (09:49 +0100)
In the cache applet (in HTX and legacy HTTP), when an cached object is sent to a
client, the request must be consumed. It is done at the end, after all the
response was copied into the channel's buffer. But only outgoing data at time
the applet is called are consumed. Then the applet is closed. If a request with
a huge body is sent, an error is triggerred because a SHUTW is catched on an
unfinished request.

Now, we consume request data as soon as possible and we do it until the end. In
fact, we don't try to shutdown the request's channel for write anymore.

This patch must be backported to 1.9 after some observation period.

src/cache.c

index e0b904be6ec42f18f42cd51b94ee7a273eccd972..a24c2847477359930b188999f2e4f2a25063b16b 100644 (file)
@@ -1097,39 +1097,22 @@ static void htx_cache_io_handler(struct appctx *appctx)
        }
 
   end:
-       if (appctx->st0 == HTX_CACHE_END) {
-               /* eat the whole request */
-               req_htx = htxbuf(&req->buf);
-               htx_reset(req_htx);
-               htx_to_buf(req_htx, &req->buf);
-               co_set_data(req, 0);
+       if (!(res->flags & CF_SHUTR) && appctx->st0 == HTX_CACHE_END) {
                res->flags |= CF_READ_NULL;
                si_shutr(si);
        }
 
-       if ((res->flags & CF_SHUTR) && (si->state == SI_ST_EST))
-               si_shutw(si);
-
-       if (appctx->st0 == HTX_CACHE_END) {
-               if ((req->flags & CF_SHUTW) && (si->state == SI_ST_EST)) {
-                       si_shutr(si);
-                       res->flags |= CF_READ_NULL;
-               }
-       }
   out:
        if (total)
                channel_add_input(res, total);
-
-       /* we have left the request in the buffer for the case where we
-        * process a POST, and this automatically re-enables activity on
-        * read. It's better to indicate that we want to stop reading when
-        * we're sending, so that we know there's at most one direction
-        * deciding to wake the applet up. It saves it from looping when
-        * emitting large blocks into small TCP windows.
-        */
        htx_to_buf(res_htx, &res->buf);
-       if (!channel_is_empty(res))
-               si_stop_get(si);
+
+       /* eat the whole request */
+       if (co_data(req)) {
+               req_htx = htx_from_buf(&req->buf);
+               co_htx_skip(req, req_htx, co_data(req));
+               htx_to_buf(req_htx, &req->buf);
+       }
        return;
 
   error:
@@ -1266,17 +1249,17 @@ static void http_cache_io_handler(struct appctx *appctx)
                }
        }
 
-       if (appctx->st0 == HTTP_CACHE_FWD) {
-               /* eat the whole request */
-               co_skip(si_oc(si), co_data(si_oc(si)));   // NOTE: when disabled does not repport the  correct status code
+       if (appctx->st0 == HTTP_CACHE_FWD)
+               appctx->st0 = HTTP_CACHE_END;
+
+       if (!(res->flags & CF_SHUTR) && appctx->st0 == HTTP_CACHE_END) {
                res->flags |= CF_READ_NULL;
                si_shutr(si);
        }
-
-       if ((res->flags & CF_SHUTR) && (si->state == SI_ST_EST))
-               si_shutw(si);
 out:
-       ;
+       /* eat the whole request */
+       if (co_data(si_oc(si)))
+           co_skip(si_oc(si), co_data(si_oc(si)));
 }
 
 static int parse_cache_rule(struct proxy *proxy, const char *name, struct act_rule *rule, char **err)