]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: mux-h1/h1-htx: move HTX convertion of H1 messages in dedicated file
authorChristopher Faulet <cfaulet@haproxy.com>
Sat, 10 Aug 2019 09:17:44 +0000 (11:17 +0200)
committerChristopher Faulet <cfaulet@haproxy.com>
Tue, 17 Sep 2019 08:18:54 +0000 (10:18 +0200)
To avoid code duplication in the futur mux FCGI, functions parsing H1 messages
and converting them into HTX have been moved in the file h1_htx.c. Some
specific parts remain in the mux H1. But most of the parsing is now generic.

Makefile
include/common/h1.h
include/proto/h1_htx.h [new file with mode: 0644]
src/h1_htx.c [new file with mode: 0644]
src/mux_h1.c

index 5fff435725f052ec094ca2a98d0383de4b16030e..87b5b82f3cf63c8b96c834119bb1259bf6fe2a83 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -787,7 +787,7 @@ OBJS = src/http_ana.o src/cfgparse-listen.o src/stream.o                      \
        src/protocol.o src/arg.o src/hpack-huff.o src/base64.o src/ring.o      \
        src/hash.o src/mailers.o src/activity.o src/version.o src/trace.o      \
        src/mworker.o src/mworker-prog.o src/debug.o src/wdt.o src/dict.o      \
-       src/xprt_handshake.o
+       src/xprt_handshake.o src/h1_htx.o
 
 EBTREE_OBJS = $(EBTREE_DIR)/ebtree.o $(EBTREE_DIR)/eb32sctree.o \
               $(EBTREE_DIR)/eb32tree.o $(EBTREE_DIR)/eb64tree.o \
index 0d652e7bbb79cd439de216b9bd48da02ca56480f..5ed26fa8f87d5d7a21b84297582f3fea061e5b40 100644 (file)
@@ -94,6 +94,8 @@ enum h1m_state {
 #define H1_MF_NO_PHDR           0x00000400 // don't add pseudo-headers in the header list
 #define H1_MF_HDRS_ONLY         0x00000800 // parse headers only
 #define H1_MF_CLEAN_CONN_HDR    0x00001000 // skip close/keep-alive values of connection headers during parsing
+#define H1_MF_METH_CONNECT      0x00002000 // Set for a response to a CONNECT request
+#define H1_MF_METH_HEAD         0x00004000 // Set for a response to a HEAD request
 
 /* Note: for a connection to be persistent, we need this for the request :
  *   - one of CLEN or CHNK
diff --git a/include/proto/h1_htx.h b/include/proto/h1_htx.h
new file mode 100644 (file)
index 0000000..da37215
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * include/types/h1_htx.h
+ * This file defines function prototypes for H1 manipulation using the
+ * internal representation.
+ *
+ * Copyright (C) 2019 HAProxy Technologies, Christopher Faulet <cfaulet@haproxy.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation, version 2.1
+ * exclusively.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef _PROTO_H1_HTX_H
+#define _PROTO_H1_HTX_H
+
+#include <common/buf.h>
+#include <common/ist.h>
+#include <common/h1.h>
+
+int h1_parse_msg_hdrs(struct h1m *h1m, union h1_sl *h1sl, struct htx *dsthtx,
+                     struct buffer *srcbuf, size_t ofs, size_t max);
+int h1_parse_msg_data(struct h1m *h1m, struct htx *dsthtx,
+                     struct buffer *srcbuf, size_t ofs, size_t max,
+                     struct buffer *htxbuf);
+int h1_parse_msg_tlrs(struct h1m *h1m, struct htx *dsthtx,
+                     struct buffer *srcbuf, size_t ofs, size_t max);
+
+#endif /* _PROTO_H1_HTX_H */
+
+/*
+ * Local variables:
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ * End:
+ */
diff --git a/src/h1_htx.c b/src/h1_htx.c
new file mode 100644 (file)
index 0000000..e2675cd
--- /dev/null
@@ -0,0 +1,595 @@
+/*
+ * Functions to manipulate H1 messages using the internal representation.
+ *
+ * Copyright (C) 2019 HAProxy Technologies, Christopher Faulet <cfaulet@haproxy.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <common/config.h>
+#include <common/debug.h>
+#include <common/cfgparse.h>
+#include <common/h1.h>
+#include <common/http.h>
+#include <common/htx.h>
+
+#include <proto/h1_htx.h>
+
+/* Estimate the size of the HTX headers after the parsing, including the EOH. */
+static size_t h1_eval_htx_hdrs_size(const struct http_hdr *hdrs)
+{
+       size_t sz = 0;
+       int i;
+
+       for (i = 0; hdrs[i].n.len; i++)
+               sz += sizeof(struct htx_blk) + hdrs[i].n.len + hdrs[i].v.len;
+       sz += sizeof(struct htx_blk) + 1;
+       return sz;
+}
+
+/* Estimate the size of the HTX request after the parsing. */
+static size_t h1_eval_htx_size(const struct ist p1, const struct ist p2, const struct ist p3,
+                              const struct http_hdr *hdrs)
+{
+       size_t sz;
+
+       /* size of the HTX start-line */
+       sz = sizeof(struct htx_blk) + sizeof(struct htx_sl) + p1.len + p2.len + p3.len;
+       sz += h1_eval_htx_hdrs_size(hdrs);
+       return sz;
+}
+
+/* Switch the message to tunnel mode. On the request, it must only be called for
+ * a CONNECT method. On the response, this function must only be called on
+ * successfull replies to CONNECT requests or on protocol switching.
+ */
+static void h1_set_tunnel_mode(struct h1m *h1m)
+{
+       h1m->flags &= ~(H1_MF_XFER_LEN|H1_MF_CLEN|H1_MF_CHNK);
+       h1m->state = H1_MSG_TUNNEL;
+}
+
+/* Check the validity of the request version. If the version is valid, it
+ * returns 1. Otherwise, it returns 0.
+ */
+static int h1_process_req_vsn(struct h1m *h1m, union h1_sl *sl)
+{
+       /* RFC7230#2.6 has enforced the format of the HTTP version string to be
+        * exactly one digit "." one digit. This check may be disabled using
+        * option accept-invalid-http-request.
+        */
+       if (h1m->err_pos == -2) { /* PR_O2_REQBUG_OK not set */
+               if (sl->rq.v.len != 8)
+                       return 0;
+
+               if (*(sl->rq.v.ptr + 4) != '/' ||
+                   !isdigit((unsigned char)*(sl->rq.v.ptr + 5)) ||
+                   *(sl->rq.v.ptr + 6) != '.' ||
+                   !isdigit((unsigned char)*(sl->rq.v.ptr + 7)))
+                       return 0;
+       }
+       else if (!sl->rq.v.len) {
+               /* try to convert HTTP/0.9 requests to HTTP/1.0 */
+
+               /* RFC 1945 allows only GET for HTTP/0.9 requests */
+               if (sl->rq.meth != HTTP_METH_GET)
+                       return 0;
+
+               /* HTTP/0.9 requests *must* have a request URI, per RFC 1945 */
+               if (!sl->rq.u.len)
+                       return 0;
+
+               /* Add HTTP version */
+               sl->rq.v = ist("HTTP/1.0");
+               return 1;
+       }
+
+       if ((sl->rq.v.len == 8) &&
+           ((*(sl->rq.v.ptr + 5) > '1') ||
+            ((*(sl->rq.v.ptr + 5) == '1') && (*(sl->rq.v.ptr + 7) >= '1'))))
+               h1m->flags |= H1_MF_VER_11;
+       return 1;
+}
+
+/* Check the validity of the response version. If the version is valid, it
+ * returns 1. Otherwise, it returns 0.
+ */
+static int h1_process_res_vsn(struct h1m *h1m, union h1_sl *sl)
+{
+       /* RFC7230#2.6 has enforced the format of the HTTP version string to be
+        * exactly one digit "." one digit. This check may be disabled using
+        * option accept-invalid-http-request.
+        */
+       if (h1m->err_pos == -2) { /* PR_O2_REQBUG_OK not set */
+               if (sl->st.v.len != 8)
+                       return 0;
+
+               if (*(sl->st.v.ptr + 4) != '/' ||
+                   !isdigit((unsigned char)*(sl->st.v.ptr + 5)) ||
+                   *(sl->st.v.ptr + 6) != '.' ||
+                   !isdigit((unsigned char)*(sl->st.v.ptr + 7)))
+                       return 0;
+       }
+
+       if ((sl->st.v.len == 8) &&
+           ((*(sl->st.v.ptr + 5) > '1') ||
+            ((*(sl->st.v.ptr + 5) == '1') && (*(sl->st.v.ptr + 7) >= '1'))))
+               h1m->flags |= H1_MF_VER_11;
+
+       return 1;
+}
+
+/* Convert H1M flags to HTX start-line flags. */
+static unsigned int h1m_htx_sl_flags(struct h1m *h1m)
+{
+       unsigned int flags = HTX_SL_F_NONE;
+
+       if (h1m->flags & H1_MF_RESP)
+               flags |= HTX_SL_F_IS_RESP;
+       if (h1m->flags & H1_MF_VER_11)
+               flags |= HTX_SL_F_VER_11;
+       if (h1m->flags & H1_MF_XFER_ENC)
+               flags |= HTX_SL_F_XFER_ENC;
+       if (h1m->flags & H1_MF_XFER_LEN) {
+               flags |= HTX_SL_F_XFER_LEN;
+               if (h1m->flags & H1_MF_CHNK)
+                       flags |= HTX_SL_F_CHNK;
+               else if (h1m->flags & H1_MF_CLEN) {
+                       flags |= HTX_SL_F_CLEN;
+                       if (h1m->body_len == 0)
+                               flags |= HTX_SL_F_BODYLESS;
+               }
+               else
+                       flags |= HTX_SL_F_BODYLESS;
+       }
+       return flags;
+}
+
+/* Postprocess the parsed headers for a request and convert them into an htx
+ * message. It returns the number of bytes parsed if > 0, or 0 if it couldn't
+ * proceed. Parsing errors are reported by setting the htx flag
+ * HTX_FL_PARSING_ERROR and filling h1m->err_pos and h1m->err_state fields.
+ */
+static int h1_postparse_req_hdrs(struct h1m *h1m, union h1_sl *h1sl, struct htx *htx,
+                                struct http_hdr *hdrs, size_t max)
+{
+       struct htx_sl *sl;
+       struct ist meth, uri, vsn;
+       unsigned int flags;
+       size_t used;
+
+       /* <h1sl> is always defined for a request */
+       meth = h1sl->rq.m;
+       uri  = h1sl->rq.u;
+       vsn  = h1sl->rq.v;
+
+       /* Be sure the message, once converted into HTX, will not exceed the max
+        * size allowed.
+        */
+       if (h1_eval_htx_size(meth, uri, vsn, hdrs) > max) {
+               if (htx_is_empty(htx))
+                       goto error;
+               h1m_init_res(h1m);
+               h1m->flags |= (H1_MF_NO_PHDR|H1_MF_CLEAN_CONN_HDR);
+               return 0;
+       }
+
+       /* By default, request have always a known length */
+       h1m->flags |= H1_MF_XFER_LEN;
+
+       if (h1sl->rq.meth == HTTP_METH_CONNECT) {
+               /* Switch CONNECT requests to tunnel mode */
+               h1_set_tunnel_mode(h1m);
+       }
+
+       used = htx_used_space(htx);
+       flags = h1m_htx_sl_flags(h1m);
+       sl = htx_add_stline(htx, HTX_BLK_REQ_SL, flags, meth, uri, vsn);
+       if (!sl || !htx_add_all_headers(htx, hdrs))
+               goto error;
+       sl->info.req.meth = h1sl->rq.meth;
+
+       /* Check if the uri contains an explicit scheme and if it is
+        * "http" or "https". */
+       if (uri.len && uri.ptr[0] != '/') {
+               sl->flags |= HTX_SL_F_HAS_SCHM;
+               if (uri.len > 4 && (uri.ptr[0] | 0x20) == 'h')
+                       sl->flags |= ((uri.ptr[4] == ':') ? HTX_SL_F_SCHM_HTTP : HTX_SL_F_SCHM_HTTPS);
+       }
+       /* Set bytes used in the HTX mesage for the headers now */
+       sl->hdrs_bytes = htx_used_space(htx) - used;
+
+       /* If body length cannot be determined, set htx->extra to
+        * ULLONG_MAX. This value is impossible in other cases.
+        */
+       htx->extra = ((h1m->flags & H1_MF_XFER_LEN) ? h1m->curr_len : ULLONG_MAX);
+
+  end:
+       return 1;
+  error:
+       h1m->err_pos = h1m->next;
+       h1m->err_state = h1m->state;
+       htx->flags |= HTX_FL_PARSING_ERROR;
+       return 0;
+}
+
+/* Postprocess the parsed headers for a response and convert them into an htx
+ * message. It returns the number of bytes parsed if > 0, or 0 if it couldn't
+ * proceed. Parsing errors are reported by setting the htx flag
+ * HTX_FL_PARSING_ERROR and filling h1m->err_pos and h1m->err_state fields.
+ */
+static int h1_postparse_res_hdrs(struct h1m *h1m, union h1_sl *h1sl, struct htx *htx,
+                                struct http_hdr *hdrs, size_t max)
+{
+       struct htx_sl *sl;
+       struct ist vsn, status, reason;
+       unsigned int flags;
+       size_t used;
+       uint16_t code = 0;
+
+       if (h1sl) {
+               /* For HTTP responses, the start-line was parsed */
+               code   = h1sl->st.status;
+               vsn    = h1sl->st.v;
+               status = h1sl->st.c;
+               reason = h1sl->st.r;
+       }
+       else {
+               /* For FCGI responses, there is no start(-line but the "Status"
+                * header must be parsed, if found.
+                */
+               int hdr;
+
+               vsn = ((h1m->flags & H1_MF_VER_11) ? ist("HTTP/1.1") : ist("HTTP/1.0"));
+               for (hdr = 0; hdrs[hdr].n.len; hdr++) {
+                       if (isteqi(hdrs[hdr].n, ist("status"))) {
+                               code = http_parse_status_val(hdrs[hdr].v, &status, &reason);
+                       }
+                       else if (isteqi(hdrs[hdr].n, ist("location"))) {
+                               code = 302;
+                               status = ist("302");
+                               reason = ist("Moved Temporarily");
+                       }
+               }
+               if (!code) {
+                       code = 200;
+                       status = ist("200");
+                       reason = ist("OK");
+               }
+               /* FIXME: Check the codes 1xx ? */
+       }
+
+       /* Be sure the message, once converted into HTX, will not exceed the max
+        * size allowed.
+        */
+       if (h1_eval_htx_size(vsn, status, reason, hdrs) > max) {
+               if (htx_is_empty(htx))
+                       goto error;
+               h1m_init_res(h1m);
+               h1m->flags |= (H1_MF_NO_PHDR|H1_MF_CLEAN_CONN_HDR);
+               return 0;
+       }
+
+       if (((h1m->flags & H1_MF_METH_CONNECT) && code == 200) || code == 101) {
+               /* Switch successfull replies to CONNECT requests and
+                * protocol switching to tunnel mode. */
+               h1_set_tunnel_mode(h1m);
+       }
+       else if ((h1m->flags & H1_MF_METH_HEAD) || (code >= 100 && code < 200) ||
+                (code == 204) || (code == 304)) {
+               /* Responses known to have no body. */
+               h1m->flags &= ~(H1_MF_CLEN|H1_MF_CHNK);
+               h1m->flags |= H1_MF_XFER_LEN;
+               h1m->curr_len = h1m->body_len = 0;
+       }
+       else if (h1m->flags & (H1_MF_CLEN|H1_MF_CHNK)) {
+               /* Responses with a known body length. */
+               h1m->flags |= H1_MF_XFER_LEN;
+       }
+       else {
+               /* Responses with an unknown body length */
+               h1m->state = H1_MSG_TUNNEL;
+       }
+
+       used = htx_used_space(htx);
+       flags = h1m_htx_sl_flags(h1m);
+       sl = htx_add_stline(htx, HTX_BLK_RES_SL, flags, vsn, status, reason);
+       if (!sl || !htx_add_all_headers(htx, hdrs))
+               goto error;
+       sl->info.res.status = code;
+
+       /* Set bytes used in the HTX mesage for the headers now */
+       sl->hdrs_bytes = htx_used_space(htx) - used;
+
+       /* If body length cannot be determined, set htx->extra to
+        * ULLONG_MAX. This value is impossible in other cases.
+        */
+       htx->extra = ((h1m->flags & H1_MF_XFER_LEN) ? h1m->curr_len : ULLONG_MAX);
+
+  end:
+       return 1;
+  error:
+       h1m->err_pos = h1m->next;
+       h1m->err_state = h1m->state;
+       htx->flags |= HTX_FL_PARSING_ERROR;
+       return 0;
+}
+
+/* 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 the htx flag
+ * HTX_FL_PARSING_ERROR and filling h1m->err_pos and h1m->err_state fields. This
+ * functions is responsible to update the parser state <h1m> and the start-line
+ * <h1sl> if not NULL.
+ * For the requests, <h1sl> must always be provided. For responses, <h1sl> may
+ * be NULL and <h1m> flags HTTP_METH_CONNECT of HTTP_METH_HEAD may be set.
+ */
+int h1_parse_msg_hdrs(struct h1m *h1m, union h1_sl *h1sl, struct htx *dsthtx,
+                      struct buffer *srcbuf, size_t ofs, size_t max)
+{
+       struct http_hdr hdrs[global.tune.max_http_hdr];
+       int ret = 0;
+
+       if (!max || !b_data(srcbuf))
+               goto end;
+
+       /* Realing input buffer if necessary */
+       if (b_head(srcbuf) + b_data(srcbuf) > b_wrap(srcbuf))
+               b_slow_realign(srcbuf, trash.area, 0);
+
+       if (!h1sl) {
+               /* If there no start-line, be sure to only parse the headers */
+               h1m->flags |= H1_MF_HDRS_ONLY;
+       }
+       ret = h1_headers_to_hdr_list(b_peek(srcbuf, ofs), b_tail(srcbuf),
+                                    hdrs, sizeof(hdrs)/sizeof(hdrs[0]), h1m, h1sl);
+       if (ret <= 0) {
+               /* Incomplete or invalid message. If the input buffer only
+                * contains headers and is full, which is detected by it being
+                * full and the offset to be zero, it's an error because
+                * headers are too large to be handled by the parser. */
+               if (ret < 0 || (!ret && !ofs && !buf_room_for_htx_data(srcbuf)))
+                       goto error;
+               goto end;
+       }
+
+       /* messages headers fully parsed, do some checks to prepare the body
+        * parsing.
+        */
+
+       if (!(h1m->flags & H1_MF_RESP)) {
+               if (!h1_process_req_vsn(h1m, h1sl)) {
+                       h1m->err_pos = h1sl->rq.v.ptr - b_head(srcbuf);
+                       h1m->err_state = h1m->state;
+                       goto vsn_error;
+               }
+               if (!h1_postparse_req_hdrs(h1m, h1sl, dsthtx, hdrs, max))
+                       ret = 0;
+       }
+       else {
+               if (h1sl && !h1_process_res_vsn(h1m, h1sl)) {
+                       h1m->err_pos = h1sl->st.v.ptr - b_head(srcbuf);
+                       h1m->err_state = h1m->state;
+                       goto vsn_error;
+               }
+               if (!h1_postparse_res_hdrs(h1m, h1sl, dsthtx, hdrs, max))
+                       ret = 0;
+       }
+
+  end:
+       return ret;
+  error:
+       h1m->err_pos = h1m->next;
+       h1m->err_state = h1m->state;
+  vsn_error:
+       dsthtx->flags |= HTX_FL_PARSING_ERROR;
+       return 0;
+
+}
+
+/* Parse HTTP/1 body. It returns the number of bytes parsed if > 0, or 0 if it
+ * couldn't proceed. Parsing errors are reported by setting the htx flags
+ * HTX_FL_PARSING_ERROR and filling h1m->err_pos and h1m->err_state fields. This
+ * functions is responsible to update the parser state <h1m>.
+ */
+int h1_parse_msg_data(struct h1m *h1m, struct htx *dsthtx,
+                     struct buffer *srcbuf, size_t ofs, size_t max,
+                     struct buffer *htxbuf)
+{
+       size_t total = 0;
+       int32_t ret = 0;
+
+       if (h1m->flags & H1_MF_CLEN) {
+               /* content-length: read only h2m->body_len */
+               ret = htx_get_max_blksz(dsthtx, max);
+               if ((uint64_t)ret > h1m->curr_len)
+                       ret = h1m->curr_len;
+               if (ret > b_contig_data(srcbuf, ofs))
+                       ret = b_contig_data(srcbuf, ofs);
+               if (ret) {
+                       /* very often with large files we'll face the following
+                        * situation :
+                        *   - htx is empty and points to <htxbuf>
+                        *   - ret == buf->data
+                        *   - buf->head == sizeof(struct htx)
+                        *   => we can swap the buffers and place an htx header into
+                        *      the target buffer instead
+                        */
+                       int32_t try = ret;
+
+                       if (unlikely(htx_is_empty(dsthtx) && ret == b_data(srcbuf) &&
+                                    !ofs && b_head_ofs(srcbuf) == sizeof(struct htx))) {
+                               void *raw_area = srcbuf->area;
+                               void *htx_area = htxbuf->area;
+                               struct htx_blk *blk;
+
+                               srcbuf->area = htx_area;
+                               htxbuf->area = raw_area;
+                               dsthtx = (struct htx *)htxbuf->area;
+                               dsthtx->size = htxbuf->size - sizeof(*dsthtx);
+                               htx_reset(dsthtx);
+                               b_set_data(htxbuf, b_size(htxbuf));
+
+                               blk = htx_add_blk(dsthtx, HTX_BLK_DATA, ret);
+                               blk->info += ret;
+                               /* nothing else to do, the old buffer now contains an
+                                * empty pre-initialized HTX header
+                                */
+                       }
+                       else
+                               ret = htx_add_data(dsthtx, ist2(b_peek(srcbuf, ofs), try));
+                       h1m->curr_len -= ret;
+                       max -= sizeof(struct htx_blk) + ret;
+                       ofs += ret;
+                       total += ret;
+                       if (ret < try)
+                               goto end;
+               }
+
+               if (!h1m->curr_len) {
+                       if (max < sizeof(struct htx_blk) + 1 || !htx_add_endof(dsthtx, HTX_BLK_EOM))
+                               goto end;
+                       h1m->state = H1_MSG_DONE;
+               }
+       }
+       else if (h1m->flags & H1_MF_CHNK) {
+               /* te:chunked : parse chunks */
+         new_chunk:
+               if (h1m->state == H1_MSG_CHUNK_CRLF) {
+                       ret = h1_skip_chunk_crlf(srcbuf, ofs, b_data(srcbuf));
+                       if (ret <= 0)
+                               goto end;
+                       h1m->state = H1_MSG_CHUNK_SIZE;
+                       ofs += ret;
+                       total += ret;
+               }
+               if (h1m->state == H1_MSG_CHUNK_SIZE) {
+                       unsigned int chksz;
+
+                       ret = h1_parse_chunk_size(srcbuf, ofs, b_data(srcbuf), &chksz);
+                       if (ret <= 0)
+                               goto end;
+                       h1m->state = ((!chksz) ? H1_MSG_TRAILERS : H1_MSG_DATA);
+                       h1m->curr_len  = chksz;
+                       h1m->body_len += chksz;
+                       ofs += ret;
+                       total += ret;
+                       if (!h1m->curr_len)
+                               goto end;
+               }
+               if (h1m->state == H1_MSG_DATA) {
+                       ret = htx_get_max_blksz(dsthtx, max);
+                       if ((uint64_t)ret > h1m->curr_len)
+                               ret = h1m->curr_len;
+                       if (ret > b_contig_data(srcbuf, ofs))
+                               ret = b_contig_data(srcbuf, ofs);
+                       if (ret) {
+                               int32_t try = ret;
+
+                               ret = htx_add_data(dsthtx, ist2(b_peek(srcbuf, ofs), try));
+                               h1m->curr_len -= ret;
+                               max -= sizeof(struct htx_blk) + ret;
+                               ofs += ret;
+                               total += ret;
+                               if (ret < try)
+                                       goto end;
+                       }
+                       if (!h1m->curr_len) {
+                               h1m->state = H1_MSG_CHUNK_CRLF;
+                               goto new_chunk;
+                       }
+                       goto end;
+               }
+       }
+       else if (h1m->flags & H1_MF_XFER_LEN) {
+               /* 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(dsthtx, HTX_BLK_EOM))
+                       goto end;
+               h1m->state = H1_MSG_DONE;
+       }
+       else {
+               /* no content length, read till SHUTW */
+               ret = htx_get_max_blksz(dsthtx, max);
+               if (ret > b_contig_data(srcbuf, ofs))
+                       ret = b_contig_data(srcbuf, ofs);
+               if (ret)
+                       total = htx_add_data(dsthtx, ist2(b_peek(srcbuf, ofs), ret));
+       }
+
+  end:
+       if (ret < 0) {
+               dsthtx->flags |= HTX_FL_PARSING_ERROR;
+               h1m->err_state = h1m->state;
+               h1m->err_pos = ofs;
+               total = 0;
+       }
+
+       /* update htx->extra, only when the body length is known */
+       if (h1m->flags & H1_MF_XFER_LEN)
+               dsthtx->extra = h1m->curr_len;
+       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 the htx flags
+ * HTX_FL_PARSING_ERROR and filling h1m->err_pos and h1m->err_state fields. This
+ * functions is responsible to update the parser state <h1m>.
+ */
+int h1_parse_msg_tlrs(struct h1m *h1m, struct htx *dsthtx,
+                     struct buffer *srcbuf, size_t ofs, size_t max)
+{
+       struct http_hdr hdrs[global.tune.max_http_hdr];
+       struct h1m tlr_h1m;
+       int ret = 0;
+
+       if (!max || !b_data(srcbuf))
+               goto end;
+
+       /* Realing input buffer if necessary */
+       if (b_peek(srcbuf, ofs) > b_tail(srcbuf))
+               b_slow_realign(srcbuf, trash.area, 0);
+
+       tlr_h1m.flags = (H1_MF_NO_PHDR|H1_MF_HDRS_ONLY);
+       ret = h1_headers_to_hdr_list(b_peek(srcbuf, ofs), b_tail(srcbuf),
+                                    hdrs, sizeof(hdrs)/sizeof(hdrs[0]), &tlr_h1m, NULL);
+       if (ret <= 0) {
+               /* Incomplete or invalid trailers. If the input buffer only
+                * contains trailers and is full, which is detected by it being
+                * full and the offset to be zero, it's an error because
+                * trailers are too large to be handled by the parser. */
+               if (ret < 0 || (!ret && !ofs && !buf_room_for_htx_data(srcbuf)))
+                       goto error;
+               goto end;
+       }
+
+       /* messages trailers fully parsed. */
+       if (h1_eval_htx_hdrs_size(hdrs) > max) {
+               if (htx_is_empty(dsthtx))
+                       goto error;
+               ret = 0;
+               goto end;
+       }
+
+       if (!htx_add_all_trailers(dsthtx, hdrs))
+               goto error;
+
+  end:
+       return ret;
+  error:
+       h1m->err_state = h1m->state;
+       h1m->err_pos = h1m->next;
+       dsthtx->flags |= HTX_FL_PARSING_ERROR;
+       return 0;
+}
+
+/*
+ * Local variables:
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ * End:
+ */
index d1022e699033f58cb77dd4ea8ab7322922508139..7cc7ba6eba76e97bce6c72a5304c17613c718984 100644 (file)
@@ -23,6 +23,7 @@
 #include <types/session.h>
 
 #include <proto/connection.h>
+#include <proto/h1_htx.h>
 #include <proto/http_htx.h>
 #include <proto/log.h>
 #include <proto/session.h>
@@ -570,82 +571,6 @@ static void h1_parse_res_vsn(struct h1m *h1m, const struct htx_sl *sl)
                h1m->flags |= H1_MF_VER_11;
 }
 
-/*
- * Check the validity of the request version. If the version is valid, it
- * returns 1. Otherwise, it returns 0.
- */
-static int h1_process_req_vsn(struct h1s *h1s, struct h1m *h1m, union h1_sl sl)
-{
-       struct h1c *h1c = h1s->h1c;
-
-       /* RFC7230#2.6 has enforced the format of the HTTP version string to be
-        * exactly one digit "." one digit. This check may be disabled using
-        * option accept-invalid-http-request.
-        */
-       if (!(h1c->px->options2 & PR_O2_REQBUG_OK)) {
-               if (sl.rq.v.len != 8)
-                       return 0;
-
-               if (*(sl.rq.v.ptr + 4) != '/' ||
-                   !isdigit((unsigned char)*(sl.rq.v.ptr + 5)) ||
-                   *(sl.rq.v.ptr + 6) != '.' ||
-                   !isdigit((unsigned char)*(sl.rq.v.ptr + 7)))
-                       return 0;
-       }
-       else if (!sl.rq.v.len) {
-               /* try to convert HTTP/0.9 requests to HTTP/1.0 */
-
-               /* RFC 1945 allows only GET for HTTP/0.9 requests */
-               if (sl.rq.meth != HTTP_METH_GET)
-                       return 0;
-
-               /* HTTP/0.9 requests *must* have a request URI, per RFC 1945 */
-               if (!sl.rq.u.len)
-                       return 0;
-
-               /* Add HTTP version */
-               sl.rq.v = ist("HTTP/1.0");
-               return 1;
-       }
-
-       if ((sl.rq.v.len == 8) &&
-           ((*(sl.rq.v.ptr + 5) > '1') ||
-            ((*(sl.rq.v.ptr + 5) == '1') && (*(sl.rq.v.ptr + 7) >= '1'))))
-               h1m->flags |= H1_MF_VER_11;
-       return 1;
-}
-
-/*
- * Check the validity of the response version. If the version is valid, it
- * returns 1. Otherwise, it returns 0.
- */
-static int h1_process_res_vsn(struct h1s *h1s, struct h1m *h1m, union h1_sl sl)
-{
-       struct h1c *h1c = h1s->h1c;
-
-       /* RFC7230#2.6 has enforced the format of the HTTP version string to be
-        * exactly one digit "." one digit. This check may be disabled using
-        * option accept-invalid-http-request.
-        */
-       if (!(h1c->px->options2 & PR_O2_RSPBUG_OK)) {
-               if (sl.st.v.len != 8)
-                       return 0;
-
-               if (*(sl.st.v.ptr + 4) != '/' ||
-                   !isdigit((unsigned char)*(sl.st.v.ptr + 5)) ||
-                   *(sl.st.v.ptr + 6) != '.' ||
-                   !isdigit((unsigned char)*(sl.st.v.ptr + 7)))
-                       return 0;
-       }
-
-       if ((sl.st.v.len == 8) &&
-           ((*(sl.st.v.ptr + 5) > '1') ||
-            ((*(sl.st.v.ptr + 5) == '1') && (*(sl.st.v.ptr + 7) >= '1'))))
-               h1m->flags |= H1_MF_VER_11;
-
-       return 1;
-}
-
 /* Deduce the connection mode of the client connection, depending on the
  * configuration and the H1 message flags. This function is called twice, the
  * first time when the request is parsed and the second time when the response
@@ -955,240 +880,56 @@ static void h1_set_res_tunnel_mode(struct h1s *h1s)
        }
 }
 
-/* Estimate the size of the HTX headers after the parsing, including the EOH. */
-static size_t h1_eval_htx_hdrs_size(struct http_hdr *hdrs)
-{
-       size_t sz = 0;
-       int i;
-
-       for (i = 0; hdrs[i].n.len; i++)
-               sz += sizeof(struct htx_blk) + hdrs[i].n.len + hdrs[i].v.len;
-       sz += sizeof(struct htx_blk) + 1;
-       return sz;
-}
-
-/* Estimate the size of the HTX request after the parsing. */
-static size_t h1_eval_htx_req_size(struct h1m *h1m, union h1_sl *h1sl, struct http_hdr *hdrs)
-{
-       size_t sz;
-
-       /* size of the HTX start-line */
-       sz = sizeof(struct htx_blk) + sizeof(struct htx_sl) + h1sl->rq.m.len + h1sl->rq.u.len + h1sl->rq.v.len;
-       sz += h1_eval_htx_hdrs_size(hdrs);
-       return sz;
-}
-
-/* Estimate the size of the HTX response after the parsing. */
-static size_t h1_eval_htx_res_size(struct h1m *h1m, union h1_sl *h1sl, struct http_hdr *hdrs)
-{
-       size_t sz;
-
-       /* size of the HTX start-line */
-       sz = sizeof(struct htx_blk) + sizeof(struct htx_sl) + h1sl->st.v.len + h1sl->st.c.len + h1sl->st.r.len;
-       sz += h1_eval_htx_hdrs_size(hdrs);
-       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)) {
-               h1s->flags |= H1S_F_APPEND_EOM;
-               return 0;
-       }
-
-       h1s->flags &= ~H1S_F_APPEND_EOM;
-       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
- * responsible to update the parser state <h1m>.
+ * flag. If relies on the function http_parse_msg_hdrs() to do the parsing.
  */
 static size_t h1_process_headers(struct h1s *h1s, struct h1m *h1m, struct htx *htx,
                                 struct buffer *buf, size_t *ofs, size_t max)
 {
-       struct htx_sl *sl;
-       struct http_hdr hdrs[global.tune.max_http_hdr];
        union h1_sl h1sl;
-       unsigned int flags = HTX_SL_F_NONE;
-       size_t used;
        int ret = 0;
 
-       if (!max || !b_data(buf))
-               goto end;
-
-       /* Realing input buffer if necessary */
-       if (b_head(buf) + b_data(buf) > b_wrap(buf))
-               b_slow_realign(buf, trash.area, 0);
-
        if (!(h1m->flags & H1_MF_RESP)) {
                /* Try to match H2 preface before parsing the request headers. */
                ret = b_isteq(buf, 0, b_data(buf), ist(H2_CONN_PREFACE));
                if (ret > 0)
                        goto h2c_upgrade;
        }
-
-       ret = h1_headers_to_hdr_list(b_peek(buf, *ofs), b_tail(buf),
-                                    hdrs, sizeof(hdrs)/sizeof(hdrs[0]), h1m, &h1sl);
-       if (ret <= 0) {
-               /* Incomplete or invalid message. If the input buffer only
-                * contains headers and is full, which is detected by it being
-                * full and the offset to be zero, it's an error because
-                * headers are too large to be handled by the parser. */
-               if (ret < 0 || (!ret && !*ofs && !buf_room_for_htx_data(buf)))
-                       goto error;
+       else {
+               if (h1s->meth == HTTP_METH_CONNECT)
+                       h1m->flags |= H1_MF_METH_CONNECT;
+               if (h1s->meth == HTTP_METH_HEAD)
+                       h1m->flags |= H1_MF_METH_HEAD;
+       }
+
+       ret = h1_parse_msg_hdrs(h1m, &h1sl, htx, buf, *ofs, max);
+       if (!ret) {
+               if (htx->flags & HTX_FL_PARSING_ERROR) {
+                       h1s->flags |= (!(h1m->flags & H1_MF_RESP) ? H1S_F_REQ_ERROR : H1S_F_RES_ERROR);
+                       h1s->cs->flags |= CS_FL_EOI;
+                       h1_capture_bad_message(h1s->h1c, h1s, h1m, buf);
+               }
                goto end;
        }
 
-       /* messages headers fully parsed, do some checks to prepare the body
-        * parsing.
-        */
-
-       /* Save the request's method or the response's status, check if the body
-        * length is known and check the VSN validity */
        if (!(h1m->flags & H1_MF_RESP)) {
                h1s->meth = h1sl.rq.meth;
-
-               /* By default, request have always a known length */
-               h1m->flags |= H1_MF_XFER_LEN;
-
-               if (h1s->meth == HTTP_METH_CONNECT) {
-                       /* Switch CONNECT requests to tunnel mode */
+               if (h1m->state == H1_MSG_TUNNEL)
                        h1_set_req_tunnel_mode(h1s);
-               }
-
-               if (!h1_process_req_vsn(h1s, h1m, h1sl)) {
-                       h1m->err_pos = h1sl.rq.v.ptr - b_head(buf);
-                       h1m->err_state = h1m->state;
-                       goto vsn_error;
-               }
        }
        else {
                h1s->status = h1sl.st.status;
-
-               if ((h1s->meth == HTTP_METH_CONNECT && h1s->status == 200) ||
-                   h1s->status == 101) {
-                       /* Switch successfull replies to CONNECT requests and
-                        * protocol switching to tunnel mode. */
+               if (h1m->state == H1_MSG_TUNNEL)
                        h1_set_res_tunnel_mode(h1s);
-               }
-               else if ((h1s->meth == HTTP_METH_HEAD) ||
-                        (h1s->status >= 100 && h1s->status < 200) ||
-                        (h1s->status == 204) || (h1s->status == 304)) {
-                       /* Responses known to have no body. */
-                       h1m->flags &= ~(H1_MF_CLEN|H1_MF_CHNK);
-                       h1m->flags |= H1_MF_XFER_LEN;
-                       h1m->curr_len = h1m->body_len = 0;
-               }
-               else if (h1m->flags & (H1_MF_CLEN|H1_MF_CHNK)) {
-                       /* Responses with a known body length. */
-                       h1m->flags |= H1_MF_XFER_LEN;
-               }
-               else {
-                       /* Responses with an unknown body length */
-                       h1m->state = H1_MSG_TUNNEL;
-               }
-
-               if (!h1_process_res_vsn(h1s, h1m, h1sl)) {
-                       h1m->err_pos = h1sl.st.v.ptr - b_head(buf);
-                       h1m->err_state = h1m->state;
-                       goto vsn_error;
-               }
        }
-
-       /* Set HTX start-line flags */
-       if (h1m->flags & H1_MF_VER_11)
-               flags |= HTX_SL_F_VER_11;
-       if (h1m->flags & H1_MF_XFER_ENC)
-               flags |= HTX_SL_F_XFER_ENC;
-       if (h1m->flags & H1_MF_XFER_LEN) {
-               flags |= HTX_SL_F_XFER_LEN;
-               if (h1m->flags & H1_MF_CHNK)
-                       flags |= HTX_SL_F_CHNK;
-               else if (h1m->flags & H1_MF_CLEN) {
-                       flags |= HTX_SL_F_CLEN;
-                       if (h1m->body_len == 0)
-                               flags |= HTX_SL_F_BODYLESS;
-               }
-               else
-                       flags |= HTX_SL_F_BODYLESS;
-       }
-
-       used = htx_used_space(htx);
-       if (!(h1m->flags & H1_MF_RESP)) {
-               if (h1_eval_htx_req_size(h1m, &h1sl, hdrs) > max) {
-                       if (htx_is_empty(htx))
-                               goto error;
-                       h1m_init_req(h1m);
-                       h1m->flags |= (H1_MF_NO_PHDR|H1_MF_CLEAN_CONN_HDR);
-                       ret = 0;
-                       goto end;
-               }
-
-               sl = htx_add_stline(htx, HTX_BLK_REQ_SL, flags, h1sl.rq.m, h1sl.rq.u, h1sl.rq.v);
-               if (!sl || !htx_add_all_headers(htx, hdrs))
-                       goto error;
-               sl->info.req.meth = h1s->meth;
-
-               /* Check if the uri contains an explicit scheme and if it is
-                * "http" or "https". */
-               if (h1sl.rq.u.len && h1sl.rq.u.ptr[0] != '/') {
-                       sl->flags |= HTX_SL_F_HAS_SCHM;
-                       if (h1sl.rq.u.len > 4 && (h1sl.rq.u.ptr[0] | 0x20) == 'h')
-                               sl->flags |= ((h1sl.rq.u.ptr[4] == ':') ? HTX_SL_F_SCHM_HTTP : HTX_SL_F_SCHM_HTTPS);
-               }
-       }
-       else {
-               if (h1_eval_htx_res_size(h1m, &h1sl, hdrs) > max) {
-                       if (htx_is_empty(htx))
-                               goto error;
-                       h1m_init_res(h1m);
-                       h1m->flags |= (H1_MF_NO_PHDR|H1_MF_CLEAN_CONN_HDR);
-                       ret = 0;
-                       goto end;
-               }
-
-               flags |= HTX_SL_F_IS_RESP;
-               sl = htx_add_stline(htx, HTX_BLK_RES_SL, flags, h1sl.st.v, h1sl.st.c, h1sl.st.r);
-               if (!sl || !htx_add_all_headers(htx, hdrs))
-                       goto error;
-               sl->info.res.status = h1s->status;
-       }
-
-       /* Set bytes used in the HTX mesage for the headers now */
-       sl->hdrs_bytes = htx_used_space(htx) - used;
-
        h1_process_input_conn_mode(h1s, h1m, htx);
-
-       /* If body length cannot be determined, set htx->extra to
-        * ULLONG_MAX. This value is impossible in other cases.
-        */
-       htx->extra = ((h1m->flags & H1_MF_XFER_LEN) ? h1m->curr_len : ULLONG_MAX);
        *ofs += ret;
+
   end:
        return ret;
 
-  error:
-       h1m->err_state = h1m->state;
-       h1m->err_pos = h1m->next;
-  vsn_error:
-       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;
-
   h2c_upgrade:
        h1s->h1c->flags |= H1C_F_UPG_H2C;
        h1s->cs->flags |= CS_FL_EOI;
@@ -1199,167 +940,30 @@ static size_t h1_process_headers(struct h1s *h1s, struct h1m *h1m, struct htx *h
 
 /*
  * Parse HTTP/1 body. 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>.
+ * couldn't proceed. Parsing errors are reported by setting H1S_F_*_ERROR flag.
+ * If relies on the function http_parse_msg_data() to do the parsing.
  */
 static size_t h1_process_data(struct h1s *h1s, struct h1m *h1m, struct htx *htx,
                              struct buffer *buf, size_t *ofs, size_t max,
                              struct buffer *htxbuf)
 {
-       size_t total = 0;
-       int32_t ret = 0;
-
-       if (h1m->flags & H1_MF_XFER_LEN) {
-               if (h1m->flags & H1_MF_CLEN) {
-                       /* content-length: read only h2m->body_len */
-                       ret = htx_get_max_blksz(htx, max);
-                       if ((uint64_t)ret > h1m->curr_len)
-                               ret = h1m->curr_len;
-                       if (ret > b_contig_data(buf, *ofs))
-                               ret = b_contig_data(buf, *ofs);
-
-                       if (ret) {
-                               /* very often with large files we'll face the following
-                                * situation :
-                                *   - htx is empty and points to <htxbuf>
-                                *   - ret == buf->data
-                                *   - buf->head == sizeof(struct htx)
-                                *   => we can swap the buffers and place an htx header into
-                                *      the target buffer instead
-                                */
-                               int32_t try = ret;
-
-                               if (unlikely(htx_is_empty(htx) && ret == b_data(buf) &&
-                                            !*ofs && b_head_ofs(buf) == sizeof(struct htx))) {
-                                       void *raw_area = buf->area;
-                                       void *htx_area = htxbuf->area;
-                                       struct htx_blk *blk;
-
-                                       buf->area = htx_area;
-                                       htxbuf->area = raw_area;
-                                       htx = (struct htx *)htxbuf->area;
-                                       htx->size = htxbuf->size - sizeof(*htx);
-                                       htx_reset(htx);
-                                       b_set_data(htxbuf, b_size(htxbuf));
-
-                                       blk = htx_add_blk(htx, HTX_BLK_DATA, ret);
-                                       blk->info += ret;
-                                       /* nothing else to do, the old buffer now contains an
-                                        * empty pre-initialized HTX header
-                                        */
-                               }
-                               else {
-                                       ret = htx_add_data(htx, ist2(b_peek(buf, *ofs), try));
-                               }
-                               h1m->curr_len -= ret;
-                               max -= sizeof(struct htx_blk) + ret;
-                               *ofs += ret;
-                               total += ret;
-                               if (ret < try)
-                                       goto end;
-                       }
-
-                       if (!h1m->curr_len) {
-                               if (!h1_process_eom(h1s, h1m, htx, max))
-                                       goto end;
-                       }
-               }
-               else if (h1m->flags & H1_MF_CHNK) {
-                 new_chunk:
-                       /* te:chunked : parse chunks */
-                       if (h1m->state == H1_MSG_CHUNK_CRLF) {
-                               ret = h1_skip_chunk_crlf(buf, *ofs, b_data(buf));
-                               if (ret <= 0)
-                                       goto end;
-                               h1m->state = H1_MSG_CHUNK_SIZE;
-
-                               *ofs += ret;
-                               total += ret;
-                       }
-
-                       if (h1m->state == H1_MSG_CHUNK_SIZE) {
-                               unsigned int chksz;
-
-                               ret = h1_parse_chunk_size(buf, *ofs, b_data(buf), &chksz);
-                               if (ret <= 0)
-                                       goto end;
-                               h1m->state = ((!chksz) ? H1_MSG_TRAILERS : H1_MSG_DATA);
-
-                               h1m->curr_len  = chksz;
-                               h1m->body_len += chksz;
-                               *ofs += ret;
-                               total += ret;
-
-                               if (!h1m->curr_len)
-                                       goto end;
-                       }
-
-                       if (h1m->state == H1_MSG_DATA) {
-                               ret = htx_get_max_blksz(htx, max);
-                               if ((uint64_t)ret > h1m->curr_len)
-                                       ret = h1m->curr_len;
-                               if (ret > b_contig_data(buf, *ofs))
-                                       ret = b_contig_data(buf, *ofs);
-
-                               if (ret) {
-                                       int32_t try = ret;
-
-                                       ret = htx_add_data(htx, ist2(b_peek(buf, *ofs), try));
-                                       h1m->curr_len -= ret;
-                                       max -= sizeof(struct htx_blk) + ret;
-                                       *ofs += ret;
-                                       total += ret;
-                                       if (ret < try)
-                                               goto end;
-                               }
-                               if (!h1m->curr_len) {
-                                       h1m->state = H1_MSG_CHUNK_CRLF;
-                                       goto new_chunk;
-                               }
-                               goto end;
-                       }
-               }
-               else {
-                       /* XFER_LEN is set but not CLEN nor CHNK, it means there
-                        * is no body. Switch the message in DONE state
-                        */
-                       if (!h1_process_eom(h1s, h1m, htx, max))
-                               goto end;
-               }
-       }
-       else {
-               /* no content length, read till SHUTW */
-               ret = htx_get_max_blksz(htx, max);
-               if (ret > b_contig_data(buf, *ofs))
-                       ret = b_contig_data(buf, *ofs);
-
-               if (ret) {
-                       int32_t try = ret;
+       int ret;
 
-                       ret = htx_add_data(htx, ist2(b_peek(buf, *ofs), try));
 
-                       *ofs += ret;
-                       total = ret;
-                       if (ret < try)
-                               goto end;
+       ret = h1_parse_msg_data(h1m, htx, buf, *ofs, max, htxbuf);
+       if (ret <= 0) {
+               if (ret < 0) {
+                       h1s->flags |= (!(h1m->flags & H1_MF_RESP) ? H1S_F_REQ_ERROR : H1S_F_RES_ERROR);
+                       h1s->cs->flags |= CS_FL_EOI;
+                       h1_capture_bad_message(h1s->h1c, h1s, h1m, buf);
                }
+               return 0;
        }
 
-  end:
-       if (ret < 0) {
-               h1s->flags |= (!(h1m->flags & H1_MF_RESP) ? H1S_F_REQ_ERROR : H1S_F_RES_ERROR);
+       if (h1m->state == H1_MSG_DONE)
                h1s->cs->flags |= CS_FL_EOI;
-               htx->flags |= HTX_FL_PARSING_ERROR;
-               h1m->err_state = h1m->state;
-               h1m->err_pos = *ofs + max + ret;
-               h1_capture_bad_message(h1s->h1c, h1s, h1m, buf);
-               return 0;
-       }
-       /* update htx->extra, only when the body length is known */
-       if (h1m->flags & H1_MF_XFER_LEN)
-               htx->extra = h1m->curr_len;
-       return total;
+       *ofs += ret;
+       return ret;
 }
 
 /*
@@ -1371,55 +975,39 @@ static size_t h1_process_data(struct h1s *h1s, struct h1m *h1m, struct htx *htx,
 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[global.tune.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);
+       int ret;
 
-       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);
+       ret = h1_parse_msg_tlrs(h1m, htx, buf, *ofs, max);
        if (ret <= 0) {
-               /* Incomplete or invalid trailers. If the input buffer only
-                * contains trailers and is full, which is detected by it being
-                * full and the offset to be zero, it's an error because
-                * trailers are too large to be handled by the parser. */
-               if (ret < 0 || (!ret && !*ofs && !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 (ret < 0) {
+                       h1s->flags |= (!(h1m->flags & H1_MF_RESP) ? H1S_F_REQ_ERROR : H1S_F_RES_ERROR);
+                       h1s->cs->flags |= CS_FL_EOI;
+                       h1_capture_bad_message(h1s->h1c, h1s, h1m, buf);
+               }
+               return 0;
        }
-
-       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);
+/*
+ * 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)) {
+               h1s->flags |= H1S_F_APPEND_EOM;
+               return 0;
+       }
+
+       h1s->flags &= ~H1S_F_APPEND_EOM;
+       h1m->state = H1_MSG_DONE;
        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;
+       return (sizeof(struct htx_blk) + 1);
 }
 
 /*