diff --git a/include/common/htx.h b/include/common/htx.h index fd0871c52..e2d4e15a1 100644 --- a/include/common/htx.h +++ b/include/common/htx.h @@ -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, diff --git a/src/cache.c b/src/cache.c index cae884600..d63cc790b 100644 --- a/src/cache.c +++ b/src/cache.c @@ -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) diff --git a/src/flt_http_comp.c b/src/flt_http_comp.c index f756b2012..a6612d913 100644 --- a/src/flt_http_comp.c +++ b/src/flt_http_comp.c @@ -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) { diff --git a/src/flt_trace.c b/src/flt_trace.c index 96a19a1ce..4eb8d5fe6 100644 --- a/src/flt_trace.c +++ b/src/flt_trace.c @@ -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); } } diff --git a/src/h2.c b/src/h2.c index 6ec69c434..84b8dbe77 100644 --- a/src/h2.c +++ b/src/h2.c @@ -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 terminated by a name being and - * emits the equivalent HTX trailers block not including the empty line. The - * output contents are emitted in , 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 terminated by a name being and emits + * the equivalent HTX trailers blocks. The output contents are emitted in , + * 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 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: diff --git a/src/htx.c b/src/htx.c index 9281ebac6..2fdebd099 100644 --- a/src/htx.c +++ b/src/htx.c @@ -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 is empty, we report a parsing error - * on . + /* Be sure to have enough space to xfer all headers/trailers in + * one time. If not while is empty, we report a parsing + * error on . */ 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 . 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 in , of size . 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 . 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 . 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 to the chunk - * . 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; -} diff --git a/src/mux_h1.c b/src/mux_h1.c index c5fdb7df1..9d6aaf23c 100644 --- a/src/mux_h1.c +++ b/src/mux_h1.c @@ -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 . 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 . + */ +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 * . 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: diff --git a/src/mux_h2.c b/src/mux_h2.c index e3c0bb003..2eb4ad411 100644 --- a/src/mux_h2.c +++ b/src/mux_h2.c @@ -3687,13 +3687,8 @@ static int h2c_decode_headers(struct h2c *h2c, struct buffer *rxbuf, uint32_t *f * 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. */