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.
diff --git a/include/common/htx.h b/include/common/htx.h
index fd0871c..e2d4e15 100644
--- a/include/common/htx.h
+++ b/include/common/htx.h
@@ -103,7 +103,8 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 cae8846..d63cc79 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -875,7 +875,7 @@
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 f756b20..a6612d9 100644
--- a/src/flt_http_comp.c
+++ b/src/flt_http_comp.c
@@ -247,6 +247,7 @@
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 96a19a1..4eb8d5f 100644
--- a/src/flt_trace.c
+++ b/src/flt_trace.c
@@ -151,7 +151,7 @@
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 6ec69c4..84b8dbe 100644
--- a/src/h2.c
+++ b/src/h2.c
@@ -943,13 +943,11 @@
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_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 @@
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 9281eba..2fdebd0 100644
--- a/src/htx.c
+++ b/src/htx.c
@@ -293,7 +293,7 @@
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 @@
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_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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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)
{
@@ -974,17 +997,5 @@
return 0;
}
- 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;
}
diff --git a/src/mux_h1.c b/src/mux_h1.c
index c5fdb7d..9d6aaf2 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 */
@@ -935,6 +933,22 @@
}
/*
+ * 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
* flag and filling h1s->err_pos and h1s->err_state fields. This functions is
@@ -1178,10 +1192,8 @@
}
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 @@
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 @@
h1m->body_len += chksz;
*ofs += ret;
total += ret;
+
+ if (!h1m->curr_len)
+ goto end;
}
if (h1m->state == H1_MSG_DATA) {
@@ -1243,55 +1257,13 @@
}
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 {
@@ -1329,6 +1301,65 @@
}
/*
+ * 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
* it couldn't proceed.
@@ -1367,12 +1398,21 @@
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 @@
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 @@
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 @@
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;
- 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;
+ 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;
+ }
break;
case H1_MSG_DONE:
@@ -1690,18 +1734,6 @@
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 e3c0bb0..2eb4ad4 100644
--- a/src/mux_h2.c
+++ b/src/mux_h2.c
@@ -3687,13 +3687,8 @@
* 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 @@
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 @@
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 @@
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.
*/