]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
BUG/MEDIUM: httpclient: channel_add_input() must use htx->data
authorWilliam Lallemand <wlallemand@haproxy.org>
Mon, 8 Nov 2021 15:55:14 +0000 (16:55 +0100)
committerWilliam Lallemand <wlallemand@haproxy.org>
Mon, 8 Nov 2021 16:36:31 +0000 (17:36 +0100)
The httpclient uses channel_add_input() to notify the channel layer that
it must forward some data. This function was used with b_data(&req->buf)
which ask to send the size of a buffer (because of the HTX metadata
which fill the buffer completely).

This is wrong and will have the consequence of trying to send data that
doesn't exist, letting HAProxy looping at 100% CPU.

When using htx channel_add_input() must be used with the size of the htx
payload, and not the size of a buffer.

When sending the request payload it also need to sets the buffer size to
0, which is achieved with a htx_to_buf() when the htx payload is empty.

src/http_client.c

index a289206eb4d748a843f289ae5607be29d223bd47..fe85f050b80489f2e75cb0d9609955090d427f22 100644 (file)
@@ -569,17 +569,19 @@ static void httpclient_applet_io_handler(struct appctx *appctx)
                switch(appctx->st0) {
 
                        case HTTPCLIENT_S_REQ:
-                               /* copy the request from the hc->req.buf buffer */
-                               /* We now that it fits the content of a buffer so can
-                                * just push this entirely */
+                               /* we know that the buffer is empty here, since
+                                * it's the first call, we can freely copy the
+                                * request from the httpclient buffer */
                                ret = b_xfer(&req->buf, &hc->req.buf, b_data(&hc->req.buf));
-                               if (ret)
-                                       channel_add_input(req, b_data(&req->buf));
+                               if (!ret)
+                                       goto more;
 
-                               htx = htxbuf(&req->buf);
+                               htx = htx_from_buf(&req->buf);
                                if (!htx)
                                        goto more;
 
+                               channel_add_input(req, htx->data);
+
                                if (htx->flags & HTX_FL_EOM) /* check if a body need to be added */
                                        appctx->st0 = HTTPCLIENT_S_RES_STLINE;
                                else
@@ -592,15 +594,29 @@ static void httpclient_applet_io_handler(struct appctx *appctx)
                                {
                                        if (hc->ops.req_payload) {
 
-                                               ret = b_xfer(&req->buf, &hc->req.buf, b_data(&hc->req.buf));
-                                               if (ret)
-                                                       channel_add_input(req, b_data(&req->buf));
-
                                                /* call the request callback */
                                                hc->ops.req_payload(hc);
+                                               /* check if the request buffer is empty */
+
+                                               htx = htx_from_buf(&req->buf);
+                                               if (!htx_is_empty(htx))
+                                                       goto more;
+                                               /* Here htx_to_buf() will set buffer data to 0 because
+                                                * the HTX is empty, and allow us to do an xfer.
+                                                */
+                                               htx_to_buf(htx, &req->buf);
+
+                                               ret = b_xfer(&req->buf, &hc->req.buf, b_data(&hc->req.buf));
+                                               if (!ret)
+                                                       goto more;
+                                               htx = htx_from_buf(&req->buf);
+                                               if (!htx)
+                                                       goto more;
+
+                                               channel_add_input(req, htx->data);
                                        }
 
-                                       htx = htxbuf(&req->buf);
+                                       htx = htx_from_buf(&req->buf);
                                        if (!htx)
                                                goto more;