]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
BUG/MEDIUM: cache/stats: Wait to have the request before sending the response
authorChristopher Faulet <cfaulet@haproxy.com>
Mon, 16 Sep 2024 17:17:33 +0000 (19:17 +0200)
committerChristopher Faulet <cfaulet@haproxy.com>
Mon, 16 Sep 2024 20:55:40 +0000 (22:55 +0200)
It seems obvious. On a classical workflow, the request headers analysis is
finished when these applets are woken up for the first time. So they don't
take care to really have the request to start to process it and to send the
response. But with a filter, it is possible to stop the request analysis
after the applet creation.

If this happens for the stats applet, this leads to a crash because we
retrieve the request start-line without checking if it is available. For the
cache applet, the response is just immediatly sent. And here it is a problem
if the compression is enabled. In that case too, this may lead to a crash
because the compression may be enabled but not initialized.

For a true server, there is no issue because the connection cannot be
established. The server is chosen only after the request analysis. The issue
with applets is that once created, an applet is quickly switched to the
established state. So it is probably a point that must be carefully reviewed
and probably reworked.

In the mean time, as a fix, in the cache and the stats applet, we just take
care to have the request before sending the response. This will do the
trick.

The patch must be backported as far as 2.6. On 2.6, the patch must be adapted.

src/cache.c
src/stats-html.c

index 32f2e471c32da1a972096251e1b99cb42195c922..b291aff4cfff4d7688bb200c7750d5c1c2f1942f 100644 (file)
@@ -1783,12 +1783,23 @@ static void http_cache_io_handler(struct appctx *appctx)
        unsigned int len;
        size_t ret;
 
-       if (applet_fl_test(appctx, APPCTX_FL_OUTBLK_ALLOC|APPCTX_FL_OUTBLK_FULL))
+       if (applet_fl_test(appctx, APPCTX_FL_INBLK_ALLOC|APPCTX_FL_OUTBLK_ALLOC|APPCTX_FL_OUTBLK_FULL))
                goto exit;
 
        if (applet_fl_test(appctx, APPCTX_FL_FASTFWD) && se_fl_test(appctx->sedesc, SE_FL_MAY_FASTFWD_PROD))
                goto exit;
 
+       if (appctx->st0 == HTX_CACHE_INIT) {
+               if (!appctx_get_buf(appctx, &appctx->inbuf) || htx_is_empty(htxbuf(&appctx->inbuf)))
+                       goto wait_request;
+
+               ctx->next = block_ptr(cache_ptr);
+               ctx->offset = sizeof(*cache_ptr);
+               ctx->sent = 0;
+               ctx->rem_data = 0;
+               appctx->st0 = HTX_CACHE_HEADER;
+       }
+
        if (!appctx_get_buf(appctx, &appctx->outbuf)) {
                goto exit;
        }
@@ -1802,14 +1813,6 @@ static void http_cache_io_handler(struct appctx *appctx)
        len = first->len - sizeof(*cache_ptr) - ctx->sent;
        res_htx = htx_from_buf(&appctx->outbuf);
 
-       if (appctx->st0 == HTX_CACHE_INIT) {
-               ctx->next = block_ptr(cache_ptr);
-               ctx->offset = sizeof(*cache_ptr);
-               ctx->sent = 0;
-               ctx->rem_data = 0;
-               appctx->st0 = HTX_CACHE_HEADER;
-       }
-
        if (appctx->st0 == HTX_CACHE_HEADER) {
                struct ist meth;
 
@@ -1884,6 +1887,11 @@ static void http_cache_io_handler(struct appctx *appctx)
        appctx->sedesc->iobuf.flags &= ~IOBUF_FL_FF_BLOCKED;
        return;
 
+  wait_request:
+       /* Wait for the request before starting to deliver the response */
+       applet_need_more_data(appctx);
+       return;
+
   error:
        /* Sent and HTTP error 500 */
        b_reset(&appctx->outbuf);
index 23cfb91a0d3cf7846d11bdbd8a8ca15d0e176a25..dc3dfa6f8f99a21e11e1736bb186289ffcc6c263 100644 (file)
@@ -1973,6 +1973,11 @@ static void http_stats_io_handler(struct appctx *appctx)
        if (applet_fl_test(appctx, APPCTX_FL_FASTFWD) && se_fl_test(appctx->sedesc, SE_FL_MAY_FASTFWD_PROD))
                goto out;
 
+       if (appctx->st0 != STAT_HTTP_END) {
+               if (!appctx_get_buf(appctx, &appctx->inbuf) || htx_is_empty(htxbuf(&appctx->inbuf)))
+                       goto wait_request;
+       }
+
        if (!appctx_get_buf(appctx, &appctx->outbuf)) {
                goto out;
        }
@@ -2062,6 +2067,13 @@ static void http_stats_io_handler(struct appctx *appctx)
        }
        else if (applet_fl_test(appctx, APPCTX_FL_OUTBLK_FULL))
                applet_wont_consume(appctx);
+       return;
+
+  wait_request:
+       /* Wait for the request before starting to deliver the response */
+       applet_need_more_data(appctx);
+       return;
+
 }
 
 static size_t http_stats_fastfwd(struct appctx *appctx, struct buffer *buf,