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