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