]> git.proxmox.com Git - ceph.git/blob - ceph/src/Beast/include/beast/http/impl/basic_parser.ipp
bump version to 12.2.2-pve1
[ceph.git] / ceph / src / Beast / include / beast / http / impl / basic_parser.ipp
1 //
2 // Copyright (c) 2013-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
8 #ifndef BEAST_HTTP_IMPL_BASIC_PARSER_IPP
9 #define BEAST_HTTP_IMPL_BASIC_PARSER_IPP
10
11 #include <beast/core/buffer_concepts.hpp>
12 #include <beast/core/detail/ci_char_traits.hpp>
13 #include <beast/core/detail/clamp.hpp>
14 #include <beast/core/detail/type_traits.hpp>
15 #include <beast/http/error.hpp>
16 #include <beast/http/rfc7230.hpp>
17 #include <boost/asio/buffer.hpp>
18 #include <algorithm>
19 #include <utility>
20
21 namespace beast {
22 namespace http {
23
24 template<bool isRequest, bool isDirect, class Derived>
25 template<bool OtherIsDirect, class OtherDerived>
26 basic_parser<isRequest, isDirect, Derived>::
27 basic_parser(basic_parser<isRequest,
28 OtherIsDirect, OtherDerived>&& other)
29 : len_(other.len_)
30 , buf_(std::move(other.buf_))
31 , buf_len_(other.buf_len_)
32 , skip_(other.skip_)
33 , x_(other.x_)
34 , f_(other.f_)
35 , state_(other.state_)
36 {
37 }
38
39 template<bool isRequest, bool isDirect, class Derived>
40 void
41 basic_parser<isRequest, isDirect, Derived>::
42 skip_body()
43 {
44 BOOST_ASSERT(! got_some());
45 f_ |= flagSkipBody;
46 }
47
48 template<bool isRequest, bool isDirect, class Derived>
49 bool
50 basic_parser<isRequest, isDirect, Derived>::
51 is_keep_alive() const
52 {
53 BOOST_ASSERT(got_header());
54 if(f_ & flagHTTP11)
55 {
56 if(f_ & flagConnectionClose)
57 return false;
58 }
59 else
60 {
61 if(! (f_ & flagConnectionKeepAlive))
62 return false;
63 }
64 return (f_ & flagNeedEOF) == 0;
65 }
66
67 template<bool isRequest, bool isDirect, class Derived>
68 template<class ConstBufferSequence>
69 std::size_t
70 basic_parser<isRequest, isDirect, Derived>::
71 write(ConstBufferSequence const& buffers,
72 error_code& ec)
73 {
74 static_assert(is_ConstBufferSequence<
75 ConstBufferSequence>::value,
76 "ConstBufferSequence requirements not met");
77 auto const buffer = maybe_flatten(buffers);
78 return write(boost::asio::const_buffers_1{
79 buffer.data(), buffer.size()}, ec);
80 }
81
82 template<bool isRequest, bool isDirect, class Derived>
83 std::size_t
84 basic_parser<isRequest, isDirect, Derived>::
85 write(boost::asio::const_buffers_1 const& buffer,
86 error_code& ec)
87 {
88 return do_write(buffer, ec,
89 std::integral_constant<bool, isDirect>{});
90 }
91
92 template<bool isRequest, bool isDirect, class Derived>
93 void
94 basic_parser<isRequest, isDirect, Derived>::
95 write_eof(error_code& ec)
96 {
97 BOOST_ASSERT(got_some());
98 if(state_ == parse_state::header)
99 {
100 ec = error::partial_message;
101 return;
102 }
103 if(f_ & (flagContentLength | flagChunked))
104 {
105 if(state_ != parse_state::complete)
106 {
107 ec = error::partial_message;
108 return;
109 }
110 return;
111 }
112 do_complete(ec);
113 if(ec)
114 return;
115 }
116
117 template<bool isRequest, bool isDirect, class Derived>
118 template<class DynamicBuffer>
119 std::size_t
120 basic_parser<isRequest, isDirect, Derived>::
121 copy_body(DynamicBuffer& dynabuf)
122 {
123 // This function not available when isDirect==false
124 static_assert(isDirect, "");
125
126 using boost::asio::buffer_copy;
127 using boost::asio::buffer_size;
128 BOOST_ASSERT(dynabuf.size() > 0);
129 BOOST_ASSERT(
130 state_ == parse_state::body ||
131 state_ == parse_state::body_to_eof ||
132 state_ == parse_state::chunk_body);
133 maybe_do_body_direct();
134 switch(state_)
135 {
136 case parse_state::body_to_eof:
137 {
138 auto const buffers =
139 impl().on_prepare(dynabuf.size());
140 BOOST_ASSERT(
141 buffer_size(buffers) >= 1 &&
142 buffer_size(buffers) <=
143 dynabuf.size());
144 auto const n = buffer_copy(
145 buffers, dynabuf.data());
146 dynabuf.consume(n);
147 impl().on_commit(n);
148 return n;
149 }
150
151 default:
152 {
153 BOOST_ASSERT(len_ > 0);
154 auto const buffers =
155 impl().on_prepare(
156 beast::detail::clamp(len_));
157 BOOST_ASSERT(
158 buffer_size(buffers) >= 1 &&
159 buffer_size(buffers) <=
160 beast::detail::clamp(len_));
161 auto const n = buffer_copy(
162 buffers, dynabuf.data());
163 commit_body(n);
164 return n;
165 }
166 }
167 }
168
169 template<bool isRequest, bool isDirect, class Derived>
170 template<class MutableBufferSequence>
171 void
172 basic_parser<isRequest, isDirect, Derived>::
173 prepare_body(boost::optional<
174 MutableBufferSequence>& buffers, std::size_t limit)
175 {
176 // This function not available when isDirect==false
177 static_assert(isDirect, "");
178
179 BOOST_ASSERT(limit > 0);
180 BOOST_ASSERT(
181 state_ == parse_state::body ||
182 state_ == parse_state::body_to_eof ||
183 state_ == parse_state::chunk_body);
184 maybe_do_body_direct();
185 std::size_t n;
186 switch(state_)
187 {
188 case parse_state::body_to_eof:
189 n = limit;
190 break;
191
192 default:
193 BOOST_ASSERT(len_ > 0);
194 n = beast::detail::clamp(len_, limit);
195 break;
196 }
197 buffers.emplace(impl().on_prepare(n));
198 }
199
200 template<bool isRequest, bool isDirect, class Derived>
201 void
202 basic_parser<isRequest, isDirect, Derived>::
203 commit_body(std::size_t n)
204 {
205 // This function not available when isDirect==false
206 static_assert(isDirect, "");
207
208 BOOST_ASSERT(f_ & flagOnBody);
209 impl().on_commit(n);
210 switch(state_)
211 {
212 case parse_state::body:
213 len_ -= n;
214 if(len_ == 0)
215 {
216 // VFALCO This is no good, throwing out ec?
217 error_code ec;
218 do_complete(ec);
219 }
220 break;
221
222 case parse_state::chunk_body:
223 len_ -= n;
224 if(len_ == 0)
225 state_ = parse_state::chunk_header;
226 break;
227
228 default:
229 break;
230 }
231 }
232
233 template<bool isRequest, bool isDirect, class Derived>
234 void
235 basic_parser<isRequest, isDirect, Derived>::
236 consume_body(error_code& ec)
237 {
238 BOOST_ASSERT(
239 state_ == parse_state::body ||
240 state_ == parse_state::body_to_eof ||
241 state_ == parse_state::chunk_body);
242 switch(state_)
243 {
244 case parse_state::body:
245 case parse_state::body_to_eof:
246 do_complete(ec);
247 if(ec)
248 return;
249 break;
250
251 case parse_state::chunk_body:
252 len_ = 0;
253 state_ = parse_state::chunk_header;
254 break;
255
256 default:
257 break;
258 }
259 }
260
261 template<bool isRequest, bool isDirect, class Derived>
262 template<class ConstBufferSequence>
263 inline
264 boost::string_ref
265 basic_parser<isRequest, isDirect, Derived>::
266 maybe_flatten(
267 ConstBufferSequence const& buffers)
268 {
269 using boost::asio::buffer;
270 using boost::asio::buffer_cast;
271 using boost::asio::buffer_copy;
272 using boost::asio::buffer_size;
273
274 auto const it = buffers.begin();
275 auto const last = buffers.end();
276 if(it == last)
277 return {nullptr, 0};
278 if(std::next(it) == last)
279 {
280 // single buffer
281 auto const b = *it;
282 return {buffer_cast<char const*>(b),
283 buffer_size(b)};
284 }
285 auto const len = buffer_size(buffers);
286 if(len > buf_len_)
287 {
288 // reallocate
289 buf_.reset(new char[len]);
290 buf_len_ = len;
291 }
292 // flatten
293 buffer_copy(
294 buffer(buf_.get(), buf_len_), buffers);
295 return {buf_.get(), buf_len_};
296 }
297
298 template<bool isRequest, bool isDirect, class Derived>
299 inline
300 std::size_t
301 basic_parser<isRequest, isDirect, Derived>::
302 do_write(boost::asio::const_buffers_1 const& buffer,
303 error_code& ec, std::true_type)
304 {
305 BOOST_ASSERT(
306 state_ == parse_state::header ||
307 state_ == parse_state::chunk_header);
308 using boost::asio::buffer_cast;
309 using boost::asio::buffer_size;
310 auto const p = buffer_cast<
311 char const*>(*buffer.begin());
312 auto const n =
313 buffer_size(*buffer.begin());
314 if(state_ == parse_state::header)
315 {
316 if(n > 0)
317 f_ |= flagGotSome;
318 return parse_header(p, n, ec);
319 }
320 else
321 {
322 maybe_do_body_direct();
323 return parse_chunk_header(p, n, ec);
324 }
325 }
326
327 template<bool isRequest, bool isDirect, class Derived>
328 inline
329 std::size_t
330 basic_parser<isRequest, isDirect, Derived>::
331 do_write(boost::asio::const_buffers_1 const& buffer,
332 error_code& ec, std::false_type)
333 {
334 BOOST_ASSERT(state_ != parse_state::complete);
335 using boost::asio::buffer_cast;
336 using boost::asio::buffer_size;
337 auto const p = buffer_cast<
338 char const*>(*buffer.begin());
339 auto const n =
340 buffer_size(*buffer.begin());
341 switch(state_)
342 {
343 case parse_state::header:
344 if(n > 0)
345 f_ |= flagGotSome;
346 return parse_header(p, n, ec);
347
348 case parse_state::body:
349 maybe_do_body_indirect(ec);
350 if(ec)
351 return 0;
352 return parse_body(p, n, ec);
353
354 case parse_state::body_to_eof:
355 maybe_do_body_indirect(ec);
356 if(ec)
357 return 0;
358 return parse_body_to_eof(p, n, ec);
359
360 case parse_state::chunk_header:
361 maybe_do_body_indirect(ec);
362 if(ec)
363 return 0;
364 return parse_chunk_header(p, n, ec);
365
366 case parse_state::chunk_body:
367 return parse_chunk_body(p, n, ec);
368
369 case parse_state::complete:
370 break;
371 }
372 return 0;
373 }
374
375
376 template<bool isRequest, bool isDirect, class Derived>
377 void
378 basic_parser<isRequest, isDirect, Derived>::
379 parse_startline(char const*& it,
380 int& version, int& status,
381 error_code& ec, std::true_type)
382 {
383 /*
384 request-line = method SP request-target SP HTTP-version CRLF
385 method = token
386 */
387 auto const method = parse_method(it);
388 if(method.empty())
389 {
390 ec = error::bad_method;
391 return;
392 }
393 if(*it++ != ' ')
394 {
395 ec = error::bad_method;
396 return;
397 }
398
399 auto const path = parse_path(it);
400 if(path.empty())
401 {
402 ec = error::bad_path;
403 return;
404 }
405 if(*it++ != ' ')
406 {
407 ec = error::bad_path;
408 return;
409 }
410
411 version = parse_version(it);
412 if(version < 0 || ! parse_crlf(it))
413 {
414 ec = error::bad_version;
415 return;
416 }
417
418 impl().on_request(
419 method, path, version, ec);
420 if(ec)
421 return;
422 }
423
424 template<bool isRequest, bool isDirect, class Derived>
425 void
426 basic_parser<isRequest, isDirect, Derived>::
427 parse_startline(char const*& it,
428 int& version, int& status,
429 error_code& ec, std::false_type)
430 {
431 /*
432 status-line = HTTP-version SP status-code SP reason-phrase CRLF
433 status-code = 3*DIGIT
434 reason-phrase = *( HTAB / SP / VCHAR / obs-text )
435 */
436 version = parse_version(it);
437 if(version < 0 || *it != ' ')
438 {
439 ec = error::bad_version;
440 return;
441 }
442 ++it;
443
444 status = parse_status(it);
445 if(status < 0 || *it != ' ')
446 {
447 ec = error::bad_status;
448 return;
449 }
450 ++it;
451
452 auto const reason = parse_reason(it);
453 if(! parse_crlf(it))
454 {
455 ec = error::bad_reason;
456 return;
457 }
458
459 impl().on_response(
460 status, reason, version, ec);
461 if(ec)
462 return;
463 }
464
465 template<bool isRequest, bool isDirect, class Derived>
466 void
467 basic_parser<isRequest, isDirect, Derived>::
468 parse_fields(char const*& it,
469 char const* last, error_code& ec)
470 {
471 /* header-field = field-name ":" OWS field-value OWS
472
473 field-name = token
474 field-value = *( field-content / obs-fold )
475 field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
476 field-vchar = VCHAR / obs-text
477
478 obs-fold = CRLF 1*( SP / HTAB )
479 ; obsolete line folding
480 ; see Section 3.2.4
481 */
482 for(;;)
483 {
484 auto term = find_eol(it, last, ec);
485 if(ec)
486 return;
487 BOOST_ASSERT(term);
488 if(it == term - 2)
489 {
490 it = term;
491 break;
492 }
493 auto const name = parse_name(it);
494 if(name.empty())
495 {
496 ec = error::bad_field;
497 return;
498 }
499 if(*it++ != ':')
500 {
501 ec = error::bad_field;
502 return;
503 }
504 if(*term != ' ' &&
505 *term != '\t')
506 {
507 auto it2 = term - 2;
508 detail::skip_ows(it, it2);
509 detail::skip_ows_rev(it2, it);
510 auto const value =
511 make_string(it, it2);
512 do_field(name, value, ec);
513 if(ec)
514 return;
515 impl().on_field(name, value, ec);
516 if(ec)
517 return;
518 it = term;
519 }
520 else
521 {
522 // obs-fold
523 for(;;)
524 {
525 auto const it2 = term - 2;
526 detail::skip_ows(it, it2);
527 if(it != it2)
528 break;
529 it = term;
530 if(*it != ' ' && *it != '\t')
531 break;
532 term = find_eol(it, last, ec);
533 if(ec)
534 return;
535 }
536 std::string s;
537 if(it != term)
538 {
539 s.append(it, term - 2);
540 it = term;
541 for(;;)
542 {
543 if(*it != ' ' && *it != '\t')
544 break;
545 s.push_back(' ');
546 detail::skip_ows(it, term - 2);
547 term = find_eol(it, last, ec);
548 if(ec)
549 return;
550 if(it != term - 2)
551 s.append(it, term - 2);
552 it = term;
553 }
554 }
555 boost::string_ref value{
556 s.data(), s.size()};
557 do_field(name, value, ec);
558 if(ec)
559 return;
560 impl().on_field(name, value, ec);
561 if(ec)
562 return;
563 }
564 }
565 }
566
567 template<bool isRequest, bool isDirect, class Derived>
568 void
569 basic_parser<isRequest, isDirect, Derived>::
570 do_field(
571 boost::string_ref const& name,
572 boost::string_ref const& value,
573 error_code& ec)
574 {
575 // Connection
576 if(strieq("connection", name) ||
577 strieq("proxy-connection", name))
578 {
579 auto const list = opt_token_list{value};
580 if(! validate_list(list))
581 {
582 // VFALCO Should this be a field specific error?
583 ec = error::bad_value;
584 return;
585 }
586 for(auto const& s : list)
587 {
588 if(strieq("close", s))
589 {
590 f_ |= flagConnectionClose;
591 continue;
592 }
593
594 if(strieq("keep-alive", s))
595 {
596 f_ |= flagConnectionKeepAlive;
597 continue;
598 }
599
600 if(strieq("upgrade", s))
601 {
602 f_ |= flagConnectionUpgrade;
603 continue;
604 }
605 }
606 return;
607 }
608
609 for(auto it = value.begin();
610 it != value.end(); ++it)
611 {
612 if(! is_text(*it))
613 {
614 ec = error::bad_value;
615 return;
616 }
617 }
618
619 // Content-Length
620 if(strieq("content-length", name))
621 {
622 if(f_ & flagContentLength)
623 {
624 // duplicate
625 ec = error::bad_content_length;
626 return;
627 }
628
629 if(f_ & flagChunked)
630 {
631 // conflicting field
632 ec = error::bad_content_length;
633 return;
634 }
635
636 std::uint64_t v;
637 if(! parse_dec(
638 value.begin(), value.end(), v))
639 {
640 ec = error::bad_content_length;
641 return;
642 }
643
644 len_ = v;
645 f_ |= flagContentLength;
646 return;
647 }
648
649 // Transfer-Encoding
650 if(strieq("transfer-encoding", name))
651 {
652 if(f_ & flagChunked)
653 {
654 // duplicate
655 ec = error::bad_transfer_encoding;
656 return;
657 }
658
659 if(f_ & flagContentLength)
660 {
661 // conflicting field
662 ec = error::bad_transfer_encoding;
663 return;
664 }
665
666 auto const v = token_list{value};
667 auto const it = std::find_if(v.begin(), v.end(),
668 [&](typename token_list::value_type const& s)
669 {
670 return strieq("chunked", s);
671 });
672 if(it == v.end())
673 return;
674 if(std::next(it) != v.end())
675 return;
676 len_ = 0;
677 f_ |= flagChunked;
678 return;
679 }
680
681 // Upgrade
682 if(strieq("upgrade", name))
683 {
684 f_ |= flagUpgrade;
685 ec = {};
686 return;
687 }
688 }
689
690 template<bool isRequest, bool isDirect, class Derived>
691 inline
692 std::size_t
693 basic_parser<isRequest, isDirect, Derived>::
694 parse_header(char const* p,
695 std::size_t n, error_code& ec)
696 {
697 if(n < 4)
698 return 0;
699 auto const term = find_eom(
700 p + skip_, p + n, ec);
701 if(ec)
702 return 0;
703 if(! term)
704 {
705 skip_ = n - 3;
706 return 0;
707 }
708
709 int version;
710 int status; // ignored for requests
711
712 skip_ = 0;
713 n = term - p;
714 parse_startline(p, version, status, ec,
715 std::integral_constant<
716 bool, isRequest>{});
717 if(ec)
718 return 0;
719 if(version >= 11)
720 f_ |= flagHTTP11;
721
722 parse_fields(p, term, ec);
723 if(ec)
724 return 0;
725 BOOST_ASSERT(p == term);
726
727 do_header(status,
728 std::integral_constant<
729 bool, isRequest>{});
730 impl().on_header(ec);
731 if(ec)
732 return 0;
733 if(state_ == parse_state::complete)
734 {
735 impl().on_complete(ec);
736 if(ec)
737 return 0;
738 }
739 return n;
740 }
741
742 template<bool isRequest, bool isDirect, class Derived>
743 void
744 basic_parser<isRequest, isDirect, Derived>::
745 do_header(int, std::true_type)
746 {
747 // RFC 7230 section 3.3
748 // https://tools.ietf.org/html/rfc7230#section-3.3
749
750 if(f_ & flagSkipBody)
751 {
752 state_ = parse_state::complete;
753 }
754 else if(f_ & flagContentLength)
755 {
756 if(len_ > 0)
757 {
758 f_ |= flagHasBody;
759 state_ = parse_state::body;
760 }
761 else
762 {
763 state_ = parse_state::complete;
764 }
765 }
766 else if(f_ & flagChunked)
767 {
768 f_ |= flagHasBody;
769 state_ = parse_state::chunk_header;
770 }
771 else
772 {
773 len_ = 0;
774 state_ = parse_state::complete;
775 }
776 }
777
778 template<bool isRequest, bool isDirect, class Derived>
779 void
780 basic_parser<isRequest, isDirect, Derived>::
781 do_header(int status, std::false_type)
782 {
783 // RFC 7230 section 3.3
784 // https://tools.ietf.org/html/rfc7230#section-3.3
785
786 if( (f_ & flagSkipBody) || // e.g. response to a HEAD request
787 status / 100 == 1 || // 1xx e.g. Continue
788 status == 204 || // No Content
789 status == 304) // Not Modified
790 {
791 state_ = parse_state::complete;
792 return;
793 }
794
795 if(f_ & flagContentLength)
796 {
797 if(len_ > 0)
798 {
799 f_ |= flagHasBody;
800 state_ = parse_state::body;
801 }
802 else
803 {
804 state_ = parse_state::complete;
805 }
806 }
807 else if(f_ & flagChunked)
808 {
809 f_ |= flagHasBody;
810 state_ = parse_state::chunk_header;
811 }
812 else
813 {
814 f_ |= flagHasBody;
815 f_ |= flagNeedEOF;
816 state_ = parse_state::body_to_eof;
817 }
818 }
819
820 template<bool isRequest, bool isDirect, class Derived>
821 void
822 basic_parser<isRequest, isDirect, Derived>::
823 maybe_do_body_direct()
824 {
825 if(f_ & flagOnBody)
826 return;
827 f_ |= flagOnBody;
828 if(got_content_length())
829 impl().on_body(len_);
830 else
831 impl().on_body();
832 }
833
834 template<bool isRequest, bool isDirect, class Derived>
835 void
836 basic_parser<isRequest, isDirect, Derived>::
837 maybe_do_body_indirect(error_code& ec)
838 {
839 if(f_ & flagOnBody)
840 return;
841 f_ |= flagOnBody;
842 if(got_content_length())
843 {
844 impl().on_body(len_, ec);
845 if(ec)
846 return;
847 }
848 else
849 {
850 impl().on_body(ec);
851 if(ec)
852 return;
853 }
854 }
855
856 template<bool isRequest, bool isDirect, class Derived>
857 std::size_t
858 basic_parser<isRequest, isDirect, Derived>::
859 parse_chunk_header(char const* p,
860 std::size_t n, error_code& ec)
861 {
862 /*
863 chunked-body = *chunk last-chunk trailer-part CRLF
864
865 chunk = chunk-size [ chunk-ext ] CRLF chunk-data CRLF
866 last-chunk = 1*("0") [ chunk-ext ] CRLF
867 trailer-part = *( header-field CRLF )
868
869 chunk-size = 1*HEXDIG
870 chunk-data = 1*OCTET ; a sequence of chunk-size octets
871 chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
872 chunk-ext-name = token
873 chunk-ext-val = token / quoted-string
874 */
875
876 auto const first = p;
877 auto const last = p + n;
878
879 // Treat the last CRLF in a chunk as
880 // part of the next chunk, so it can
881 // be parsed in one call instead of two.
882 if(f_ & flagExpectCRLF)
883 {
884 if(n < 2)
885 return 0;
886 if(! parse_crlf(p))
887 {
888 ec = error::bad_chunk;
889 return 0;
890 }
891 n -= 2;
892 }
893
894 char const* term;
895
896 if(! (f_ & flagFinalChunk))
897 {
898 if(n < 2)
899 return 0;
900 term = find_eol(p + skip_, last, ec);
901 if(ec)
902 return 0;
903 if(! term)
904 {
905 skip_ = n - 1;
906 return 0;
907 }
908 std::uint64_t v;
909 if(! parse_hex(p, v))
910 {
911 ec = error::bad_chunk;
912 return 0;
913 }
914 if(v != 0)
915 {
916 if(*p == ';')
917 {
918 // VFALCO We need to parse the chunk
919 // extension to validate it here.
920 ext_ = make_string(p, term - 2);
921 impl().on_chunk(v, ext_, ec);
922 if(ec)
923 return 0;
924 }
925 else if(p != term - 2)
926 {
927 ec = error::bad_chunk;
928 return 0;
929 }
930 p = term;
931 len_ = v;
932 skip_ = 0;
933 f_ |= flagExpectCRLF;
934 state_ = parse_state::chunk_body;
935 return p - first;
936 }
937
938 // This is the offset from the buffer
939 // to the beginning of the first '\r\n'
940 x_ = term - 2 - first;
941 skip_ = x_;
942
943 f_ |= flagFinalChunk;
944 }
945 else
946 {
947 // We are parsing the value again
948 // to advance p to the right place.
949 std::uint64_t v;
950 auto const result = parse_hex(p, v);
951 BOOST_ASSERT(result && v == 0);
952 beast::detail::ignore_unused(result);
953 beast::detail::ignore_unused(v);
954 }
955
956 term = find_eom(
957 first + skip_, last, ec);
958 if(ec)
959 return 0;
960 if(! term)
961 {
962 if(n > 3)
963 skip_ = (last - first) - 3;
964 return 0;
965 }
966
967 if(*p == ';')
968 {
969 ext_ = make_string(p, first + x_);
970 impl().on_chunk(0, ext_, ec);
971 if(ec)
972 return 0;
973 p = first + x_;
974 }
975 if(! parse_crlf(p))
976 {
977 ec = error::bad_chunk;
978 return 0;
979 }
980 parse_fields(p, term, ec);
981 if(ec)
982 return 0;
983 BOOST_ASSERT(p == term);
984
985 do_complete(ec);
986 if(ec)
987 return 0;
988 return p - first;
989 }
990
991 template<bool isRequest, bool isDirect, class Derived>
992 inline
993 std::size_t
994 basic_parser<isRequest, isDirect, Derived>::
995 parse_body(char const* p,
996 std::size_t n, error_code& ec)
997 {
998 n = beast::detail::clamp(len_, n);
999 body_ = boost::string_ref{p, n};
1000 impl().on_data(body_, ec);
1001 if(ec)
1002 return 0;
1003 len_ -= n;
1004 if(len_ == 0)
1005 {
1006 do_complete(ec);
1007 if(ec)
1008 return 0;
1009 }
1010 return n;
1011 }
1012
1013 template<bool isRequest, bool isDirect, class Derived>
1014 inline
1015 std::size_t
1016 basic_parser<isRequest, isDirect, Derived>::
1017 parse_body_to_eof(char const* p,
1018 std::size_t n, error_code& ec)
1019 {
1020 body_ = boost::string_ref{p, n};
1021 impl().on_data(body_, ec);
1022 if(ec)
1023 return 0;
1024 return n;
1025 }
1026
1027 template<bool isRequest, bool isDirect, class Derived>
1028 inline
1029 std::size_t
1030 basic_parser<isRequest, isDirect, Derived>::
1031 parse_chunk_body(char const* p,
1032 std::size_t n, error_code& ec)
1033 {
1034 n = beast::detail::clamp(len_, n);
1035 body_ = boost::string_ref{p, n};
1036 impl().on_data(body_, ec);
1037 if(ec)
1038 return 0;
1039 len_ -= n;
1040 if(len_ == 0)
1041 {
1042 body_ = {};
1043 state_ = parse_state::chunk_header;
1044 }
1045 return n;
1046 }
1047
1048 template<bool isRequest, bool isDirect, class Derived>
1049 void
1050 basic_parser<isRequest, isDirect, Derived>::
1051 do_complete(error_code& ec)
1052 {
1053 impl().on_complete(ec);
1054 if(ec)
1055 return;
1056 state_ = parse_state::complete;
1057 }
1058
1059 } // http
1060 } // beast
1061
1062 #endif