]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/boost/beast/http/impl/basic_parser.ipp
update sources to v12.2.3
[ceph.git] / ceph / src / boost / boost / beast / http / impl / basic_parser.ipp
1 //
2 // Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot 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/boostorg/beast
8 //
9
10 #ifndef BOOST_BEAST_HTTP_IMPL_BASIC_PARSER_IPP
11 #define BOOST_BEAST_HTTP_IMPL_BASIC_PARSER_IPP
12
13 #include <boost/beast/core/static_string.hpp>
14 #include <boost/beast/core/type_traits.hpp>
15 #include <boost/beast/core/detail/clamp.hpp>
16 #include <boost/beast/core/detail/config.hpp>
17 #include <boost/beast/http/error.hpp>
18 #include <boost/beast/http/rfc7230.hpp>
19 #include <boost/asio/buffer.hpp>
20 #include <boost/make_unique.hpp>
21 #include <algorithm>
22 #include <utility>
23
24 namespace boost {
25 namespace beast {
26 namespace http {
27
28 template<bool isRequest, class Derived>
29 template<class OtherDerived>
30 basic_parser<isRequest, Derived>::
31 basic_parser(basic_parser<
32 isRequest, OtherDerived>&& other)
33 : body_limit_(other.body_limit_)
34 , len_(other.len_)
35 , buf_(std::move(other.buf_))
36 , buf_len_(other.buf_len_)
37 , skip_(other.skip_)
38 , header_limit_(other.header_limit_)
39 , status_(other.status_)
40 , state_(other.state_)
41 , f_(other.f_)
42 {
43 }
44
45 template<bool isRequest, class Derived>
46 bool
47 basic_parser<isRequest, Derived>::
48 keep_alive() const
49 {
50 BOOST_ASSERT(is_header_done());
51 if(f_ & flagHTTP11)
52 {
53 if(f_ & flagConnectionClose)
54 return false;
55 }
56 else
57 {
58 if(! (f_ & flagConnectionKeepAlive))
59 return false;
60 }
61 return (f_ & flagNeedEOF) == 0;
62 }
63
64 template<bool isRequest, class Derived>
65 boost::optional<std::uint64_t>
66 basic_parser<isRequest, Derived>::
67 content_length() const
68 {
69 BOOST_ASSERT(is_header_done());
70 if(! (f_ & flagContentLength))
71 return boost::none;
72 return len_;
73 }
74
75 template<bool isRequest, class Derived>
76 void
77 basic_parser<isRequest, Derived>::
78 skip(bool v)
79 {
80 BOOST_ASSERT(! got_some());
81 if(v)
82 f_ |= flagSkipBody;
83 else
84 f_ &= ~flagSkipBody;
85 }
86
87 template<bool isRequest, class Derived>
88 template<class ConstBufferSequence>
89 std::size_t
90 basic_parser<isRequest, Derived>::
91 put(ConstBufferSequence const& buffers,
92 error_code& ec)
93 {
94 static_assert(boost::asio::is_const_buffer_sequence<
95 ConstBufferSequence>::value,
96 "ConstBufferSequence requirements not met");
97 using boost::asio::buffer_copy;
98 using boost::asio::buffer_size;
99 auto const p = boost::asio::buffer_sequence_begin(buffers);
100 auto const last = boost::asio::buffer_sequence_end(buffers);
101 if(p == last)
102 {
103 ec.assign(0, ec.category());
104 return 0;
105 }
106 if(std::next(p) == last)
107 {
108 // single buffer
109 return put(boost::asio::const_buffer(*p), ec);
110 }
111 auto const size = buffer_size(buffers);
112 if(size <= max_stack_buffer)
113 return put_from_stack(size, buffers, ec);
114 if(size > buf_len_)
115 {
116 // reallocate
117 buf_ = boost::make_unique_noinit<char[]>(size);
118 buf_len_ = size;
119 }
120 // flatten
121 buffer_copy(boost::asio::buffer(
122 buf_.get(), buf_len_), buffers);
123 return put(boost::asio::const_buffer{
124 buf_.get(), buf_len_}, ec);
125 }
126
127 template<bool isRequest, class Derived>
128 std::size_t
129 basic_parser<isRequest, Derived>::
130 put(boost::asio::const_buffer const& buffer,
131 error_code& ec)
132 {
133 BOOST_ASSERT(state_ != state::complete);
134 using boost::asio::buffer_size;
135 auto p = reinterpret_cast<
136 char const*>(buffer.data());
137 auto n = buffer.size();
138 auto const p0 = p;
139 auto const p1 = p0 + n;
140 ec.assign(0, ec.category());
141 loop:
142 switch(state_)
143 {
144 case state::nothing_yet:
145 if(n == 0)
146 {
147 ec = error::need_more;
148 return 0;
149 }
150 state_ = state::start_line;
151 BOOST_BEAST_FALLTHROUGH;
152
153 case state::start_line:
154 {
155 maybe_need_more(p, n, ec);
156 if(ec)
157 goto done;
158 parse_start_line(p, p + (std::min<std::size_t>)(
159 header_limit_, n), ec, is_request{});
160 if(ec)
161 {
162 if(ec == error::need_more)
163 {
164 if(n >= header_limit_)
165 {
166 ec = error::header_limit;
167 goto done;
168 }
169 if(p + 3 <= p1)
170 skip_ = static_cast<
171 std::size_t>(p1 - p - 3);
172 }
173 goto done;
174 }
175 BOOST_ASSERT(! is_done());
176 n = static_cast<std::size_t>(p1 - p);
177 if(p >= p1)
178 {
179 ec = error::need_more;
180 goto done;
181 }
182 BOOST_BEAST_FALLTHROUGH;
183 }
184
185 case state::fields:
186 maybe_need_more(p, n, ec);
187 if(ec)
188 goto done;
189 parse_fields(p, p + (std::min<std::size_t>)(
190 header_limit_, n), ec);
191 if(ec)
192 {
193 if(ec == error::need_more)
194 {
195 if(n >= header_limit_)
196 {
197 ec = error::header_limit;
198 goto done;
199 }
200 if(p + 3 <= p1)
201 skip_ = static_cast<
202 std::size_t>(p1 - p - 3);
203 }
204 goto done;
205 }
206 finish_header(ec, is_request{});
207 break;
208
209 case state::body0:
210 BOOST_ASSERT(! skip_);
211 impl().on_body_init_impl(content_length(), ec);
212 if(ec)
213 goto done;
214 state_ = state::body;
215 BOOST_BEAST_FALLTHROUGH;
216
217 case state::body:
218 BOOST_ASSERT(! skip_);
219 parse_body(p, n, ec);
220 if(ec)
221 goto done;
222 break;
223
224 case state::body_to_eof0:
225 BOOST_ASSERT(! skip_);
226 impl().on_body_init_impl(content_length(), ec);
227 if(ec)
228 goto done;
229 state_ = state::body_to_eof;
230 BOOST_BEAST_FALLTHROUGH;
231
232 case state::body_to_eof:
233 BOOST_ASSERT(! skip_);
234 parse_body_to_eof(p, n, ec);
235 if(ec)
236 goto done;
237 break;
238
239 case state::chunk_header0:
240 impl().on_body_init_impl(content_length(), ec);
241 if(ec)
242 goto done;
243 state_ = state::chunk_header;
244 BOOST_BEAST_FALLTHROUGH;
245
246 case state::chunk_header:
247 parse_chunk_header(p, n, ec);
248 if(ec)
249 goto done;
250 break;
251
252 case state::chunk_body:
253 parse_chunk_body(p, n, ec);
254 if(ec)
255 goto done;
256 break;
257
258 case state::complete:
259 ec.assign(0, ec.category());
260 goto done;
261 }
262 if(p < p1 && ! is_done() && eager())
263 {
264 n = static_cast<std::size_t>(p1 - p);
265 goto loop;
266 }
267 done:
268 return static_cast<std::size_t>(p - p0);
269 }
270
271 template<bool isRequest, class Derived>
272 void
273 basic_parser<isRequest, Derived>::
274 put_eof(error_code& ec)
275 {
276 BOOST_ASSERT(got_some());
277 if( state_ == state::start_line ||
278 state_ == state::fields)
279 {
280 ec = error::partial_message;
281 return;
282 }
283 if(f_ & (flagContentLength | flagChunked))
284 {
285 if(state_ != state::complete)
286 {
287 ec = error::partial_message;
288 return;
289 }
290 ec.assign(0, ec.category());
291 return;
292 }
293 impl().on_finish_impl(ec);
294 if(ec)
295 return;
296 state_ = state::complete;
297 }
298
299 template<bool isRequest, class Derived>
300 template<class ConstBufferSequence>
301 std::size_t
302 basic_parser<isRequest, Derived>::
303 put_from_stack(std::size_t size,
304 ConstBufferSequence const& buffers,
305 error_code& ec)
306 {
307 char buf[max_stack_buffer];
308 using boost::asio::buffer;
309 using boost::asio::buffer_copy;
310 buffer_copy(buffer(buf, sizeof(buf)), buffers);
311 return put(boost::asio::const_buffer{
312 buf, size}, ec);
313 }
314
315 template<bool isRequest, class Derived>
316 inline
317 void
318 basic_parser<isRequest, Derived>::
319 maybe_need_more(
320 char const* p, std::size_t n,
321 error_code& ec)
322 {
323 if(skip_ == 0)
324 return;
325 if( n > header_limit_)
326 n = header_limit_;
327 if(n < skip_ + 4)
328 {
329 ec = error::need_more;
330 return;
331 }
332 auto const term =
333 find_eom(p + skip_, p + n);
334 if(! term)
335 {
336 skip_ = n - 3;
337 if(skip_ + 4 > header_limit_)
338 {
339 ec = error::header_limit;
340 return;
341 }
342 ec = error::need_more;
343 return;
344 }
345 skip_ = 0;
346 }
347
348 template<bool isRequest, class Derived>
349 inline
350 void
351 basic_parser<isRequest, Derived>::
352 parse_start_line(
353 char const*& in, char const* last,
354 error_code& ec, std::true_type)
355 {
356 /*
357 request-line = method SP request-target SP HTTP-version CRLF
358 method = token
359 */
360 auto p = in;
361
362 string_view method;
363 parse_method(p, last, method, ec);
364 if(ec)
365 return;
366
367 string_view target;
368 parse_target(p, last, target, ec);
369 if(ec)
370 return;
371
372 int version = 0;
373 parse_version(p, last, version, ec);
374 if(ec)
375 return;
376 if(version < 10 || version > 11)
377 {
378 ec = error::bad_version;
379 return;
380 }
381
382 if(p + 2 > last)
383 {
384 ec = error::need_more;
385 return;
386 }
387 if(p[0] != '\r' || p[1] != '\n')
388 {
389 ec = error::bad_version;
390 return;
391 }
392 p += 2;
393
394 if(version >= 11)
395 f_ |= flagHTTP11;
396
397 impl().on_request_impl(string_to_verb(method),
398 method, target, version, ec);
399 if(ec)
400 return;
401
402 in = p;
403 state_ = state::fields;
404 }
405
406 template<bool isRequest, class Derived>
407 inline
408 void
409 basic_parser<isRequest, Derived>::
410 parse_start_line(
411 char const*& in, char const* last,
412 error_code& ec, std::false_type)
413 {
414 /*
415 status-line = HTTP-version SP status-code SP reason-phrase CRLF
416 status-code = 3*DIGIT
417 reason-phrase = *( HTAB / SP / VCHAR / obs-text )
418 */
419 auto p = in;
420
421 int version = 0;
422 parse_version(p, last, version, ec);
423 if(ec)
424 return;
425 if(version < 10 || version > 11)
426 {
427 ec = error::bad_version;
428 return;
429 }
430
431 // SP
432 if(p + 1 > last)
433 {
434 ec = error::need_more;
435 return;
436 }
437 if(*p++ != ' ')
438 {
439 ec = error::bad_version;
440 return;
441 }
442
443 parse_status(p, last, status_, ec);
444 if(ec)
445 return;
446
447 // parse reason CRLF
448 string_view reason;
449 parse_reason(p, last, reason, ec);
450 if(ec)
451 return;
452
453 if(version >= 11)
454 f_ |= flagHTTP11;
455
456 impl().on_response_impl(
457 status_, reason, version, ec);
458 if(ec)
459 return;
460
461 in = p;
462 state_ = state::fields;
463 }
464
465 template<bool isRequest, class Derived>
466 void
467 basic_parser<isRequest, Derived>::
468 parse_fields(char const*& in,
469 char const* last, error_code& ec)
470 {
471 string_view name;
472 string_view value;
473 // https://stackoverflow.com/questions/686217/maximum-on-http-header-values
474 static_string<max_obs_fold> buf;
475 auto p = in;
476 for(;;)
477 {
478 if(p + 2 > last)
479 {
480 ec = error::need_more;
481 return;
482 }
483 if(p[0] == '\r')
484 {
485 if(p[1] != '\n')
486 ec = error::bad_line_ending;
487 in = p + 2;
488 return;
489 }
490 parse_field(p, last, name, value, buf, ec);
491 if(ec)
492 return;
493 auto const f = string_to_field(name);
494 do_field(f, value, ec);
495 if(ec)
496 return;
497 impl().on_field_impl(f, name, value, ec);
498 if(ec)
499 return;
500 in = p;
501 }
502 }
503
504 template<bool isRequest, class Derived>
505 inline
506 void
507 basic_parser<isRequest, Derived>::
508 finish_header(error_code& ec, std::true_type)
509 {
510 // RFC 7230 section 3.3
511 // https://tools.ietf.org/html/rfc7230#section-3.3
512
513 if(f_ & flagSkipBody)
514 {
515 state_ = state::complete;
516 }
517 else if(f_ & flagContentLength)
518 {
519 if(len_ > body_limit_)
520 {
521 ec = error::body_limit;
522 return;
523 }
524 if(len_ > 0)
525 {
526 f_ |= flagHasBody;
527 state_ = state::body0;
528 }
529 else
530 {
531 state_ = state::complete;
532 }
533 }
534 else if(f_ & flagChunked)
535 {
536 f_ |= flagHasBody;
537 state_ = state::chunk_header0;
538 }
539 else
540 {
541 len_ = 0;
542 state_ = state::complete;
543 }
544
545 impl().on_header_impl(ec);
546 if(ec)
547 return;
548 if(state_ == state::complete)
549 {
550 impl().on_finish_impl(ec);
551 if(ec)
552 return;
553 }
554 }
555
556 template<bool isRequest, class Derived>
557 inline
558 void
559 basic_parser<isRequest, Derived>::
560 finish_header(error_code& ec, std::false_type)
561 {
562 // RFC 7230 section 3.3
563 // https://tools.ietf.org/html/rfc7230#section-3.3
564
565 if( (f_ & flagSkipBody) || // e.g. response to a HEAD request
566 status_ / 100 == 1 || // 1xx e.g. Continue
567 status_ == 204 || // No Content
568 status_ == 304) // Not Modified
569 {
570 // VFALCO Content-Length may be present, but we
571 // treat the message as not having a body.
572 // https://github.com/boostorg/beast/issues/692
573 state_ = state::complete;
574 }
575 else if(f_ & flagContentLength)
576 {
577 if(len_ > body_limit_)
578 {
579 ec = error::body_limit;
580 return;
581 }
582 if(len_ > 0)
583 {
584 f_ |= flagHasBody;
585 state_ = state::body0;
586 }
587 else
588 {
589 state_ = state::complete;
590 }
591 }
592 else if(f_ & flagChunked)
593 {
594 f_ |= flagHasBody;
595 state_ = state::chunk_header0;
596 }
597 else
598 {
599 f_ |= flagHasBody;
600 f_ |= flagNeedEOF;
601 state_ = state::body_to_eof0;
602 }
603
604 impl().on_header_impl(ec);
605 if(ec)
606 return;
607 if(state_ == state::complete)
608 {
609 impl().on_finish_impl(ec);
610 if(ec)
611 return;
612 }
613 }
614
615 template<bool isRequest, class Derived>
616 inline
617 void
618 basic_parser<isRequest, Derived>::
619 parse_body(char const*& p,
620 std::size_t n, error_code& ec)
621 {
622 n = impl().on_body_impl(string_view{p,
623 beast::detail::clamp(len_, n)}, ec);
624 p += n;
625 len_ -= n;
626 if(ec)
627 return;
628 if(len_ > 0)
629 return;
630 impl().on_finish_impl(ec);
631 if(ec)
632 return;
633 state_ = state::complete;
634 }
635
636 template<bool isRequest, class Derived>
637 inline
638 void
639 basic_parser<isRequest, Derived>::
640 parse_body_to_eof(char const*& p,
641 std::size_t n, error_code& ec)
642 {
643 if(n > body_limit_)
644 {
645 ec = error::body_limit;
646 return;
647 }
648 body_limit_ = body_limit_ - n;
649 n = impl().on_body_impl(string_view{p, n}, ec);
650 p += n;
651 if(ec)
652 return;
653 }
654
655 template<bool isRequest, class Derived>
656 void
657 basic_parser<isRequest, Derived>::
658 parse_chunk_header(char const*& p0,
659 std::size_t n, error_code& ec)
660 {
661 /*
662 chunked-body = *chunk last-chunk trailer-part CRLF
663
664 chunk = chunk-size [ chunk-ext ] CRLF chunk-data CRLF
665 last-chunk = 1*("0") [ chunk-ext ] CRLF
666 trailer-part = *( header-field CRLF )
667
668 chunk-size = 1*HEXDIG
669 chunk-data = 1*OCTET ; a sequence of chunk-size octets
670 chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
671 chunk-ext-name = token
672 chunk-ext-val = token / quoted-string
673 */
674
675 auto p = p0;
676 auto const pend = p + n;
677 char const* eol;
678
679 if(! (f_ & flagFinalChunk))
680 {
681 if(n < skip_ + 2)
682 {
683 ec = error::need_more;
684 return;
685 }
686 if(f_ & flagExpectCRLF)
687 {
688 // Treat the last CRLF in a chunk as
689 // part of the next chunk, so p can
690 // be parsed in one call instead of two.
691 if(! parse_crlf(p))
692 {
693 ec = error::bad_chunk;
694 return;
695 }
696 }
697 eol = find_eol(p0 + skip_, pend, ec);
698 if(ec)
699 return;
700 if(! eol)
701 {
702 ec = error::need_more;
703 skip_ = n - 1;
704 return;
705 }
706 skip_ = static_cast<
707 std::size_t>(eol - 2 - p0);
708
709 std::uint64_t size;
710 if(! parse_hex(p, size))
711 {
712 ec = error::bad_chunk;
713 return;
714 }
715 if(size != 0)
716 {
717 if(size > body_limit_)
718 {
719 ec = error::body_limit;
720 return;
721 }
722 body_limit_ -= size;
723 auto const start = p;
724 parse_chunk_extensions(p, pend, ec);
725 if(ec)
726 return;
727 if(p != eol -2 )
728 {
729 ec = error::bad_chunk_extension;
730 return;
731 }
732 auto const ext = make_string(start, p);
733 impl().on_chunk_header_impl(size, ext, ec);
734 if(ec)
735 return;
736 len_ = size;
737 skip_ = 2;
738 p0 = eol;
739 f_ |= flagExpectCRLF;
740 state_ = state::chunk_body;
741 return;
742 }
743
744 f_ |= flagFinalChunk;
745 }
746 else
747 {
748 BOOST_ASSERT(n >= 5);
749 if(f_ & flagExpectCRLF)
750 BOOST_VERIFY(parse_crlf(p));
751 std::uint64_t size;
752 BOOST_VERIFY(parse_hex(p, size));
753 eol = find_eol(p, pend, ec);
754 BOOST_ASSERT(! ec);
755 }
756
757 auto eom = find_eom(p0 + skip_, pend);
758 if(! eom)
759 {
760 BOOST_ASSERT(n >= 3);
761 skip_ = n - 3;
762 ec = error::need_more;
763 return;
764 }
765
766 auto const start = p;
767 parse_chunk_extensions(p, pend, ec);
768 if(ec)
769 return;
770 if(p != eol - 2)
771 {
772 ec = error::bad_chunk_extension;
773 return;
774 }
775 auto const ext = make_string(start, p);
776 impl().on_chunk_header_impl(0, ext, ec);
777 if(ec)
778 return;
779 p = eol;
780 parse_fields(p, eom, ec);
781 if(ec)
782 return;
783 BOOST_ASSERT(p == eom);
784 p0 = eom;
785
786 impl().on_finish_impl(ec);
787 if(ec)
788 return;
789 state_ = state::complete;
790 }
791
792 template<bool isRequest, class Derived>
793 inline
794 void
795 basic_parser<isRequest, Derived>::
796 parse_chunk_body(char const*& p,
797 std::size_t n, error_code& ec)
798 {
799 n = impl().on_chunk_body_impl(
800 len_, string_view{p,
801 beast::detail::clamp(len_, n)}, ec);
802 p += n;
803 len_ -= n;
804 if(len_ == 0)
805 state_ = state::chunk_header;
806 }
807
808 template<bool isRequest, class Derived>
809 void
810 basic_parser<isRequest, Derived>::
811 do_field(field f,
812 string_view value, error_code& ec)
813 {
814 // Connection
815 if(f == field::connection ||
816 f == field::proxy_connection)
817 {
818 auto const list = opt_token_list{value};
819 if(! validate_list(list))
820 {
821 // VFALCO Should this be a field specific error?
822 ec = error::bad_value;
823 return;
824 }
825 for(auto const& s : list)
826 {
827 if(iequals({"close", 5}, s))
828 {
829 f_ |= flagConnectionClose;
830 continue;
831 }
832
833 if(iequals({"keep-alive", 10}, s))
834 {
835 f_ |= flagConnectionKeepAlive;
836 continue;
837 }
838
839 if(iequals({"upgrade", 7}, s))
840 {
841 f_ |= flagConnectionUpgrade;
842 continue;
843 }
844 }
845 ec.assign(0, ec.category());
846 return;
847 }
848
849 // Content-Length
850 if(f == field::content_length)
851 {
852 if(f_ & flagContentLength)
853 {
854 // duplicate
855 ec = error::bad_content_length;
856 return;
857 }
858
859 if(f_ & flagChunked)
860 {
861 // conflicting field
862 ec = error::bad_content_length;
863 return;
864 }
865
866 std::uint64_t v;
867 if(! parse_dec(
868 value.begin(), value.end(), v))
869 {
870 ec = error::bad_content_length;
871 return;
872 }
873
874 ec.assign(0, ec.category());
875 len_ = v;
876 f_ |= flagContentLength;
877 return;
878 }
879
880 // Transfer-Encoding
881 if(f == field::transfer_encoding)
882 {
883 if(f_ & flagChunked)
884 {
885 // duplicate
886 ec = error::bad_transfer_encoding;
887 return;
888 }
889
890 if(f_ & flagContentLength)
891 {
892 // conflicting field
893 ec = error::bad_transfer_encoding;
894 return;
895 }
896
897 ec.assign(0, ec.category());
898 auto const v = token_list{value};
899 auto const p = std::find_if(v.begin(), v.end(),
900 [&](typename token_list::value_type const& s)
901 {
902 return iequals({"chunked", 7}, s);
903 });
904 if(p == v.end())
905 return;
906 if(std::next(p) != v.end())
907 return;
908 len_ = 0;
909 f_ |= flagChunked;
910 return;
911 }
912
913 // Upgrade
914 if(f == field::upgrade)
915 {
916 ec.assign(0, ec.category());
917 f_ |= flagUpgrade;
918 return;
919 }
920
921 ec.assign(0, ec.category());
922 }
923
924 } // http
925 } // beast
926 } // boost
927
928 #endif