TLA Line data Source code
1 : //
2 : // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
3 : // Copyright (c) 2024 Mohammad Nejati
4 : //
5 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
6 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 : //
8 : // Official repository: https://github.com/cppalliance/http
9 : //
10 :
11 : #include "src/rfc/detail/rules.hpp"
12 : #include "src/rfc/detail/transfer_coding_rule.hpp"
13 :
14 : #include <boost/http/detail/header.hpp>
15 : #include <boost/http/field.hpp>
16 : #include <boost/http/header_limits.hpp>
17 : #include <boost/http/rfc/list_rule.hpp>
18 : #include <boost/http/rfc/token_rule.hpp>
19 : #include <boost/http/rfc/upgrade_rule.hpp>
20 : #include <boost/assert.hpp>
21 : #include <boost/assert/source_location.hpp>
22 : #include <boost/static_assert.hpp>
23 : #include <boost/url/grammar/ci_string.hpp>
24 : #include <boost/url/grammar/parse.hpp>
25 : #include <boost/url/grammar/range_rule.hpp>
26 : #include <boost/url/grammar/recycled.hpp>
27 : #include <boost/url/grammar/unsigned_rule.hpp>
28 :
29 : #include <utility>
30 :
31 : namespace boost {
32 : namespace http {
33 : namespace detail {
34 :
35 : //------------------------------------------------
36 :
37 : auto
38 HIT 87 : header::
39 : entry::
40 : operator+(
41 : std::size_t dv) const noexcept ->
42 : entry
43 : {
44 : return {
45 : static_cast<
46 87 : offset_type>(np + dv),
47 87 : nn,
48 : static_cast<
49 87 : offset_type>(vp + dv),
50 87 : vn,
51 87 : id };
52 : }
53 :
54 : auto
55 101 : header::
56 : entry::
57 : operator-(
58 : std::size_t dv) const noexcept ->
59 : entry
60 : {
61 : return {
62 : static_cast<
63 101 : offset_type>(np - dv),
64 101 : nn,
65 : static_cast<
66 101 : offset_type>(vp - dv),
67 101 : vn,
68 101 : id };
69 : }
70 :
71 : //------------------------------------------------
72 :
73 : constexpr field header::unknown_field;
74 :
75 : //------------------------------------------------
76 :
77 : constexpr
78 : header::
79 : header(fields_tag) noexcept
80 : : kind(detail::kind::fields)
81 : , cbuf("\r\n")
82 : , size(2)
83 : , fld{}
84 : {
85 : }
86 :
87 : constexpr
88 : header::
89 : header(request_tag) noexcept
90 : : kind(detail::kind::request)
91 : , cbuf("GET / HTTP/1.1\r\n\r\n")
92 : , size(18)
93 : , prefix(16)
94 : , req{ 3, 1,
95 : http::method::get }
96 : {
97 : }
98 :
99 : constexpr
100 : header::
101 : header(response_tag) noexcept
102 : : kind(detail::kind::response)
103 : , cbuf("HTTP/1.1 200 OK\r\n\r\n")
104 : , size(19)
105 : , prefix(17)
106 : , res{ 200,
107 : http::status::ok }
108 : {
109 : }
110 :
111 : //------------------------------------------------
112 :
113 : header const*
114 4872 : header::
115 : get_default(detail::kind k) noexcept
116 : {
117 : static constexpr header h[3] = {
118 : fields_tag{},
119 : request_tag{},
120 : response_tag{}};
121 4872 : return &h[k];
122 : }
123 :
124 13370 : header::
125 13370 : header(empty v) noexcept
126 13370 : : kind(v.param)
127 : {
128 13370 : }
129 :
130 2679 : header::
131 2679 : header(detail::kind k) noexcept
132 2679 : : header(*get_default(k))
133 : {
134 2679 : }
135 :
136 : void
137 79 : header::
138 : swap(header& h) noexcept
139 : {
140 79 : std::swap(cbuf, h.cbuf);
141 79 : std::swap(buf, h.buf);
142 79 : std::swap(cap, h.cap);
143 79 : std::swap(size, h.size);
144 79 : std::swap(count, h.count);
145 79 : std::swap(prefix, h.prefix);
146 79 : std::swap(version, h.version);
147 79 : std::swap(md, h.md);
148 79 : switch(kind)
149 : {
150 15 : default:
151 : case detail::kind::fields:
152 15 : break;
153 56 : case detail::kind::request:
154 56 : std::swap(
155 56 : req.method_len, h.req.method_len);
156 56 : std::swap(
157 56 : req.target_len, h.req.target_len);
158 56 : std::swap(req.method, h.req.method);
159 56 : break;
160 8 : case detail::kind::response:
161 8 : std::swap(
162 8 : res.status_int, h.res.status_int);
163 8 : std::swap(res.status, h.res.status);
164 8 : break;
165 : }
166 79 : }
167 :
168 : /* References:
169 :
170 : 6.3. Persistence
171 : https://datatracker.ietf.org/doc/html/rfc7230#section-6.3
172 : */
173 : bool
174 24 : header::
175 : keep_alive() const noexcept
176 : {
177 24 : if(md.payload == payload::error)
178 1 : return false;
179 23 : if( version ==
180 : http::version::http_1_1)
181 : {
182 15 : if(md.connection.close)
183 5 : return false;
184 : }
185 : else
186 : {
187 8 : if(! md.connection.keep_alive)
188 4 : return false;
189 : }
190 : // can't use to_eof in requests
191 14 : BOOST_ASSERT(
192 : kind != detail::kind::request ||
193 : md.payload != payload::to_eof);
194 14 : if(md.payload == payload::to_eof)
195 3 : return false;
196 11 : return true;
197 : }
198 :
199 : //------------------------------------------------
200 :
201 : // return total bytes needed
202 : // to store message of `size`
203 : // bytes and `count` fields.
204 : std::size_t
205 3156 : header::
206 : bytes_needed(
207 : std::size_t size,
208 : std::size_t count) noexcept
209 : {
210 : // make sure `size` is big enough
211 : // to hold the largest default buffer:
212 : // "HTTP/1.1 200 OK\r\n\r\n"
213 3156 : if(size < 19)
214 2388 : size = 19;
215 :
216 : // align size up to alignof(entry)
217 3156 : size = (size + alignof(entry) - 1) & ~(alignof(entry) - 1);
218 :
219 3156 : return size + count * sizeof(entry);
220 : }
221 :
222 : std::size_t
223 9585 : header::
224 : table_space(
225 : std::size_t count) noexcept
226 : {
227 : return count *
228 9585 : sizeof(header::entry);
229 : }
230 :
231 : std::size_t
232 9585 : header::
233 : table_space() const noexcept
234 : {
235 9585 : return table_space(count);
236 : }
237 :
238 : auto
239 2669 : header::
240 : tab() const noexcept ->
241 : table
242 : {
243 2669 : BOOST_ASSERT(cap > 0);
244 2669 : BOOST_ASSERT(buf != nullptr);
245 2669 : return table(buf + cap);
246 : }
247 :
248 : auto
249 680 : header::
250 : tab_() const noexcept ->
251 : entry*
252 : {
253 : return reinterpret_cast<
254 680 : entry*>(buf + cap);
255 : }
256 :
257 : // return true if header cbuf is a default
258 : bool
259 45 : header::
260 : is_default() const noexcept
261 : {
262 45 : return buf == nullptr;
263 : }
264 :
265 : std::size_t
266 135 : header::
267 : find(
268 : field id) const noexcept
269 : {
270 135 : if(count == 0)
271 64 : return 0;
272 71 : std::size_t i = 0;
273 71 : auto const* p = &tab()[0];
274 118 : while(i < count)
275 : {
276 95 : if(p->id == id)
277 48 : break;
278 47 : ++i;
279 47 : --p;
280 : }
281 71 : return i;
282 : }
283 :
284 : std::size_t
285 42 : header::
286 : find(
287 : core::string_view name) const noexcept
288 : {
289 42 : if(count == 0)
290 6 : return 0;
291 36 : std::size_t i = 0;
292 36 : auto const* p = &tab()[0];
293 57 : while(i < count)
294 : {
295 : core::string_view s(
296 54 : cbuf + prefix + p->np,
297 54 : p->nn);
298 54 : if(grammar::ci_is_equal(s, name))
299 33 : break;
300 21 : ++i;
301 21 : --p;
302 : }
303 36 : return i;
304 : }
305 :
306 : void
307 2267 : header::
308 : copy_table(
309 : void* dest,
310 : std::size_t n) const noexcept
311 : {
312 : // When `n == 0`, cbuf + cap may have incorrect
313 : // alignment, which can trigger UB sanitizer.
314 2267 : if(n == 0)
315 2246 : return;
316 :
317 21 : std::memcpy(
318 : reinterpret_cast<
319 21 : entry*>(dest) - n,
320 : reinterpret_cast<
321 : entry const*>(
322 21 : cbuf + cap) - n,
323 : n * sizeof(entry));
324 : }
325 :
326 : void
327 2267 : header::
328 : copy_table(
329 : void* dest) const noexcept
330 : {
331 2267 : copy_table(dest, count);
332 2267 : }
333 :
334 : // assign all the members but
335 : // preserve the allocated memory
336 : void
337 2217 : header::
338 : assign_to(
339 : header& dest) const noexcept
340 : {
341 2217 : auto const buf_ = dest.buf;
342 2217 : auto const cbuf_ = dest.cbuf;
343 2217 : auto const cap_ = dest.cap;
344 2217 : dest = *this;
345 2217 : dest.buf = buf_;
346 2217 : dest.cbuf = cbuf_;
347 2217 : dest.cap = cap_;
348 2217 : }
349 :
350 : //------------------------------------------------
351 : //
352 : // Metadata
353 : //
354 : //------------------------------------------------
355 :
356 : std::size_t
357 MIS 0 : header::
358 : maybe_count(
359 : field id) const noexcept
360 : {
361 0 : if(kind == detail::kind::fields)
362 0 : return std::size_t(-1);
363 0 : switch(id)
364 : {
365 0 : case field::connection:
366 0 : return md.connection.count;
367 0 : case field::content_encoding:
368 0 : return md.content_encoding.count;
369 0 : case field::content_length:
370 0 : return md.content_length.count;
371 0 : case field::expect:
372 0 : return md.expect.count;
373 0 : case field::transfer_encoding:
374 0 : return md.transfer_encoding.count;
375 0 : case field::upgrade:
376 0 : return md.upgrade.count;
377 0 : default:
378 0 : break;
379 : }
380 0 : return std::size_t(-1);
381 : }
382 :
383 : bool
384 HIT 24 : header::
385 : is_special(
386 : field id) const noexcept
387 : {
388 24 : if(kind == detail::kind::fields)
389 5 : return false;
390 19 : switch(id)
391 : {
392 9 : case field::connection:
393 : case field::content_encoding:
394 : case field::content_length:
395 : case field::expect:
396 : case field::transfer_encoding:
397 : case field::upgrade:
398 9 : return true;
399 10 : default:
400 10 : break;
401 : }
402 10 : return false;
403 : }
404 :
405 : //------------------------------------------------
406 :
407 : // called when the start-line changes
408 : void
409 10668 : header::
410 : on_start_line()
411 : {
412 : // items in both the request-line
413 : // and the status-line can affect
414 : // the payload, for example whether
415 : // or not EOF marks the end of the
416 : // payload.
417 :
418 10668 : update_payload();
419 10668 : }
420 :
421 : // called after a field is inserted
422 : void
423 11031 : header::
424 : on_insert(
425 : field id,
426 : core::string_view v)
427 : {
428 11031 : if(kind == detail::kind::fields)
429 482 : return;
430 10549 : switch(id)
431 : {
432 7 : case field::content_encoding:
433 7 : return on_insert_content_encoding(v);
434 4557 : case field::content_length:
435 4557 : return on_insert_content_length(v);
436 141 : case field::connection:
437 141 : return on_insert_connection(v);
438 47 : case field::expect:
439 47 : return on_insert_expect(v);
440 4413 : case field::transfer_encoding:
441 4413 : return on_insert_transfer_encoding(v);
442 24 : case field::upgrade:
443 24 : return on_insert_upgrade(v);
444 1360 : default:
445 1360 : break;
446 : }
447 : }
448 :
449 : // called when one field is erased
450 : void
451 39 : header::
452 : on_erase(field id)
453 : {
454 39 : if(kind == detail::kind::fields)
455 3 : return;
456 36 : switch(id)
457 : {
458 9 : case field::connection:
459 9 : return on_erase_connection();
460 MIS 0 : case field::content_encoding:
461 0 : return on_erase_content_encoding();
462 HIT 4 : case field::content_length:
463 4 : return on_erase_content_length();
464 10 : case field::expect:
465 10 : return on_erase_expect();
466 4 : case field::transfer_encoding:
467 4 : return on_erase_transfer_encoding();
468 4 : case field::upgrade:
469 4 : return on_erase_upgrade();
470 5 : default:
471 5 : break;
472 : }
473 : }
474 :
475 : //------------------------------------------------
476 :
477 : /*
478 : https://datatracker.ietf.org/doc/html/rfc7230#section-6.1
479 : */
480 : void
481 148 : header::
482 : on_insert_connection(
483 : core::string_view v)
484 : {
485 148 : ++md.connection.count;
486 148 : if(md.connection.ec)
487 5 : return;
488 : auto rv = grammar::parse(
489 147 : v, list_rule(token_rule, 1));
490 147 : if(! rv)
491 : {
492 4 : md.connection.ec =
493 8 : BOOST_HTTP_ERR(
494 : error::bad_connection);
495 4 : return;
496 : }
497 143 : md.connection.ec = {};
498 297 : for(auto t : *rv)
499 : {
500 154 : if(grammar::ci_is_equal(
501 : t, "close"))
502 99 : md.connection.close = true;
503 55 : else if(grammar::ci_is_equal(
504 : t, "keep-alive"))
505 28 : md.connection.keep_alive = true;
506 27 : else if(grammar::ci_is_equal(
507 : t, "upgrade"))
508 20 : md.connection.upgrade = true;
509 : }
510 147 : }
511 :
512 : void
513 4558 : header::
514 : on_insert_content_length(
515 : core::string_view v)
516 : {
517 : static
518 : constexpr
519 : grammar::unsigned_rule<
520 : std::uint64_t> num_rule{};
521 :
522 4558 : ++md.content_length.count;
523 4558 : if(md.content_length.ec)
524 4495 : return;
525 : auto rv =
526 4556 : grammar::parse(v, num_rule);
527 4556 : if(! rv)
528 : {
529 : // parse failure
530 5 : md.content_length.ec =
531 10 : BOOST_HTTP_ERR(
532 : error::bad_content_length);
533 5 : md.content_length.value = 0;
534 5 : update_payload();
535 5 : return;
536 : }
537 4551 : if(md.content_length.count == 1)
538 : {
539 : // one value
540 4481 : md.content_length.ec = {};
541 4481 : md.content_length.value = *rv;
542 4481 : update_payload();
543 4481 : return;
544 : }
545 70 : if(*rv == md.content_length.value)
546 : {
547 : // ok: duplicate value
548 7 : return;
549 : }
550 : // bad: different values
551 63 : md.content_length.ec =
552 126 : BOOST_HTTP_ERR(
553 : error::multiple_content_length);
554 63 : md.content_length.value = 0;
555 63 : update_payload();
556 : }
557 :
558 : void
559 53 : header::
560 : on_insert_expect(
561 : core::string_view v)
562 : {
563 53 : ++md.expect.count;
564 53 : if(kind != detail::kind::request)
565 8 : return;
566 45 : if(md.expect.ec)
567 4 : return;
568 : // VFALCO Should we allow duplicate
569 : // Expect fields that have 100-continue?
570 73 : if( md.expect.count > 1 ||
571 73 : ! grammar::ci_is_equal(v,
572 : "100-continue"))
573 : {
574 19 : md.expect.ec =
575 38 : BOOST_HTTP_ERR(
576 : error::bad_expect);
577 19 : md.expect.is_100_continue = false;
578 19 : return;
579 : }
580 22 : md.expect.is_100_continue = true;
581 : }
582 :
583 : void
584 4415 : header::
585 : on_insert_transfer_encoding(
586 : core::string_view v)
587 : {
588 4415 : ++md.transfer_encoding.count;
589 4415 : if(md.transfer_encoding.ec)
590 4407 : return;
591 :
592 : auto rv = grammar::parse(
593 4414 : v, list_rule(transfer_coding_rule, 1));
594 4414 : if(! rv)
595 : {
596 : // parse error
597 4 : goto error;
598 : }
599 8823 : for(auto t : *rv)
600 : {
601 4417 : if(! md.transfer_encoding.is_chunked)
602 : {
603 4413 : if(t.id == transfer_coding_rule_t::chunked)
604 4394 : md.transfer_encoding.is_chunked = true;
605 4413 : continue;
606 : }
607 4 : if(t.id == transfer_coding_rule_t::chunked)
608 : {
609 : // chunked appears twice
610 2 : goto error;
611 : }
612 : // chunked must be last
613 2 : goto error;
614 8831 : }
615 4406 : update_payload();
616 4406 : return;
617 :
618 8 : error:
619 8 : md.transfer_encoding.ec =
620 16 : BOOST_HTTP_ERR(
621 : error::bad_transfer_encoding);
622 8 : md.transfer_encoding.is_chunked = false;
623 8 : update_payload();
624 4414 : }
625 :
626 : void
627 7 : header::
628 : on_insert_content_encoding(
629 : core::string_view v)
630 : {
631 7 : ++md.content_encoding.count;
632 7 : if(md.content_encoding.ec)
633 3 : return;
634 :
635 : auto rv = grammar::parse(
636 7 : v, list_rule(token_rule, 1));
637 7 : if(!rv)
638 : {
639 1 : md.content_encoding.ec =
640 2 : BOOST_HTTP_ERR(
641 : error::bad_content_encoding);
642 1 : md.content_encoding.coding =
643 : content_coding::unknown;
644 1 : return;
645 : }
646 :
647 6 : if(rv->size() > 1 || md.content_encoding.count > 1)
648 : {
649 2 : md.content_encoding.coding =
650 : content_coding::unknown;
651 2 : return;
652 : }
653 :
654 8 : if(grammar::ci_is_equal(
655 8 : *rv->begin(), "deflate"))
656 : {
657 MIS 0 : md.content_encoding.coding =
658 : content_coding::deflate;
659 : }
660 HIT 8 : else if(grammar::ci_is_equal(
661 8 : *rv->begin(), "gzip"))
662 : {
663 2 : md.content_encoding.coding =
664 : content_coding::gzip;
665 : }
666 4 : else if(grammar::ci_is_equal(
667 4 : *rv->begin(), "br"))
668 : {
669 1 : md.content_encoding.coding =
670 : content_coding::br;
671 : }
672 2 : else if(grammar::ci_is_equal(
673 2 : *rv->begin(), "zstd"))
674 : {
675 1 : md.content_encoding.coding =
676 : content_coding::zstd;
677 : }
678 : else
679 : {
680 MIS 0 : md.content_encoding.coding =
681 : content_coding::unknown;
682 : }
683 HIT 7 : }
684 :
685 : void
686 26 : header::
687 : on_insert_upgrade(
688 : core::string_view v)
689 : {
690 26 : ++md.upgrade.count;
691 26 : if(md.upgrade.ec)
692 5 : return;
693 25 : if( version !=
694 : http::version::http_1_1)
695 : {
696 1 : md.upgrade.ec =
697 2 : BOOST_HTTP_ERR(
698 : error::bad_upgrade);
699 1 : md.upgrade.websocket = false;
700 1 : return;
701 : }
702 : auto rv = grammar::parse(
703 24 : v, upgrade_rule);
704 24 : if(! rv)
705 : {
706 3 : md.upgrade.ec =
707 6 : BOOST_HTTP_ERR(
708 : error::bad_upgrade);
709 3 : md.upgrade.websocket = false;
710 3 : return;
711 : }
712 21 : if(! md.upgrade.websocket)
713 : {
714 23 : for(auto t : *rv)
715 : {
716 16 : if( grammar::ci_is_equal(
717 26 : t.name, "websocket") &&
718 10 : t.version.empty())
719 : {
720 9 : md.upgrade.websocket = true;
721 9 : break;
722 : }
723 : }
724 : }
725 24 : }
726 :
727 : //------------------------------------------------
728 :
729 : void
730 9 : header::
731 : on_erase_connection()
732 : {
733 9 : BOOST_ASSERT(
734 : md.connection.count > 0);
735 : // reset and re-insert
736 9 : auto n = md.connection.count - 1;
737 9 : auto const p = cbuf + prefix;
738 9 : auto const* e = &tab()[0];
739 9 : md.connection = {};
740 17 : while(n > 0)
741 : {
742 8 : if(e->id == field::connection)
743 : {
744 7 : on_insert_connection(
745 : core::string_view(
746 7 : p + e->vp, e->vn));
747 7 : --n;
748 : }
749 8 : --e;
750 : }
751 9 : }
752 :
753 : void
754 4 : header::
755 : on_erase_content_length()
756 : {
757 4 : BOOST_ASSERT(
758 : md.content_length.count > 0);
759 4 : --md.content_length.count;
760 4 : if(md.content_length.count == 0)
761 : {
762 : // no Content-Length
763 1 : md.content_length = {};
764 1 : update_payload();
765 1 : return;
766 : }
767 3 : if(! md.content_length.ec)
768 : {
769 : // removing a duplicate value
770 2 : return;
771 : }
772 : // reset and re-insert
773 1 : auto n = md.content_length.count;
774 1 : auto const p = cbuf + prefix;
775 1 : auto const* e = &tab()[0];
776 1 : md.content_length = {};
777 2 : while(n > 0)
778 : {
779 1 : if(e->id == field::content_length)
780 : {
781 1 : on_insert_content_length(
782 : core::string_view(
783 1 : p + e->vp, e->vn));
784 1 : --n;
785 : }
786 1 : --e;
787 : }
788 1 : update_payload();
789 : }
790 :
791 : void
792 10 : header::
793 : on_erase_expect()
794 : {
795 10 : BOOST_ASSERT(
796 : md.expect.count > 0);
797 10 : --md.expect.count;
798 10 : if(kind != detail::kind::request)
799 1 : return;
800 9 : if(md.expect.count == 0)
801 : {
802 : // no Expect
803 3 : md.expect = {};
804 3 : return;
805 : }
806 : // VFALCO This should be uncommented
807 : // if we want to allow multiple Expect
808 : // fields with the value 100-continue
809 : /*
810 : if(! md.expect.ec)
811 : return;
812 : */
813 : // reset and re-insert
814 6 : auto n = md.expect.count;
815 6 : auto const p = cbuf + prefix;
816 6 : auto const* e = &tab()[0];
817 6 : md.expect = {};
818 18 : while(n > 0)
819 : {
820 12 : if(e->id == field::expect)
821 : {
822 6 : on_insert_expect(
823 : core::string_view(
824 6 : p + e->vp, e->vn));
825 6 : --n;
826 : }
827 12 : --e;
828 : }
829 : }
830 :
831 : void
832 4 : header::
833 : on_erase_transfer_encoding()
834 : {
835 4 : BOOST_ASSERT(
836 : md.transfer_encoding.count > 0);
837 : // reset and re-insert
838 4 : auto n = md.transfer_encoding.count - 1;
839 4 : auto const p = cbuf + prefix;
840 4 : auto const* e = &tab()[0];
841 4 : md.transfer_encoding = {};
842 7 : while(n > 0)
843 : {
844 3 : if(e->id == field::transfer_encoding)
845 : {
846 2 : on_insert_transfer_encoding(
847 : core::string_view(
848 2 : p + e->vp, e->vn));
849 2 : --n;
850 : }
851 3 : --e;
852 : }
853 4 : }
854 :
855 : void
856 MIS 0 : header::
857 : on_erase_content_encoding()
858 : {
859 0 : BOOST_ASSERT(
860 : md.content_encoding.count > 0);
861 0 : --md.content_encoding.count;
862 0 : if(md.content_encoding.count == 0)
863 : {
864 : // no Content-Encoding
865 0 : md.content_encoding = {};
866 0 : return;
867 : }
868 : // re-insert everything
869 0 : --md.content_encoding.count;
870 : // TODO
871 : // on_insert_content_encoding();
872 : }
873 :
874 : // called when Upgrade is erased
875 : void
876 HIT 4 : header::
877 : on_erase_upgrade()
878 : {
879 4 : BOOST_ASSERT(
880 : md.upgrade.count > 0);
881 4 : --md.upgrade.count;
882 4 : if(md.upgrade.count == 0)
883 : {
884 : // no Upgrade
885 2 : md.upgrade = {};
886 2 : return;
887 : }
888 : // reset and re-insert
889 2 : auto n = md.upgrade.count;
890 2 : auto const p = cbuf + prefix;
891 2 : auto const* e = &tab()[0];
892 2 : md.upgrade = {};
893 4 : while(n > 0)
894 : {
895 2 : if(e->id == field::upgrade)
896 2 : on_insert_upgrade(
897 : core::string_view(
898 2 : p + e->vp, e->vn));
899 2 : --n;
900 2 : --e;
901 : }
902 : }
903 :
904 : //------------------------------------------------
905 :
906 : // called when all fields with id are removed
907 : void
908 72 : header::
909 : on_erase_all(
910 : field id)
911 : {
912 72 : if(kind == detail::kind::fields)
913 21 : return;
914 51 : switch(id)
915 : {
916 3 : case field::connection:
917 3 : md.connection = {};
918 3 : return;
919 :
920 2 : case field::content_length:
921 2 : md.content_length = {};
922 2 : update_payload();
923 2 : return;
924 :
925 5 : case field::expect:
926 5 : md.expect = {};
927 5 : update_payload();
928 5 : return;
929 :
930 1 : case field::transfer_encoding:
931 1 : md.transfer_encoding = {};
932 1 : update_payload();
933 1 : return;
934 :
935 1 : case field::upgrade:
936 1 : md.upgrade = {};
937 1 : return;
938 :
939 39 : default:
940 39 : break;
941 : }
942 : }
943 :
944 : //------------------------------------------------
945 :
946 : /* References:
947 :
948 : 3.3. Message Body
949 : https://datatracker.ietf.org/doc/html/rfc7230#section-3.3
950 :
951 : 3.3.1. Transfer-Encoding
952 : https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.1
953 :
954 : 3.3.2. Content-Length
955 : https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.2
956 : */
957 : void
958 19641 : header::
959 : update_payload() noexcept
960 : {
961 19641 : BOOST_ASSERT(kind !=
962 : detail::kind::fields);
963 19641 : if(md.payload_override)
964 : {
965 : // e.g. response to
966 : // a HEAD request
967 MIS 0 : return;
968 : }
969 :
970 : /* If there is an error in either Content-Length
971 : or Transfer-Encoding, then the payload is
972 : undefined. Clients should probably close the
973 : connection. Servers can send a Bad Request
974 : and avoid reading any payload bytes.
975 : */
976 HIT 19641 : if(md.content_length.ec)
977 : {
978 : // invalid Content-Length
979 68 : md.payload = payload::error;
980 68 : md.payload_size = 0;
981 68 : return;
982 : }
983 19573 : if(md.transfer_encoding.ec)
984 : {
985 : // invalid Transfer-Encoding
986 8 : md.payload = payload::error;
987 8 : md.payload_size = 0;
988 8 : return;
989 : }
990 :
991 : /* A sender MUST NOT send a Content-Length
992 : header field in any message that contains
993 : a Transfer-Encoding header field.
994 : https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.2
995 : */
996 19565 : if( md.content_length.count > 0 &&
997 4485 : md.transfer_encoding.count > 0)
998 : {
999 3 : md.payload = payload::error;
1000 3 : md.payload_size = 0;
1001 3 : return;
1002 : }
1003 :
1004 19562 : if(kind == detail::kind::response)
1005 1924 : goto do_response;
1006 :
1007 : //--------------------------------------------
1008 :
1009 : /* The presence of a message body in a
1010 : request is signaled by a Content-Length
1011 : or Transfer-Encoding header field. Request
1012 : message framing is independent of method
1013 : semantics, even if the method does not
1014 : define any use for a message body.
1015 : */
1016 17638 : if(md.content_length.count > 0)
1017 : {
1018 4177 : if(md.content_length.value > 0)
1019 : {
1020 : // non-zero Content-Length
1021 4150 : md.payload = payload::size;
1022 4150 : md.payload_size = md.content_length.value;
1023 4150 : return;
1024 : }
1025 : // Content-Length: 0
1026 27 : md.payload = payload::none;
1027 27 : md.payload_size = 0;
1028 27 : return;
1029 : }
1030 13461 : if(md.transfer_encoding.is_chunked)
1031 : {
1032 : // chunked
1033 4012 : md.payload = payload::chunked;
1034 4012 : md.payload_size = 0;
1035 4012 : return;
1036 : }
1037 : // no payload
1038 9449 : md.payload = payload::none;
1039 9449 : md.payload_size = 0;
1040 9449 : return;
1041 :
1042 : //--------------------------------------------
1043 1924 : do_response:
1044 :
1045 1924 : if( res.status_int / 100 == 1 || // 1xx e.g. Continue
1046 1913 : res.status_int == 204 || // No Content
1047 1910 : res.status_int == 304) // Not Modified
1048 : {
1049 : /* The correctness of any Content-Length
1050 : here is defined by the particular
1051 : resource, and cannot be determined
1052 : here. In any case there is no payload.
1053 : */
1054 16 : md.payload = payload::none;
1055 16 : md.payload_size = 0;
1056 16 : return;
1057 : }
1058 1908 : if(md.content_length.count > 0)
1059 : {
1060 302 : if(md.content_length.value > 0)
1061 : {
1062 : // Content-Length > 0
1063 283 : md.payload = payload::size;
1064 283 : md.payload_size = md.content_length.value;
1065 283 : return;
1066 : }
1067 : // Content-Length: 0
1068 19 : md.payload = payload::none;
1069 19 : md.payload_size = 0;
1070 19 : return;
1071 : }
1072 1606 : if(md.transfer_encoding.is_chunked)
1073 : {
1074 : // chunked
1075 377 : md.payload = payload::chunked;
1076 377 : md.payload_size = 0;
1077 377 : return;
1078 : }
1079 :
1080 : // eof needed
1081 1229 : md.payload = payload::to_eof;
1082 1229 : md.payload_size = 0;
1083 : }
1084 :
1085 : //------------------------------------------------
1086 :
1087 : std::size_t
1088 549 : header::
1089 : count_crlf(
1090 : core::string_view s) noexcept
1091 : {
1092 549 : auto it = s.data();
1093 549 : auto len = s.size();
1094 549 : std::size_t n = 0;
1095 19120 : while(len >= 2)
1096 : {
1097 18571 : if( it[0] == '\r' &&
1098 1749 : it[1] != '\r')
1099 : {
1100 1749 : if(it[1] == '\n')
1101 1749 : n++;
1102 1749 : it += 2;
1103 1749 : len -= 2;
1104 : }
1105 : else
1106 : {
1107 16822 : it++;
1108 16822 : len--;
1109 : }
1110 : }
1111 549 : return n;
1112 : }
1113 :
1114 : static
1115 : void
1116 29145 : parse_start_line(
1117 : header& h,
1118 : header_limits const& lim,
1119 : std::size_t new_size,
1120 : system::error_code& ec) noexcept
1121 : {
1122 29145 : BOOST_ASSERT(h.size == 0);
1123 29145 : BOOST_ASSERT(h.prefix == 0);
1124 29145 : BOOST_ASSERT(h.cbuf != nullptr);
1125 29145 : BOOST_ASSERT(
1126 : h.kind != detail::kind::fields);
1127 :
1128 29145 : auto const it0 = h.cbuf;
1129 29145 : auto const end = it0 + new_size;
1130 29145 : char const* it = it0;
1131 29145 : if( new_size > lim.max_start_line)
1132 10 : new_size = lim.max_start_line;
1133 29145 : if(h.kind == detail::kind::request)
1134 : {
1135 : auto rv = grammar::parse(
1136 11375 : it, end, request_line_rule);
1137 11375 : if(! rv)
1138 : {
1139 1991 : ec = rv.error();
1140 3982 : if( ec == grammar::error::need_more &&
1141 1991 : new_size == lim.max_start_line)
1142 MIS 0 : ec = BOOST_HTTP_ERR(
1143 : error::start_line_limit);
1144 HIT 1991 : return;
1145 : }
1146 : // method
1147 9384 : auto sm = std::get<0>(*rv);
1148 9384 : h.req.method = string_to_method(sm);
1149 9384 : h.req.method_len =
1150 9384 : static_cast<header::offset_type>(sm.size());
1151 : // target
1152 9384 : auto st = std::get<1>(*rv);
1153 9384 : h.req.target_len =
1154 9384 : static_cast<header::offset_type>(st.size());
1155 : // version
1156 9384 : switch(std::get<2>(*rv))
1157 : {
1158 25 : case 10:
1159 25 : h.version =
1160 : http::version::http_1_0;
1161 25 : break;
1162 9359 : case 11:
1163 9359 : h.version =
1164 : http::version::http_1_1;
1165 9359 : break;
1166 MIS 0 : default:
1167 : {
1168 0 : ec = BOOST_HTTP_ERR(
1169 : error::bad_version);
1170 0 : return;
1171 : }
1172 : }
1173 : }
1174 : else
1175 : {
1176 : auto rv = grammar::parse(
1177 HIT 17770 : it, end, status_line_rule);
1178 17770 : if(! rv)
1179 : {
1180 16554 : ec = rv.error();
1181 33108 : if( ec == grammar::error::need_more &&
1182 16554 : new_size == lim.max_start_line)
1183 MIS 0 : ec = BOOST_HTTP_ERR(
1184 : error::start_line_limit);
1185 HIT 16554 : return;
1186 : }
1187 : // version
1188 1216 : switch(std::get<0>(*rv))
1189 : {
1190 4 : case 10:
1191 4 : h.version =
1192 : http::version::http_1_0;
1193 4 : break;
1194 1212 : case 11:
1195 1212 : h.version =
1196 : http::version::http_1_1;
1197 1212 : break;
1198 MIS 0 : default:
1199 : {
1200 0 : ec = BOOST_HTTP_ERR(
1201 : error::bad_version);
1202 0 : return;
1203 : }
1204 : }
1205 : // status-code
1206 HIT 1216 : h.res.status_int =
1207 : static_cast<unsigned short>(
1208 1216 : std::get<1>(*rv).v);
1209 1216 : h.res.status = std::get<1>(*rv).st;
1210 : }
1211 10600 : h.prefix = static_cast<header::offset_type>(it - it0);
1212 10600 : h.size = h.prefix;
1213 10600 : h.on_start_line();
1214 : }
1215 :
1216 : // returns: true if we added a field
1217 : static
1218 : void
1219 37744 : parse_field(
1220 : header& h,
1221 : header_limits const& lim,
1222 : std::size_t new_size,
1223 : system::error_code& ec) noexcept
1224 : {
1225 37744 : if( new_size > lim.max_field)
1226 20 : new_size = lim.max_field;
1227 37744 : auto const it0 = h.cbuf + h.size;
1228 37744 : auto const end = h.cbuf + new_size;
1229 37744 : char const* it = it0;
1230 37744 : auto rv = grammar::parse(
1231 : it, end, field_rule);
1232 37744 : if(rv.has_error())
1233 : {
1234 27030 : ec = rv.error();
1235 27030 : if(ec == grammar::error::end_of_range)
1236 : {
1237 : // final CRLF
1238 10131 : h.size = static_cast<
1239 10131 : header::offset_type>(it - h.cbuf);
1240 27030 : return;
1241 : }
1242 33539 : if( ec == grammar::error::need_more &&
1243 16640 : new_size == lim.max_field)
1244 : {
1245 MIS 0 : ec = BOOST_HTTP_ERR(
1246 : error::field_size_limit);
1247 : }
1248 HIT 16899 : return;
1249 : }
1250 10714 : if(h.count >= lim.max_fields)
1251 : {
1252 MIS 0 : ec = BOOST_HTTP_ERR(
1253 : error::fields_limit);
1254 0 : return;
1255 : }
1256 HIT 10714 : if(rv->has_obs_fold)
1257 : {
1258 : // obs fold not allowed in test views
1259 210 : BOOST_ASSERT(h.buf != nullptr);
1260 210 : remove_obs_fold(h.buf + h.size, it);
1261 : }
1262 10714 : auto id = string_to_field(rv->name)
1263 10714 : .value_or(header::unknown_field);
1264 10714 : h.size = static_cast<header::offset_type>(it - h.cbuf);
1265 :
1266 : // add field table entry
1267 10714 : if(h.buf != nullptr)
1268 : {
1269 21428 : auto& e = header::table(
1270 10714 : h.buf + h.cap)[h.count];
1271 10714 : auto const base =
1272 10714 : h.buf + h.prefix;
1273 10714 : e.np = static_cast<header::offset_type>(
1274 10714 : rv->name.data() - base);
1275 10714 : e.nn = static_cast<header::offset_type>(
1276 10714 : rv->name.size());
1277 10714 : e.vp = static_cast<header::offset_type>(
1278 10714 : rv->value.data() - base);
1279 10714 : e.vn = static_cast<header::offset_type>(
1280 10714 : rv->value.size());
1281 10714 : e.id = id;
1282 : }
1283 10714 : ++h.count;
1284 10714 : h.on_insert(id, rv->value);
1285 10714 : ec = {};
1286 : }
1287 :
1288 : void
1289 45575 : header::
1290 : parse(
1291 : std::size_t new_size,
1292 : header_limits const& lim,
1293 : system::error_code& ec) noexcept
1294 : {
1295 45575 : if( new_size > lim.max_size)
1296 10 : new_size = lim.max_size;
1297 45575 : if( this->prefix == 0 &&
1298 29379 : this->kind !=
1299 : detail::kind::fields)
1300 : {
1301 29145 : parse_start_line(
1302 : *this, lim, new_size, ec);
1303 29145 : if(ec)
1304 : {
1305 37090 : if( ec == grammar::error::need_more &&
1306 18545 : new_size == lim.max_fields)
1307 : {
1308 MIS 0 : ec = BOOST_HTTP_ERR(
1309 : error::headers_limit);
1310 : }
1311 HIT 18545 : return;
1312 : }
1313 : }
1314 : for(;;)
1315 : {
1316 37744 : parse_field(
1317 : *this, lim, new_size, ec);
1318 37744 : if(ec)
1319 : {
1320 43670 : if( ec == grammar::error::need_more &&
1321 16640 : new_size == lim.max_size)
1322 : {
1323 MIS 0 : ec = BOOST_HTTP_ERR(
1324 : error::headers_limit);
1325 0 : return;
1326 : }
1327 HIT 27030 : break;
1328 : }
1329 10714 : }
1330 27030 : if(ec == grammar::error::end_of_range)
1331 10131 : ec = {};
1332 : }
1333 :
1334 : } // detail
1335 : } // http
1336 : } // boost
|