1 /*
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
13 #include <haproxy/api.h>
14 #include <haproxy/cfgparse.h>
15 #include <haproxy/global.h>
16 #include <haproxy/h1.h>
17 #include <haproxy/h1_htx.h>
18 #include <haproxy/http.h>
19 #include <haproxy/htx.h>
20 #include <haproxy/tools.h>
21
22 /* Estimate the size of the HTX headers after the parsing, including the EOH. */
h1_eval_htx_hdrs_size(const struct http_hdr * hdrs)23 static 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. */
h1_eval_htx_size(const struct ist p1,const struct ist p2,const struct ist p3,const struct http_hdr * hdrs)35 static 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
48 * successful replies to CONNECT requests or on protocol switching.
49 */
h1_set_tunnel_mode(struct h1m * h1m)50 static 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 */
h1_process_req_vsn(struct h1m * h1m,union h1_sl * sl)59 static 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 */
h1_process_res_vsn(struct h1m * h1m,union h1_sl * sl)101 static 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. */
h1m_htx_sl_flags(struct h1m * h1m)127 static 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 }
149 return flags;
150 }
151
152 /* Postprocess the parsed headers for a request and convert them into an htx
153 * message. It returns the number of bytes parsed if > 0, or 0 if it couldn't
154 * proceed. Parsing errors are reported by setting the htx flag
155 * HTX_FL_PARSING_ERROR and filling h1m->err_pos and h1m->err_state fields.
156 */
h1_postparse_req_hdrs(struct h1m * h1m,union h1_sl * h1sl,struct htx * htx,struct http_hdr * hdrs,size_t max)157 static int h1_postparse_req_hdrs(struct h1m *h1m, union h1_sl *h1sl, struct htx *htx,
158 struct http_hdr *hdrs, size_t max)
159 {
160 struct htx_sl *sl;
161 struct ist meth, uri, vsn;
162 unsigned int flags;
163 size_t used;
164
165 /* <h1sl> is always defined for a request */
166 meth = h1sl->rq.m;
167 uri = h1sl->rq.u;
168 vsn = h1sl->rq.v;
169
170 /* Be sure the message, once converted into HTX, will not exceed the max
171 * size allowed.
172 */
173 if (h1_eval_htx_size(meth, uri, vsn, hdrs) > max) {
174 if (htx_is_empty(htx))
175 goto error;
176 goto output_full;
177 }
178
179 /* By default, request have always a known length */
180 h1m->flags |= H1_MF_XFER_LEN;
181
182 if (h1sl->rq.meth == HTTP_METH_CONNECT) {
183 /* Switch CONNECT requests to tunnel mode */
184 h1_set_tunnel_mode(h1m);
185 }
186
187 used = htx_used_space(htx);
188 flags = h1m_htx_sl_flags(h1m);
189 sl = htx_add_stline(htx, HTX_BLK_REQ_SL, flags, meth, uri, vsn);
190 if (!sl || !htx_add_all_headers(htx, hdrs))
191 goto error;
192 sl->info.req.meth = h1sl->rq.meth;
193
194 /* Check if the uri contains an authority. Also check if it contains an
195 * explicit scheme and if it is "http" or "https". */
196 if (h1sl->rq.meth == HTTP_METH_CONNECT)
197 sl->flags |= HTX_SL_F_HAS_AUTHORITY;
198 else if (uri.len && uri.ptr[0] != '/' && uri.ptr[0] != '*') {
199 sl->flags |= (HTX_SL_F_HAS_AUTHORITY|HTX_SL_F_HAS_SCHM);
200 if (uri.len > 4 && (uri.ptr[0] | 0x20) == 'h')
201 sl->flags |= ((uri.ptr[4] == ':') ? HTX_SL_F_SCHM_HTTP : HTX_SL_F_SCHM_HTTPS);
202 }
203 /* Set bytes used in the HTX message for the headers now */
204 sl->hdrs_bytes = htx_used_space(htx) - used;
205
206 /* If body length cannot be determined, set htx->extra to
207 * ULLONG_MAX. This value is impossible in other cases.
208 */
209 htx->extra = ((h1m->flags & H1_MF_XFER_LEN) ? h1m->curr_len : ULLONG_MAX);
210
211 end:
212 return 1;
213 output_full:
214 h1m_init_req(h1m);
215 h1m->flags |= (H1_MF_NO_PHDR|H1_MF_CLEAN_CONN_HDR);
216 return -2;
217 error:
218 h1m->err_pos = h1m->next;
219 h1m->err_state = h1m->state;
220 htx->flags |= HTX_FL_PARSING_ERROR;
221 return -1;
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 */
h1_postparse_res_hdrs(struct h1m * h1m,union h1_sl * h1sl,struct htx * htx,struct http_hdr * hdrs,size_t max)229 static 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 goto output_full;
277 }
278
279 if (((h1m->flags & H1_MF_METH_CONNECT) && code == 200) || code == 101) {
280 /* Switch successful replies to CONNECT requests and
281 * protocol switching to tunnel mode. */
282 h1_set_tunnel_mode(h1m);
283 }
284 else if ((h1m->flags & H1_MF_METH_HEAD) || (code >= 100 && code < 200) ||
285 (code == 204) || (code == 304)) {
286 /* Responses known to have no body. */
287 h1m->flags &= ~(H1_MF_CLEN|H1_MF_CHNK);
288 h1m->flags |= H1_MF_XFER_LEN;
289 h1m->curr_len = h1m->body_len = 0;
290 }
291 else if (h1m->flags & (H1_MF_CLEN|H1_MF_CHNK)) {
292 /* Responses with a known body length. */
293 h1m->flags |= H1_MF_XFER_LEN;
294 }
295 else {
296 /* Responses with an unknown body length */
297 h1m->state = H1_MSG_TUNNEL;
298 }
299
300 used = htx_used_space(htx);
301 flags = h1m_htx_sl_flags(h1m);
302 sl = htx_add_stline(htx, HTX_BLK_RES_SL, flags, vsn, status, reason);
303 if (!sl || !htx_add_all_headers(htx, hdrs))
304 goto error;
305 sl->info.res.status = code;
306
307 /* Set bytes used in the HTX message for the headers now */
308 sl->hdrs_bytes = htx_used_space(htx) - used;
309
310 /* If body length cannot be determined, set htx->extra to
311 * ULLONG_MAX. This value is impossible in other cases.
312 */
313 htx->extra = ((h1m->flags & H1_MF_XFER_LEN) ? h1m->curr_len : ULLONG_MAX);
314
315 end:
316 return 1;
317 output_full:
318 h1m_init_res(h1m);
319 h1m->flags |= (H1_MF_NO_PHDR|H1_MF_CLEAN_CONN_HDR);
320 return -2;
321 error:
322 h1m->err_pos = h1m->next;
323 h1m->err_state = h1m->state;
324 htx->flags |= HTX_FL_PARSING_ERROR;
325 return -1;
326 }
327
328 /* Parse HTTP/1 headers. It returns the number of bytes parsed on success, 0 if
329 * headers are incomplete, -1 if an error occurred or -2 if it needs more space
330 * to proceed while the output buffer is not empty. Parsing errors are reported
331 * by setting the htx flag HTX_FL_PARSING_ERROR and filling h1m->err_pos and
332 * h1m->err_state fields. This functions is responsible to update the parser
333 * state <h1m> and the start-line <h1sl> if not NULL. For the requests, <h1sl>
334 * must always be provided. For responses, <h1sl> may be NULL and <h1m> flags
335 * HTTP_METH_CONNECT of HTTP_METH_HEAD may be set.
336 */
h1_parse_msg_hdrs(struct h1m * h1m,union h1_sl * h1sl,struct htx * dsthtx,struct buffer * srcbuf,size_t ofs,size_t max)337 int h1_parse_msg_hdrs(struct h1m *h1m, union h1_sl *h1sl, struct htx *dsthtx,
338 struct buffer *srcbuf, size_t ofs, size_t max)
339 {
340 struct http_hdr hdrs[global.tune.max_http_hdr];
341 int total = 0, ret = 0;
342
343 if (!max || !b_data(srcbuf))
344 goto end;
345
346 /* Realing input buffer if necessary */
347 if (b_head(srcbuf) + b_data(srcbuf) > b_wrap(srcbuf))
348 b_slow_realign(srcbuf, trash.area, 0);
349
350 if (!h1sl) {
351 /* If there no start-line, be sure to only parse the headers */
352 h1m->flags |= H1_MF_HDRS_ONLY;
353 }
354 ret = h1_headers_to_hdr_list(b_peek(srcbuf, ofs), b_tail(srcbuf),
355 hdrs, sizeof(hdrs)/sizeof(hdrs[0]), h1m, h1sl);
356 if (ret <= 0) {
357 /* Incomplete or invalid message. If the input buffer only
358 * contains headers and is full, which is detected by it being
359 * full and the offset to be zero, it's an error because
360 * headers are too large to be handled by the parser. */
361 if (ret < 0 || (!ret && !ofs && !buf_room_for_htx_data(srcbuf)))
362 goto error;
363 goto end;
364 }
365 total = ret;
366
367 /* messages headers fully parsed, do some checks to prepare the body
368 * parsing.
369 */
370
371 if (!(h1m->flags & H1_MF_RESP)) {
372 if (!h1_process_req_vsn(h1m, h1sl)) {
373 h1m->err_pos = h1sl->rq.v.ptr - b_head(srcbuf);
374 h1m->err_state = h1m->state;
375 goto vsn_error;
376 }
377 ret = h1_postparse_req_hdrs(h1m, h1sl, dsthtx, hdrs, max);
378 if (ret < 0)
379 return ret;
380 }
381 else {
382 if (h1sl && !h1_process_res_vsn(h1m, h1sl)) {
383 h1m->err_pos = h1sl->st.v.ptr - b_head(srcbuf);
384 h1m->err_state = h1m->state;
385 goto vsn_error;
386 }
387 ret = h1_postparse_res_hdrs(h1m, h1sl, dsthtx, hdrs, max);
388 if (ret < 0)
389 return ret;
390 }
391
392 /* Switch messages without any payload to DONE state */
393 if (((h1m->flags & H1_MF_CLEN) && h1m->body_len == 0) ||
394 ((h1m->flags & (H1_MF_XFER_LEN|H1_MF_CLEN|H1_MF_CHNK)) == H1_MF_XFER_LEN))
395 h1m->state = H1_MSG_DONE;
396
397 end:
398 return total;
399 error:
400 h1m->err_pos = h1m->next;
401 h1m->err_state = h1m->state;
402 vsn_error:
403 dsthtx->flags |= HTX_FL_PARSING_ERROR;
404 return -1;
405
406 }
407
408 /* Copy data from <srbuf> into an DATA block in <dsthtx>. If possible, a
409 * zero-copy is performed. It returns the number of bytes copied.
410 */
h1_copy_msg_data(struct htx ** dsthtx,struct buffer * srcbuf,size_t ofs,size_t count,struct buffer * htxbuf)411 static int h1_copy_msg_data(struct htx **dsthtx, struct buffer *srcbuf, size_t ofs,
412 size_t count, struct buffer *htxbuf)
413 {
414 struct htx *tmp_htx = *dsthtx;
415
416 /* very often with large files we'll face the following
417 * situation :
418 * - htx is empty and points to <htxbuf>
419 * - ret == srcbuf->data
420 * - srcbuf->head == sizeof(struct htx)
421 * => we can swap the buffers and place an htx header into
422 * the target buffer instead
423 */
424 if (unlikely(htx_is_empty(tmp_htx) && count == b_data(srcbuf) &&
425 !ofs && b_head_ofs(srcbuf) == sizeof(struct htx))) {
426 void *raw_area = srcbuf->area;
427 void *htx_area = htxbuf->area;
428 struct htx_blk *blk;
429
430 srcbuf->area = htx_area;
431 htxbuf->area = raw_area;
432 tmp_htx = (struct htx *)htxbuf->area;
433 tmp_htx->size = htxbuf->size - sizeof(*tmp_htx);
434 htx_reset(tmp_htx);
435 b_set_data(htxbuf, b_size(htxbuf));
436
437 blk = htx_add_blk(tmp_htx, HTX_BLK_DATA, count);
438 blk->info += count;
439
440 *dsthtx = tmp_htx;
441 /* nothing else to do, the old buffer now contains an
442 * empty pre-initialized HTX header
443 */
444 return count;
445 }
446
447 return htx_add_data(*dsthtx, ist2(b_peek(srcbuf, ofs), count));
448 }
449
450 /* Parse HTTP/1 body. It returns the number of bytes parsed if > 0, or 0 if it
451 * couldn't proceed. Parsing errors are reported by setting the htx flags
452 * HTX_FL_PARSING_ERROR and filling h1m->err_pos and h1m->err_state fields. This
453 * functions is responsible to update the parser state <h1m>.
454 */
h1_parse_msg_data(struct h1m * h1m,struct htx ** dsthtx,struct buffer * srcbuf,size_t ofs,size_t max,struct buffer * htxbuf)455 int h1_parse_msg_data(struct h1m *h1m, struct htx **dsthtx,
456 struct buffer *srcbuf, size_t ofs, size_t max,
457 struct buffer *htxbuf)
458 {
459 size_t total = 0;
460 int32_t ret = 0;
461
462 if (h1m->flags & H1_MF_CLEN) {
463 /* content-length: read only h2m->body_len */
464 ret = htx_get_max_blksz(*dsthtx, max);
465 if ((uint64_t)ret > h1m->curr_len)
466 ret = h1m->curr_len;
467 if (ret > b_contig_data(srcbuf, ofs))
468 ret = b_contig_data(srcbuf, ofs);
469 if (ret) {
470 int32_t try = ret;
471
472 ret = h1_copy_msg_data(dsthtx, srcbuf, ofs, try, htxbuf);
473 h1m->curr_len -= ret;
474 max -= sizeof(struct htx_blk) + ret;
475 ofs += ret;
476 total += ret;
477 if (ret < try)
478 goto end;
479 }
480
481 if (!h1m->curr_len)
482 h1m->state = H1_MSG_DONE;
483 }
484 else if (h1m->flags & H1_MF_CHNK) {
485 /* te:chunked : parse chunks */
486 new_chunk:
487 if (h1m->state == H1_MSG_CHUNK_CRLF) {
488 ret = h1_skip_chunk_crlf(srcbuf, ofs, b_data(srcbuf));
489 if (ret <= 0)
490 goto end;
491 h1m->state = H1_MSG_CHUNK_SIZE;
492 ofs += ret;
493 total += ret;
494 }
495 if (h1m->state == H1_MSG_CHUNK_SIZE) {
496 uint64_t chksz;
497
498 ret = h1_parse_chunk_size(srcbuf, ofs, b_data(srcbuf), &chksz);
499 if (ret <= 0)
500 goto end;
501 h1m->state = ((!chksz) ? H1_MSG_TRAILERS : H1_MSG_DATA);
502 h1m->curr_len = chksz;
503 h1m->body_len += chksz;
504 ofs += ret;
505 total += ret;
506 if (!h1m->curr_len)
507 goto end;
508 }
509 if (h1m->state == H1_MSG_DATA) {
510 ret = htx_get_max_blksz(*dsthtx, max);
511 if ((uint64_t)ret > h1m->curr_len)
512 ret = h1m->curr_len;
513 if (ret > b_contig_data(srcbuf, ofs))
514 ret = b_contig_data(srcbuf, ofs);
515 if (ret) {
516 int32_t try = ret;
517
518 ret = h1_copy_msg_data(dsthtx, srcbuf, ofs, try, htxbuf);
519 h1m->curr_len -= ret;
520 max -= sizeof(struct htx_blk) + ret;
521 ofs += ret;
522 total += ret;
523 if (ret < try)
524 goto end;
525 }
526 if (!h1m->curr_len) {
527 h1m->state = H1_MSG_CHUNK_CRLF;
528 goto new_chunk;
529 }
530 goto end;
531 }
532 }
533 else if (h1m->flags & H1_MF_XFER_LEN) {
534 /* XFER_LEN is set but not CLEN nor CHNK, it means there is no
535 * body. Switch the message in DONE state
536 */
537 h1m->state = H1_MSG_DONE;
538 }
539 else {
540 /* no content length, read till SHUTW */
541 ret = htx_get_max_blksz(*dsthtx, max);
542 if (ret > b_contig_data(srcbuf, ofs))
543 ret = b_contig_data(srcbuf, ofs);
544 if (ret)
545 total += h1_copy_msg_data(dsthtx, srcbuf, ofs, ret, htxbuf);
546 }
547
548 end:
549 if (ret < 0) {
550 (*dsthtx)->flags |= HTX_FL_PARSING_ERROR;
551 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)
558 (*dsthtx)->extra = h1m->curr_len;
559 return total;
560 }
561
562 /* 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>.
568 */
h1_parse_msg_tlrs(struct h1m * h1m,struct htx * dsthtx,struct buffer * srcbuf,size_t ofs,size_t max)569 int 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
576 if (!max || !b_data(srcbuf))
577 goto end;
578
579 /* Realing input buffer if necessary */
580 if (b_peek(srcbuf, ofs) > b_tail(srcbuf))
581 b_slow_realign(srcbuf, trash.area, 0);
582
583 tlr_h1m.flags = (H1_MF_NO_PHDR|H1_MF_HDRS_ONLY);
584 ret = h1_headers_to_hdr_list(b_peek(srcbuf, ofs), b_tail(srcbuf),
585 hdrs, sizeof(hdrs)/sizeof(hdrs[0]), &tlr_h1m, NULL);
586 if (ret <= 0) {
587 /* Incomplete or invalid trailers. If the input buffer only
588 * contains trailers and is full, which is detected by it being
589 * full and the offset to be zero, it's an error because
590 * trailers are too large to be handled by the parser. */
591 if (ret < 0 || (!ret && !ofs && !buf_room_for_htx_data(srcbuf)))
592 goto error;
593 goto end;
594 }
595
596 /* messages trailers fully parsed. */
597 if (h1_eval_htx_hdrs_size(hdrs) > max) {
598 if (htx_is_empty(dsthtx))
599 goto error;
600 goto output_full;
601 }
602
603 if (!htx_add_all_trailers(dsthtx, hdrs))
604 goto error;
605
606 h1m->state = H1_MSG_DONE;
607
608 end:
609 return ret;
610 output_full:
611 return -2;
612 error:
613 h1m->err_state = h1m->state;
614 h1m->err_pos = h1m->next;
615 dsthtx->flags |= HTX_FL_PARSING_ERROR;
616 return -1;
617 }
618
619 /* Finish HTTP/1 parsing by adding the HTX EOM block. It returns 1 on success or
620 * 0 if it couldn't proceed. There is no parsing at this stage, but a parsing
621 * error is reported if the message state is not H1_MSG_DONE. */
h1_parse_msg_eom(struct h1m * h1m,struct htx * dsthtx,size_t max)622 int h1_parse_msg_eom(struct h1m *h1m, struct htx *dsthtx, size_t max)
623 {
624 if (h1m->state != H1_MSG_DONE) {
625 h1m->err_state = h1m->state;
626 h1m->err_pos = h1m->next;
627 dsthtx->flags |= HTX_FL_PARSING_ERROR;
628 return 0;
629 }
630
631 dsthtx->flags |= HTX_FL_EOI; /* no more data are expected. Only EOM remains to add now */
632 if (max < sizeof(struct htx_blk) + 1 || !htx_add_endof(dsthtx, HTX_BLK_EOM))
633 return 0;
634
635 return 1;
636 }
637
638
639 /* Appends the H1 representation of the request line <sl> to the chunk <chk>. It
640 * returns 1 if data are successfully appended, otherwise it returns 0.
641 */
h1_format_htx_reqline(const struct htx_sl * sl,struct buffer * chk)642 int h1_format_htx_reqline(const struct htx_sl *sl, struct buffer *chk)
643 {
644 struct ist uri;
645 size_t sz = chk->data;
646
647 uri = h1_get_uri(sl);
648 if (!chunk_memcat(chk, HTX_SL_REQ_MPTR(sl), HTX_SL_REQ_MLEN(sl)) ||
649 !chunk_memcat(chk, " ", 1) ||
650 !chunk_memcat(chk, uri.ptr, uri.len) ||
651 !chunk_memcat(chk, " ", 1))
652 goto full;
653
654 if (sl->flags & HTX_SL_F_VER_11) {
655 if (!chunk_memcat(chk, "HTTP/1.1", 8))
656 goto full;
657 }
658 else {
659 if (!chunk_memcat(chk, HTX_SL_REQ_VPTR(sl), HTX_SL_REQ_VLEN(sl)))
660 goto full;
661 }
662
663 if (!chunk_memcat(chk, "\r\n", 2))
664 goto full;
665
666 return 1;
667
668 full:
669 chk->data = sz;
670 return 0;
671 }
672
673 /* Appends the H1 representation of the status line <sl> to the chunk <chk>. It
674 * returns 1 if data are successfully appended, otherwise it returns 0.
675 */
h1_format_htx_stline(const struct htx_sl * sl,struct buffer * chk)676 int h1_format_htx_stline(const struct htx_sl *sl, struct buffer *chk)
677 {
678 size_t sz = chk->data;
679
680 if (HTX_SL_LEN(sl) + 4 > b_room(chk))
681 return 0;
682
683 if (sl->flags & HTX_SL_F_VER_11) {
684 if (!chunk_memcat(chk, "HTTP/1.1", 8))
685 goto full;
686 }
687 else {
688 if (!chunk_memcat(chk, HTX_SL_RES_VPTR(sl), HTX_SL_RES_VLEN(sl)))
689 goto full;
690 }
691 if (!chunk_memcat(chk, " ", 1) ||
692 !chunk_memcat(chk, HTX_SL_RES_CPTR(sl), HTX_SL_RES_CLEN(sl)) ||
693 !chunk_memcat(chk, " ", 1) ||
694 !chunk_memcat(chk, HTX_SL_RES_RPTR(sl), HTX_SL_RES_RLEN(sl)) ||
695 !chunk_memcat(chk, "\r\n", 2))
696 goto full;
697
698 return 1;
699
700 full:
701 chk->data = sz;
702 return 0;
703 }
704
705 /* Appends the H1 representation of the header <n> with the value <v> to the
706 * chunk <chk>. It returns 1 if data are successfully appended, otherwise it
707 * returns 0.
708 */
h1_format_htx_hdr(const struct ist n,const struct ist v,struct buffer * chk)709 int h1_format_htx_hdr(const struct ist n, const struct ist v, struct buffer *chk)
710 {
711 size_t sz = chk->data;
712
713 if (n.len + v.len + 4 > b_room(chk))
714 return 0;
715
716 if (!chunk_memcat(chk, n.ptr, n.len) ||
717 !chunk_memcat(chk, ": ", 2) ||
718 !chunk_memcat(chk, v.ptr, v.len) ||
719 !chunk_memcat(chk, "\r\n", 2))
720 goto full;
721
722 return 1;
723
724 full:
725 chk->data = sz;
726 return 0;
727 }
728
729 /* Appends the H1 representation of the data <data> to the chunk <chk>. If
730 * <chunked> is non-zero, it emits HTTP/1 chunk-encoded data. It returns 1 if
731 * data are successfully appended, otherwise it returns 0.
732 */
h1_format_htx_data(const struct ist data,struct buffer * chk,int chunked)733 int h1_format_htx_data(const struct ist data, struct buffer *chk, int chunked)
734 {
735 size_t sz = chk->data;
736
737 if (chunked) {
738 uint32_t chksz;
739 char tmp[10];
740 char *beg, *end;
741
742 chksz = data.len;
743
744 beg = end = tmp+10;
745 *--beg = '\n';
746 *--beg = '\r';
747 do {
748 *--beg = hextab[chksz & 0xF];
749 } while (chksz >>= 4);
750
751 if (!chunk_memcat(chk, beg, end - beg) ||
752 !chunk_memcat(chk, data.ptr, data.len) ||
753 !chunk_memcat(chk, "\r\n", 2))
754 goto full;
755 }
756 else {
757 if (!chunk_memcat(chk, data.ptr, data.len))
758 return 0;
759 }
760
761 return 1;
762
763 full:
764 chk->data = sz;
765 return 0;
766 }
767
768 /*
769 * Local variables:
770 * c-indent-level: 8
771 * c-basic-offset: 8
772 * End:
773 */
774