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 \
#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
--- /dev/null
+/*
+ * 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:
+ */
--- /dev/null
+/*
+ * 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:
+ */
#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>
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
}
}
-/* 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;
/*
* 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;
}
/*
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);
}
/*