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/header_limits.hpp>
17 : #include <boost/http_proto/rfc/list_rule.hpp>
18 : #include <boost/http_proto/rfc/token_rule.hpp>
19 : #include <boost/http_proto/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_proto {
33 : namespace detail {
34 :
35 : //------------------------------------------------
36 :
37 : auto
38 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_proto::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_proto::status::ok }
108 : {
109 : }
110 :
111 : //------------------------------------------------
112 :
113 : header const*
114 2360 : 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 2360 : return &h[k];
122 : }
123 :
124 11902 : header::
125 11902 : header(empty v) noexcept
126 11902 : : kind(v.param)
127 : {
128 11902 : }
129 :
130 1289 : header::
131 1289 : header(detail::kind k) noexcept
132 1289 : : header(*get_default(k))
133 : {
134 1289 : }
135 :
136 : void
137 78 : header::
138 : swap(header& h) noexcept
139 : {
140 78 : std::swap(cbuf, h.cbuf);
141 78 : std::swap(buf, h.buf);
142 78 : std::swap(cap, h.cap);
143 78 : std::swap(size, h.size);
144 78 : std::swap(count, h.count);
145 78 : std::swap(prefix, h.prefix);
146 78 : std::swap(version, h.version);
147 78 : std::swap(md, h.md);
148 78 : switch(kind)
149 : {
150 15 : default:
151 : case detail::kind::fields:
152 15 : break;
153 55 : case detail::kind::request:
154 55 : std::swap(
155 55 : req.method_len, h.req.method_len);
156 55 : std::swap(
157 55 : req.target_len, h.req.target_len);
158 55 : std::swap(req.method, h.req.method);
159 55 : 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 78 : }
167 :
168 : /* References:
169 :
170 : 6.3. Persistence
171 : https://datatracker.ietf.org/doc/html/rfc7230#section-6.3
172 : */
173 : bool
174 22 : header::
175 : keep_alive() const noexcept
176 : {
177 22 : if(md.payload == payload::error)
178 1 : return false;
179 21 : if( version ==
180 : http_proto::version::http_1_1)
181 : {
182 13 : if(md.connection.close)
183 3 : 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 1994 : 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 1994 : if(size < 19)
214 1241 : size = 19;
215 :
216 : // align size up to alignof(entry)
217 1994 : size = (size + alignof(entry) - 1) & ~(alignof(entry) - 1);
218 :
219 1994 : return size + count * sizeof(entry);
220 : }
221 :
222 : std::size_t
223 10006 : header::
224 : table_space(
225 : std::size_t count) noexcept
226 : {
227 : return count *
228 10006 : sizeof(header::entry);
229 : }
230 :
231 : std::size_t
232 10006 : header::
233 : table_space() const noexcept
234 : {
235 10006 : return table_space(count);
236 : }
237 :
238 : auto
239 2599 : header::
240 : tab() const noexcept ->
241 : table
242 : {
243 2599 : BOOST_ASSERT(cap > 0);
244 2599 : BOOST_ASSERT(buf != nullptr);
245 2599 : return table(buf + cap);
246 : }
247 :
248 : auto
249 702 : header::
250 : tab_() const noexcept ->
251 : entry*
252 : {
253 : return reinterpret_cast<
254 702 : entry*>(buf + cap);
255 : }
256 :
257 : // return true if header cbuf is a default
258 : bool
259 41 : header::
260 : is_default() const noexcept
261 : {
262 41 : return buf == nullptr;
263 : }
264 :
265 : std::size_t
266 136 : header::
267 : find(
268 : field id) const noexcept
269 : {
270 136 : if(count == 0)
271 59 : return 0;
272 77 : std::size_t i = 0;
273 77 : auto const* p = &tab()[0];
274 120 : while(i < count)
275 : {
276 91 : if(p->id == id)
277 48 : break;
278 43 : ++i;
279 43 : --p;
280 : }
281 77 : 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 1116 : 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 1116 : if(n == 0)
315 1096 : return;
316 :
317 20 : std::memcpy(
318 : reinterpret_cast<
319 20 : entry*>(dest) - n,
320 : reinterpret_cast<
321 : entry const*>(
322 20 : cbuf + cap) - n,
323 : n * sizeof(entry));
324 : }
325 :
326 : void
327 1116 : header::
328 : copy_table(
329 : void* dest) const noexcept
330 : {
331 1116 : copy_table(dest, count);
332 1116 : }
333 :
334 : // assign all the members but
335 : // preserve the allocated memory
336 : void
337 1095 : header::
338 : assign_to(
339 : header& dest) const noexcept
340 : {
341 1095 : auto const buf_ = dest.buf;
342 1095 : auto const cbuf_ = dest.cbuf;
343 1095 : auto const cap_ = dest.cap;
344 1095 : dest = *this;
345 1095 : dest.buf = buf_;
346 1095 : dest.cbuf = cbuf_;
347 1095 : dest.cap = cap_;
348 1095 : }
349 :
350 : //------------------------------------------------
351 : //
352 : // Metadata
353 : //
354 : //------------------------------------------------
355 :
356 : std::size_t
357 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 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 10610 : 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 10610 : update_payload();
419 10610 : }
420 :
421 : // called after a field is inserted
422 : void
423 11780 : header::
424 : on_insert(
425 : field id,
426 : core::string_view v)
427 : {
428 11780 : if(kind == detail::kind::fields)
429 483 : return;
430 11297 : switch(id)
431 : {
432 129 : case field::content_encoding:
433 129 : return on_insert_content_encoding(v);
434 4877 : case field::content_length:
435 4877 : return on_insert_content_length(v);
436 136 : case field::connection:
437 136 : return on_insert_connection(v);
438 47 : case field::expect:
439 47 : return on_insert_expect(v);
440 4229 : case field::transfer_encoding:
441 4229 : return on_insert_transfer_encoding(v);
442 24 : case field::upgrade:
443 24 : return on_insert_upgrade(v);
444 1855 : default:
445 1855 : 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 0 : case field::content_encoding:
461 0 : return on_erase_content_encoding();
462 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 143 : header::
482 : on_insert_connection(
483 : core::string_view v)
484 : {
485 143 : ++md.connection.count;
486 143 : if(md.connection.ec.failed())
487 5 : return;
488 : auto rv = grammar::parse(
489 142 : v, list_rule(token_rule, 1));
490 142 : if(! rv)
491 : {
492 4 : md.connection.ec =
493 8 : BOOST_HTTP_PROTO_ERR(
494 : error::bad_connection);
495 4 : return;
496 : }
497 138 : md.connection.ec = {};
498 287 : for(auto t : *rv)
499 : {
500 149 : if(grammar::ci_is_equal(
501 : t, "close"))
502 96 : md.connection.close = true;
503 53 : else if(grammar::ci_is_equal(
504 : t, "keep-alive"))
505 26 : md.connection.keep_alive = true;
506 27 : else if(grammar::ci_is_equal(
507 : t, "upgrade"))
508 20 : md.connection.upgrade = true;
509 : }
510 142 : }
511 :
512 : void
513 4878 : 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 4878 : ++md.content_length.count;
523 4878 : if(md.content_length.ec.failed())
524 4695 : return;
525 : auto rv =
526 4876 : grammar::parse(v, num_rule);
527 4876 : if(! rv)
528 : {
529 : // parse failure
530 5 : md.content_length.ec =
531 10 : BOOST_HTTP_PROTO_ERR(
532 : error::bad_content_length);
533 5 : md.content_length.value = 0;
534 5 : update_payload();
535 5 : return;
536 : }
537 4871 : if(md.content_length.count == 1)
538 : {
539 : // one value
540 4681 : md.content_length.ec = {};
541 4681 : md.content_length.value = *rv;
542 4681 : update_payload();
543 4681 : return;
544 : }
545 190 : if(*rv == md.content_length.value)
546 : {
547 : // ok: duplicate value
548 7 : return;
549 : }
550 : // bad: different values
551 183 : md.content_length.ec =
552 366 : BOOST_HTTP_PROTO_ERR(
553 : error::multiple_content_length);
554 183 : md.content_length.value = 0;
555 183 : 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.failed())
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_PROTO_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 4231 : header::
585 : on_insert_transfer_encoding(
586 : core::string_view v)
587 : {
588 4231 : ++md.transfer_encoding.count;
589 4231 : if(md.transfer_encoding.ec.failed())
590 4223 : return;
591 :
592 : auto rv = grammar::parse(
593 4230 : v, list_rule(transfer_coding_rule, 1));
594 4230 : if(! rv)
595 : {
596 : // parse error
597 4 : goto error;
598 : }
599 8455 : for(auto t : *rv)
600 : {
601 4233 : if(! md.transfer_encoding.is_chunked)
602 : {
603 4229 : if(t.id == transfer_coding_rule_t::chunked)
604 4210 : md.transfer_encoding.is_chunked = true;
605 4229 : 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 8463 : }
615 4222 : update_payload();
616 4222 : return;
617 :
618 8 : error:
619 8 : md.transfer_encoding.ec =
620 16 : BOOST_HTTP_PROTO_ERR(
621 : error::bad_transfer_encoding);
622 8 : md.transfer_encoding.is_chunked = false;
623 8 : update_payload();
624 4230 : }
625 :
626 : void
627 129 : header::
628 : on_insert_content_encoding(
629 : core::string_view v)
630 : {
631 129 : ++md.content_encoding.count;
632 129 : if(md.content_encoding.ec.failed())
633 3 : return;
634 :
635 : auto rv = grammar::parse(
636 129 : v, list_rule(token_rule, 1));
637 129 : if(!rv)
638 : {
639 1 : md.content_encoding.ec =
640 2 : BOOST_HTTP_PROTO_ERR(
641 : error::bad_content_encoding);
642 1 : md.content_encoding.coding =
643 : content_coding::unknown;
644 1 : return;
645 : }
646 :
647 128 : 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 252 : if(grammar::ci_is_equal(
655 252 : *rv->begin(), "deflate"))
656 : {
657 62 : md.content_encoding.coding =
658 : content_coding::deflate;
659 : }
660 128 : else if(grammar::ci_is_equal(
661 128 : *rv->begin(), "gzip"))
662 : {
663 64 : md.content_encoding.coding =
664 : content_coding::gzip;
665 : }
666 0 : else if(grammar::ci_is_equal(
667 0 : *rv->begin(), "br"))
668 : {
669 0 : md.content_encoding.coding =
670 : content_coding::br;
671 : }
672 : else
673 : {
674 0 : md.content_encoding.coding =
675 : content_coding::unknown;
676 : }
677 129 : }
678 :
679 : void
680 26 : header::
681 : on_insert_upgrade(
682 : core::string_view v)
683 : {
684 26 : ++md.upgrade.count;
685 26 : if(md.upgrade.ec.failed())
686 5 : return;
687 25 : if( version !=
688 : http_proto::version::http_1_1)
689 : {
690 1 : md.upgrade.ec =
691 2 : BOOST_HTTP_PROTO_ERR(
692 : error::bad_upgrade);
693 1 : md.upgrade.websocket = false;
694 1 : return;
695 : }
696 : auto rv = grammar::parse(
697 24 : v, upgrade_rule);
698 24 : if(! rv)
699 : {
700 3 : md.upgrade.ec =
701 6 : BOOST_HTTP_PROTO_ERR(
702 : error::bad_upgrade);
703 3 : md.upgrade.websocket = false;
704 3 : return;
705 : }
706 21 : if(! md.upgrade.websocket)
707 : {
708 23 : for(auto t : *rv)
709 : {
710 16 : if( grammar::ci_is_equal(
711 26 : t.name, "websocket") &&
712 10 : t.version.empty())
713 : {
714 9 : md.upgrade.websocket = true;
715 9 : break;
716 : }
717 : }
718 : }
719 24 : }
720 :
721 : //------------------------------------------------
722 :
723 : void
724 9 : header::
725 : on_erase_connection()
726 : {
727 9 : BOOST_ASSERT(
728 : md.connection.count > 0);
729 : // reset and re-insert
730 9 : auto n = md.connection.count - 1;
731 9 : auto const p = cbuf + prefix;
732 9 : auto const* e = &tab()[0];
733 9 : md.connection = {};
734 17 : while(n > 0)
735 : {
736 8 : if(e->id == field::connection)
737 : {
738 7 : on_insert_connection(
739 : core::string_view(
740 7 : p + e->vp, e->vn));
741 7 : --n;
742 : }
743 8 : --e;
744 : }
745 9 : }
746 :
747 : void
748 4 : header::
749 : on_erase_content_length()
750 : {
751 4 : BOOST_ASSERT(
752 : md.content_length.count > 0);
753 4 : --md.content_length.count;
754 4 : if(md.content_length.count == 0)
755 : {
756 : // no Content-Length
757 1 : md.content_length = {};
758 1 : update_payload();
759 1 : return;
760 : }
761 3 : if(! md.content_length.ec.failed())
762 : {
763 : // removing a duplicate value
764 2 : return;
765 : }
766 : // reset and re-insert
767 1 : auto n = md.content_length.count;
768 1 : auto const p = cbuf + prefix;
769 1 : auto const* e = &tab()[0];
770 1 : md.content_length = {};
771 2 : while(n > 0)
772 : {
773 1 : if(e->id == field::content_length)
774 : {
775 1 : on_insert_content_length(
776 : core::string_view(
777 1 : p + e->vp, e->vn));
778 1 : --n;
779 : }
780 1 : --e;
781 : }
782 1 : update_payload();
783 : }
784 :
785 : void
786 10 : header::
787 : on_erase_expect()
788 : {
789 10 : BOOST_ASSERT(
790 : md.expect.count > 0);
791 10 : --md.expect.count;
792 10 : if(kind != detail::kind::request)
793 1 : return;
794 9 : if(md.expect.count == 0)
795 : {
796 : // no Expect
797 3 : md.expect = {};
798 3 : return;
799 : }
800 : // VFALCO This should be uncommented
801 : // if we want to allow multiple Expect
802 : // fields with the value 100-continue
803 : /*
804 : if(! md.expect.ec.failed())
805 : return;
806 : */
807 : // reset and re-insert
808 6 : auto n = md.expect.count;
809 6 : auto const p = cbuf + prefix;
810 6 : auto const* e = &tab()[0];
811 6 : md.expect = {};
812 18 : while(n > 0)
813 : {
814 12 : if(e->id == field::expect)
815 : {
816 6 : on_insert_expect(
817 : core::string_view(
818 6 : p + e->vp, e->vn));
819 6 : --n;
820 : }
821 12 : --e;
822 : }
823 : }
824 :
825 : void
826 4 : header::
827 : on_erase_transfer_encoding()
828 : {
829 4 : BOOST_ASSERT(
830 : md.transfer_encoding.count > 0);
831 : // reset and re-insert
832 4 : auto n = md.transfer_encoding.count - 1;
833 4 : auto const p = cbuf + prefix;
834 4 : auto const* e = &tab()[0];
835 4 : md.transfer_encoding = {};
836 7 : while(n > 0)
837 : {
838 3 : if(e->id == field::transfer_encoding)
839 : {
840 2 : on_insert_transfer_encoding(
841 : core::string_view(
842 2 : p + e->vp, e->vn));
843 2 : --n;
844 : }
845 3 : --e;
846 : }
847 4 : }
848 :
849 : void
850 0 : header::
851 : on_erase_content_encoding()
852 : {
853 0 : BOOST_ASSERT(
854 : md.content_encoding.count > 0);
855 0 : --md.content_encoding.count;
856 0 : if(md.content_encoding.count == 0)
857 : {
858 : // no Content-Encoding
859 0 : md.content_encoding = {};
860 0 : return;
861 : }
862 : // re-insert everything
863 0 : --md.content_encoding.count;
864 : // TODO
865 : // on_insert_content_encoding();
866 : }
867 :
868 : // called when Upgrade is erased
869 : void
870 4 : header::
871 : on_erase_upgrade()
872 : {
873 4 : BOOST_ASSERT(
874 : md.upgrade.count > 0);
875 4 : --md.upgrade.count;
876 4 : if(md.upgrade.count == 0)
877 : {
878 : // no Upgrade
879 2 : md.upgrade = {};
880 2 : return;
881 : }
882 : // reset and re-insert
883 2 : auto n = md.upgrade.count;
884 2 : auto const p = cbuf + prefix;
885 2 : auto const* e = &tab()[0];
886 2 : md.upgrade = {};
887 4 : while(n > 0)
888 : {
889 2 : if(e->id == field::upgrade)
890 2 : on_insert_upgrade(
891 : core::string_view(
892 2 : p + e->vp, e->vn));
893 2 : --n;
894 2 : --e;
895 : }
896 : }
897 :
898 : //------------------------------------------------
899 :
900 : // called when all fields with id are removed
901 : void
902 72 : header::
903 : on_erase_all(
904 : field id)
905 : {
906 72 : if(kind == detail::kind::fields)
907 21 : return;
908 51 : switch(id)
909 : {
910 3 : case field::connection:
911 3 : md.connection = {};
912 3 : return;
913 :
914 2 : case field::content_length:
915 2 : md.content_length = {};
916 2 : update_payload();
917 2 : return;
918 :
919 5 : case field::expect:
920 5 : md.expect = {};
921 5 : update_payload();
922 5 : return;
923 :
924 1 : case field::transfer_encoding:
925 1 : md.transfer_encoding = {};
926 1 : update_payload();
927 1 : return;
928 :
929 1 : case field::upgrade:
930 1 : md.upgrade = {};
931 1 : return;
932 :
933 39 : default:
934 39 : break;
935 : }
936 : }
937 :
938 : //------------------------------------------------
939 :
940 : /* References:
941 :
942 : 3.3. Message Body
943 : https://datatracker.ietf.org/doc/html/rfc7230#section-3.3
944 :
945 : 3.3.1. Transfer-Encoding
946 : https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.1
947 :
948 : 3.3.2. Content-Length
949 : https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.2
950 : */
951 : void
952 19719 : header::
953 : update_payload() noexcept
954 : {
955 19719 : BOOST_ASSERT(kind !=
956 : detail::kind::fields);
957 19719 : if(md.payload_override)
958 : {
959 : // e.g. response to
960 : // a HEAD request
961 0 : return;
962 : }
963 :
964 : /* If there is an error in either Content-Length
965 : or Transfer-Encoding, then the payload is
966 : undefined. Clients should probably close the
967 : connection. Servers can send a Bad Request
968 : and avoid reading any payload bytes.
969 : */
970 19719 : if(md.content_length.ec.failed())
971 : {
972 : // invalid Content-Length
973 188 : md.payload = payload::error;
974 188 : md.payload_size = 0;
975 188 : return;
976 : }
977 19531 : if(md.transfer_encoding.ec.failed())
978 : {
979 : // invalid Transfer-Encoding
980 8 : md.payload = payload::error;
981 8 : md.payload_size = 0;
982 8 : return;
983 : }
984 :
985 : /* A sender MUST NOT send a Content-Length
986 : header field in any message that contains
987 : a Transfer-Encoding header field.
988 : https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.2
989 : */
990 19523 : if( md.content_length.count > 0 &&
991 4685 : md.transfer_encoding.count > 0)
992 : {
993 3 : md.payload = payload::error;
994 3 : md.payload_size = 0;
995 3 : return;
996 : }
997 :
998 19520 : if(kind == detail::kind::response)
999 1310 : goto do_response;
1000 :
1001 : //--------------------------------------------
1002 :
1003 : /* The presence of a message body in a
1004 : request is signaled by a Content-Length
1005 : or Transfer-Encoding header field. Request
1006 : message framing is independent of method
1007 : semantics, even if the method does not
1008 : define any use for a message body.
1009 : */
1010 18210 : if(md.content_length.count > 0)
1011 : {
1012 4420 : if(md.content_length.value > 0)
1013 : {
1014 : // non-zero Content-Length
1015 4393 : md.payload = payload::size;
1016 4393 : md.payload_size = md.content_length.value;
1017 4393 : return;
1018 : }
1019 : // Content-Length: 0
1020 27 : md.payload = payload::none;
1021 27 : md.payload_size = 0;
1022 27 : return;
1023 : }
1024 13790 : if(md.transfer_encoding.is_chunked)
1025 : {
1026 : // chunked
1027 4012 : md.payload = payload::chunked;
1028 4012 : md.payload_size = 0;
1029 4012 : return;
1030 : }
1031 : // no payload
1032 9778 : md.payload = payload::none;
1033 9778 : md.payload_size = 0;
1034 9778 : return;
1035 :
1036 : //--------------------------------------------
1037 1310 : do_response:
1038 :
1039 1310 : if( res.status_int / 100 == 1 || // 1xx e.g. Continue
1040 1299 : res.status_int == 204 || // No Content
1041 1297 : res.status_int == 304) // Not Modified
1042 : {
1043 : /* The correctness of any Content-Length
1044 : here is defined by the particular
1045 : resource, and cannot be determined
1046 : here. In any case there is no payload.
1047 : */
1048 15 : md.payload = payload::none;
1049 15 : md.payload_size = 0;
1050 15 : return;
1051 : }
1052 1295 : if(md.content_length.count > 0)
1053 : {
1054 259 : if(md.content_length.value > 0)
1055 : {
1056 : // Content-Length > 0
1057 237 : md.payload = payload::size;
1058 237 : md.payload_size = md.content_length.value;
1059 237 : return;
1060 : }
1061 : // Content-Length: 0
1062 22 : md.payload = payload::none;
1063 22 : md.payload_size = 0;
1064 22 : return;
1065 : }
1066 1036 : if(md.transfer_encoding.is_chunked)
1067 : {
1068 : // chunked
1069 193 : md.payload = payload::chunked;
1070 193 : md.payload_size = 0;
1071 193 : return;
1072 : }
1073 :
1074 : // eof needed
1075 843 : md.payload = payload::to_eof;
1076 843 : md.payload_size = 0;
1077 : }
1078 :
1079 : //------------------------------------------------
1080 :
1081 : std::size_t
1082 546 : header::
1083 : count_crlf(
1084 : core::string_view s) noexcept
1085 : {
1086 546 : auto it = s.data();
1087 546 : auto len = s.size();
1088 546 : std::size_t n = 0;
1089 18941 : while(len >= 2)
1090 : {
1091 18395 : if( it[0] == '\r' &&
1092 1737 : it[1] != '\r')
1093 : {
1094 1737 : if(it[1] == '\n')
1095 1737 : n++;
1096 1737 : it += 2;
1097 1737 : len -= 2;
1098 : }
1099 : else
1100 : {
1101 16658 : it++;
1102 16658 : len--;
1103 : }
1104 : }
1105 546 : return n;
1106 : }
1107 :
1108 : static
1109 : void
1110 14379 : parse_start_line(
1111 : header& h,
1112 : header_limits const& lim,
1113 : std::size_t new_size,
1114 : system::error_code& ec) noexcept
1115 : {
1116 14379 : BOOST_ASSERT(h.size == 0);
1117 14379 : BOOST_ASSERT(h.prefix == 0);
1118 14379 : BOOST_ASSERT(h.cbuf != nullptr);
1119 14379 : BOOST_ASSERT(
1120 : h.kind != detail::kind::fields);
1121 :
1122 14379 : auto const it0 = h.cbuf;
1123 14379 : auto const end = it0 + new_size;
1124 14379 : char const* it = it0;
1125 14379 : if( new_size > lim.max_start_line)
1126 36 : new_size = lim.max_start_line;
1127 14379 : if(h.kind == detail::kind::request)
1128 : {
1129 : auto rv = grammar::parse(
1130 12432 : it, end, request_line_rule);
1131 12432 : if(! rv)
1132 : {
1133 2693 : ec = rv.error();
1134 5386 : if( ec == grammar::error::need_more &&
1135 2693 : new_size == lim.max_start_line)
1136 0 : ec = BOOST_HTTP_PROTO_ERR(
1137 : error::start_line_limit);
1138 2693 : return;
1139 : }
1140 : // method
1141 9739 : auto sm = std::get<0>(*rv);
1142 9739 : h.req.method = string_to_method(sm);
1143 9739 : h.req.method_len =
1144 9739 : static_cast<header::offset_type>(sm.size());
1145 : // target
1146 9739 : auto st = std::get<1>(*rv);
1147 9739 : h.req.target_len =
1148 9739 : static_cast<header::offset_type>(st.size());
1149 : // version
1150 9739 : switch(std::get<2>(*rv))
1151 : {
1152 25 : case 10:
1153 25 : h.version =
1154 : http_proto::version::http_1_0;
1155 25 : break;
1156 9714 : case 11:
1157 9714 : h.version =
1158 : http_proto::version::http_1_1;
1159 9714 : break;
1160 0 : default:
1161 : {
1162 0 : ec = BOOST_HTTP_PROTO_ERR(
1163 : error::bad_version);
1164 0 : return;
1165 : }
1166 : }
1167 : }
1168 : else
1169 : {
1170 : auto rv = grammar::parse(
1171 1947 : it, end, status_line_rule);
1172 1947 : if(! rv)
1173 : {
1174 1112 : ec = rv.error();
1175 2224 : if( ec == grammar::error::need_more &&
1176 1112 : new_size == lim.max_start_line)
1177 0 : ec = BOOST_HTTP_PROTO_ERR(
1178 : error::start_line_limit);
1179 1112 : return;
1180 : }
1181 : // version
1182 835 : switch(std::get<0>(*rv))
1183 : {
1184 4 : case 10:
1185 4 : h.version =
1186 : http_proto::version::http_1_0;
1187 4 : break;
1188 831 : case 11:
1189 831 : h.version =
1190 : http_proto::version::http_1_1;
1191 831 : break;
1192 0 : default:
1193 : {
1194 0 : ec = BOOST_HTTP_PROTO_ERR(
1195 : error::bad_version);
1196 0 : return;
1197 : }
1198 : }
1199 : // status-code
1200 835 : h.res.status_int =
1201 : static_cast<unsigned short>(
1202 835 : std::get<1>(*rv).v);
1203 835 : h.res.status = std::get<1>(*rv).st;
1204 : }
1205 10574 : h.prefix = static_cast<header::offset_type>(it - it0);
1206 10574 : h.size = h.prefix;
1207 10574 : h.on_start_line();
1208 : }
1209 :
1210 : // returns: true if we added a field
1211 : static
1212 : void
1213 24856 : parse_field(
1214 : header& h,
1215 : header_limits const& lim,
1216 : std::size_t new_size,
1217 : system::error_code& ec) noexcept
1218 : {
1219 24856 : if( new_size > lim.max_field)
1220 96 : new_size = lim.max_field;
1221 24856 : auto const it0 = h.cbuf + h.size;
1222 24856 : auto const end = h.cbuf + new_size;
1223 24856 : char const* it = it0;
1224 24856 : auto rv = grammar::parse(
1225 : it, end, field_rule);
1226 24856 : if(rv.has_error())
1227 : {
1228 13388 : ec = rv.error();
1229 13388 : if(ec == grammar::error::end_of_range)
1230 : {
1231 : // final CRLF
1232 10549 : h.size = static_cast<
1233 10549 : header::offset_type>(it - h.cbuf);
1234 13388 : return;
1235 : }
1236 5419 : if( ec == grammar::error::need_more &&
1237 2580 : new_size == lim.max_field)
1238 : {
1239 0 : ec = BOOST_HTTP_PROTO_ERR(
1240 : error::field_size_limit);
1241 : }
1242 2839 : return;
1243 : }
1244 11468 : if(h.count >= lim.max_fields)
1245 : {
1246 0 : ec = BOOST_HTTP_PROTO_ERR(
1247 : error::fields_limit);
1248 0 : return;
1249 : }
1250 11468 : if(rv->has_obs_fold)
1251 : {
1252 : // obs fold not allowed in test views
1253 210 : BOOST_ASSERT(h.buf != nullptr);
1254 210 : remove_obs_fold(h.buf + h.size, it);
1255 : }
1256 11468 : auto id = string_to_field(rv->name)
1257 11468 : .value_or(header::unknown_field);
1258 11468 : h.size = static_cast<header::offset_type>(it - h.cbuf);
1259 :
1260 : // add field table entry
1261 11468 : if(h.buf != nullptr)
1262 : {
1263 22936 : auto& e = header::table(
1264 11468 : h.buf + h.cap)[h.count];
1265 11468 : auto const base =
1266 11468 : h.buf + h.prefix;
1267 11468 : e.np = static_cast<header::offset_type>(
1268 11468 : rv->name.data() - base);
1269 11468 : e.nn = static_cast<header::offset_type>(
1270 11468 : rv->name.size());
1271 11468 : e.vp = static_cast<header::offset_type>(
1272 11468 : rv->value.data() - base);
1273 11468 : e.vn = static_cast<header::offset_type>(
1274 11468 : rv->value.size());
1275 11468 : e.id = id;
1276 : }
1277 11468 : ++h.count;
1278 11468 : h.on_insert(id, rv->value);
1279 11468 : ec = {};
1280 : }
1281 :
1282 : void
1283 17193 : header::
1284 : parse(
1285 : std::size_t new_size,
1286 : header_limits const& lim,
1287 : system::error_code& ec) noexcept
1288 : {
1289 17193 : if( new_size > lim.max_size)
1290 36 : new_size = lim.max_size;
1291 17193 : if( this->prefix == 0 &&
1292 14613 : this->kind !=
1293 : detail::kind::fields)
1294 : {
1295 14379 : parse_start_line(
1296 : *this, lim, new_size, ec);
1297 14379 : if(ec.failed())
1298 : {
1299 7610 : if( ec == grammar::error::need_more &&
1300 3805 : new_size == lim.max_fields)
1301 : {
1302 0 : ec = BOOST_HTTP_PROTO_ERR(
1303 : error::headers_limit);
1304 : }
1305 3805 : return;
1306 : }
1307 : }
1308 : for(;;)
1309 : {
1310 24856 : parse_field(
1311 : *this, lim, new_size, ec);
1312 24856 : if(ec.failed())
1313 : {
1314 15968 : if( ec == grammar::error::need_more &&
1315 2580 : new_size == lim.max_size)
1316 : {
1317 0 : ec = BOOST_HTTP_PROTO_ERR(
1318 : error::headers_limit);
1319 0 : return;
1320 : }
1321 13388 : break;
1322 : }
1323 11468 : }
1324 13388 : if(ec == grammar::error::end_of_range)
1325 10549 : ec = {};
1326 : }
1327 :
1328 : } // detail
1329 : } // http_proto
1330 : } // boost
|