BUG/MINOR: httpclient: consume partly the blocks when necessary

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.
This commit is contained in:
William Lallemand 2022-03-09 11:58:51 +01:00
parent f5ba296ec8
commit c8f1eb99b4

View File

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