]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
BUG/MINOR: httpclient: consume partly the blocks when necessary
authorWilliam Lallemand <wlallemand@haproxy.org>
Wed, 9 Mar 2022 10:58:51 +0000 (11:58 +0100)
committerWilliam Lallemand <wlallemand@haproxy.org>
Mon, 14 Mar 2022 14:10:12 +0000 (15:10 +0100)
Consume partly the blocks in the httpclient I/O handler when there is
not enough room in the destination buffer for the whole block or when
the block is not contained entirely in the channel's output.

It prevents the I/O handler to be stuck in cases when we need to modify
the buffer with a filter for exemple.

Must be backported in 2.5.

src/http_client.c

index ad3c40c3ce82fae8dcb37aaaff272cc4e92e3378..f17e805e6d9c1f05b24d8d7c91cfd0731981323d 100644 (file)
@@ -849,40 +849,54 @@ static void httpclient_applet_io_handler(struct appctx *appctx)
 
                                /* decapsule the htx data to raw data */
                                for (pos = htx_get_first(htx); pos != -1; pos = htx_get_next(htx, pos)) {
-                                       enum htx_blk_type type;
-                                       uint32_t sz;
-
-                                       blk = htx_get_blk(htx, pos);
-                                       type = htx_get_blk_type(blk);
-                                       sz = htx_get_blksz(blk);
-
-                                       /* we need to check if the data are part of the ouput */
-                                       if (co_data(res) < sz)
+                                       struct htx_blk *blk = htx_get_blk(htx, pos);
+                                       enum htx_blk_type type = htx_get_blk_type(blk);
+                                       size_t count = co_data(res);
+                                       uint32_t blksz = htx_get_blksz(blk);
+                                       uint32_t room = b_room(&hc->res.buf);
+                                       uint32_t vlen;
+
+                                       /* we should try to copy the maximum output data in a block, which fit
+                                        * the destination buffer */
+                                       vlen = MIN(count, blksz);
+                                       vlen = MIN(vlen, room);
+
+                                       if (vlen == 0)
                                                goto process_data;
 
                                        if (type == HTX_BLK_DATA) {
                                                struct ist v = htx_get_blk_value(htx, blk);
 
-                                               if ((b_room(&hc->res.buf) < v.len))
-                                                       goto process_data;
+                                               __b_putblk(&hc->res.buf, v.ptr, vlen);
+                                               c_rew(res, vlen);
 
-                                               __b_putblk(&hc->res.buf, v.ptr, v.len);
-                                               co_set_data(res, co_data(res) - sz);
-                                               htx_remove_blk(htx, blk);
+                                               if (vlen == blksz)
+                                                       htx_remove_blk(htx, blk);
+                                               else
+                                                       htx_cut_data_blk(htx, blk, vlen);
 
                                                /* the data must be processed by the caller in the receive phase */
                                                if (hc->ops.res_payload)
                                                        hc->ops.res_payload(hc);
+
+                                               /* cannot copy everything, need to processs */
+                                               if (vlen != blksz)
+                                                       goto process_data;
                                        } else {
+                                               if (vlen != blksz)
+                                                       goto process_data;
+
                                                /* remove any block which is not a data block */
-                                               co_set_data(res, co_data(res) - sz);
+                                               c_rew(res, blksz);
                                                htx_remove_blk(htx, blk);
                                        }
                                }
+
                                /* if not finished, should be called again */
-                               if (!(htx->flags & HTX_FL_EOM))
+                               if (!(htx_is_empty(htx) && (htx->flags & HTX_FL_EOM)))
                                        goto more;
 
+
                                /* end of message, we should quit */
                                appctx->st0 = HTTPCLIENT_S_RES_END;
                        break;