MEDIUM: htx: Refactor htx_xfer_blks() to not rely on hdrs_bytes field
It is the only function using the hdrs_bytes start-line field. Thus the function has been refactored to no longer rely on it. To do so, we first copy HTX blocks to the destination message, without removing them from the source message. If the copy is interrupted on headers or trailers, we roll back. Otherwise, data are drained from the source buffer. Most of time, the copy will succeeds. So the roll back is only performed in the worst but very rare case.
This commit is contained in:
parent
5e9b24f4b4
commit
c92ec0ba71
86
src/htx.c
86
src/htx.c
|
@ -651,49 +651,30 @@ struct htx_ret htx_xfer_blks(struct htx *dst, struct htx *src, uint32_t count,
|
|||
enum htx_blk_type mark)
|
||||
{
|
||||
struct htx_blk *blk, *dstblk;
|
||||
struct htx_blk *srcref, *dstref;
|
||||
enum htx_blk_type type;
|
||||
uint32_t info, max, sz, ret;
|
||||
int inside_trailers = 0;
|
||||
|
||||
ret = htx_used_space(dst);
|
||||
blk = htx_get_blk(src, htx_get_head(src));
|
||||
dstblk = NULL;
|
||||
srcref = dstref = dstblk = NULL;
|
||||
|
||||
while (blk && count) {
|
||||
/* blocks are not removed yet from <src> HTX message to be able to
|
||||
* rollback the transfer if all the headers/trailers are not copied.
|
||||
*/
|
||||
for (blk = htx_get_head_blk(src); blk && count; blk = htx_get_next_blk(src, blk)) {
|
||||
type = htx_get_blk_type(blk);
|
||||
|
||||
/* Ignore unused block */
|
||||
if (type == HTX_BLK_UNUSED)
|
||||
goto next;
|
||||
continue;
|
||||
|
||||
/* Be sure to have enough space to xfer all headers/trailers in
|
||||
* one time. If not while <dst> is empty, we report a parsing
|
||||
* error on <src>.
|
||||
*/
|
||||
if (mark >= HTX_BLK_EOH && (type == HTX_BLK_REQ_SL || type == HTX_BLK_RES_SL)) {
|
||||
struct htx_sl *sl = htx_get_blk_ptr(src, blk);
|
||||
|
||||
if (sl->hdrs_bytes != -1 && sl->hdrs_bytes > count) {
|
||||
if (htx_is_empty(dst))
|
||||
src->flags |= HTX_FL_PARSING_ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if ((type == HTX_BLK_TLR || type == HTX_BLK_EOT) &&
|
||||
!inside_trailers && mark >= HTX_BLK_EOT) {
|
||||
inside_trailers = 1;
|
||||
if (htx_used_space(src) > count) {
|
||||
if (htx_is_empty(dst))
|
||||
src->flags |= HTX_FL_PARSING_ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sz = htx_get_blksz(blk);
|
||||
info = blk->info;
|
||||
max = htx_get_max_blksz(dst, count);
|
||||
if (!max)
|
||||
break;
|
||||
|
||||
sz = htx_get_blksz(blk);
|
||||
info = blk->info;
|
||||
if (sz > max) {
|
||||
/* Only DATA blocks can be partially xferred */
|
||||
if (type != HTX_BLK_DATA)
|
||||
|
@ -715,12 +696,53 @@ struct htx_ret htx_xfer_blks(struct htx *dst, struct htx *src, uint32_t count,
|
|||
htx_cut_data_blk(src, blk, sz);
|
||||
break;
|
||||
}
|
||||
next:
|
||||
blk = htx_remove_blk(src, blk);
|
||||
if (type != HTX_BLK_UNUSED && type == mark)
|
||||
|
||||
if (type == mark) {
|
||||
blk = htx_get_next_blk(src, blk);
|
||||
srcref = dstref = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Save <blk> to <srcref> and <dstblk> to <dstref> when we start
|
||||
* to xfer headers or trailers. When EOH/EOT block is reached,
|
||||
* both are reset. It is mandatory to be able to rollback a
|
||||
* partial transfer.
|
||||
*/
|
||||
if (!srcref && !dstref &&
|
||||
(type == HTX_BLK_REQ_SL || type == HTX_BLK_RES_SL || type == HTX_BLK_TLR)) {
|
||||
srcref = blk;
|
||||
dstref = dstblk;
|
||||
}
|
||||
else if (type == HTX_BLK_EOH || type == HTX_BLK_EOT)
|
||||
srcref = dstref = NULL;
|
||||
}
|
||||
|
||||
if (unlikely(dstref)) {
|
||||
/* Headers or trailers part was partially xferred, so rollback the copy
|
||||
* by removing all block between <dstref> and <dstblk>, both included.
|
||||
*/
|
||||
while (dstref && dstref != dstblk)
|
||||
dstref = htx_remove_blk(dst, dstref);
|
||||
htx_remove_blk(dst, dstblk);
|
||||
|
||||
/* <dst> HTX message is empty, it means the headers or trailers
|
||||
* part is too big to be copied at once.
|
||||
*/
|
||||
if (htx_is_empty(dst))
|
||||
src->flags |= HTX_FL_PARSING_ERROR;
|
||||
}
|
||||
|
||||
/* Now, remove xferred blocks from <src> htx message */
|
||||
if (!blk && !srcref) {
|
||||
/* End of src reached, all blocks were consumed, drain all data */
|
||||
htx_drain(src, src->data);
|
||||
}
|
||||
else {
|
||||
/* Remove all block from the head to <blk>, or <srcref> if defined, excluded */
|
||||
srcref = (srcref ? srcref : blk);
|
||||
for (blk = htx_get_head_blk(src); blk && blk != srcref; blk = htx_remove_blk(src, blk));
|
||||
}
|
||||
|
||||
end:
|
||||
ret = htx_used_space(dst) - ret;
|
||||
return (struct htx_ret){.ret = ret, .blk = dstblk};
|
||||
|
|
Loading…
Reference in New Issue