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