1 // 2 // Copyright (c) 2016-2019 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 // Official repository: https://github.com/boostorg/beast 8 // 9 10 #ifndef BOOST_BEAST_HTTP_PARSER_HPP 11 #define BOOST_BEAST_HTTP_PARSER_HPP 12 13 #include <boost/beast/core/detail/config.hpp> 14 #include <boost/beast/http/basic_parser.hpp> 15 #include <boost/beast/http/message.hpp> 16 #include <boost/beast/http/type_traits.hpp> 17 #include <boost/optional.hpp> 18 #include <boost/throw_exception.hpp> 19 #include <functional> 20 #include <memory> 21 #include <type_traits> 22 #include <utility> 23 24 namespace boost { 25 namespace beast { 26 namespace http { 27 28 /** An HTTP/1 parser for producing a message. 29 30 This class uses the basic HTTP/1 wire format parser to convert 31 a series of octets into a @ref message using the @ref basic_fields 32 container to represent the fields. 33 34 @tparam isRequest Indicates whether a request or response 35 will be parsed. 36 37 @tparam Body The type used to represent the body. This must 38 meet the requirements of <em>Body</em>. 39 40 @tparam Allocator The type of allocator used with the 41 @ref basic_fields container. 42 43 @note A new instance of the parser is required for each message. 44 */ 45 template< 46 bool isRequest, 47 class Body, 48 class Allocator = std::allocator<char>> 49 class parser 50 : public basic_parser<isRequest> 51 { 52 static_assert(is_body<Body>::value, 53 "Body type requirements not met"); 54 55 static_assert(is_body_reader<Body>::value, 56 "BodyReader type requirements not met"); 57 58 template<bool, class, class> 59 friend class parser; 60 61 message<isRequest, Body, basic_fields<Allocator>> m_; 62 typename Body::reader rd_; 63 bool rd_inited_ = false; 64 bool used_ = false; 65 66 std::function<void( 67 std::uint64_t, 68 string_view, 69 error_code&)> cb_h_; 70 71 std::function<std::size_t( 72 std::uint64_t, 73 string_view, 74 error_code&)> cb_b_; 75 76 public: 77 /// The type of message returned by the parser 78 using value_type = 79 message<isRequest, Body, basic_fields<Allocator>>; 80 81 /// Destructor 82 ~parser() = default; 83 84 /// Constructor (disallowed) 85 parser(parser const&) = delete; 86 87 /// Assignment (disallowed) 88 parser& operator=(parser const&) = delete; 89 90 /// Constructor (disallowed) 91 parser(parser&& other) = delete; 92 93 /// Constructor 94 parser(); 95 96 /** Constructor 97 98 @param args Optional arguments forwarded to the 99 @ref http::message constructor. 100 101 @note This function participates in overload 102 resolution only if the first argument is not a 103 @ref parser. 104 */ 105 #if BOOST_BEAST_DOXYGEN 106 template<class... Args> 107 explicit 108 parser(Args&&... args); 109 #else 110 template<class Arg1, class... ArgN, 111 class = typename std::enable_if< 112 ! detail::is_parser<typename 113 std::decay<Arg1>::type>::value>::type> 114 explicit 115 parser(Arg1&& arg1, ArgN&&... argn); 116 #endif 117 118 /** Construct a parser from another parser, changing the Body type. 119 120 This constructs a new parser by move constructing the 121 header from another parser with a different body type. The 122 constructed-from parser must not have any parsed body octets or 123 initialized <em>BodyReader</em>, otherwise an exception is generated. 124 125 @par Example 126 @code 127 // Deferred body type commitment 128 request_parser<empty_body> req0; 129 ... 130 request_parser<string_body> req{std::move(req0)}; 131 @endcode 132 133 If an exception is thrown, the state of the constructed-from 134 parser is undefined. 135 136 @param parser The other parser to construct from. After 137 this call returns, the constructed-from parser may only 138 be destroyed. 139 140 @param args Optional arguments forwarded to the message 141 constructor. 142 143 @throws std::invalid_argument Thrown when the constructed-from 144 parser has already initialized a body reader. 145 146 @note This function participates in overload resolution only 147 if the other parser uses a different body type. 148 */ 149 #if BOOST_BEAST_DOXYGEN 150 template<class OtherBody, class... Args> 151 #else 152 template<class OtherBody, class... Args, 153 class = typename std::enable_if< 154 ! std::is_same<Body, OtherBody>::value>::type> 155 #endif 156 explicit 157 parser(parser<isRequest, OtherBody, 158 Allocator>&& parser, Args&&... args); 159 160 /** Returns the parsed message. 161 162 Depending on the parser's progress, 163 parts of this object may be incomplete. 164 */ 165 value_type const& get() const166 get() const 167 { 168 return m_; 169 } 170 171 /** Returns the parsed message. 172 173 Depending on the parser's progress, 174 parts of this object may be incomplete. 175 */ 176 value_type& get()177 get() 178 { 179 return m_; 180 } 181 182 /** Returns ownership of the parsed message. 183 184 Ownership is transferred to the caller. 185 Depending on the parser's progress, 186 parts of this object may be incomplete. 187 188 @par Requires 189 190 @ref value_type is @b MoveConstructible 191 */ 192 value_type release()193 release() 194 { 195 static_assert(std::is_move_constructible<decltype(m_)>::value, 196 "MoveConstructible requirements not met"); 197 return std::move(m_); 198 } 199 200 /** Set a callback to be invoked on each chunk header. 201 202 The callback will be invoked once for every chunk in the message 203 payload, as well as once for the last chunk. The invocation 204 happens after the chunk header is available but before any body 205 octets have been parsed. 206 207 The extensions are provided in raw, validated form, use 208 @ref chunk_extensions::parse to parse the extensions into a 209 structured container for easier access. 210 The implementation type-erases the callback without requiring 211 a dynamic allocation. For this reason, the callback object is 212 passed by a non-constant reference. 213 214 @par Example 215 @code 216 auto callback = 217 [](std::uint64_t size, string_view extensions, error_code& ec) 218 { 219 //... 220 }; 221 parser.on_chunk_header(callback); 222 @endcode 223 224 @param cb The function to set, which must be invocable with 225 this equivalent signature: 226 @code 227 void 228 on_chunk_header( 229 std::uint64_t size, // Size of the chunk, zero for the last chunk 230 string_view extensions, // The chunk-extensions in raw form 231 error_code& ec); // May be set by the callback to indicate an error 232 @endcode 233 */ 234 template<class Callback> 235 void on_chunk_header(Callback & cb)236 on_chunk_header(Callback& cb) 237 { 238 // Callback may not be constant, caller is responsible for 239 // managing the lifetime of the callback. Copies are not made. 240 BOOST_STATIC_ASSERT(! std::is_const<Callback>::value); 241 242 // Can't set the callback after receiving any chunk data! 243 BOOST_ASSERT(! rd_inited_); 244 245 cb_h_ = std::ref(cb); 246 } 247 248 /** Set a callback to be invoked on chunk body data 249 250 The provided function object will be invoked one or more times 251 to provide buffers corresponding to the chunk body for the current 252 chunk. The callback receives the number of octets remaining in this 253 chunk body including the octets in the buffer provided. 254 255 The callback must return the number of octets actually consumed. 256 Any octets not consumed will be presented again in a subsequent 257 invocation of the callback. 258 The implementation type-erases the callback without requiring 259 a dynamic allocation. For this reason, the callback object is 260 passed by a non-constant reference. 261 262 @par Example 263 @code 264 auto callback = 265 [](std::uint64_t remain, string_view body, error_code& ec) 266 { 267 //... 268 }; 269 parser.on_chunk_body(callback); 270 @endcode 271 272 @param cb The function to set, which must be invocable with 273 this equivalent signature: 274 @code 275 std::size_t 276 on_chunk_header( 277 std::uint64_t remain, // Octets remaining in this chunk, includes `body` 278 string_view body, // A buffer holding some or all of the remainder of the chunk body 279 error_code& ec); // May be set by the callback to indicate an error 280 @endcode 281 */ 282 template<class Callback> 283 void on_chunk_body(Callback & cb)284 on_chunk_body(Callback& cb) 285 { 286 // Callback may not be constant, caller is responsible for 287 // managing the lifetime of the callback. Copies are not made. 288 BOOST_STATIC_ASSERT(! std::is_const<Callback>::value); 289 290 // Can't set the callback after receiving any chunk data! 291 BOOST_ASSERT(! rd_inited_); 292 293 cb_b_ = std::ref(cb); 294 } 295 296 private: 297 parser(std::true_type); 298 parser(std::false_type); 299 300 template<class OtherBody, class... Args, 301 class = typename std::enable_if< 302 ! std::is_same<Body, OtherBody>::value>::type> 303 parser( 304 std::true_type, 305 parser<isRequest, OtherBody, Allocator>&& parser, 306 Args&&... args); 307 308 template<class OtherBody, class... Args, 309 class = typename std::enable_if< 310 ! std::is_same<Body, OtherBody>::value>::type> 311 parser( 312 std::false_type, 313 parser<isRequest, OtherBody, Allocator>&& parser, 314 Args&&... args); 315 316 template<class Arg1, class... ArgN, 317 class = typename std::enable_if< 318 ! detail::is_parser<typename 319 std::decay<Arg1>::type>::value>::type> 320 explicit 321 parser(Arg1&& arg1, std::true_type, ArgN&&... argn); 322 323 template<class Arg1, class... ArgN, 324 class = typename std::enable_if< 325 ! detail::is_parser<typename 326 std::decay<Arg1>::type>::value>::type> 327 explicit 328 parser(Arg1&& arg1, std::false_type, ArgN&&... argn); 329 330 void on_request_impl(verb method,string_view method_str,string_view target,int version,error_code & ec,std::true_type)331 on_request_impl( 332 verb method, 333 string_view method_str, 334 string_view target, 335 int version, 336 error_code& ec, 337 std::true_type) 338 { 339 // If this assert goes off, it means you tried to re-use a 340 // parser after it was done reading a message. This is not 341 // allowed, you need to create a new parser for each message. 342 // The easiest way to do that is to store the parser in 343 // an optional object. 344 345 BOOST_ASSERT(! used_); 346 if(used_) 347 { 348 ec = error::stale_parser; 349 return; 350 } 351 used_ = true; 352 353 m_.target(target); 354 if(method != verb::unknown) 355 m_.method(method); 356 else 357 m_.method_string(method_str); 358 m_.version(version); 359 } 360 361 void on_request_impl(verb,string_view,string_view,int,error_code &,std::false_type)362 on_request_impl( 363 verb, string_view, string_view, 364 int, error_code&, std::false_type) 365 { 366 } 367 368 void on_request_impl(verb method,string_view method_str,string_view target,int version,error_code & ec)369 on_request_impl( 370 verb method, 371 string_view method_str, 372 string_view target, 373 int version, 374 error_code& ec) override 375 { 376 this->on_request_impl( 377 method, method_str, target, version, ec, 378 std::integral_constant<bool, isRequest>{}); 379 } 380 381 void on_response_impl(int code,string_view reason,int version,error_code & ec,std::true_type)382 on_response_impl( 383 int code, 384 string_view reason, 385 int version, 386 error_code& ec, 387 std::true_type) 388 { 389 // If this assert goes off, it means you tried to re-use a 390 // parser after it was done reading a message. This is not 391 // allowed, you need to create a new parser for each message. 392 // The easiest way to do that is to store the parser in 393 // an optional object. 394 395 BOOST_ASSERT(! used_); 396 if(used_) 397 { 398 ec = error::stale_parser; 399 return; 400 } 401 used_ = true; 402 403 m_.result(code); 404 m_.version(version); 405 m_.reason(reason); 406 } 407 408 void on_response_impl(int,string_view,int,error_code &,std::false_type)409 on_response_impl( 410 int, string_view, int, 411 error_code&, std::false_type) 412 { 413 } 414 415 void on_response_impl(int code,string_view reason,int version,error_code & ec)416 on_response_impl( 417 int code, 418 string_view reason, 419 int version, 420 error_code& ec) override 421 { 422 this->on_response_impl( 423 code, reason, version, ec, 424 std::integral_constant<bool, ! isRequest>{}); 425 } 426 427 void on_field_impl(field name,string_view name_string,string_view value,error_code &)428 on_field_impl( 429 field name, 430 string_view name_string, 431 string_view value, 432 error_code&) override 433 { 434 m_.insert(name, name_string, value); 435 } 436 437 void on_header_impl(error_code & ec)438 on_header_impl(error_code& ec) override 439 { 440 ec = {}; 441 } 442 443 void on_body_init_impl(boost::optional<std::uint64_t> const & content_length,error_code & ec)444 on_body_init_impl( 445 boost::optional<std::uint64_t> const& content_length, 446 error_code& ec) override 447 { 448 rd_.init(content_length, ec); 449 rd_inited_ = true; 450 } 451 452 std::size_t on_body_impl(string_view body,error_code & ec)453 on_body_impl( 454 string_view body, 455 error_code& ec) override 456 { 457 return rd_.put(net::buffer( 458 body.data(), body.size()), ec); 459 } 460 461 void on_chunk_header_impl(std::uint64_t size,string_view extensions,error_code & ec)462 on_chunk_header_impl( 463 std::uint64_t size, 464 string_view extensions, 465 error_code& ec) override 466 { 467 if(cb_h_) 468 return cb_h_(size, extensions, ec); 469 } 470 471 std::size_t on_chunk_body_impl(std::uint64_t remain,string_view body,error_code & ec)472 on_chunk_body_impl( 473 std::uint64_t remain, 474 string_view body, 475 error_code& ec) override 476 { 477 if(cb_b_) 478 return cb_b_(remain, body, ec); 479 return rd_.put(net::buffer( 480 body.data(), body.size()), ec); 481 } 482 483 void on_finish_impl(error_code & ec)484 on_finish_impl( 485 error_code& ec) override 486 { 487 rd_.finish(ec); 488 } 489 }; 490 491 /// An HTTP/1 parser for producing a request message. 492 template<class Body, class Allocator = std::allocator<char>> 493 using request_parser = parser<true, Body, Allocator>; 494 495 /// An HTTP/1 parser for producing a response message. 496 template<class Body, class Allocator = std::allocator<char>> 497 using response_parser = parser<false, Body, Allocator>; 498 499 } // http 500 } // beast 501 } // boost 502 503 #include <boost/beast/http/impl/parser.hpp> 504 505 #endif 506