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