diff --git a/include/proto/htx.h b/include/proto/htx.h index fdd305646..d803947cd 100644 --- a/include/proto/htx.h +++ b/include/proto/htx.h @@ -541,21 +541,49 @@ static inline size_t buf_room_for_htx_data(const struct buffer *buf) return room; } -/* Returns an HTX message using the buffer . */ -static inline struct htx *htx_from_buf(struct buffer *buf) -{ - struct htx *htx; - if (b_is_null(buf)) - return &htx_empty; - htx = (struct htx *)(buf->area); - if (!b_data(buf)) { +/* Returns an HTX message using the buffer . Unlike htx_from_buf(), this + * function does not update to the buffer. */ +static inline struct htx *htxbuf(const struct buffer *buf) +{ + struct htx *htx; + + if (b_is_null(buf)) + return &htx_empty; + htx = ((struct htx *)(buf->area)); + if (!b_data(buf)) { htx->size = buf->size - sizeof(*htx); - htx_reset(htx); + htx_reset(htx); } return htx; } +/* Returns an HTX message using the buffer . is updated to appear as + * full. It is the caller responsibility to call htx_to_buf() when it finish to + * manipulate the HTX message to update accordingly. + * + * If the caller can call htxbuf() function to avoir any update of the + * buffer. + */ +static inline struct htx *htx_from_buf(struct buffer *buf) +{ + struct htx *htx = htxbuf(buf); + + b_set_data(buf, b_size(buf)); + return htx; +} + +/* Upate accordingly to the HTX message */ +static inline void htx_to_buf(struct htx *htx, struct buffer *buf) +{ + if (!htx->used) { + htx_reset(htx); + b_set_data(buf, 0); + } + else + b_set_data(buf, b_size(buf)); +} + /* Returns 1 if the message is empty, otherwise it returns 0. */ static inline int htx_is_empty(const struct htx *htx) { diff --git a/src/filters.c b/src/filters.c index 2b8238957..6368b20b9 100644 --- a/src/filters.c +++ b/src/filters.c @@ -930,7 +930,7 @@ flt_analyze_http_headers(struct stream *s, struct channel *chn, unsigned int an_ } RESUME_FILTER_END; if (IS_HTX_STRM(s)) { - struct htx *htx = htx_from_buf(&chn->buf); + struct htx *htx = htxbuf(&chn->buf); int32_t pos; for (pos = htx_get_head(htx); pos != -1; pos = htx_get_next(htx, pos)) { diff --git a/src/flt_http_comp.c b/src/flt_http_comp.c index 5e0ee59a7..7ffff7631 100644 --- a/src/flt_http_comp.c +++ b/src/flt_http_comp.c @@ -197,7 +197,7 @@ comp_http_payload(struct stream *s, struct filter *filter, struct http_msg *msg, unsigned int offset, unsigned int len) { struct comp_state *st = filter->ctx; - struct htx *htx = htx_from_buf(&msg->chn->buf); + struct htx *htx = htxbuf(&msg->chn->buf); struct htx_blk *blk; struct htx_ret htx_ret; int ret, consumed = 0, to_forward = 0; @@ -575,7 +575,7 @@ http_select_comp_reqhdr(struct comp_state *st, struct stream *s, struct http_msg static int htx_select_comp_reqhdr(struct comp_state *st, struct stream *s, struct http_msg *msg) { - struct htx *htx = htx_from_buf(&msg->chn->buf); + struct htx *htx = htxbuf(&msg->chn->buf); struct http_hdr_ctx ctx; struct comp_algo *comp_algo = NULL; struct comp_algo *comp_algo_back = NULL; @@ -818,7 +818,7 @@ http_select_comp_reshdr(struct comp_state *st, struct stream *s, struct http_msg static int htx_select_comp_reshdr(struct comp_state *st, struct stream *s, struct http_msg *msg) { - struct htx *htx = htx_from_buf(&msg->chn->buf); + struct htx *htx = htxbuf(&msg->chn->buf); struct http_txn *txn = s->txn; struct http_hdr_ctx ctx; struct comp_type *comp_type; diff --git a/src/flt_trace.c b/src/flt_trace.c index 0dd655c6b..92a51bf15 100644 --- a/src/flt_trace.c +++ b/src/flt_trace.c @@ -412,7 +412,7 @@ trace_http_headers(struct stream *s, struct filter *filter, channel_label(msg->chn), proxy_mode(s), stream_pos(s)); if (IS_HTX_STRM(s)) { - struct htx *htx = htx_from_buf(&msg->chn->buf); + struct htx *htx = htxbuf(&msg->chn->buf); struct htx_sl *sl = http_find_stline(htx); int32_t pos; @@ -473,7 +473,7 @@ trace_http_payload(struct stream *s, struct filter *filter, struct http_msg *msg offset, len, ret); if (conf->hexdump) - trace_htx_hexdump(htx_from_buf(&msg->chn->buf), offset, len); + trace_htx_hexdump(htxbuf(&msg->chn->buf), offset, len); if (ret != len) task_wakeup(s->task, TASK_WOKEN_MSG); diff --git a/src/http_fetch.c b/src/http_fetch.c index 81f6669c9..5f579d1f1 100644 --- a/src/http_fetch.c +++ b/src/http_fetch.c @@ -73,7 +73,7 @@ static int get_http_auth(struct sample *smp) if (IS_HTX_STRM(s) || (smp->px->mode == PR_MODE_TCP)) { /* HTX version */ - struct htx *htx = htx_from_buf(&s->req.buf); + struct htx *htx = htxbuf(&s->req.buf); struct http_hdr_ctx ctx = { .blk = NULL }; struct ist hdr; @@ -82,7 +82,6 @@ static int get_http_auth(struct sample *smp) else hdr = ist("Authorization"); - htx = htx_from_buf(&s->req.buf); ctx.blk = NULL; if (!http_find_header(htx, hdr, &ctx, 0)) return 0; @@ -191,7 +190,7 @@ struct htx *smp_prefetch_htx(struct sample *smp, const struct arg *args) if (px->mode == PR_MODE_HTTP) { if ((opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ) { - htx = htx_from_buf(&s->req.buf); + htx = htxbuf(&s->req.buf); if (htx_is_empty(htx) || htx_get_tail_type(htx) < HTX_BLK_EOH) { /* Parsing is done by the mux, just wait */ smp->flags |= SMP_F_MAY_CHANGE; @@ -212,7 +211,7 @@ struct htx *smp_prefetch_htx(struct sample *smp, const struct arg *args) /* otherwise everything's ready for the request */ } else { - htx = htx_from_buf(&s->res.buf); + htx = htxbuf(&s->res.buf); if (htx_is_empty(htx) || htx_get_tail_type(htx) < HTX_BLK_EOH) { /* Parsing is done by the mux, just wait */ smp->flags |= SMP_F_MAY_CHANGE; diff --git a/src/http_htx.c b/src/http_htx.c index 58629785c..9a1dfb469 100644 --- a/src/http_htx.c +++ b/src/http_htx.c @@ -675,8 +675,6 @@ static struct htx *http_str_to_htx(struct buffer *buf, struct ist raw) } if (!htx_add_endof(htx, HTX_BLK_EOM)) goto error; - - b_set_data(buf, b_size(buf)); return htx; error: diff --git a/src/htx.c b/src/htx.c index 0d7ddc153..d691c2e58 100644 --- a/src/htx.c +++ b/src/htx.c @@ -24,7 +24,7 @@ struct htx htx_empty = { .size = 0, .data = 0, .used = 0 }; struct htx_blk *htx_defrag(struct htx *htx, struct htx_blk *blk) { struct buffer *chunk = get_trash_chunk(); - struct htx *tmp = htx_from_buf(chunk); + struct htx *tmp = htxbuf(chunk); struct htx_blk *newblk, *oldblk; uint32_t new, old; uint32_t addr, blksz; diff --git a/src/mux_h1.c b/src/mux_h1.c index 464681212..0b085c2c6 100644 --- a/src/mux_h1.c +++ b/src/mux_h1.c @@ -1207,7 +1207,6 @@ static size_t h1_process_input(struct h1c *h1c, struct buffer *buf, int flags) int errflag; htx = htx_from_buf(buf); - b_set_data(buf, b_size(buf)); count = b_data(&h1c->ibuf); max = htx_free_space(htx); if (flags & CO_RFL_KEEP_RSV) { @@ -1261,12 +1260,7 @@ static size_t h1_process_input(struct h1c *h1c, struct buffer *buf, int flags) b_del(&h1c->ibuf, total); end: - if (htx_is_not_empty(htx)) - b_set_data(buf, b_size(buf)); - else { - htx_reset(htx); - b_set_data(buf, 0); - } + htx_to_buf(htx, buf); if (h1c->flags & H1C_F_IN_FULL && buf_room_for_htx_data(&h1c->ibuf)) { h1c->flags &= ~H1C_F_IN_FULL; @@ -1288,10 +1282,9 @@ static size_t h1_process_input(struct h1c *h1c, struct buffer *buf, int flags) return total; parsing_err: - // FIXME: create an error snapshot here b_reset(&h1c->ibuf); htx->flags |= HTX_FL_PARSING_ERROR; - b_set_data(buf, b_size(buf)); + htx_to_buf(htx, buf); h1s->cs->flags |= CS_FL_EOS; return 0; } @@ -1497,10 +1490,7 @@ static size_t h1_process_output(struct h1c *h1c, struct buffer *buf, size_t coun if (!buf_room_for_htx_data(&h1c->obuf)) h1c->flags |= H1C_F_OUT_FULL; - if (htx_is_empty(chn_htx)) { - htx_reset(chn_htx); - b_set_data(buf, 0); - } + htx_to_buf(chn_htx, buf); end: return total; } diff --git a/src/mux_h2.c b/src/mux_h2.c index 5585ac2c2..e94ec67e5 100644 --- a/src/mux_h2.c +++ b/src/mux_h2.c @@ -3118,6 +3118,8 @@ static int h2s_decode_headers(struct h2s *h2s) } leave: + if (htx) + htx_to_buf(htx, &h2s->rxbuf); free_trash_chunk(copy); return outlen; fail: @@ -3319,9 +3321,12 @@ static int h2_frt_transfer_data(struct h2s *h2s) h2s->flags |= H2_SF_ES_RCVD; h2s->cs->flags |= CS_FL_REOS; } - + if (htx) + htx_to_buf(htx, csbuf); return 1; fail: + if (htx) + htx_to_buf(htx, csbuf); return 0; } @@ -4515,8 +4520,8 @@ static size_t h2_rcv_buf(struct conn_stream *cs, struct buffer *buf, size_t coun htx_ret = htx_xfer_blks(buf_htx, h2s_htx, count, HTX_BLK_EOM); buf_htx->extra = h2s_htx->extra; - if (htx_is_not_empty(buf_htx)) - b_set_data(buf, b_size(buf)); + htx_to_buf(buf_htx, buf); + htx_to_buf(h2s_htx, &h2s->rxbuf); ret = htx_ret.ret; } else { @@ -4719,10 +4724,7 @@ static size_t h2_snd_buf(struct conn_stream *cs, struct buffer *buf, size_t coun } if (htx) { - if (htx_is_empty(htx)) { - htx_reset(htx); - b_set_data(buf, 0); - } + htx_to_buf(htx, buf); } else { b_del(buf, total); } diff --git a/src/proto_htx.c b/src/proto_htx.c index e76d0bffc..3232d0a2e 100644 --- a/src/proto_htx.c +++ b/src/proto_htx.c @@ -95,7 +95,7 @@ int htx_wait_for_request(struct stream *s, struct channel *req, int an_bit) ci_data(req), req->analysers); - htx = htx_from_buf(&req->buf); + htx = htxbuf(&req->buf); /* we're speaking HTTP here, so let's speak HTTP to the client */ s->srv_error = http_return_srv_error; @@ -496,7 +496,7 @@ int htx_process_req_common(struct stream *s, struct channel *req, int an_bit, st ci_data(req), req->analysers); - htx = htx_from_buf(&req->buf); + htx = htxbuf(&req->buf); /* just in case we have some per-backend tracking */ stream_inc_be_http_req_ctr(s); @@ -771,7 +771,7 @@ int htx_process_request(struct stream *s, struct channel *req, int an_bit) * whatever we want with the remaining request. Also, now we * may have separate values for ->fe, ->be. */ - htx = htx_from_buf(&req->buf); + htx = htxbuf(&req->buf); /* * If HTTP PROXY is set we simply get remote server address parsing @@ -1057,7 +1057,7 @@ int htx_wait_for_request_body(struct stream *s, struct channel *req, int an_bit) ci_data(req), req->analysers); - htx = htx_from_buf(&req->buf); + htx = htxbuf(&req->buf); if (msg->msg_state < HTTP_MSG_BODY) goto missing_data; @@ -1178,7 +1178,7 @@ int htx_request_forward_body(struct stream *s, struct channel *req, int an_bit) ci_data(req), req->analysers); - htx = htx_from_buf(&req->buf); + htx = htxbuf(&req->buf); if ((req->flags & (CF_READ_ERROR|CF_READ_TIMEOUT|CF_WRITE_ERROR|CF_WRITE_TIMEOUT)) || ((req->flags & CF_SHUTW) && (req->to_forward || co_data(req)))) { @@ -1450,7 +1450,7 @@ int htx_wait_for_response(struct stream *s, struct channel *rep, int an_bit) ci_data(rep), rep->analysers); - htx = htx_from_buf(&rep->buf); + htx = htxbuf(&rep->buf); /* * Now we quickly check if we have found a full valid response. @@ -1820,7 +1820,7 @@ int htx_process_res_common(struct stream *s, struct channel *rep, int an_bit, st ci_data(rep), rep->analysers); - htx = htx_from_buf(&rep->buf); + htx = htxbuf(&rep->buf); /* The stats applet needs to adjust the Connection header but we don't * apply any filter there. @@ -2136,7 +2136,7 @@ int htx_response_forward_body(struct stream *s, struct channel *res, int an_bit) ci_data(res), res->analysers); - htx = htx_from_buf(&res->buf); + htx = htxbuf(&res->buf); if ((res->flags & (CF_READ_ERROR|CF_READ_TIMEOUT|CF_WRITE_ERROR|CF_WRITE_TIMEOUT)) || ((res->flags & CF_SHUTW) && (res->to_forward || co_data(res)))) { @@ -2359,7 +2359,7 @@ int htx_apply_redirect_rule(struct redirect_rule *rule, struct stream *s, struct /* * Create the location */ - htx = htx_from_buf(&req->buf); + htx = htxbuf(&req->buf); switch(rule->type) { case REDIRECT_TYPE_SCHEME: { struct http_hdr_ctx ctx; @@ -2540,7 +2540,6 @@ int htx_apply_redirect_rule(struct redirect_rule *rule, struct stream *s, struct s->logs.tv_request = now; data = htx->data - co_data(res); - b_set_data(&res->buf, b_size(&res->buf)); c_adv(res, data); res->total += data; @@ -2632,7 +2631,6 @@ static int htx_reply_103_early_hints(struct channel *res) } data = htx->data - co_data(res); - b_set_data(&res->buf, b_size(&res->buf)); c_adv(res, data); res->total += data; return 0; @@ -2669,7 +2667,6 @@ static int htx_add_early_hint_header(struct stream *s, int early_hints, const st goto fail; free_trash_chunk(value); - b_set_data(&res->buf, b_size(&res->buf)); return 1; fail: @@ -2697,7 +2694,7 @@ static int htx_add_early_hint_header(struct stream *s, int early_hints, const st int htx_req_replace_stline(int action, const char *replace, int len, struct proxy *px, struct stream *s) { - struct htx *htx = htx_from_buf(&s->req.buf); + struct htx *htx = htxbuf(&s->req.buf); switch (action) { case 0: // method @@ -2731,7 +2728,7 @@ int htx_req_replace_stline(int action, const char *replace, int len, */ void htx_res_set_status(unsigned int status, const char *reason, struct stream *s) { - struct htx *htx = htx_from_buf(&s->res.buf); + struct htx *htx = htxbuf(&s->res.buf); char *res; chunk_reset(&trash); @@ -2769,7 +2766,7 @@ static enum rule_result htx_req_get_intercept_rule(struct proxy *px, struct list int act_flags = 0; int early_hints = 0; - htx = htx_from_buf(&s->req.buf); + htx = htxbuf(&s->req.buf); /* If "the current_rule_list" match the executed rule list, we are in * resume condition. If a resume is needed it is always in the action @@ -3157,7 +3154,7 @@ static enum rule_result htx_res_get_intercept_rule(struct proxy *px, struct list enum rule_result rule_ret = HTTP_RULE_RES_CONT; int act_flags = 0; - htx = htx_from_buf(&s->res.buf); + htx = htxbuf(&s->res.buf); /* If "the current_rule_list" match the executed rule list, we are in * resume condition. If a resume is needed it is always in the action @@ -3493,7 +3490,7 @@ static int htx_apply_filter_to_req_headers(struct stream *s, struct channel *req struct buffer *hdr = get_trash_chunk(); int32_t pos; - htx = htx_from_buf(&req->buf); + htx = htxbuf(&req->buf); for (pos = htx_get_head(htx); pos != -1; pos = htx_get_next(htx, pos)) { struct htx_blk *blk = htx_get_blk(htx, pos); @@ -3587,7 +3584,7 @@ static int htx_apply_filter_to_req_line(struct stream *s, struct channel *req, s struct buffer *reqline = get_trash_chunk(); int done; - htx = htx_from_buf(&req->buf); + htx = htxbuf(&req->buf); if (unlikely(txn->flags & (TX_CLDENY | TX_CLTARPIT))) return 1; @@ -3709,7 +3706,7 @@ static int htx_apply_filter_to_resp_headers(struct stream *s, struct channel *re struct buffer *hdr = get_trash_chunk(); int32_t pos; - htx = htx_from_buf(&res->buf); + htx = htxbuf(&res->buf); for (pos = htx_get_head(htx); pos != -1; pos = htx_get_next(htx, pos)) { struct htx_blk *blk = htx_get_blk(htx, pos); @@ -3798,7 +3795,7 @@ static int htx_apply_filter_to_sts_line(struct stream *s, struct channel *res, s struct buffer *resline = get_trash_chunk(); int done; - htx = htx_from_buf(&res->buf); + htx = htxbuf(&res->buf); if (unlikely(txn->flags & TX_SVDENY)) return 1; @@ -3920,7 +3917,7 @@ static void htx_manage_client_side_cookies(struct stream *s, struct channel *req char *prev, *att_beg, *att_end, *equal, *val_beg, *val_end, *next; int preserve_hdr; - htx = htx_from_buf(&req->buf); + htx = htxbuf(&req->buf); ctx.blk = NULL; while (http_find_header(htx, ist("Cookie"), &ctx, 1)) { del_from = NULL; /* nothing to be deleted */ @@ -4321,7 +4318,7 @@ static void htx_manage_server_side_cookies(struct stream *s, struct channel *res char *prev, *att_beg, *att_end, *equal, *val_beg, *val_end, *next; int is_cookie2; - htx = htx_from_buf(&res->buf); + htx = htxbuf(&res->buf); ctx.blk = NULL; while (1) { @@ -4601,7 +4598,7 @@ void htx_check_request_for_cacheability(struct stream *s, struct channel *req) if ((txn->flags & (TX_CACHEABLE|TX_CACHE_IGNORE)) == TX_CACHE_IGNORE) return; /* nothing more to do here */ - htx = htx_from_buf(&req->buf); + htx = htxbuf(&req->buf); pragma_found = cc_found = 0; for (pos = htx_get_head(htx); pos != -1; pos = htx_get_next(htx, pos)) { struct htx_blk *blk = htx_get_blk(htx, pos); @@ -4690,7 +4687,7 @@ void htx_check_response_for_cacheability(struct stream *s, struct channel *res) return; } - htx = htx_from_buf(&res->buf); + htx = htxbuf(&res->buf); for (pos = htx_get_head(htx); pos != -1; pos = htx_get_next(htx, pos)) { struct htx_blk *blk = htx_get_blk(htx, pos); enum htx_blk_type type = htx_get_blk_type(blk); @@ -4767,7 +4764,7 @@ int htx_send_name_header(struct stream *s, struct proxy *be, const char *srv_nam uint32_t data; hdr = ist2(be->server_id_hdr_name, be->server_id_hdr_len); - htx = htx_from_buf(&s->req.buf); + htx = htxbuf(&s->req.buf); data = htx->data; ctx.blk = NULL; @@ -4806,7 +4803,7 @@ static int htx_stats_check_uri(struct stream *s, struct http_txn *txn, struct pr if (txn->meth != HTTP_METH_GET && txn->meth != HTTP_METH_HEAD && txn->meth != HTTP_METH_POST) return 0; - htx = htx_from_buf(&s->req.buf); + htx = htxbuf(&s->req.buf); sl = http_find_stline(htx); uri = htx_sl_req_uri(sl); @@ -4849,7 +4846,7 @@ static int htx_handle_stats(struct stream *s, struct channel *req) if ((msg->flags & HTTP_MSGF_VER_11) && (txn->meth != HTTP_METH_HEAD)) appctx->ctx.stats.flags |= STAT_CHUNKED; - htx = htx_from_buf(&req->buf); + htx = htxbuf(&req->buf); sl = http_find_stline(htx); lookup = HTX_SL_REQ_UPTR(sl) + uri_auth->uri_len; end = HTX_SL_REQ_UPTR(sl) + HTX_SL_REQ_ULEN(sl); @@ -5002,7 +4999,7 @@ void htx_perform_server_redirect(struct stream *s, struct stream_interface *si) } /* 2: add the request Path */ - htx = htx_from_buf(&req->buf); + htx = htxbuf(&req->buf); sl = http_find_stline(htx); path = http_get_path(htx_sl_req_uri(sl)); if (!path.ptr) @@ -5037,7 +5034,6 @@ void htx_perform_server_redirect(struct stream *s, struct stream_interface *si) * Send the message */ data = htx->data - co_data(res); - b_set_data(&res->buf, b_size(&res->buf)); c_adv(res, data); res->total += data; @@ -5333,7 +5329,6 @@ void htx_server_error(struct stream *s, struct stream_interface *si, int err, chn->buf.data = msg->data; memcpy(chn->buf.area, msg->area, msg->data); htx = htx_from_buf(&chn->buf); - b_set_data(&chn->buf, b_size(&chn->buf)); c_adv(chn, htx->data); chn->total += htx->data; } @@ -5364,7 +5359,6 @@ void htx_reply_and_close(struct stream *s, short status, struct buffer *msg) chn->buf.data = msg->data; memcpy(chn->buf.area, msg->area, msg->data); htx = htx_from_buf(&chn->buf); - b_set_data(&chn->buf, b_size(&chn->buf)); c_adv(chn, htx->data); chn->total += htx->data; } @@ -5410,7 +5404,6 @@ static int htx_reply_100_continue(struct stream *s) goto fail; data = htx->data - co_data(res); - b_set_data(&res->buf, b_size(&res->buf)); c_adv(res, data); res->total += data; return 0; @@ -5472,7 +5465,6 @@ static int htx_reply_40x_unauthorized(struct stream *s, const char *auth_realm) goto fail; data = htx->data - co_data(res); - b_set_data(&res->buf, b_size(&res->buf)); c_adv(res, data); res->total += data; diff --git a/src/stats.c b/src/stats.c index 57968860f..c85d411d4 100644 --- a/src/stats.c +++ b/src/stats.c @@ -3218,8 +3218,6 @@ static void htx_stats_io_handler(struct appctx *appctx) } if (appctx->st0 == STAT_HTTP_DONE) { - struct htx_blk *blk; - /* Don't add EOD and TLR because mux-h1 will take care of it */ if (!htx_add_endof(res_htx, HTX_BLK_EOM)) { si_rx_room_blk(si); @@ -3227,20 +3225,10 @@ static void htx_stats_io_handler(struct appctx *appctx) } /* eat the whole request */ - req_htx = htx_from_buf(&req->buf); - blk = htx_get_head_blk(req_htx); - while (blk) { - enum htx_blk_type type = htx_get_blk_type(blk); - - blk = htx_remove_blk(req_htx, blk); - if (type == HTX_BLK_EOM) - break; - } + req_htx = htxbuf(&req->buf); + htx_reset(req_htx); + htx_to_buf(req_htx, &req->buf); co_set_data(req, 0); - if (htx_is_empty(req_htx)) { - htx_reset(req_htx); - b_set_data(&req->buf, 0); - } res->flags |= CF_READ_NULL; si_shutr(si); } @@ -3262,14 +3250,9 @@ static void htx_stats_io_handler(struct appctx *appctx) * deciding to wake the applet up. It saves it from looping when * emitting large blocks into small TCP windows. */ - if (htx_is_empty(res_htx)) { - htx_reset(res_htx); - b_set_data(&res->buf, 0); - } - else { - b_set_data(&res->buf, b_size(&res->buf)); + htx_to_buf(res_htx, &res->buf); + if (!channel_is_empty(res)) si_stop_get(si); - } }