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