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