2 // Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
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)
8 #ifndef BEAST_HTTP_IMPL_BASIC_PARSER_IPP
9 #define BEAST_HTTP_IMPL_BASIC_PARSER_IPP
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>
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)
30 , buf_(std::move(other.buf_))
31 , buf_len_(other.buf_len_)
35 , state_(other.state_)
39 template<bool isRequest, bool isDirect, class Derived>
41 basic_parser<isRequest, isDirect, Derived>::
44 BOOST_ASSERT(! got_some());
48 template<bool isRequest, bool isDirect, class Derived>
50 basic_parser<isRequest, isDirect, Derived>::
53 BOOST_ASSERT(got_header());
56 if(f_ & flagConnectionClose)
61 if(! (f_ & flagConnectionKeepAlive))
64 return (f_ & flagNeedEOF) == 0;
67 template<bool isRequest, bool isDirect, class Derived>
68 template<class ConstBufferSequence>
70 basic_parser<isRequest, isDirect, Derived>::
71 write(ConstBufferSequence const& buffers,
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);
82 template<bool isRequest, bool isDirect, class Derived>
84 basic_parser<isRequest, isDirect, Derived>::
85 write(boost::asio::const_buffers_1 const& buffer,
88 return do_write(buffer, ec,
89 std::integral_constant<bool, isDirect>{});
92 template<bool isRequest, bool isDirect, class Derived>
94 basic_parser<isRequest, isDirect, Derived>::
95 write_eof(error_code& ec)
97 BOOST_ASSERT(got_some());
98 if(state_ == parse_state::header)
100 ec = error::partial_message;
103 if(f_ & (flagContentLength | flagChunked))
105 if(state_ != parse_state::complete)
107 ec = error::partial_message;
117 template<bool isRequest, bool isDirect, class Derived>
118 template<class DynamicBuffer>
120 basic_parser<isRequest, isDirect, Derived>::
121 copy_body(DynamicBuffer& dynabuf)
123 // This function not available when isDirect==false
124 static_assert(isDirect, "");
126 using boost::asio::buffer_copy;
127 using boost::asio::buffer_size;
128 BOOST_ASSERT(dynabuf.size() > 0);
130 state_ == parse_state::body ||
131 state_ == parse_state::body_to_eof ||
132 state_ == parse_state::chunk_body);
133 maybe_do_body_direct();
136 case parse_state::body_to_eof:
139 impl().on_prepare(dynabuf.size());
141 buffer_size(buffers) >= 1 &&
142 buffer_size(buffers) <=
144 auto const n = buffer_copy(
145 buffers, dynabuf.data());
153 BOOST_ASSERT(len_ > 0);
156 beast::detail::clamp(len_));
158 buffer_size(buffers) >= 1 &&
159 buffer_size(buffers) <=
160 beast::detail::clamp(len_));
161 auto const n = buffer_copy(
162 buffers, dynabuf.data());
169 template<bool isRequest, bool isDirect, class Derived>
170 template<class MutableBufferSequence>
172 basic_parser<isRequest, isDirect, Derived>::
173 prepare_body(boost::optional<
174 MutableBufferSequence>& buffers, std::size_t limit)
176 // This function not available when isDirect==false
177 static_assert(isDirect, "");
179 BOOST_ASSERT(limit > 0);
181 state_ == parse_state::body ||
182 state_ == parse_state::body_to_eof ||
183 state_ == parse_state::chunk_body);
184 maybe_do_body_direct();
188 case parse_state::body_to_eof:
193 BOOST_ASSERT(len_ > 0);
194 n = beast::detail::clamp(len_, limit);
197 buffers.emplace(impl().on_prepare(n));
200 template<bool isRequest, bool isDirect, class Derived>
202 basic_parser<isRequest, isDirect, Derived>::
203 commit_body(std::size_t n)
205 // This function not available when isDirect==false
206 static_assert(isDirect, "");
208 BOOST_ASSERT(f_ & flagOnBody);
212 case parse_state::body:
216 // VFALCO This is no good, throwing out ec?
222 case parse_state::chunk_body:
225 state_ = parse_state::chunk_header;
233 template<bool isRequest, bool isDirect, class Derived>
235 basic_parser<isRequest, isDirect, Derived>::
236 consume_body(error_code& ec)
239 state_ == parse_state::body ||
240 state_ == parse_state::body_to_eof ||
241 state_ == parse_state::chunk_body);
244 case parse_state::body:
245 case parse_state::body_to_eof:
251 case parse_state::chunk_body:
253 state_ = parse_state::chunk_header;
261 template<bool isRequest, bool isDirect, class Derived>
262 template<class ConstBufferSequence>
265 basic_parser<isRequest, isDirect, Derived>::
267 ConstBufferSequence const& buffers)
269 using boost::asio::buffer;
270 using boost::asio::buffer_cast;
271 using boost::asio::buffer_copy;
272 using boost::asio::buffer_size;
274 auto const it = buffers.begin();
275 auto const last = buffers.end();
278 if(std::next(it) == last)
282 return {buffer_cast<char const*>(b),
285 auto const len = buffer_size(buffers);
289 buf_.reset(new char[len]);
294 buffer(buf_.get(), buf_len_), buffers);
295 return {buf_.get(), buf_len_};
298 template<bool isRequest, bool isDirect, class Derived>
301 basic_parser<isRequest, isDirect, Derived>::
302 do_write(boost::asio::const_buffers_1 const& buffer,
303 error_code& ec, std::true_type)
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());
313 buffer_size(*buffer.begin());
314 if(state_ == parse_state::header)
318 return parse_header(p, n, ec);
322 maybe_do_body_direct();
323 return parse_chunk_header(p, n, ec);
327 template<bool isRequest, bool isDirect, class Derived>
330 basic_parser<isRequest, isDirect, Derived>::
331 do_write(boost::asio::const_buffers_1 const& buffer,
332 error_code& ec, std::false_type)
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());
340 buffer_size(*buffer.begin());
343 case parse_state::header:
346 return parse_header(p, n, ec);
348 case parse_state::body:
349 maybe_do_body_indirect(ec);
352 return parse_body(p, n, ec);
354 case parse_state::body_to_eof:
355 maybe_do_body_indirect(ec);
358 return parse_body_to_eof(p, n, ec);
360 case parse_state::chunk_header:
361 maybe_do_body_indirect(ec);
364 return parse_chunk_header(p, n, ec);
366 case parse_state::chunk_body:
367 return parse_chunk_body(p, n, ec);
369 case parse_state::complete:
376 template<bool isRequest, bool isDirect, class Derived>
378 basic_parser<isRequest, isDirect, Derived>::
379 parse_startline(char const*& it,
380 int& version, int& status,
381 error_code& ec, std::true_type)
384 request-line = method SP request-target SP HTTP-version CRLF
387 auto const method = parse_method(it);
390 ec = error::bad_method;
395 ec = error::bad_method;
399 auto const path = parse_path(it);
402 ec = error::bad_path;
407 ec = error::bad_path;
411 version = parse_version(it);
412 if(version < 0 || ! parse_crlf(it))
414 ec = error::bad_version;
419 method, path, version, ec);
424 template<bool isRequest, bool isDirect, class Derived>
426 basic_parser<isRequest, isDirect, Derived>::
427 parse_startline(char const*& it,
428 int& version, int& status,
429 error_code& ec, std::false_type)
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 )
436 version = parse_version(it);
437 if(version < 0 || *it != ' ')
439 ec = error::bad_version;
444 status = parse_status(it);
445 if(status < 0 || *it != ' ')
447 ec = error::bad_status;
452 auto const reason = parse_reason(it);
455 ec = error::bad_reason;
460 status, reason, version, ec);
465 template<bool isRequest, bool isDirect, class Derived>
467 basic_parser<isRequest, isDirect, Derived>::
468 parse_fields(char const*& it,
469 char const* last, error_code& ec)
471 /* header-field = field-name ":" OWS field-value OWS
474 field-value = *( field-content / obs-fold )
475 field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
476 field-vchar = VCHAR / obs-text
478 obs-fold = CRLF 1*( SP / HTAB )
479 ; obsolete line folding
484 auto term = find_eol(it, last, ec);
493 auto const name = parse_name(it);
496 ec = error::bad_field;
501 ec = error::bad_field;
508 detail::skip_ows(it, it2);
509 detail::skip_ows_rev(it2, it);
511 make_string(it, it2);
512 do_field(name, value, ec);
515 impl().on_field(name, value, ec);
525 auto const it2 = term - 2;
526 detail::skip_ows(it, it2);
530 if(*it != ' ' && *it != '\t')
532 term = find_eol(it, last, ec);
539 s.append(it, term - 2);
543 if(*it != ' ' && *it != '\t')
546 detail::skip_ows(it, term - 2);
547 term = find_eol(it, last, ec);
551 s.append(it, term - 2);
555 boost::string_ref value{
557 do_field(name, value, ec);
560 impl().on_field(name, value, ec);
567 template<bool isRequest, bool isDirect, class Derived>
569 basic_parser<isRequest, isDirect, Derived>::
571 boost::string_ref const& name,
572 boost::string_ref const& value,
576 if(strieq("connection", name) ||
577 strieq("proxy-connection", name))
579 auto const list = opt_token_list{value};
580 if(! validate_list(list))
582 // VFALCO Should this be a field specific error?
583 ec = error::bad_value;
586 for(auto const& s : list)
588 if(strieq("close", s))
590 f_ |= flagConnectionClose;
594 if(strieq("keep-alive", s))
596 f_ |= flagConnectionKeepAlive;
600 if(strieq("upgrade", s))
602 f_ |= flagConnectionUpgrade;
609 for(auto it = value.begin();
610 it != value.end(); ++it)
614 ec = error::bad_value;
620 if(strieq("content-length", name))
622 if(f_ & flagContentLength)
625 ec = error::bad_content_length;
632 ec = error::bad_content_length;
638 value.begin(), value.end(), v))
640 ec = error::bad_content_length;
645 f_ |= flagContentLength;
650 if(strieq("transfer-encoding", name))
655 ec = error::bad_transfer_encoding;
659 if(f_ & flagContentLength)
662 ec = error::bad_transfer_encoding;
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)
670 return strieq("chunked", s);
674 if(std::next(it) != v.end())
682 if(strieq("upgrade", name))
690 template<bool isRequest, bool isDirect, class Derived>
693 basic_parser<isRequest, isDirect, Derived>::
694 parse_header(char const* p,
695 std::size_t n, error_code& ec)
699 auto const term = find_eom(
700 p + skip_, p + n, ec);
710 int status; // ignored for requests
714 parse_startline(p, version, status, ec,
715 std::integral_constant<
722 parse_fields(p, term, ec);
725 BOOST_ASSERT(p == term);
728 std::integral_constant<
730 impl().on_header(ec);
733 if(state_ == parse_state::complete)
735 impl().on_complete(ec);
742 template<bool isRequest, bool isDirect, class Derived>
744 basic_parser<isRequest, isDirect, Derived>::
745 do_header(int, std::true_type)
747 // RFC 7230 section 3.3
748 // https://tools.ietf.org/html/rfc7230#section-3.3
750 if(f_ & flagSkipBody)
752 state_ = parse_state::complete;
754 else if(f_ & flagContentLength)
759 state_ = parse_state::body;
763 state_ = parse_state::complete;
766 else if(f_ & flagChunked)
769 state_ = parse_state::chunk_header;
774 state_ = parse_state::complete;
778 template<bool isRequest, bool isDirect, class Derived>
780 basic_parser<isRequest, isDirect, Derived>::
781 do_header(int status, std::false_type)
783 // RFC 7230 section 3.3
784 // https://tools.ietf.org/html/rfc7230#section-3.3
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
791 state_ = parse_state::complete;
795 if(f_ & flagContentLength)
800 state_ = parse_state::body;
804 state_ = parse_state::complete;
807 else if(f_ & flagChunked)
810 state_ = parse_state::chunk_header;
816 state_ = parse_state::body_to_eof;
820 template<bool isRequest, bool isDirect, class Derived>
822 basic_parser<isRequest, isDirect, Derived>::
823 maybe_do_body_direct()
828 if(got_content_length())
829 impl().on_body(len_);
834 template<bool isRequest, bool isDirect, class Derived>
836 basic_parser<isRequest, isDirect, Derived>::
837 maybe_do_body_indirect(error_code& ec)
842 if(got_content_length())
844 impl().on_body(len_, ec);
856 template<bool isRequest, bool isDirect, class Derived>
858 basic_parser<isRequest, isDirect, Derived>::
859 parse_chunk_header(char const* p,
860 std::size_t n, error_code& ec)
863 chunked-body = *chunk last-chunk trailer-part CRLF
865 chunk = chunk-size [ chunk-ext ] CRLF chunk-data CRLF
866 last-chunk = 1*("0") [ chunk-ext ] CRLF
867 trailer-part = *( header-field CRLF )
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
876 auto const first = p;
877 auto const last = p + n;
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)
888 ec = error::bad_chunk;
896 if(! (f_ & flagFinalChunk))
900 term = find_eol(p + skip_, last, ec);
909 if(! parse_hex(p, v))
911 ec = error::bad_chunk;
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);
925 else if(p != term - 2)
927 ec = error::bad_chunk;
933 f_ |= flagExpectCRLF;
934 state_ = parse_state::chunk_body;
938 // This is the offset from the buffer
939 // to the beginning of the first '\r\n'
940 x_ = term - 2 - first;
943 f_ |= flagFinalChunk;
947 // We are parsing the value again
948 // to advance p to the right place.
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);
957 first + skip_, last, ec);
963 skip_ = (last - first) - 3;
969 ext_ = make_string(p, first + x_);
970 impl().on_chunk(0, ext_, ec);
977 ec = error::bad_chunk;
980 parse_fields(p, term, ec);
983 BOOST_ASSERT(p == term);
991 template<bool isRequest, bool isDirect, class Derived>
994 basic_parser<isRequest, isDirect, Derived>::
995 parse_body(char const* p,
996 std::size_t n, error_code& ec)
998 n = beast::detail::clamp(len_, n);
999 body_ = boost::string_ref{p, n};
1000 impl().on_data(body_, ec);
1013 template<bool isRequest, bool isDirect, class Derived>
1016 basic_parser<isRequest, isDirect, Derived>::
1017 parse_body_to_eof(char const* p,
1018 std::size_t n, error_code& ec)
1020 body_ = boost::string_ref{p, n};
1021 impl().on_data(body_, ec);
1027 template<bool isRequest, bool isDirect, class Derived>
1030 basic_parser<isRequest, isDirect, Derived>::
1031 parse_chunk_body(char const* p,
1032 std::size_t n, error_code& ec)
1034 n = beast::detail::clamp(len_, n);
1035 body_ = boost::string_ref{p, n};
1036 impl().on_data(body_, ec);
1043 state_ = parse_state::chunk_header;
1048 template<bool isRequest, bool isDirect, class Derived>
1050 basic_parser<isRequest, isDirect, Derived>::
1051 do_complete(error_code& ec)
1053 impl().on_complete(ec);
1056 state_ = parse_state::complete;