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