blob: 6aa389e13c3dc6e652edc78d6eac8d999583804a [file] [log] [blame]
Christopher Faulet4f0f88a2019-08-10 11:17:44 +02001/*
2 * Functions to manipulate H1 messages using the internal representation.
3 *
4 * Copyright (C) 2019 HAProxy Technologies, Christopher Faulet <cfaulet@haproxy.com>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 *
11 */
12
Willy Tarreau4c7e4b72020-05-27 12:58:42 +020013#include <haproxy/api.h>
Willy Tarreau6be78492020-06-05 00:00:29 +020014#include <haproxy/cfgparse.h>
Willy Tarreaudfd3de82020-06-04 23:46:14 +020015#include <haproxy/global.h>
Willy Tarreau5413a872020-06-02 19:33:08 +020016#include <haproxy/h1.h>
Willy Tarreauc6fe8842020-06-04 09:00:02 +020017#include <haproxy/h1_htx.h>
Willy Tarreaucd72d8c2020-06-02 19:11:26 +020018#include <haproxy/http.h>
Amaury Denoyellec00d8142021-07-07 10:49:27 +020019#include <haproxy/http_htx.h>
Willy Tarreau16f958c2020-06-03 08:44:35 +020020#include <haproxy/htx.h>
Willy Tarreau36979d92020-06-05 17:27:29 +020021#include <haproxy/tools.h>
Christopher Faulet4f0f88a2019-08-10 11:17:44 +020022
Christopher Faulet4f0f88a2019-08-10 11:17:44 +020023/* Estimate the size of the HTX headers after the parsing, including the EOH. */
24static size_t h1_eval_htx_hdrs_size(const struct http_hdr *hdrs)
25{
26 size_t sz = 0;
27 int i;
28
29 for (i = 0; hdrs[i].n.len; i++)
30 sz += sizeof(struct htx_blk) + hdrs[i].n.len + hdrs[i].v.len;
31 sz += sizeof(struct htx_blk) + 1;
32 return sz;
33}
34
35/* Estimate the size of the HTX request after the parsing. */
36static size_t h1_eval_htx_size(const struct ist p1, const struct ist p2, const struct ist p3,
37 const struct http_hdr *hdrs)
38{
39 size_t sz;
40
41 /* size of the HTX start-line */
42 sz = sizeof(struct htx_blk) + sizeof(struct htx_sl) + p1.len + p2.len + p3.len;
43 sz += h1_eval_htx_hdrs_size(hdrs);
44 return sz;
45}
46
Christopher Faulet4f0f88a2019-08-10 11:17:44 +020047/* Check the validity of the request version. If the version is valid, it
48 * returns 1. Otherwise, it returns 0.
49 */
50static int h1_process_req_vsn(struct h1m *h1m, union h1_sl *sl)
51{
52 /* RFC7230#2.6 has enforced the format of the HTTP version string to be
53 * exactly one digit "." one digit. This check may be disabled using
54 * option accept-invalid-http-request.
55 */
56 if (h1m->err_pos == -2) { /* PR_O2_REQBUG_OK not set */
57 if (sl->rq.v.len != 8)
58 return 0;
59
60 if (*(sl->rq.v.ptr + 4) != '/' ||
61 !isdigit((unsigned char)*(sl->rq.v.ptr + 5)) ||
62 *(sl->rq.v.ptr + 6) != '.' ||
63 !isdigit((unsigned char)*(sl->rq.v.ptr + 7)))
64 return 0;
65 }
66 else if (!sl->rq.v.len) {
67 /* try to convert HTTP/0.9 requests to HTTP/1.0 */
68
69 /* RFC 1945 allows only GET for HTTP/0.9 requests */
70 if (sl->rq.meth != HTTP_METH_GET)
71 return 0;
72
73 /* HTTP/0.9 requests *must* have a request URI, per RFC 1945 */
74 if (!sl->rq.u.len)
75 return 0;
76
77 /* Add HTTP version */
78 sl->rq.v = ist("HTTP/1.0");
79 return 1;
80 }
81
82 if ((sl->rq.v.len == 8) &&
83 ((*(sl->rq.v.ptr + 5) > '1') ||
84 ((*(sl->rq.v.ptr + 5) == '1') && (*(sl->rq.v.ptr + 7) >= '1'))))
85 h1m->flags |= H1_MF_VER_11;
86 return 1;
87}
88
89/* Check the validity of the response version. If the version is valid, it
90 * returns 1. Otherwise, it returns 0.
91 */
92static int h1_process_res_vsn(struct h1m *h1m, union h1_sl *sl)
93{
94 /* RFC7230#2.6 has enforced the format of the HTTP version string to be
95 * exactly one digit "." one digit. This check may be disabled using
96 * option accept-invalid-http-request.
97 */
98 if (h1m->err_pos == -2) { /* PR_O2_REQBUG_OK not set */
99 if (sl->st.v.len != 8)
100 return 0;
101
102 if (*(sl->st.v.ptr + 4) != '/' ||
103 !isdigit((unsigned char)*(sl->st.v.ptr + 5)) ||
104 *(sl->st.v.ptr + 6) != '.' ||
105 !isdigit((unsigned char)*(sl->st.v.ptr + 7)))
106 return 0;
107 }
108
109 if ((sl->st.v.len == 8) &&
110 ((*(sl->st.v.ptr + 5) > '1') ||
111 ((*(sl->st.v.ptr + 5) == '1') && (*(sl->st.v.ptr + 7) >= '1'))))
112 h1m->flags |= H1_MF_VER_11;
113
114 return 1;
115}
116
117/* Convert H1M flags to HTX start-line flags. */
118static unsigned int h1m_htx_sl_flags(struct h1m *h1m)
119{
120 unsigned int flags = HTX_SL_F_NONE;
121
122 if (h1m->flags & H1_MF_RESP)
123 flags |= HTX_SL_F_IS_RESP;
124 if (h1m->flags & H1_MF_VER_11)
125 flags |= HTX_SL_F_VER_11;
126 if (h1m->flags & H1_MF_XFER_ENC)
127 flags |= HTX_SL_F_XFER_ENC;
128 if (h1m->flags & H1_MF_XFER_LEN) {
129 flags |= HTX_SL_F_XFER_LEN;
130 if (h1m->flags & H1_MF_CHNK)
131 flags |= HTX_SL_F_CHNK;
132 else if (h1m->flags & H1_MF_CLEN) {
133 flags |= HTX_SL_F_CLEN;
134 if (h1m->body_len == 0)
135 flags |= HTX_SL_F_BODYLESS;
136 }
137 else
138 flags |= HTX_SL_F_BODYLESS;
139 }
Christopher Faulet576c3582021-01-08 15:53:01 +0100140 if (h1m->flags & H1_MF_CONN_UPG)
141 flags |= HTX_SL_F_CONN_UPG;
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200142 return flags;
143}
144
145/* Postprocess the parsed headers for a request and convert them into an htx
146 * message. It returns the number of bytes parsed if > 0, or 0 if it couldn't
147 * proceed. Parsing errors are reported by setting the htx flag
148 * HTX_FL_PARSING_ERROR and filling h1m->err_pos and h1m->err_state fields.
149 */
150static int h1_postparse_req_hdrs(struct h1m *h1m, union h1_sl *h1sl, struct htx *htx,
151 struct http_hdr *hdrs, size_t max)
152{
153 struct htx_sl *sl;
154 struct ist meth, uri, vsn;
155 unsigned int flags;
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200156
157 /* <h1sl> is always defined for a request */
158 meth = h1sl->rq.m;
159 uri = h1sl->rq.u;
160 vsn = h1sl->rq.v;
161
162 /* Be sure the message, once converted into HTX, will not exceed the max
163 * size allowed.
164 */
165 if (h1_eval_htx_size(meth, uri, vsn, hdrs) > max) {
166 if (htx_is_empty(htx))
167 goto error;
Christopher Faulet58f21da2021-09-20 07:47:27 +0200168 goto output_full;
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200169 }
170
171 /* By default, request have always a known length */
172 h1m->flags |= H1_MF_XFER_LEN;
173
174 if (h1sl->rq.meth == HTTP_METH_CONNECT) {
Christopher Faulet5be651d2021-01-22 15:28:03 +0100175 h1m->flags &= ~(H1_MF_CLEN|H1_MF_CHNK);
176 h1m->curr_len = h1m->body_len = 0;
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200177 }
178
Christopher Faulet673504a2021-09-09 09:52:51 +0200179
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200180 flags = h1m_htx_sl_flags(h1m);
Christopher Faulet673504a2021-09-09 09:52:51 +0200181 if ((flags & (HTX_SL_F_CONN_UPG|HTX_SL_F_BODYLESS)) == HTX_SL_F_CONN_UPG) {
182 int i;
183
184 for (i = 0; hdrs[i].n.len; i++) {
185 if (isteqi(hdrs[i].n, ist("upgrade")))
186 hdrs[i].v = IST_NULL;
187 }
188 h1m->flags &=~ H1_MF_CONN_UPG;
189 flags &= ~HTX_SL_F_CONN_UPG;
190 }
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200191 sl = htx_add_stline(htx, HTX_BLK_REQ_SL, flags, meth, uri, vsn);
192 if (!sl || !htx_add_all_headers(htx, hdrs))
193 goto error;
194 sl->info.req.meth = h1sl->rq.meth;
195
Christopher Fauletfe451fb2019-10-08 15:01:34 +0200196 /* Check if the uri contains an authority. Also check if it contains an
197 * explicit scheme and if it is "http" or "https". */
198 if (h1sl->rq.meth == HTTP_METH_CONNECT)
199 sl->flags |= HTX_SL_F_HAS_AUTHORITY;
200 else if (uri.len && uri.ptr[0] != '/' && uri.ptr[0] != '*') {
201 sl->flags |= (HTX_SL_F_HAS_AUTHORITY|HTX_SL_F_HAS_SCHM);
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200202 if (uri.len > 4 && (uri.ptr[0] | 0x20) == 'h')
203 sl->flags |= ((uri.ptr[4] == ':') ? HTX_SL_F_SCHM_HTTP : HTX_SL_F_SCHM_HTTPS);
Amaury Denoyellec00d8142021-07-07 10:49:27 +0200204
205 /* absolute-form target URI present, proceed to scheme-based
206 * normalization */
207 http_scheme_based_normalize(htx);
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200208 }
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200209
210 /* If body length cannot be determined, set htx->extra to
211 * ULLONG_MAX. This value is impossible in other cases.
212 */
213 htx->extra = ((h1m->flags & H1_MF_XFER_LEN) ? h1m->curr_len : ULLONG_MAX);
214
215 end:
216 return 1;
Christopher Faulet58f21da2021-09-20 07:47:27 +0200217 output_full:
218 h1m_init_req(h1m);
219 h1m->flags |= (H1_MF_NO_PHDR|H1_MF_CLEAN_CONN_HDR);
220 return -2;
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200221 error:
222 h1m->err_pos = h1m->next;
223 h1m->err_state = h1m->state;
224 htx->flags |= HTX_FL_PARSING_ERROR;
Christopher Faulet58f21da2021-09-20 07:47:27 +0200225 return -1;
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200226}
227
228/* Postprocess the parsed headers for a response and convert them into an htx
229 * message. It returns the number of bytes parsed if > 0, or 0 if it couldn't
230 * proceed. Parsing errors are reported by setting the htx flag
231 * HTX_FL_PARSING_ERROR and filling h1m->err_pos and h1m->err_state fields.
232 */
233static int h1_postparse_res_hdrs(struct h1m *h1m, union h1_sl *h1sl, struct htx *htx,
234 struct http_hdr *hdrs, size_t max)
235{
236 struct htx_sl *sl;
237 struct ist vsn, status, reason;
238 unsigned int flags;
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200239 uint16_t code = 0;
240
241 if (h1sl) {
242 /* For HTTP responses, the start-line was parsed */
243 code = h1sl->st.status;
244 vsn = h1sl->st.v;
245 status = h1sl->st.c;
246 reason = h1sl->st.r;
247 }
248 else {
249 /* For FCGI responses, there is no start(-line but the "Status"
250 * header must be parsed, if found.
251 */
252 int hdr;
253
254 vsn = ((h1m->flags & H1_MF_VER_11) ? ist("HTTP/1.1") : ist("HTTP/1.0"));
255 for (hdr = 0; hdrs[hdr].n.len; hdr++) {
256 if (isteqi(hdrs[hdr].n, ist("status"))) {
257 code = http_parse_status_val(hdrs[hdr].v, &status, &reason);
258 }
259 else if (isteqi(hdrs[hdr].n, ist("location"))) {
260 code = 302;
261 status = ist("302");
262 reason = ist("Moved Temporarily");
263 }
264 }
265 if (!code) {
266 code = 200;
267 status = ist("200");
268 reason = ist("OK");
269 }
270 /* FIXME: Check the codes 1xx ? */
271 }
272
273 /* Be sure the message, once converted into HTX, will not exceed the max
274 * size allowed.
275 */
276 if (h1_eval_htx_size(vsn, status, reason, hdrs) > max) {
277 if (htx_is_empty(htx))
278 goto error;
Christopher Faulet58f21da2021-09-20 07:47:27 +0200279 goto output_full;
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200280 }
281
Christopher Fauletc75668e2020-12-07 18:10:32 +0100282 if (((h1m->flags & H1_MF_METH_CONNECT) && code >= 200 && code < 300) || code == 101) {
Christopher Faulet5be651d2021-01-22 15:28:03 +0100283 h1m->flags &= ~(H1_MF_CLEN|H1_MF_CHNK);
284 h1m->flags |= H1_MF_XFER_LEN;
285 h1m->curr_len = h1m->body_len = 0;
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200286 }
287 else if ((h1m->flags & H1_MF_METH_HEAD) || (code >= 100 && code < 200) ||
288 (code == 204) || (code == 304)) {
289 /* Responses known to have no body. */
290 h1m->flags &= ~(H1_MF_CLEN|H1_MF_CHNK);
291 h1m->flags |= H1_MF_XFER_LEN;
292 h1m->curr_len = h1m->body_len = 0;
293 }
294 else if (h1m->flags & (H1_MF_CLEN|H1_MF_CHNK)) {
295 /* Responses with a known body length. */
296 h1m->flags |= H1_MF_XFER_LEN;
297 }
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200298
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200299 flags = h1m_htx_sl_flags(h1m);
300 sl = htx_add_stline(htx, HTX_BLK_RES_SL, flags, vsn, status, reason);
301 if (!sl || !htx_add_all_headers(htx, hdrs))
302 goto error;
303 sl->info.res.status = code;
304
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200305 /* If body length cannot be determined, set htx->extra to
306 * ULLONG_MAX. This value is impossible in other cases.
307 */
308 htx->extra = ((h1m->flags & H1_MF_XFER_LEN) ? h1m->curr_len : ULLONG_MAX);
309
310 end:
311 return 1;
Christopher Faulet58f21da2021-09-20 07:47:27 +0200312 output_full:
313 h1m_init_res(h1m);
314 h1m->flags |= (H1_MF_NO_PHDR|H1_MF_CLEAN_CONN_HDR);
315 return -2;
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200316 error:
317 h1m->err_pos = h1m->next;
318 h1m->err_state = h1m->state;
319 htx->flags |= HTX_FL_PARSING_ERROR;
Christopher Faulet58f21da2021-09-20 07:47:27 +0200320 return -1;
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200321}
322
Christopher Faulet58f21da2021-09-20 07:47:27 +0200323/* Parse HTTP/1 headers. It returns the number of bytes parsed on success, 0 if
324 * headers are incomplete, -1 if an error occurred or -2 if it needs more space
325 * to proceed while the output buffer is not empty. Parsing errors are reported
326 * by setting the htx flag HTX_FL_PARSING_ERROR and filling h1m->err_pos and
327 * h1m->err_state fields. This functions is responsible to update the parser
328 * state <h1m> and the start-line <h1sl> if not NULL. For the requests, <h1sl>
329 * must always be provided. For responses, <h1sl> may be NULL and <h1m> flags
330 * HTTP_METH_CONNECT of HTTP_METH_HEAD may be set.
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200331 */
332int h1_parse_msg_hdrs(struct h1m *h1m, union h1_sl *h1sl, struct htx *dsthtx,
Christopher Faulet58f21da2021-09-20 07:47:27 +0200333 struct buffer *srcbuf, size_t ofs, size_t max)
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200334{
335 struct http_hdr hdrs[global.tune.max_http_hdr];
Christopher Faulet58f21da2021-09-20 07:47:27 +0200336 int total = 0, ret = 0;
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200337
338 if (!max || !b_data(srcbuf))
339 goto end;
340
341 /* Realing input buffer if necessary */
342 if (b_head(srcbuf) + b_data(srcbuf) > b_wrap(srcbuf))
343 b_slow_realign(srcbuf, trash.area, 0);
344
345 if (!h1sl) {
346 /* If there no start-line, be sure to only parse the headers */
347 h1m->flags |= H1_MF_HDRS_ONLY;
348 }
349 ret = h1_headers_to_hdr_list(b_peek(srcbuf, ofs), b_tail(srcbuf),
350 hdrs, sizeof(hdrs)/sizeof(hdrs[0]), h1m, h1sl);
351 if (ret <= 0) {
352 /* Incomplete or invalid message. If the input buffer only
353 * contains headers and is full, which is detected by it being
354 * full and the offset to be zero, it's an error because
355 * headers are too large to be handled by the parser. */
356 if (ret < 0 || (!ret && !ofs && !buf_room_for_htx_data(srcbuf)))
357 goto error;
358 goto end;
359 }
Christopher Faulet58f21da2021-09-20 07:47:27 +0200360 total = ret;
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200361
362 /* messages headers fully parsed, do some checks to prepare the body
363 * parsing.
364 */
365
366 if (!(h1m->flags & H1_MF_RESP)) {
367 if (!h1_process_req_vsn(h1m, h1sl)) {
368 h1m->err_pos = h1sl->rq.v.ptr - b_head(srcbuf);
369 h1m->err_state = h1m->state;
370 goto vsn_error;
371 }
Christopher Faulet58f21da2021-09-20 07:47:27 +0200372 ret = h1_postparse_req_hdrs(h1m, h1sl, dsthtx, hdrs, max);
373 if (ret < 0)
374 return ret;
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200375 }
376 else {
377 if (h1sl && !h1_process_res_vsn(h1m, h1sl)) {
378 h1m->err_pos = h1sl->st.v.ptr - b_head(srcbuf);
379 h1m->err_state = h1m->state;
380 goto vsn_error;
381 }
Christopher Faulet58f21da2021-09-20 07:47:27 +0200382 ret = h1_postparse_res_hdrs(h1m, h1sl, dsthtx, hdrs, max);
383 if (ret < 0)
384 return ret;
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200385 }
386
Christopher Faulet76014fd2019-12-10 11:47:22 +0100387 /* Switch messages without any payload to DONE state */
388 if (((h1m->flags & H1_MF_CLEN) && h1m->body_len == 0) ||
Christopher Fauletd1ac2b92020-12-02 19:12:22 +0100389 ((h1m->flags & (H1_MF_XFER_LEN|H1_MF_CLEN|H1_MF_CHNK)) == H1_MF_XFER_LEN)) {
Christopher Faulet76014fd2019-12-10 11:47:22 +0100390 h1m->state = H1_MSG_DONE;
Christopher Fauletd1ac2b92020-12-02 19:12:22 +0100391 dsthtx->flags |= HTX_FL_EOM;
392 }
Christopher Faulet76014fd2019-12-10 11:47:22 +0100393
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200394 end:
Christopher Faulet58f21da2021-09-20 07:47:27 +0200395 return total;
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200396 error:
397 h1m->err_pos = h1m->next;
398 h1m->err_state = h1m->state;
399 vsn_error:
400 dsthtx->flags |= HTX_FL_PARSING_ERROR;
Christopher Faulet58f21da2021-09-20 07:47:27 +0200401 return -1;
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200402
403}
404
Christopher Fauletcc3124c2019-08-12 22:42:21 +0200405/* Copy data from <srbuf> into an DATA block in <dsthtx>. If possible, a
406 * zero-copy is performed. It returns the number of bytes copied.
407 */
Christopher Fauletaf542632019-10-01 21:52:49 +0200408static int h1_copy_msg_data(struct htx **dsthtx, struct buffer *srcbuf, size_t ofs,
Christopher Fauletcc3124c2019-08-12 22:42:21 +0200409 size_t count, struct buffer *htxbuf)
410{
Christopher Fauletaf542632019-10-01 21:52:49 +0200411 struct htx *tmp_htx = *dsthtx;
412
Christopher Fauletcc3124c2019-08-12 22:42:21 +0200413 /* very often with large files we'll face the following
414 * situation :
415 * - htx is empty and points to <htxbuf>
416 * - ret == srcbuf->data
417 * - srcbuf->head == sizeof(struct htx)
418 * => we can swap the buffers and place an htx header into
419 * the target buffer instead
420 */
Christopher Fauletaf542632019-10-01 21:52:49 +0200421 if (unlikely(htx_is_empty(tmp_htx) && count == b_data(srcbuf) &&
Christopher Fauletcc3124c2019-08-12 22:42:21 +0200422 !ofs && b_head_ofs(srcbuf) == sizeof(struct htx))) {
423 void *raw_area = srcbuf->area;
424 void *htx_area = htxbuf->area;
425 struct htx_blk *blk;
426
427 srcbuf->area = htx_area;
428 htxbuf->area = raw_area;
Christopher Fauletaf542632019-10-01 21:52:49 +0200429 tmp_htx = (struct htx *)htxbuf->area;
430 tmp_htx->size = htxbuf->size - sizeof(*tmp_htx);
431 htx_reset(tmp_htx);
Christopher Fauletcc3124c2019-08-12 22:42:21 +0200432 b_set_data(htxbuf, b_size(htxbuf));
433
Christopher Fauletaf542632019-10-01 21:52:49 +0200434 blk = htx_add_blk(tmp_htx, HTX_BLK_DATA, count);
Christopher Fauletcc3124c2019-08-12 22:42:21 +0200435 blk->info += count;
Christopher Fauletaf542632019-10-01 21:52:49 +0200436
437 *dsthtx = tmp_htx;
Christopher Fauletcc3124c2019-08-12 22:42:21 +0200438 /* nothing else to do, the old buffer now contains an
439 * empty pre-initialized HTX header
440 */
441 return count;
442 }
443
Christopher Fauletaf542632019-10-01 21:52:49 +0200444 return htx_add_data(*dsthtx, ist2(b_peek(srcbuf, ofs), count));
Christopher Fauletcc3124c2019-08-12 22:42:21 +0200445}
446
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200447/* Parse HTTP/1 body. It returns the number of bytes parsed if > 0, or 0 if it
448 * couldn't proceed. Parsing errors are reported by setting the htx flags
449 * HTX_FL_PARSING_ERROR and filling h1m->err_pos and h1m->err_state fields. This
450 * functions is responsible to update the parser state <h1m>.
451 */
Christopher Fauletaf542632019-10-01 21:52:49 +0200452int h1_parse_msg_data(struct h1m *h1m, struct htx **dsthtx,
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200453 struct buffer *srcbuf, size_t ofs, size_t max,
454 struct buffer *htxbuf)
455{
456 size_t total = 0;
457 int32_t ret = 0;
458
459 if (h1m->flags & H1_MF_CLEN) {
460 /* content-length: read only h2m->body_len */
Christopher Fauletaf542632019-10-01 21:52:49 +0200461 ret = htx_get_max_blksz(*dsthtx, max);
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200462 if ((uint64_t)ret > h1m->curr_len)
463 ret = h1m->curr_len;
464 if (ret > b_contig_data(srcbuf, ofs))
465 ret = b_contig_data(srcbuf, ofs);
466 if (ret) {
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200467 int32_t try = ret;
468
Christopher Fauletcc3124c2019-08-12 22:42:21 +0200469 ret = h1_copy_msg_data(dsthtx, srcbuf, ofs, try, htxbuf);
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200470 h1m->curr_len -= ret;
471 max -= sizeof(struct htx_blk) + ret;
472 ofs += ret;
473 total += ret;
474 if (ret < try)
475 goto end;
476 }
477
Christopher Fauletd1ac2b92020-12-02 19:12:22 +0100478 if (!h1m->curr_len) {
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200479 h1m->state = H1_MSG_DONE;
Christopher Fauletd1ac2b92020-12-02 19:12:22 +0100480 (*dsthtx)->flags |= HTX_FL_EOM;
481 }
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200482 }
483 else if (h1m->flags & H1_MF_CHNK) {
484 /* te:chunked : parse chunks */
485 new_chunk:
486 if (h1m->state == H1_MSG_CHUNK_CRLF) {
487 ret = h1_skip_chunk_crlf(srcbuf, ofs, b_data(srcbuf));
488 if (ret <= 0)
489 goto end;
490 h1m->state = H1_MSG_CHUNK_SIZE;
491 ofs += ret;
492 total += ret;
493 }
494 if (h1m->state == H1_MSG_CHUNK_SIZE) {
Christopher Faulet405f0542021-01-27 15:17:13 +0100495 uint64_t chksz;
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200496
497 ret = h1_parse_chunk_size(srcbuf, ofs, b_data(srcbuf), &chksz);
498 if (ret <= 0)
499 goto end;
500 h1m->state = ((!chksz) ? H1_MSG_TRAILERS : H1_MSG_DATA);
501 h1m->curr_len = chksz;
502 h1m->body_len += chksz;
503 ofs += ret;
504 total += ret;
505 if (!h1m->curr_len)
506 goto end;
507 }
508 if (h1m->state == H1_MSG_DATA) {
Christopher Fauletaf542632019-10-01 21:52:49 +0200509 ret = htx_get_max_blksz(*dsthtx, max);
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200510 if ((uint64_t)ret > h1m->curr_len)
511 ret = h1m->curr_len;
512 if (ret > b_contig_data(srcbuf, ofs))
513 ret = b_contig_data(srcbuf, ofs);
514 if (ret) {
515 int32_t try = ret;
516
Christopher Fauletcc3124c2019-08-12 22:42:21 +0200517 ret = h1_copy_msg_data(dsthtx, srcbuf, ofs, try, htxbuf);
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200518 h1m->curr_len -= ret;
519 max -= sizeof(struct htx_blk) + ret;
520 ofs += ret;
521 total += ret;
522 if (ret < try)
523 goto end;
524 }
525 if (!h1m->curr_len) {
526 h1m->state = H1_MSG_CHUNK_CRLF;
527 goto new_chunk;
528 }
529 goto end;
530 }
531 }
532 else if (h1m->flags & H1_MF_XFER_LEN) {
533 /* XFER_LEN is set but not CLEN nor CHNK, it means there is no
534 * body. Switch the message in DONE state
535 */
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200536 h1m->state = H1_MSG_DONE;
Christopher Fauletd1ac2b92020-12-02 19:12:22 +0100537 (*dsthtx)->flags |= HTX_FL_EOM;
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200538 }
539 else {
540 /* no content length, read till SHUTW */
Christopher Fauletaf542632019-10-01 21:52:49 +0200541 ret = htx_get_max_blksz(*dsthtx, max);
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200542 if (ret > b_contig_data(srcbuf, ofs))
543 ret = b_contig_data(srcbuf, ofs);
544 if (ret)
Christopher Fauletcc3124c2019-08-12 22:42:21 +0200545 total += h1_copy_msg_data(dsthtx, srcbuf, ofs, ret, htxbuf);
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200546 }
547
548 end:
549 if (ret < 0) {
Christopher Fauletaf542632019-10-01 21:52:49 +0200550 (*dsthtx)->flags |= HTX_FL_PARSING_ERROR;
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200551 h1m->err_state = h1m->state;
552 h1m->err_pos = ofs;
553 total = 0;
554 }
555
556 /* update htx->extra, only when the body length is known */
557 if (h1m->flags & H1_MF_XFER_LEN)
Christopher Fauletaf542632019-10-01 21:52:49 +0200558 (*dsthtx)->extra = h1m->curr_len;
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200559 return total;
560}
561
Christopher Faulet58f21da2021-09-20 07:47:27 +0200562/* Parse HTTP/1 trailers. It returns the number of bytes parsed on success, 0 if
563 * trailers are incomplete, -1 if an error occurred or -2 if it needs more space
564 * to proceed while the output buffer is not empty. Parsing errors are reported
565 * by setting the htx flags HTX_FL_PARSING_ERROR and filling h1m->err_pos and
566 * h1m->err_state fields. This functions is responsible to update the parser
567 * state <h1m>.
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200568 */
569int h1_parse_msg_tlrs(struct h1m *h1m, struct htx *dsthtx,
570 struct buffer *srcbuf, size_t ofs, size_t max)
571{
572 struct http_hdr hdrs[global.tune.max_http_hdr];
573 struct h1m tlr_h1m;
574 int ret = 0;
575
Christopher Faulet5d0efb72022-04-13 17:48:54 +0200576 if (b_data(srcbuf) == ofs) {
577 /* Nothing to parse */
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200578 goto end;
Christopher Faulet5d0efb72022-04-13 17:48:54 +0200579 }
580 if (!max) {
581 /* No more room */
582 goto output_full;
583 }
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200584
585 /* Realing input buffer if necessary */
586 if (b_peek(srcbuf, ofs) > b_tail(srcbuf))
587 b_slow_realign(srcbuf, trash.area, 0);
588
589 tlr_h1m.flags = (H1_MF_NO_PHDR|H1_MF_HDRS_ONLY);
590 ret = h1_headers_to_hdr_list(b_peek(srcbuf, ofs), b_tail(srcbuf),
591 hdrs, sizeof(hdrs)/sizeof(hdrs[0]), &tlr_h1m, NULL);
592 if (ret <= 0) {
593 /* Incomplete or invalid trailers. If the input buffer only
594 * contains trailers and is full, which is detected by it being
595 * full and the offset to be zero, it's an error because
596 * trailers are too large to be handled by the parser. */
597 if (ret < 0 || (!ret && !ofs && !buf_room_for_htx_data(srcbuf)))
598 goto error;
599 goto end;
600 }
601
602 /* messages trailers fully parsed. */
603 if (h1_eval_htx_hdrs_size(hdrs) > max) {
604 if (htx_is_empty(dsthtx))
605 goto error;
Christopher Faulet58f21da2021-09-20 07:47:27 +0200606 goto output_full;
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200607 }
608
609 if (!htx_add_all_trailers(dsthtx, hdrs))
610 goto error;
611
Christopher Faulet76014fd2019-12-10 11:47:22 +0100612 h1m->state = H1_MSG_DONE;
Christopher Fauletd1ac2b92020-12-02 19:12:22 +0100613 dsthtx->flags |= HTX_FL_EOM;
Christopher Faulet76014fd2019-12-10 11:47:22 +0100614
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200615 end:
616 return ret;
Christopher Faulet58f21da2021-09-20 07:47:27 +0200617 output_full:
618 return -2;
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200619 error:
620 h1m->err_state = h1m->state;
621 h1m->err_pos = h1m->next;
622 dsthtx->flags |= HTX_FL_PARSING_ERROR;
Christopher Faulet58f21da2021-09-20 07:47:27 +0200623 return -1;
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200624}
625
Christopher Faulet53a899b2019-10-08 16:38:42 +0200626/* Appends the H1 representation of the request line <sl> to the chunk <chk>. It
627 * returns 1 if data are successfully appended, otherwise it returns 0.
628 */
629int h1_format_htx_reqline(const struct htx_sl *sl, struct buffer *chk)
630{
631 struct ist uri;
632 size_t sz = chk->data;
633
Christopher Fauletfb38c912021-04-26 09:38:55 +0200634 uri = h1_get_uri(sl);
Christopher Faulet53a899b2019-10-08 16:38:42 +0200635 if (!chunk_memcat(chk, HTX_SL_REQ_MPTR(sl), HTX_SL_REQ_MLEN(sl)) ||
636 !chunk_memcat(chk, " ", 1) ||
637 !chunk_memcat(chk, uri.ptr, uri.len) ||
638 !chunk_memcat(chk, " ", 1))
639 goto full;
640
641 if (sl->flags & HTX_SL_F_VER_11) {
642 if (!chunk_memcat(chk, "HTTP/1.1", 8))
643 goto full;
644 }
645 else {
646 if (!chunk_memcat(chk, HTX_SL_REQ_VPTR(sl), HTX_SL_REQ_VLEN(sl)))
647 goto full;
648 }
649
650 if (!chunk_memcat(chk, "\r\n", 2))
651 goto full;
652
653 return 1;
654
655 full:
656 chk->data = sz;
657 return 0;
658}
659
660/* Appends the H1 representation of the status line <sl> to the chunk <chk>. It
661 * returns 1 if data are successfully appended, otherwise it returns 0.
662 */
663int h1_format_htx_stline(const struct htx_sl *sl, struct buffer *chk)
664{
665 size_t sz = chk->data;
666
667 if (HTX_SL_LEN(sl) + 4 > b_room(chk))
668 return 0;
669
670 if (sl->flags & HTX_SL_F_VER_11) {
671 if (!chunk_memcat(chk, "HTTP/1.1", 8))
672 goto full;
673 }
674 else {
675 if (!chunk_memcat(chk, HTX_SL_RES_VPTR(sl), HTX_SL_RES_VLEN(sl)))
676 goto full;
677 }
678 if (!chunk_memcat(chk, " ", 1) ||
679 !chunk_memcat(chk, HTX_SL_RES_CPTR(sl), HTX_SL_RES_CLEN(sl)) ||
680 !chunk_memcat(chk, " ", 1) ||
681 !chunk_memcat(chk, HTX_SL_RES_RPTR(sl), HTX_SL_RES_RLEN(sl)) ||
682 !chunk_memcat(chk, "\r\n", 2))
683 goto full;
684
685 return 1;
686
687 full:
688 chk->data = sz;
689 return 0;
690}
691
Ilya Shipitsin47d17182020-06-21 21:42:57 +0500692/* Appends the H1 representation of the header <n> with the value <v> to the
Christopher Faulet53a899b2019-10-08 16:38:42 +0200693 * chunk <chk>. It returns 1 if data are successfully appended, otherwise it
694 * returns 0.
695 */
696int h1_format_htx_hdr(const struct ist n, const struct ist v, struct buffer *chk)
697{
698 size_t sz = chk->data;
699
700 if (n.len + v.len + 4 > b_room(chk))
701 return 0;
702
703 if (!chunk_memcat(chk, n.ptr, n.len) ||
704 !chunk_memcat(chk, ": ", 2) ||
705 !chunk_memcat(chk, v.ptr, v.len) ||
706 !chunk_memcat(chk, "\r\n", 2))
707 goto full;
708
709 return 1;
710
711 full:
712 chk->data = sz;
713 return 0;
714}
715
716/* Appends the H1 representation of the data <data> to the chunk <chk>. If
717 * <chunked> is non-zero, it emits HTTP/1 chunk-encoded data. It returns 1 if
718 * data are successfully appended, otherwise it returns 0.
719 */
720int h1_format_htx_data(const struct ist data, struct buffer *chk, int chunked)
721{
722 size_t sz = chk->data;
723
724 if (chunked) {
725 uint32_t chksz;
726 char tmp[10];
727 char *beg, *end;
728
729 chksz = data.len;
730
731 beg = end = tmp+10;
732 *--beg = '\n';
733 *--beg = '\r';
734 do {
735 *--beg = hextab[chksz & 0xF];
736 } while (chksz >>= 4);
737
738 if (!chunk_memcat(chk, beg, end - beg) ||
739 !chunk_memcat(chk, data.ptr, data.len) ||
740 !chunk_memcat(chk, "\r\n", 2))
741 goto full;
742 }
743 else {
744 if (!chunk_memcat(chk, data.ptr, data.len))
745 return 0;
746 }
747
748 return 1;
749
750 full:
751 chk->data = sz;
752 return 0;
753}
754
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200755/*
756 * Local variables:
757 * c-indent-level: 8
758 * c-basic-offset: 8
759 * End:
760 */