blob: 30e2b229e833a48100e8a1adfb599416f4a5eb14 [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>
Willy Tarreau16f958c2020-06-03 08:44:35 +020019#include <haproxy/htx.h>
Willy Tarreau36979d92020-06-05 17:27:29 +020020#include <haproxy/tools.h>
Christopher Faulet4f0f88a2019-08-10 11:17:44 +020021
Christopher Faulet4f0f88a2019-08-10 11:17:44 +020022/* Estimate the size of the HTX headers after the parsing, including the EOH. */
23static size_t h1_eval_htx_hdrs_size(const struct http_hdr *hdrs)
24{
25 size_t sz = 0;
26 int i;
27
28 for (i = 0; hdrs[i].n.len; i++)
29 sz += sizeof(struct htx_blk) + hdrs[i].n.len + hdrs[i].v.len;
30 sz += sizeof(struct htx_blk) + 1;
31 return sz;
32}
33
34/* Estimate the size of the HTX request after the parsing. */
35static size_t h1_eval_htx_size(const struct ist p1, const struct ist p2, const struct ist p3,
36 const struct http_hdr *hdrs)
37{
38 size_t sz;
39
40 /* size of the HTX start-line */
41 sz = sizeof(struct htx_blk) + sizeof(struct htx_sl) + p1.len + p2.len + p3.len;
42 sz += h1_eval_htx_hdrs_size(hdrs);
43 return sz;
44}
45
46/* Switch the message to tunnel mode. On the request, it must only be called for
47 * a CONNECT method. On the response, this function must only be called on
Ilya Shipitsin47d17182020-06-21 21:42:57 +050048 * successful replies to CONNECT requests or on protocol switching.
Christopher Faulet4f0f88a2019-08-10 11:17:44 +020049 */
50static void h1_set_tunnel_mode(struct h1m *h1m)
51{
52 h1m->flags &= ~(H1_MF_XFER_LEN|H1_MF_CLEN|H1_MF_CHNK);
53 h1m->state = H1_MSG_TUNNEL;
54}
55
56/* Check the validity of the request version. If the version is valid, it
57 * returns 1. Otherwise, it returns 0.
58 */
59static int h1_process_req_vsn(struct h1m *h1m, union h1_sl *sl)
60{
61 /* RFC7230#2.6 has enforced the format of the HTTP version string to be
62 * exactly one digit "." one digit. This check may be disabled using
63 * option accept-invalid-http-request.
64 */
65 if (h1m->err_pos == -2) { /* PR_O2_REQBUG_OK not set */
66 if (sl->rq.v.len != 8)
67 return 0;
68
69 if (*(sl->rq.v.ptr + 4) != '/' ||
70 !isdigit((unsigned char)*(sl->rq.v.ptr + 5)) ||
71 *(sl->rq.v.ptr + 6) != '.' ||
72 !isdigit((unsigned char)*(sl->rq.v.ptr + 7)))
73 return 0;
74 }
75 else if (!sl->rq.v.len) {
76 /* try to convert HTTP/0.9 requests to HTTP/1.0 */
77
78 /* RFC 1945 allows only GET for HTTP/0.9 requests */
79 if (sl->rq.meth != HTTP_METH_GET)
80 return 0;
81
82 /* HTTP/0.9 requests *must* have a request URI, per RFC 1945 */
83 if (!sl->rq.u.len)
84 return 0;
85
86 /* Add HTTP version */
87 sl->rq.v = ist("HTTP/1.0");
88 return 1;
89 }
90
91 if ((sl->rq.v.len == 8) &&
92 ((*(sl->rq.v.ptr + 5) > '1') ||
93 ((*(sl->rq.v.ptr + 5) == '1') && (*(sl->rq.v.ptr + 7) >= '1'))))
94 h1m->flags |= H1_MF_VER_11;
95 return 1;
96}
97
98/* Check the validity of the response version. If the version is valid, it
99 * returns 1. Otherwise, it returns 0.
100 */
101static int h1_process_res_vsn(struct h1m *h1m, union h1_sl *sl)
102{
103 /* RFC7230#2.6 has enforced the format of the HTTP version string to be
104 * exactly one digit "." one digit. This check may be disabled using
105 * option accept-invalid-http-request.
106 */
107 if (h1m->err_pos == -2) { /* PR_O2_REQBUG_OK not set */
108 if (sl->st.v.len != 8)
109 return 0;
110
111 if (*(sl->st.v.ptr + 4) != '/' ||
112 !isdigit((unsigned char)*(sl->st.v.ptr + 5)) ||
113 *(sl->st.v.ptr + 6) != '.' ||
114 !isdigit((unsigned char)*(sl->st.v.ptr + 7)))
115 return 0;
116 }
117
118 if ((sl->st.v.len == 8) &&
119 ((*(sl->st.v.ptr + 5) > '1') ||
120 ((*(sl->st.v.ptr + 5) == '1') && (*(sl->st.v.ptr + 7) >= '1'))))
121 h1m->flags |= H1_MF_VER_11;
122
123 return 1;
124}
125
126/* Convert H1M flags to HTX start-line flags. */
127static unsigned int h1m_htx_sl_flags(struct h1m *h1m)
128{
129 unsigned int flags = HTX_SL_F_NONE;
130
131 if (h1m->flags & H1_MF_RESP)
132 flags |= HTX_SL_F_IS_RESP;
133 if (h1m->flags & H1_MF_VER_11)
134 flags |= HTX_SL_F_VER_11;
135 if (h1m->flags & H1_MF_XFER_ENC)
136 flags |= HTX_SL_F_XFER_ENC;
137 if (h1m->flags & H1_MF_XFER_LEN) {
138 flags |= HTX_SL_F_XFER_LEN;
139 if (h1m->flags & H1_MF_CHNK)
140 flags |= HTX_SL_F_CHNK;
141 else if (h1m->flags & H1_MF_CLEN) {
142 flags |= HTX_SL_F_CLEN;
143 if (h1m->body_len == 0)
144 flags |= HTX_SL_F_BODYLESS;
145 }
146 else
147 flags |= HTX_SL_F_BODYLESS;
148 }
Christopher Fauletc31bc722020-11-20 14:30:38 +0100149 if (h1m->state == H1_MSG_TUNNEL)
150 flags |= HTX_SL_F_BODYLESS;
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200151 return flags;
152}
153
154/* Postprocess the parsed headers for a request and convert them into an htx
155 * message. It returns the number of bytes parsed if > 0, or 0 if it couldn't
156 * proceed. Parsing errors are reported by setting the htx flag
157 * HTX_FL_PARSING_ERROR and filling h1m->err_pos and h1m->err_state fields.
158 */
159static int h1_postparse_req_hdrs(struct h1m *h1m, union h1_sl *h1sl, struct htx *htx,
160 struct http_hdr *hdrs, size_t max)
161{
162 struct htx_sl *sl;
163 struct ist meth, uri, vsn;
164 unsigned int flags;
165 size_t used;
166
167 /* <h1sl> is always defined for a request */
168 meth = h1sl->rq.m;
169 uri = h1sl->rq.u;
170 vsn = h1sl->rq.v;
171
172 /* Be sure the message, once converted into HTX, will not exceed the max
173 * size allowed.
174 */
175 if (h1_eval_htx_size(meth, uri, vsn, hdrs) > max) {
176 if (htx_is_empty(htx))
177 goto error;
178 h1m_init_res(h1m);
179 h1m->flags |= (H1_MF_NO_PHDR|H1_MF_CLEAN_CONN_HDR);
180 return 0;
181 }
182
183 /* By default, request have always a known length */
184 h1m->flags |= H1_MF_XFER_LEN;
185
186 if (h1sl->rq.meth == HTTP_METH_CONNECT) {
187 /* Switch CONNECT requests to tunnel mode */
188 h1_set_tunnel_mode(h1m);
189 }
190
191 used = htx_used_space(htx);
192 flags = h1m_htx_sl_flags(h1m);
193 sl = htx_add_stline(htx, HTX_BLK_REQ_SL, flags, meth, uri, vsn);
194 if (!sl || !htx_add_all_headers(htx, hdrs))
195 goto error;
196 sl->info.req.meth = h1sl->rq.meth;
197
Christopher Fauletfe451fb2019-10-08 15:01:34 +0200198 /* Check if the uri contains an authority. Also check if it contains an
199 * explicit scheme and if it is "http" or "https". */
200 if (h1sl->rq.meth == HTTP_METH_CONNECT)
201 sl->flags |= HTX_SL_F_HAS_AUTHORITY;
202 else if (uri.len && uri.ptr[0] != '/' && uri.ptr[0] != '*') {
203 sl->flags |= (HTX_SL_F_HAS_AUTHORITY|HTX_SL_F_HAS_SCHM);
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200204 if (uri.len > 4 && (uri.ptr[0] | 0x20) == 'h')
205 sl->flags |= ((uri.ptr[4] == ':') ? HTX_SL_F_SCHM_HTTP : HTX_SL_F_SCHM_HTTPS);
206 }
Ilya Shipitsin47d17182020-06-21 21:42:57 +0500207 /* Set bytes used in the HTX message for the headers now */
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200208 sl->hdrs_bytes = htx_used_space(htx) - used;
209
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;
217 error:
218 h1m->err_pos = h1m->next;
219 h1m->err_state = h1m->state;
220 htx->flags |= HTX_FL_PARSING_ERROR;
221 return 0;
222}
223
224/* Postprocess the parsed headers for a response and convert them into an htx
225 * message. It returns the number of bytes parsed if > 0, or 0 if it couldn't
226 * proceed. Parsing errors are reported by setting the htx flag
227 * HTX_FL_PARSING_ERROR and filling h1m->err_pos and h1m->err_state fields.
228 */
229static int h1_postparse_res_hdrs(struct h1m *h1m, union h1_sl *h1sl, struct htx *htx,
230 struct http_hdr *hdrs, size_t max)
231{
232 struct htx_sl *sl;
233 struct ist vsn, status, reason;
234 unsigned int flags;
235 size_t used;
236 uint16_t code = 0;
237
238 if (h1sl) {
239 /* For HTTP responses, the start-line was parsed */
240 code = h1sl->st.status;
241 vsn = h1sl->st.v;
242 status = h1sl->st.c;
243 reason = h1sl->st.r;
244 }
245 else {
246 /* For FCGI responses, there is no start(-line but the "Status"
247 * header must be parsed, if found.
248 */
249 int hdr;
250
251 vsn = ((h1m->flags & H1_MF_VER_11) ? ist("HTTP/1.1") : ist("HTTP/1.0"));
252 for (hdr = 0; hdrs[hdr].n.len; hdr++) {
253 if (isteqi(hdrs[hdr].n, ist("status"))) {
254 code = http_parse_status_val(hdrs[hdr].v, &status, &reason);
255 }
256 else if (isteqi(hdrs[hdr].n, ist("location"))) {
257 code = 302;
258 status = ist("302");
259 reason = ist("Moved Temporarily");
260 }
261 }
262 if (!code) {
263 code = 200;
264 status = ist("200");
265 reason = ist("OK");
266 }
267 /* FIXME: Check the codes 1xx ? */
268 }
269
270 /* Be sure the message, once converted into HTX, will not exceed the max
271 * size allowed.
272 */
273 if (h1_eval_htx_size(vsn, status, reason, hdrs) > max) {
274 if (htx_is_empty(htx))
275 goto error;
276 h1m_init_res(h1m);
277 h1m->flags |= (H1_MF_NO_PHDR|H1_MF_CLEAN_CONN_HDR);
278 return 0;
279 }
280
Christopher Fauletc75668e2020-12-07 18:10:32 +0100281 if (((h1m->flags & H1_MF_METH_CONNECT) && code >= 200 && code < 300) || code == 101) {
Ilya Shipitsin47d17182020-06-21 21:42:57 +0500282 /* Switch successful replies to CONNECT requests and
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200283 * protocol switching to tunnel mode. */
284 h1_set_tunnel_mode(h1m);
285 }
286 else if ((h1m->flags & H1_MF_METH_HEAD) || (code >= 100 && code < 200) ||
287 (code == 204) || (code == 304)) {
288 /* Responses known to have no body. */
289 h1m->flags &= ~(H1_MF_CLEN|H1_MF_CHNK);
290 h1m->flags |= H1_MF_XFER_LEN;
291 h1m->curr_len = h1m->body_len = 0;
292 }
293 else if (h1m->flags & (H1_MF_CLEN|H1_MF_CHNK)) {
294 /* Responses with a known body length. */
295 h1m->flags |= H1_MF_XFER_LEN;
296 }
297 else {
298 /* Responses with an unknown body length */
299 h1m->state = H1_MSG_TUNNEL;
300 }
301
302 used = htx_used_space(htx);
303 flags = h1m_htx_sl_flags(h1m);
304 sl = htx_add_stline(htx, HTX_BLK_RES_SL, flags, vsn, status, reason);
305 if (!sl || !htx_add_all_headers(htx, hdrs))
306 goto error;
307 sl->info.res.status = code;
308
Ilya Shipitsin47d17182020-06-21 21:42:57 +0500309 /* Set bytes used in the HTX message for the headers now */
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200310 sl->hdrs_bytes = htx_used_space(htx) - used;
311
312 /* If body length cannot be determined, set htx->extra to
313 * ULLONG_MAX. This value is impossible in other cases.
314 */
315 htx->extra = ((h1m->flags & H1_MF_XFER_LEN) ? h1m->curr_len : ULLONG_MAX);
316
317 end:
318 return 1;
319 error:
320 h1m->err_pos = h1m->next;
321 h1m->err_state = h1m->state;
322 htx->flags |= HTX_FL_PARSING_ERROR;
323 return 0;
324}
325
326/* Parse HTTP/1 headers. It returns the number of bytes parsed if > 0, or 0 if
327 * it couldn't proceed. Parsing errors are reported by setting the htx flag
328 * HTX_FL_PARSING_ERROR and filling h1m->err_pos and h1m->err_state fields. This
329 * functions is responsible to update the parser state <h1m> and the start-line
330 * <h1sl> if not NULL.
331 * For the requests, <h1sl> must always be provided. For responses, <h1sl> may
332 * be NULL and <h1m> flags HTTP_METH_CONNECT of HTTP_METH_HEAD may be set.
333 */
334int h1_parse_msg_hdrs(struct h1m *h1m, union h1_sl *h1sl, struct htx *dsthtx,
335 struct buffer *srcbuf, size_t ofs, size_t max)
336{
337 struct http_hdr hdrs[global.tune.max_http_hdr];
338 int ret = 0;
339
340 if (!max || !b_data(srcbuf))
341 goto end;
342
343 /* Realing input buffer if necessary */
344 if (b_head(srcbuf) + b_data(srcbuf) > b_wrap(srcbuf))
345 b_slow_realign(srcbuf, trash.area, 0);
346
347 if (!h1sl) {
348 /* If there no start-line, be sure to only parse the headers */
349 h1m->flags |= H1_MF_HDRS_ONLY;
350 }
351 ret = h1_headers_to_hdr_list(b_peek(srcbuf, ofs), b_tail(srcbuf),
352 hdrs, sizeof(hdrs)/sizeof(hdrs[0]), h1m, h1sl);
353 if (ret <= 0) {
354 /* Incomplete or invalid message. If the input buffer only
355 * contains headers and is full, which is detected by it being
356 * full and the offset to be zero, it's an error because
357 * headers are too large to be handled by the parser. */
358 if (ret < 0 || (!ret && !ofs && !buf_room_for_htx_data(srcbuf)))
359 goto error;
360 goto end;
361 }
362
363 /* messages headers fully parsed, do some checks to prepare the body
364 * parsing.
365 */
366
367 if (!(h1m->flags & H1_MF_RESP)) {
368 if (!h1_process_req_vsn(h1m, h1sl)) {
369 h1m->err_pos = h1sl->rq.v.ptr - b_head(srcbuf);
370 h1m->err_state = h1m->state;
371 goto vsn_error;
372 }
373 if (!h1_postparse_req_hdrs(h1m, h1sl, dsthtx, hdrs, max))
374 ret = 0;
375 }
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 }
382 if (!h1_postparse_res_hdrs(h1m, h1sl, dsthtx, hdrs, max))
383 ret = 0;
384 }
385
Christopher Faulet76014fd2019-12-10 11:47:22 +0100386 /* Switch messages without any payload to DONE state */
387 if (((h1m->flags & H1_MF_CLEN) && h1m->body_len == 0) ||
388 ((h1m->flags & (H1_MF_XFER_LEN|H1_MF_CLEN|H1_MF_CHNK)) == H1_MF_XFER_LEN))
389 h1m->state = H1_MSG_DONE;
390
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200391 end:
392 return ret;
393 error:
394 h1m->err_pos = h1m->next;
395 h1m->err_state = h1m->state;
396 vsn_error:
397 dsthtx->flags |= HTX_FL_PARSING_ERROR;
398 return 0;
399
400}
401
Christopher Fauletcc3124c2019-08-12 22:42:21 +0200402/* Copy data from <srbuf> into an DATA block in <dsthtx>. If possible, a
403 * zero-copy is performed. It returns the number of bytes copied.
404 */
Christopher Fauletaf542632019-10-01 21:52:49 +0200405static int h1_copy_msg_data(struct htx **dsthtx, struct buffer *srcbuf, size_t ofs,
Christopher Fauletcc3124c2019-08-12 22:42:21 +0200406 size_t count, struct buffer *htxbuf)
407{
Christopher Fauletaf542632019-10-01 21:52:49 +0200408 struct htx *tmp_htx = *dsthtx;
409
Christopher Fauletcc3124c2019-08-12 22:42:21 +0200410 /* very often with large files we'll face the following
411 * situation :
412 * - htx is empty and points to <htxbuf>
413 * - ret == srcbuf->data
414 * - srcbuf->head == sizeof(struct htx)
415 * => we can swap the buffers and place an htx header into
416 * the target buffer instead
417 */
Christopher Fauletaf542632019-10-01 21:52:49 +0200418 if (unlikely(htx_is_empty(tmp_htx) && count == b_data(srcbuf) &&
Christopher Fauletcc3124c2019-08-12 22:42:21 +0200419 !ofs && b_head_ofs(srcbuf) == sizeof(struct htx))) {
420 void *raw_area = srcbuf->area;
421 void *htx_area = htxbuf->area;
422 struct htx_blk *blk;
423
424 srcbuf->area = htx_area;
425 htxbuf->area = raw_area;
Christopher Fauletaf542632019-10-01 21:52:49 +0200426 tmp_htx = (struct htx *)htxbuf->area;
427 tmp_htx->size = htxbuf->size - sizeof(*tmp_htx);
428 htx_reset(tmp_htx);
Christopher Fauletcc3124c2019-08-12 22:42:21 +0200429 b_set_data(htxbuf, b_size(htxbuf));
430
Christopher Fauletaf542632019-10-01 21:52:49 +0200431 blk = htx_add_blk(tmp_htx, HTX_BLK_DATA, count);
Christopher Fauletcc3124c2019-08-12 22:42:21 +0200432 blk->info += count;
Christopher Fauletaf542632019-10-01 21:52:49 +0200433
434 *dsthtx = tmp_htx;
Christopher Fauletcc3124c2019-08-12 22:42:21 +0200435 /* nothing else to do, the old buffer now contains an
436 * empty pre-initialized HTX header
437 */
438 return count;
439 }
440
Christopher Fauletaf542632019-10-01 21:52:49 +0200441 return htx_add_data(*dsthtx, ist2(b_peek(srcbuf, ofs), count));
Christopher Fauletcc3124c2019-08-12 22:42:21 +0200442}
443
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200444/* Parse HTTP/1 body. It returns the number of bytes parsed if > 0, or 0 if it
445 * couldn't proceed. Parsing errors are reported by setting the htx flags
446 * HTX_FL_PARSING_ERROR and filling h1m->err_pos and h1m->err_state fields. This
447 * functions is responsible to update the parser state <h1m>.
448 */
Christopher Fauletaf542632019-10-01 21:52:49 +0200449int h1_parse_msg_data(struct h1m *h1m, struct htx **dsthtx,
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200450 struct buffer *srcbuf, size_t ofs, size_t max,
451 struct buffer *htxbuf)
452{
453 size_t total = 0;
454 int32_t ret = 0;
455
456 if (h1m->flags & H1_MF_CLEN) {
457 /* content-length: read only h2m->body_len */
Christopher Fauletaf542632019-10-01 21:52:49 +0200458 ret = htx_get_max_blksz(*dsthtx, max);
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200459 if ((uint64_t)ret > h1m->curr_len)
460 ret = h1m->curr_len;
461 if (ret > b_contig_data(srcbuf, ofs))
462 ret = b_contig_data(srcbuf, ofs);
463 if (ret) {
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200464 int32_t try = ret;
465
Christopher Fauletcc3124c2019-08-12 22:42:21 +0200466 ret = h1_copy_msg_data(dsthtx, srcbuf, ofs, try, htxbuf);
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200467 h1m->curr_len -= ret;
468 max -= sizeof(struct htx_blk) + ret;
469 ofs += ret;
470 total += ret;
471 if (ret < try)
472 goto end;
473 }
474
Christopher Faulet76014fd2019-12-10 11:47:22 +0100475 if (!h1m->curr_len)
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200476 h1m->state = H1_MSG_DONE;
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200477 }
478 else if (h1m->flags & H1_MF_CHNK) {
479 /* te:chunked : parse chunks */
480 new_chunk:
481 if (h1m->state == H1_MSG_CHUNK_CRLF) {
482 ret = h1_skip_chunk_crlf(srcbuf, ofs, b_data(srcbuf));
483 if (ret <= 0)
484 goto end;
485 h1m->state = H1_MSG_CHUNK_SIZE;
486 ofs += ret;
487 total += ret;
488 }
489 if (h1m->state == H1_MSG_CHUNK_SIZE) {
490 unsigned int chksz;
491
492 ret = h1_parse_chunk_size(srcbuf, ofs, b_data(srcbuf), &chksz);
493 if (ret <= 0)
494 goto end;
495 h1m->state = ((!chksz) ? H1_MSG_TRAILERS : H1_MSG_DATA);
496 h1m->curr_len = chksz;
497 h1m->body_len += chksz;
498 ofs += ret;
499 total += ret;
500 if (!h1m->curr_len)
501 goto end;
502 }
503 if (h1m->state == H1_MSG_DATA) {
Christopher Fauletaf542632019-10-01 21:52:49 +0200504 ret = htx_get_max_blksz(*dsthtx, max);
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200505 if ((uint64_t)ret > h1m->curr_len)
506 ret = h1m->curr_len;
507 if (ret > b_contig_data(srcbuf, ofs))
508 ret = b_contig_data(srcbuf, ofs);
509 if (ret) {
510 int32_t try = ret;
511
Christopher Fauletcc3124c2019-08-12 22:42:21 +0200512 ret = h1_copy_msg_data(dsthtx, srcbuf, ofs, try, htxbuf);
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200513 h1m->curr_len -= ret;
514 max -= sizeof(struct htx_blk) + ret;
515 ofs += ret;
516 total += ret;
517 if (ret < try)
518 goto end;
519 }
520 if (!h1m->curr_len) {
521 h1m->state = H1_MSG_CHUNK_CRLF;
522 goto new_chunk;
523 }
524 goto end;
525 }
526 }
527 else if (h1m->flags & H1_MF_XFER_LEN) {
528 /* XFER_LEN is set but not CLEN nor CHNK, it means there is no
529 * body. Switch the message in DONE state
530 */
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200531 h1m->state = H1_MSG_DONE;
532 }
533 else {
534 /* no content length, read till SHUTW */
Christopher Fauletaf542632019-10-01 21:52:49 +0200535 ret = htx_get_max_blksz(*dsthtx, max);
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200536 if (ret > b_contig_data(srcbuf, ofs))
537 ret = b_contig_data(srcbuf, ofs);
538 if (ret)
Christopher Fauletcc3124c2019-08-12 22:42:21 +0200539 total += h1_copy_msg_data(dsthtx, srcbuf, ofs, ret, htxbuf);
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200540 }
541
542 end:
543 if (ret < 0) {
Christopher Fauletaf542632019-10-01 21:52:49 +0200544 (*dsthtx)->flags |= HTX_FL_PARSING_ERROR;
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200545 h1m->err_state = h1m->state;
546 h1m->err_pos = ofs;
547 total = 0;
548 }
549
550 /* update htx->extra, only when the body length is known */
551 if (h1m->flags & H1_MF_XFER_LEN)
Christopher Fauletaf542632019-10-01 21:52:49 +0200552 (*dsthtx)->extra = h1m->curr_len;
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200553 return total;
554}
555
556/* Parse HTTP/1 trailers. It returns the number of bytes parsed if > 0, or 0 if
557 * it couldn't proceed. Parsing errors are reported by setting the htx flags
558 * HTX_FL_PARSING_ERROR and filling h1m->err_pos and h1m->err_state fields. This
559 * functions is responsible to update the parser state <h1m>.
560 */
561int h1_parse_msg_tlrs(struct h1m *h1m, struct htx *dsthtx,
562 struct buffer *srcbuf, size_t ofs, size_t max)
563{
564 struct http_hdr hdrs[global.tune.max_http_hdr];
565 struct h1m tlr_h1m;
566 int ret = 0;
567
568 if (!max || !b_data(srcbuf))
569 goto end;
570
571 /* Realing input buffer if necessary */
572 if (b_peek(srcbuf, ofs) > b_tail(srcbuf))
573 b_slow_realign(srcbuf, trash.area, 0);
574
575 tlr_h1m.flags = (H1_MF_NO_PHDR|H1_MF_HDRS_ONLY);
576 ret = h1_headers_to_hdr_list(b_peek(srcbuf, ofs), b_tail(srcbuf),
577 hdrs, sizeof(hdrs)/sizeof(hdrs[0]), &tlr_h1m, NULL);
578 if (ret <= 0) {
579 /* Incomplete or invalid trailers. If the input buffer only
580 * contains trailers and is full, which is detected by it being
581 * full and the offset to be zero, it's an error because
582 * trailers are too large to be handled by the parser. */
583 if (ret < 0 || (!ret && !ofs && !buf_room_for_htx_data(srcbuf)))
584 goto error;
585 goto end;
586 }
587
588 /* messages trailers fully parsed. */
589 if (h1_eval_htx_hdrs_size(hdrs) > max) {
590 if (htx_is_empty(dsthtx))
591 goto error;
592 ret = 0;
593 goto end;
594 }
595
596 if (!htx_add_all_trailers(dsthtx, hdrs))
597 goto error;
598
Christopher Faulet76014fd2019-12-10 11:47:22 +0100599 h1m->state = H1_MSG_DONE;
600
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200601 end:
602 return ret;
603 error:
604 h1m->err_state = h1m->state;
605 h1m->err_pos = h1m->next;
606 dsthtx->flags |= HTX_FL_PARSING_ERROR;
607 return 0;
608}
609
Christopher Faulet76014fd2019-12-10 11:47:22 +0100610/* Finish HTTP/1 parsing by adding the HTX EOM block. It returns 1 on success or
611 * 0 if it couldn't proceed. There is no parsing at this stage, but a parsing
612 * error is reported if the message state is not H1_MSG_DONE. */
613int h1_parse_msg_eom(struct h1m *h1m, struct htx *dsthtx, size_t max)
614{
615 if (h1m->state != H1_MSG_DONE) {
616 h1m->err_state = h1m->state;
617 h1m->err_pos = h1m->next;
618 dsthtx->flags |= HTX_FL_PARSING_ERROR;
619 return 0;
620 }
Christopher Faulet810df062020-07-22 16:20:34 +0200621
622 dsthtx->flags |= HTX_FL_EOI; /* no more data are expected. Only EOM remains to add now */
Christopher Faulet76014fd2019-12-10 11:47:22 +0100623 if (max < sizeof(struct htx_blk) + 1 || !htx_add_endof(dsthtx, HTX_BLK_EOM))
624 return 0;
625
626 return 1;
627}
628
Christopher Faulet53a899b2019-10-08 16:38:42 +0200629
630/* Appends the H1 representation of the request line <sl> to the chunk <chk>. It
631 * returns 1 if data are successfully appended, otherwise it returns 0.
632 */
633int h1_format_htx_reqline(const struct htx_sl *sl, struct buffer *chk)
634{
635 struct ist uri;
636 size_t sz = chk->data;
637
638 uri = htx_sl_req_uri(sl);
639 if (sl->flags & HTX_SL_F_NORMALIZED_URI) {
640 uri = http_get_path(uri);
641 if (unlikely(!uri.len)) {
642 if (sl->info.req.meth == HTTP_METH_OPTIONS)
643 uri = ist("*");
644 else
645 uri = ist("/");
646 }
647 }
648
649 if (!chunk_memcat(chk, HTX_SL_REQ_MPTR(sl), HTX_SL_REQ_MLEN(sl)) ||
650 !chunk_memcat(chk, " ", 1) ||
651 !chunk_memcat(chk, uri.ptr, uri.len) ||
652 !chunk_memcat(chk, " ", 1))
653 goto full;
654
655 if (sl->flags & HTX_SL_F_VER_11) {
656 if (!chunk_memcat(chk, "HTTP/1.1", 8))
657 goto full;
658 }
659 else {
660 if (!chunk_memcat(chk, HTX_SL_REQ_VPTR(sl), HTX_SL_REQ_VLEN(sl)))
661 goto full;
662 }
663
664 if (!chunk_memcat(chk, "\r\n", 2))
665 goto full;
666
667 return 1;
668
669 full:
670 chk->data = sz;
671 return 0;
672}
673
674/* Appends the H1 representation of the status line <sl> to the chunk <chk>. It
675 * returns 1 if data are successfully appended, otherwise it returns 0.
676 */
677int h1_format_htx_stline(const struct htx_sl *sl, struct buffer *chk)
678{
679 size_t sz = chk->data;
680
681 if (HTX_SL_LEN(sl) + 4 > b_room(chk))
682 return 0;
683
684 if (sl->flags & HTX_SL_F_VER_11) {
685 if (!chunk_memcat(chk, "HTTP/1.1", 8))
686 goto full;
687 }
688 else {
689 if (!chunk_memcat(chk, HTX_SL_RES_VPTR(sl), HTX_SL_RES_VLEN(sl)))
690 goto full;
691 }
692 if (!chunk_memcat(chk, " ", 1) ||
693 !chunk_memcat(chk, HTX_SL_RES_CPTR(sl), HTX_SL_RES_CLEN(sl)) ||
694 !chunk_memcat(chk, " ", 1) ||
695 !chunk_memcat(chk, HTX_SL_RES_RPTR(sl), HTX_SL_RES_RLEN(sl)) ||
696 !chunk_memcat(chk, "\r\n", 2))
697 goto full;
698
699 return 1;
700
701 full:
702 chk->data = sz;
703 return 0;
704}
705
Ilya Shipitsin47d17182020-06-21 21:42:57 +0500706/* Appends the H1 representation of the header <n> with the value <v> to the
Christopher Faulet53a899b2019-10-08 16:38:42 +0200707 * chunk <chk>. It returns 1 if data are successfully appended, otherwise it
708 * returns 0.
709 */
710int h1_format_htx_hdr(const struct ist n, const struct ist v, struct buffer *chk)
711{
712 size_t sz = chk->data;
713
714 if (n.len + v.len + 4 > b_room(chk))
715 return 0;
716
717 if (!chunk_memcat(chk, n.ptr, n.len) ||
718 !chunk_memcat(chk, ": ", 2) ||
719 !chunk_memcat(chk, v.ptr, v.len) ||
720 !chunk_memcat(chk, "\r\n", 2))
721 goto full;
722
723 return 1;
724
725 full:
726 chk->data = sz;
727 return 0;
728}
729
730/* Appends the H1 representation of the data <data> to the chunk <chk>. If
731 * <chunked> is non-zero, it emits HTTP/1 chunk-encoded data. It returns 1 if
732 * data are successfully appended, otherwise it returns 0.
733 */
734int h1_format_htx_data(const struct ist data, struct buffer *chk, int chunked)
735{
736 size_t sz = chk->data;
737
738 if (chunked) {
739 uint32_t chksz;
740 char tmp[10];
741 char *beg, *end;
742
743 chksz = data.len;
744
745 beg = end = tmp+10;
746 *--beg = '\n';
747 *--beg = '\r';
748 do {
749 *--beg = hextab[chksz & 0xF];
750 } while (chksz >>= 4);
751
752 if (!chunk_memcat(chk, beg, end - beg) ||
753 !chunk_memcat(chk, data.ptr, data.len) ||
754 !chunk_memcat(chk, "\r\n", 2))
755 goto full;
756 }
757 else {
758 if (!chunk_memcat(chk, data.ptr, data.len))
759 return 0;
760 }
761
762 return 1;
763
764 full:
765 chk->data = sz;
766 return 0;
767}
768
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200769/*
770 * Local variables:
771 * c-indent-level: 8
772 * c-basic-offset: 8
773 * End:
774 */