MEDIUM: htx: Add the parsing of trailers of chunked messages

HTTP trailers are now parsed in the same way headers are. It means trailers are
converted to K/V blocks followed by an end-of-trailer marker. For now, to make
things simple, the type for trailer blocks are not the same than for header
blocks. But the aim is to make no difference between headers and trailers by
using the same type. Probably for the end-of marker too.
This commit is contained in:
Christopher Faulet 2019-06-03 10:41:26 +02:00
parent 8f3c256f7e
commit 2d7c5395ed
8 changed files with 205 additions and 196 deletions

View File

@ -103,7 +103,8 @@ enum htx_blk_type {
HTX_BLK_DATA = 4, /* data block */
HTX_BLK_EOD = 5, /* end-of-data block */
HTX_BLK_TLR = 6, /* trailer name/value block */
HTX_BLK_EOM = 7, /* end-of-message block */
HTX_BLK_EOT = 7, /* end-of-trailers block */
HTX_BLK_EOM = 8, /* end-of-message block */
/* 8 .. 14 unused */
HTX_BLK_UNUSED = 15, /* unused/removed block */
};
@ -182,19 +183,19 @@ struct htx_blk *htx_replace_header(struct htx *htx, struct htx_blk *blk,
const struct ist name, const struct ist value);
struct htx_blk *htx_add_header(struct htx *htx, const struct ist name, const struct ist value);
struct htx_blk *htx_add_trailer(struct htx *htx, const struct ist name, const struct ist value);
struct htx_blk *htx_add_blk_type_size(struct htx *htx, enum htx_blk_type type, uint32_t blksz);
struct htx_blk *htx_add_all_headers(struct htx *htx, const struct http_hdr *hdrs);
struct htx_blk *htx_add_all_trailers(struct htx *htx, const struct http_hdr *hdrs);
struct htx_blk *htx_add_endof(struct htx *htx, enum htx_blk_type type);
struct htx_blk *htx_add_data_atonce(struct htx *htx, const struct ist data);
size_t htx_add_data(struct htx *htx, const struct ist data);
struct htx_blk *htx_add_trailer(struct htx *htx, const struct ist tlr);
struct htx_blk *htx_add_data_before(struct htx *htx, const struct htx_blk *ref, const struct ist data);
int htx_reqline_to_h1(const struct htx_sl *sl, struct buffer *chk);
int htx_stline_to_h1(const struct htx_sl *sl, struct buffer *chk);
int htx_hdr_to_h1(const struct ist n, const struct ist v, struct buffer *chk);
int htx_data_to_h1(const struct ist data, struct buffer *chk, int chunked);
int htx_trailer_to_h1(const struct ist tlr, struct buffer *chk);
/* Functions and macros to get parts of the start-line or legnth of these
* parts
@ -299,6 +300,7 @@ static inline uint32_t htx_get_blksz(const struct htx_blk *blk)
switch (type) {
case HTX_BLK_HDR:
case HTX_BLK_TLR:
/* name.length + value.length */
return ((blk->info & 0xff) + ((blk->info >> 8) & 0xfffff));
default:
@ -513,12 +515,12 @@ static inline void htx_set_blk_value_len(struct htx_blk *blk, uint32_t vlen)
switch (type) {
case HTX_BLK_HDR:
case HTX_BLK_TLR:
blk->info = (type << 28) + (vlen << 8) + (blk->info & 0xff);
break;
case HTX_BLK_REQ_SL:
case HTX_BLK_RES_SL:
case HTX_BLK_DATA:
case HTX_BLK_TLR:
blk->info = (type << 28) + vlen;
break;
default:
@ -543,6 +545,7 @@ static inline struct ist htx_get_blk_name(const struct htx *htx, const struct ht
switch (type) {
case HTX_BLK_HDR:
case HTX_BLK_TLR:
ret.ptr = htx_get_blk_ptr(htx, blk);
ret.len = blk->info & 0xff;
break;
@ -564,6 +567,7 @@ static inline struct ist htx_get_blk_value(const struct htx *htx, const struct h
switch (type) {
case HTX_BLK_HDR:
case HTX_BLK_TLR:
ret.ptr = htx_get_blk_ptr(htx, blk) + (blk->info & 0xff);
ret.len = (blk->info >> 8) & 0xfffff;
break;
@ -571,7 +575,6 @@ static inline struct ist htx_get_blk_value(const struct htx *htx, const struct h
case HTX_BLK_REQ_SL:
case HTX_BLK_RES_SL:
case HTX_BLK_DATA:
case HTX_BLK_TLR:
ret.ptr = htx_get_blk_ptr(htx, blk);
ret.len = blk->info & 0xfffffff;
break;
@ -755,6 +758,7 @@ static inline const char *htx_blk_type_str(enum htx_blk_type type)
case HTX_BLK_DATA: return "HTX_BLK_DATA";
case HTX_BLK_EOD: return "HTX_BLK_EOD";
case HTX_BLK_TLR: return "HTX_BLK_TLR";
case HTX_BLK_EOT: return "HTX_BLK_EOT";
case HTX_BLK_EOM: return "HTX_BLK_EOM";
case HTX_BLK_UNUSED: return "HTX_BLK_UNUSED";
default: return "HTX_BLK_???";
@ -789,7 +793,7 @@ static inline void htx_dump(struct htx *htx)
HTX_SL_P2_LEN(sl), HTX_SL_P2_PTR(sl),
HTX_SL_P3_LEN(sl), HTX_SL_P3_PTR(sl));
}
else if (type == HTX_BLK_HDR)
else if (type == HTX_BLK_HDR || type == HTX_BLK_TLR)
fprintf(stderr, "\t\t[%u] type=%-17s - size=%-6u - addr=%-6u\t%.*s: %.*s\n",
pos, htx_blk_type_str(type), sz, blk->addr,
(int)n.len, n.ptr,

View File

@ -875,7 +875,7 @@ static unsigned int htx_cache_dump_blk(struct appctx *appctx, struct htx *htx, e
max = htx_get_max_blksz(htx, channel_htx_recv_max(si_ic(appctx->owner), htx));
if (!max)
return 0;
blksz = ((type == HTX_BLK_HDR)
blksz = ((type == HTX_BLK_HDR || type == HTX_BLK_TLR)
? (info & 0xff) + ((info >> 8) & 0xfffff)
: info & 0xfffffff);
if (blksz > max)

View File

@ -247,6 +247,7 @@ comp_http_payload(struct stream *s, struct filter *filter, struct http_msg *msg,
case HTX_BLK_EOD:
case HTX_BLK_TLR:
case HTX_BLK_EOT:
case HTX_BLK_EOM:
if (msg->flags & HTTP_MSGF_COMPRESSING) {
if (htx_compression_buffer_init(htx, &trash) < 0) {

View File

@ -151,7 +151,7 @@ trace_htx_hexdump(struct htx *htx, unsigned int offset, unsigned int len)
if (v.len > len)
v.len = len;
len -= v.len;
if (type == HTX_BLK_DATA || type == HTX_BLK_TLR)
if (type == HTX_BLK_DATA)
trace_hexdump(v);
}
}

View File

@ -943,13 +943,11 @@ int h2_make_htx_response(struct http_hdr *list, struct htx *htx, unsigned int *m
return -1;
}
/* Takes an H2 headers list <list> terminated by a name being <NULL,0> and
* emits the equivalent HTX trailers block not including the empty line. The
* output contents are emitted in <htx>, and a positive value is returned if
* some bytes were emitted. In case of error, a negative error code is
* returned. The caller must have verified that the message in the buffer is
* compatible with receipt of trailers. Note that for now the HTX trailers
* block is in fact an H1 block and it must contain the trailing CRLF.
/* Takes an H2 headers list <list> terminated by a name being <NULL,0> and emits
* the equivalent HTX trailers blocks. The output contents are emitted in <htx>,
* and a positive value is returned if some bytes were emitted. In case of
* error, a negative error code is returned. The caller must have verified that
* the message in the buffer is compatible with receipt of trailers.
*
* The headers list <list> must be composed of :
* - n.name != NULL, n.len > 0 : literal header name
@ -961,13 +959,9 @@ int h2_make_htx_response(struct http_hdr *list, struct htx *htx, unsigned int *m
*/
int h2_make_htx_trailers(struct http_hdr *list, struct htx *htx)
{
struct htx_blk *blk;
char *out;
uint32_t idx;
int len;
int i;
len = 2; // CRLF
for (idx = 0; list[idx].n.len != 0; idx++) {
if (!list[idx].n.ptr) {
/* This is an indexed pseudo-header (RFC7540#8.1.2.1) */
@ -995,29 +989,13 @@ int h2_make_htx_trailers(struct http_hdr *list, struct htx *htx)
isteq(list[idx].n, ist("transfer-encoding")))
goto fail;
len += list[idx].n.len + 2 + list[idx].v.len + 2;
if (!htx_add_trailer(htx, list[idx].n, list[idx].v))
goto fail;
}
blk = htx_add_blk_type_size(htx, HTX_BLK_TLR, len);
if (!blk)
if (!htx_add_endof(htx, HTX_BLK_EOT))
goto fail;
out = htx_get_blk_ptr(htx, blk);
for (idx = 0; list[idx].n.len != 0; idx++) {
/* copy "name: value" */
memcpy(out, list[idx].n.ptr, list[idx].n.len);
out += list[idx].n.len;
*(out++) = ':';
*(out++) = ' ';
memcpy(out, list[idx].v.ptr, list[idx].v.len);
out += list[idx].v.len;
*(out++) = '\r';
*(out++) = '\n';
}
*(out++) = '\r';
*(out++) = '\n';
return 1;
fail:

View File

@ -293,7 +293,7 @@ void htx_truncate(struct htx *htx, uint32_t offset)
offset -= sz;
continue;
}
if (type == HTX_BLK_DATA || type == HTX_BLK_TLR) {
if (type == HTX_BLK_DATA) {
htx_set_blk_value_len(blk, offset);
htx->data -= (sz - offset);
}
@ -407,7 +407,7 @@ struct htx_blk *htx_add_data_atonce(struct htx *htx, const struct ist data)
return tailblk;
add_new_block:
/* FIXME: check tlr.len (< 256MB) */
/* FIXME: check data.len (< 256MB) */
blk = htx_add_blk(htx, HTX_BLK_DATA, data.len);
if (!blk)
return NULL;
@ -492,6 +492,7 @@ struct htx_ret htx_xfer_blks(struct htx *dst, struct htx *src, uint32_t count,
struct htx_blk *blk, *dstblk;
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));
@ -504,9 +505,9 @@ struct htx_ret htx_xfer_blks(struct htx *dst, struct htx *src, uint32_t count,
if (type == HTX_BLK_UNUSED)
goto next;
/* Be sure to have enough space to xfer all headers in one
* time. If not while <dst> is empty, we report a parsing error
* on <src>.
/* 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);
@ -517,6 +518,15 @@ struct htx_ret htx_xfer_blks(struct htx *dst, struct htx *src, uint32_t count,
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;
@ -714,6 +724,25 @@ struct htx_blk *htx_add_header(struct htx *htx, const struct ist name,
return blk;
}
/* Adds an HTX block of type TLR in <htx>. It returns the new block on
* success. Otherwise, it returns NULL. The header name is always lower cased.
*/
struct htx_blk *htx_add_trailer(struct htx *htx, const struct ist name,
const struct ist value)
{
struct htx_blk *blk;
/* FIXME: check name.len (< 256B) and value.len (< 1MB) */
blk = htx_add_blk(htx, HTX_BLK_TLR, name.len + value.len);
if (!blk)
return NULL;
blk->info += (value.len << 8) + name.len;
ist2bin_lc(htx_get_blk_ptr(htx, blk), name);
memcpy(htx_get_blk_ptr(htx, blk) + name.len, value.ptr, value.len);
return blk;
}
/* Adds an HTX block of type <type> in <htx>, of size <blksz>. It returns the
* new block on success. Otherwise, it returns NULL. The caller is responsible
* for filling the block itself.
@ -741,6 +770,17 @@ struct htx_blk *htx_add_all_headers(struct htx *htx, const struct http_hdr *hdrs
return htx_add_endof(htx, HTX_BLK_EOH);
}
struct htx_blk *htx_add_all_trailers(struct htx *htx, const struct http_hdr *hdrs)
{
int i;
for (i = 0; hdrs[i].n.len; i++) {
if (!htx_add_trailer(htx, hdrs[i].n, hdrs[i].v))
return NULL;
}
return htx_add_endof(htx, HTX_BLK_EOT);
}
/* Adds an HTX block of type EOH,EOD or EOM in <htx>. It returns the new block
* on success. Otherwise, it returns NULL.
*/
@ -818,7 +858,7 @@ size_t htx_add_data(struct htx *htx, const struct ist data)
return len;
add_new_block:
/* FIXME: check tlr.len (< 256MB) */
/* FIXME: check data.len (< 256MB) */
blk = htx_add_blk(htx, HTX_BLK_DATA, len);
if (!blk)
return 0;
@ -828,23 +868,6 @@ size_t htx_add_data(struct htx *htx, const struct ist data)
return len;
}
/* Adds an HTX block of type TLR in <htx>. It returns the new block on
* success. Otherwise, it returns NULL.
*/
struct htx_blk *htx_add_trailer(struct htx *htx, const struct ist tlr)
{
struct htx_blk *blk;
/* FIXME: check tlr.len (< 256MB) */
blk = htx_add_blk(htx, HTX_BLK_TLR, tlr.len);
if (!blk)
return NULL;
blk->info += tlr.len;
memcpy(htx_get_blk_ptr(htx, blk), tlr.ptr, tlr.len);
return blk;
}
struct htx_blk *htx_add_data_before(struct htx *htx, const struct htx_blk *ref,
const struct ist data)
{
@ -976,15 +999,3 @@ int htx_data_to_h1(const struct ist data, struct buffer *chk, int chunked)
return 1;
}
/* Appends the h1 representation of the trailer block <blk> to the chunk
* <chk>. It returns 1 if data are successfully appended, otherwise it returns
* 0.
*/
int htx_trailer_to_h1(const struct ist tlr, struct buffer *chk)
{
/* FIXME: be sure the CRLF is here or remove it when inserted */
if (!chunk_memcat(chk, tlr.ptr, tlr.len))
return 0;
return 1;
}

View File

@ -66,10 +66,8 @@
#define H1S_F_NOT_FIRST 0x00000080 /* The H1 stream is not the first one */
#define H1S_F_BUF_FLUSH 0x00000100 /* Flush input buffer and don't read more data */
#define H1S_F_SPLICED_DATA 0x00000200 /* Set when the kernel splicing is in used */
#define H1S_F_HAVE_I_EOD 0x00000400 /* Set during input process to know the last empty chunk was processed */
#define H1S_F_HAVE_I_TLR 0x00000800 /* Set during input process to know the trailers were processed */
#define H1S_F_HAVE_O_EOD 0x00001000 /* Set during output process to know the last empty chunk was processed */
#define H1S_F_HAVE_O_TLR 0x00002000 /* Set during output process to know the trailers were processed */
/* 0x00001000 .. 0x00002000 unused */
#define H1S_F_HAVE_O_CONN 0x00004000 /* Set during output process to know connection mode was processed */
/* H1 connection descriptor */
@ -934,6 +932,22 @@ static size_t h1_eval_htx_res_size(struct h1m *h1m, union h1_sl *h1sl, struct ht
return sz;
}
/*
* Add the EOM in the HTX message and switch the message to the DONE state. It
* returns the number of bytes parsed if > 0, or 0 if iet couldn't proceed. This
* functions is responsible to update the parser state <h1m>. It also add the
* flag CS_FL_EOI on the CS.
*/
static size_t h1_process_eom(struct h1s *h1s, struct h1m *h1m, struct htx *htx, size_t max)
{
if (max < sizeof(struct htx_blk) + 1 || !htx_add_endof(htx, HTX_BLK_EOM))
return 0;
h1m->state = H1_MSG_DONE;
h1s->cs->flags |= CS_FL_EOI;
return (sizeof(struct htx_blk) + 1);
}
/*
* Parse HTTP/1 headers. It returns the number of bytes parsed if > 0, or 0 if
* it couldn't proceed. Parsing errors are reported by setting H1S_F_*_ERROR
@ -1178,10 +1192,8 @@ static size_t h1_process_data(struct h1s *h1s, struct h1m *h1m, struct htx *htx,
}
if (!h1m->curr_len) {
if (max < sizeof(struct htx_blk) + 1 || !htx_add_endof(htx, HTX_BLK_EOM))
if (!h1_process_eom(h1s, h1m, htx, max))
goto end;
h1m->state = H1_MSG_DONE;
h1s->cs->flags |= CS_FL_EOI;
}
}
else if (h1m->flags & H1_MF_CHNK) {
@ -1206,7 +1218,6 @@ static size_t h1_process_data(struct h1s *h1s, struct h1m *h1m, struct htx *htx,
if (!chksz) {
if (max < sizeof(struct htx_blk) + 1 || !htx_add_endof(htx, HTX_BLK_EOD))
goto end;
h1s->flags |= H1S_F_HAVE_I_EOD;
h1m->state = H1_MSG_TRAILERS;
max -= sizeof(struct htx_blk) + 1;
}
@ -1217,6 +1228,9 @@ static size_t h1_process_data(struct h1s *h1s, struct h1m *h1m, struct htx *htx,
h1m->body_len += chksz;
*ofs += ret;
total += ret;
if (!h1m->curr_len)
goto end;
}
if (h1m->state == H1_MSG_DATA) {
@ -1243,55 +1257,13 @@ static size_t h1_process_data(struct h1s *h1s, struct h1m *h1m, struct htx *htx,
}
goto end;
}
if (h1m->state == H1_MSG_TRAILERS) {
/* Trailers were alread parsed, only the EOM
* need to be added */
if (h1s->flags & H1S_F_HAVE_I_TLR)
goto skip_tlr_parsing;
ret = htx_get_max_blksz(htx, max);
ret = h1_measure_trailers(buf, *ofs, ret);
if (ret <= 0) {
if (!ret && b_full(buf))
ret = -1;
goto end;
}
/* Realing input buffer if tailers wrap. For now
* this is a workaroung. Because trailers are
* not split on CRLF, like headers, there is no
* way to know where to split it when trailers
* wrap. This is a limitation of
* h1_measure_trailers.
*/
if (b_peek(buf, *ofs) > b_peek(buf, *ofs + ret))
b_slow_realign(buf, trash.area, 0);
if (!htx_add_trailer(htx, ist2(b_peek(buf, *ofs), ret)))
goto end;
h1s->flags |= H1S_F_HAVE_I_TLR;
max -= sizeof(struct htx_blk) + ret;
*ofs += ret;
total += ret;
skip_tlr_parsing:
if (max < sizeof(struct htx_blk) + 1 || !htx_add_endof(htx, HTX_BLK_EOM))
goto end;
max -= sizeof(struct htx_blk) + 1;
h1m->state = H1_MSG_DONE;
h1s->cs->flags |= CS_FL_EOI;
}
}
else {
/* XFER_LEN is set but not CLEN nor CHNK, it means there
* is no body. Switch the message in DONE state
*/
if (max < sizeof(struct htx_blk) + 1 || !htx_add_endof(htx, HTX_BLK_EOM))
if (!h1_process_eom(h1s, h1m, htx, max))
goto end;
max -= sizeof(struct htx_blk) + 1;
h1m->state = H1_MSG_DONE;
h1s->cs->flags |= CS_FL_EOI;
}
}
else {
@ -1328,6 +1300,65 @@ static size_t h1_process_data(struct h1s *h1s, struct h1m *h1m, struct htx *htx,
return total;
}
/*
* Parse HTTP/1 trailers. It returns the number of bytes parsed if > 0, or 0 if
* it couldn't proceed. Parsing errors are reported by setting H1S_F_*_ERROR
* flag and filling h1s->err_pos and h1s->err_state fields. This functions is
* responsible to update the parser state <h1m>.
*/
static size_t h1_process_trailers(struct h1s *h1s, struct h1m *h1m, struct htx *htx,
struct buffer *buf, size_t *ofs, size_t max)
{
struct http_hdr hdrs[MAX_HTTP_HDR];
struct h1m tlr_h1m;
int ret = 0;
if (!max || !b_data(buf))
goto end;
/* Realing input buffer if necessary */
if (b_peek(buf, *ofs) > b_tail(buf))
b_slow_realign(buf, trash.area, 0);
tlr_h1m.flags = (H1_MF_NO_PHDR|H1_MF_HDRS_ONLY);
ret = h1_headers_to_hdr_list(b_peek(buf, *ofs), b_tail(buf),
hdrs, sizeof(hdrs)/sizeof(hdrs[0]), &tlr_h1m, NULL);
if (ret <= 0) {
/* Incomplete or invalid trailers. If the buffer is full, it's
* an error because traliers are too large to be handled by the
* parser. */
if (ret < 0 || (!ret && !buf_room_for_htx_data(buf)))
goto error;
goto end;
}
/* messages trailers fully parsed. */
if (h1_eval_htx_hdrs_size(hdrs) > max) {
if (htx_is_empty(htx))
goto error;
ret = 0;
goto end;
}
if (!htx_add_all_trailers(htx, hdrs))
goto error;
*ofs += ret;
h1s->flags |= H1S_F_HAVE_I_TLR;
end:
return ret;
error:
h1m->err_state = h1m->state;
h1m->err_pos = h1m->next;
h1s->flags |= (!(h1m->flags & H1_MF_RESP) ? H1S_F_REQ_ERROR : H1S_F_RES_ERROR);
h1s->cs->flags |= CS_FL_EOI;
htx->flags |= HTX_FL_PARSING_ERROR;
h1_capture_bad_message(h1s->h1c, h1s, h1m, buf);
ret = 0;
goto end;
}
/*
* Process incoming data. It parses data and transfer them from h1c->ibuf into
* <buf>. It returns the number of bytes parsed and transferred if > 0, or 0 if
@ -1367,12 +1398,21 @@ static size_t h1_process_input(struct h1c *h1c, struct buffer *buf, size_t count
h1m->flags |= (H1_MF_NO_PHDR|H1_MF_CLEAN_CONN_HDR);
}
}
else if (h1m->state <= H1_MSG_TRAILERS) {
else if (h1m->state < H1_MSG_TRAILERS) {
ret = h1_process_data(h1s, h1m, htx, &h1c->ibuf, &total, count, buf);
htx = htx_from_buf(buf);
if (!ret)
break;
}
else if (h1m->state == H1_MSG_TRAILERS) {
if (!(h1s->flags & H1S_F_HAVE_I_TLR)) {
ret = h1_process_trailers(h1s, h1m, htx, &h1c->ibuf, &total, count);
if (!ret)
break;
}
if (!h1_process_eom(h1s, h1m, htx, count))
break;
}
else if (h1m->state == H1_MSG_DONE) {
if (h1s->req.state < H1_MSG_DONE || h1s->res.state < H1_MSG_DONE)
h1c->flags |= H1C_F_IN_BUSY;
@ -1523,7 +1563,7 @@ static size_t h1_process_output(struct h1c *h1c, struct buffer *buf, size_t coun
vlen = sz;
if (vlen > count) {
if (type != HTX_BLK_DATA && type != HTX_BLK_TLR)
if (type != HTX_BLK_DATA)
goto copy;
vlen = count;
}
@ -1647,17 +1687,21 @@ static size_t h1_process_output(struct h1c *h1c, struct buffer *buf, size_t coun
break;
case H1_MSG_DATA:
if (type == HTX_BLK_EOD) {
if (type == HTX_BLK_EOM) {
/* Chunked message without explicit trailers */
if (h1m->flags & H1_MF_CHNK) {
if (!chunk_memcat(tmp, "0\r\n\r\n", 5))
goto copy;
}
goto done;
}
else if (type == HTX_BLK_EOD)
break;
else if (type == HTX_BLK_EOT || type == HTX_BLK_TLR) {
if (!chunk_memcat(tmp, "0\r\n", 3))
goto copy;
h1s->flags |= H1S_F_HAVE_O_EOD;
h1m->state = H1_MSG_TRAILERS;
break;
}
if (type == HTX_BLK_TLR)
goto trailers;
else if (type == HTX_BLK_EOM)
goto done;
}
else if (type != HTX_BLK_DATA)
goto error;
v = htx_get_blk_value(chn_htx, blk);
@ -1669,20 +1713,20 @@ static size_t h1_process_output(struct h1c *h1c, struct buffer *buf, size_t coun
case H1_MSG_TRAILERS:
if (type == HTX_BLK_EOM)
goto done;
else if (type != HTX_BLK_TLR)
else if (type != HTX_BLK_TLR && type != HTX_BLK_EOT)
goto error;
trailers:
h1m->state = H1_MSG_TRAILERS;
if (!(h1s->flags & H1S_F_HAVE_O_EOD)) {
if (!chunk_memcat(tmp, "0\r\n", 3))
if (type == HTX_BLK_EOT) {
if (!chunk_memcat(tmp, "\r\n", 2))
goto copy;
}
else { // HTX_BLK_TLR
n = htx_get_blk_name(chn_htx, blk);
v = htx_get_blk_value(chn_htx, blk);
if (!htx_hdr_to_h1(n, v, tmp))
goto copy;
h1s->flags |= H1S_F_HAVE_O_EOD;
}
v = htx_get_blk_value(chn_htx, blk);
v.len = vlen;
if (!htx_trailer_to_h1(v, tmp))
goto copy;
h1s->flags |= H1S_F_HAVE_O_TLR;
break;
case H1_MSG_DONE:
@ -1690,18 +1734,6 @@ static size_t h1_process_output(struct h1c *h1c, struct buffer *buf, size_t coun
goto error;
done:
h1m->state = H1_MSG_DONE;
if ((h1m->flags & H1_MF_CHNK)) {
if (!(h1s->flags & H1S_F_HAVE_O_EOD)) {
if (!chunk_memcat(tmp, "0\r\n", 3))
goto copy;
h1s->flags |= H1S_F_HAVE_O_EOD;
}
if (!(h1s->flags & H1S_F_HAVE_O_TLR)) {
if (!chunk_memcat(tmp, "\r\n", 2))
goto copy;
h1s->flags |= H1S_F_HAVE_O_TLR;
}
}
break;
default:

View File

@ -3687,13 +3687,8 @@ next_frame:
* the EOM block we must remove the TLR block we've just added.
*/
if (htx) {
if (!htx_add_endof(htx, HTX_BLK_EOM)) {
struct htx_blk *tail = htx_get_tail_blk(htx);
if (tail && htx_get_blk_type(tail) == HTX_BLK_TLR)
htx_remove_blk(htx, tail);
if (!htx_add_endof(htx, HTX_BLK_EOM))
goto fail;
}
}
else if (*flags & H2_SF_DATA_CHNK) {
if (!b_putblk(rxbuf, "\r\n", 2))
@ -5150,67 +5145,48 @@ static size_t h2s_htx_make_trailers(struct h2s *h2s, struct htx *htx)
struct htx_blk *blk_end;
struct buffer outbuf;
struct buffer *mbuf;
struct h1m h1m;
enum htx_blk_type type;
uint32_t size;
int ret = 0;
int hdr;
int idx;
void *start;
if (h2c_mux_busy(h2c, h2s)) {
h2s->flags |= H2_SF_BLK_MBUSY;
goto end;
}
/* The principle is that we parse each and every trailers block using
* the H1 headers parser, and append it to the list. We don't proceed
* until EOM is met. blk_end will point to the EOM block.
*/
hdr = 0;
memset(list, 0, sizeof(list));
/* determine the first block which must not be deleted, blk_end may
* be NULL if all blocks have to be deleted. also get trailers.
*/
idx = htx_get_head(htx);
blk_end = NULL;
for (idx = htx_get_head(htx); idx != -1; idx = htx_get_next(htx, idx)) {
hdr = 0;
while (idx != -1) {
blk = htx_get_blk(htx, idx);
type = htx_get_blk_type(blk);
idx = htx_get_next(htx, idx);
if (type == HTX_BLK_UNUSED)
continue;
if (type == HTX_BLK_EOT) {
if (idx != -1)
blk_end = blk;
break;
}
if (type != HTX_BLK_TLR)
break;
if (unlikely(hdr >= sizeof(list)/sizeof(list[0]) - 1))
goto fail;
size = htx_get_blksz(blk);
start = htx_get_blk_ptr(htx, blk);
h1m.flags = H1_MF_HDRS_ONLY | H1_MF_TOLOWER;
h1m.err_pos = 0;
ret = h1_headers_to_hdr_list(start, start + size,
list + hdr, sizeof(list)/sizeof(list[0]) - hdr,
&h1m, NULL);
if (ret < 0)
goto fail;
/* ret == 0 if an incomplete trailers block was found (missing
* empty line), or > 0 if it was found. We have to continue on
* incomplete messages because the trailers block might be
* incomplete.
*/
/* search the new end */
while (hdr <= sizeof(list)/sizeof(list[0])) {
if (!list[hdr].n.len)
break;
hdr++;
}
list[hdr].n = htx_get_blk_name(htx, blk);
list[hdr].v = htx_get_blk_value(htx, blk);
hdr++;
}
if (list[hdr].n.len != 0)
goto fail; // empty trailer not found: internal error
/* marker for end of trailers */
list[hdr].n = ist("");
mbuf = br_tail(h2c->mbuf);
retry:
@ -5294,6 +5270,12 @@ static size_t h2s_htx_make_trailers(struct h2s *h2s, struct htx *htx)
ret += htx_get_blksz(blk);
blk = htx_remove_blk(htx, blk);
}
if (blk_end && htx_get_blk_type(blk_end) == HTX_BLK_EOM) {
ret += htx_get_blksz(blk_end);
htx_remove_blk(htx, blk_end);
}
end:
return ret;
full:
@ -5577,6 +5559,7 @@ static size_t h2_snd_buf(struct conn_stream *cs, struct buffer *buf, size_t coun
break;
case HTX_BLK_TLR:
case HTX_BLK_EOT:
/* This is the first trailers block, all the subsequent ones AND
* the EOM will be swallowed by the parser.
*/