Line data Source code
1 : // 2 : // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.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/cppalliance/http_proto 8 : // 9 : 10 : #ifndef BOOST_HTTP_PROTO_PARSER_HPP 11 : #define BOOST_HTTP_PROTO_PARSER_HPP 12 : 13 : #include <boost/http_proto/detail/config.hpp> 14 : #include <boost/http_proto/error.hpp> 15 : #include <boost/http_proto/header_limits.hpp> 16 : #include <boost/http_proto/sink.hpp> 17 : #include <boost/http_proto/detail/header.hpp> 18 : #include <boost/http_proto/detail/type_traits.hpp> 19 : #include <boost/http_proto/detail/workspace.hpp> 20 : #include <boost/buffers/circular_buffer.hpp> 21 : #include <boost/buffers/flat_buffer.hpp> 22 : #include <boost/buffers/mutable_buffer_pair.hpp> 23 : #include <boost/buffers/mutable_buffer_span.hpp> 24 : #include <boost/buffers/type_traits.hpp> 25 : #include <boost/buffers/any_dynamic_buffer.hpp> 26 : #include <boost/url/grammar/error.hpp> 27 : #include <cstddef> 28 : #include <cstdint> 29 : #include <functional> 30 : #include <memory> 31 : #include <utility> 32 : 33 : namespace boost { 34 : namespace http_proto { 35 : 36 : #ifndef BOOST_HTTP_PROTO_DOCS 37 : class parser_service; 38 : class filter; 39 : class request_parser; 40 : class response_parser; 41 : class context; 42 : 43 : #endif 44 : 45 : /** A parser for HTTP/1 messages. 46 : 47 : The parser is strict. Any malformed 48 : inputs according to the documented 49 : HTTP ABNFs is treated as an 50 : unrecoverable error. 51 : */ 52 : class BOOST_SYMBOL_VISIBLE 53 : parser 54 : { 55 : BOOST_HTTP_PROTO_DECL 56 : parser(context& ctx, detail::kind); 57 : 58 : public: 59 : /** Parser configuration settings 60 : 61 : @see 62 : @li <a href="https://stackoverflow.com/questions/686217/maximum-on-http-header-values" 63 : >Maximum on HTTP header values (Stackoverflow)</a> 64 : */ 65 : struct config_base 66 : { 67 : header_limits headers; 68 : 69 : /** Largest allowed size for a content body. 70 : 71 : The size of the body is measured 72 : after removing any transfer encodings, 73 : including a chunked encoding. 74 : */ 75 : std::uint64_t body_limit = 64 * 1024; 76 : 77 : /** True if parser can decode deflate transfer and content encodings. 78 : 79 : The deflate decoder must already be 80 : installed thusly, or else an exception 81 : is thrown. 82 : 83 : @par Install Deflate Decoder 84 : @code 85 : deflate_decoder_service::config cfg; 86 : cfg.install( ctx ); 87 : @endcode 88 : */ 89 : bool apply_deflate_decoder = false; 90 : 91 : /** Minimum space for payload buffering. 92 : 93 : This value controls the following 94 : settings: 95 : 96 : @li The smallest allocated size of 97 : the buffers used for reading 98 : and decoding the payload. 99 : 100 : @li The lowest guaranteed size of 101 : an in-place body. 102 : 103 : @li The largest size used to reserve 104 : space in dynamic buffer bodies 105 : when the payload size is not 106 : known ahead of time. 107 : 108 : This cannot be zero, and this cannot 109 : be greater than @ref body_limit. 110 : */ 111 : std::size_t min_buffer = 4096; 112 : 113 : /** Largest permissible output size in prepare. 114 : 115 : This cannot be zero. 116 : */ 117 : std::size_t max_prepare = std::size_t(-1); 118 : 119 : /** Space to reserve for type-erasure. 120 : */ 121 : std::size_t max_type_erase = 1024; 122 : }; 123 : 124 : using mutable_buffers_type = 125 : buffers::mutable_buffer_span; 126 : 127 : using const_buffers_type = 128 : buffers::const_buffer_span; 129 : 130 : struct stream; 131 : 132 : //-------------------------------------------- 133 : // 134 : // Special Members 135 : // 136 : //-------------------------------------------- 137 : 138 : /** Destructor. 139 : */ 140 : BOOST_HTTP_PROTO_DECL 141 : ~parser(); 142 : 143 : /** Constructor (deleted) 144 : */ 145 : parser(parser&&) = delete; 146 : 147 : /** Assignment (deleted) 148 : */ 149 : parser& operator=(parser&&) = delete; 150 : 151 : //-------------------------------------------- 152 : // 153 : // Observers 154 : // 155 : //-------------------------------------------- 156 : 157 : #if 0 158 : /** Return true if any input was committed. 159 : */ 160 : bool 161 : got_some() const noexcept 162 : { 163 : return st_ != state::need_start; 164 : } 165 : #endif 166 : 167 : /** Return true if the complete header was parsed. 168 : */ 169 : bool 170 3622 : got_header() const noexcept 171 : { 172 3622 : return st_ > state::header; 173 : } 174 : 175 : /** Returns `true` if a complete message has been parsed. 176 : 177 : Calling @ref reset prepares the parser 178 : to process the next message in the stream. 179 : 180 : */ 181 : bool 182 2249 : is_complete() const noexcept 183 : { 184 2249 : return st_ == state::complete; 185 : } 186 : 187 : /** Returns `true` if the end of the stream was reached. 188 : 189 : The end of the stream is encountered 190 : when one of the following conditions 191 : occurs: 192 : 193 : @li @ref commit_eof was called and there 194 : is no more data left to parse, or 195 : 196 : @li An unrecoverable error occurred 197 : during parsing. 198 : 199 : When the end of stream is reached, the 200 : function @ref reset must be called 201 : to start parsing a new stream. 202 : */ 203 : bool 204 714 : is_end_of_stream() const noexcept 205 : { 206 : return 207 1296 : st_ == state::reset || 208 582 : ( st_ == state::complete && 209 1296 : got_eof_); 210 : } 211 : 212 : //-------------------------------------------- 213 : // 214 : // Modifiers 215 : // 216 : //-------------------------------------------- 217 : 218 : /** Prepare for a new stream. 219 : */ 220 : BOOST_HTTP_PROTO_DECL 221 : void 222 : reset() noexcept; 223 : 224 : /** Prepare for the next message on the stream. 225 : */ 226 : void 227 1741 : start() 228 : { 229 1741 : start_impl(false); 230 1736 : } 231 : 232 : private: 233 : // New message on the current stream 234 : BOOST_HTTP_PROTO_DECL void 235 : start_impl(bool head_response); 236 : public: 237 : 238 : /** Return the input buffer 239 : */ 240 : BOOST_HTTP_PROTO_DECL 241 : mutable_buffers_type 242 : prepare(); 243 : 244 : /** Commit bytes to the input buffer 245 : */ 246 : BOOST_HTTP_PROTO_DECL 247 : void 248 : commit( 249 : std::size_t n); 250 : 251 : /** Indicate there will be no more input 252 : 253 : @par Postconditions 254 : All buffer sequences previously obtained 255 : by calling @ref prepare are invalidated. 256 : */ 257 : BOOST_HTTP_PROTO_DECL 258 : void 259 : commit_eof(); 260 : 261 : /** Parse pending input data 262 : */ 263 : // VFALCO return result<void>? 264 : BOOST_HTTP_PROTO_DECL 265 : void 266 : parse( 267 : system::error_code& ec); 268 : 269 : /** Attach a body. 270 : 271 : This function attaches the specified elastic 272 : buffer as the storage for the message body. 273 : The parser acquires ownership of the object 274 : `eb` and destroys it when: 275 : 276 : @li @ref is_complete returns `true`, or 277 : @li @ref reset is called, or 278 : @li an unrecoverable parsing error occurs, or 279 : @li the parser is destroyed. 280 : */ 281 : // VFALCO Should this function have 282 : // error_code& ec and call parse? 283 : template<class ElasticBuffer> 284 : #ifndef BOOST_HTTP_PROTO_DOCS 285 : typename std::enable_if< 286 : ! detail::is_reference_wrapper< 287 : ElasticBuffer>::value && 288 : ! is_sink<ElasticBuffer>::value>::type 289 : #else 290 : void 291 : #endif 292 : set_body(ElasticBuffer&& eb); 293 : 294 : /** Attach a body. 295 : 296 : This function attaches the specified elastic 297 : buffer reference as the storage for the message body. 298 : Ownership is not transferred; the caller must 299 : ensure that the lifetime of the object 300 : reference by `eb` extends until: 301 : 302 : @li @ref is_complete returns `true`, or 303 : @li @ref reset is called, or 304 : @li an unrecoverable parsing error occurs, or 305 : @li the parser is destroyed. 306 : */ 307 : template<class ElasticBuffer> 308 : void set_body( 309 : std::reference_wrapper<ElasticBuffer> eb); 310 : 311 : /** Attach a body 312 : */ 313 : template<class Sink> 314 : #ifndef BOOST_HTTP_PROTO_DOCS 315 : typename std::enable_if< 316 : is_sink<Sink>::value, 317 : typename std::decay<Sink>::type 318 : >::type& 319 : #else 320 : typename std::decay<Sink>::type& 321 : #endif 322 : set_body(Sink&& sink); 323 : 324 : /** Return the available body data and consume it. 325 : 326 : The buffer referenced by the string view 327 : will be invalidated if any member function 328 : of the parser is called. 329 : */ 330 : BOOST_HTTP_PROTO_DECL 331 : const_buffers_type 332 : pull_some(); 333 : 334 : /** Return the complete body as a contiguous character buffer. 335 : */ 336 : BOOST_HTTP_PROTO_DECL 337 : core::string_view 338 : body() const noexcept; 339 : 340 : //-------------------------------------------- 341 : 342 : /** Return any leftover data 343 : 344 : This is used to forward unconsumed data 345 : that could lie past the last message. 346 : For example on a CONNECT request there 347 : could be additional protocol-dependent 348 : data that we want to retrieve. 349 : */ 350 : // VFALCO rename to get_leftovers()? 351 : BOOST_HTTP_PROTO_DECL 352 : core::string_view 353 : release_buffered_data() noexcept; 354 : 355 : private: 356 : friend class request_parser; 357 : friend class response_parser; 358 : 359 : detail::header const* 360 : safe_get_header() const; 361 : bool is_plain() const noexcept; 362 : void on_headers(system::error_code&); 363 : BOOST_HTTP_PROTO_DECL void on_set_body(); 364 : void init_dynamic(system::error_code&); 365 : 366 : static constexpr unsigned buffers_N = 8; 367 : 368 : enum class state 369 : { 370 : // order matters 371 : reset, 372 : start, 373 : header, 374 : body, 375 : set_body, 376 : complete 377 : }; 378 : 379 : enum class how 380 : { 381 : in_place, 382 : elastic, 383 : sink, 384 : pull 385 : }; 386 : 387 : context& ctx_; 388 : parser_service& svc_; 389 : detail::workspace ws_; 390 : detail::header h_; 391 : std::uint64_t body_avail_; 392 : std::uint64_t body_total_; 393 : std::uint64_t payload_remain_; 394 : std::size_t nprepare_; 395 : 396 : buffers::flat_buffer fb_; 397 : buffers::circular_buffer cb0_; 398 : buffers::circular_buffer cb1_; 399 : buffers::circular_buffer* body_buf_; 400 : buffers::mutable_buffer_pair mbp_; 401 : buffers::any_dynamic_buffer* eb_; 402 : filter* filt_; 403 : sink* sink_; 404 : 405 : state st_; 406 : how how_; 407 : bool got_eof_; 408 : // bool need_more_; 409 : bool head_response_; 410 : }; 411 : 412 : //------------------------------------------------ 413 : 414 : /** Install the parser service. 415 : */ 416 : BOOST_HTTP_PROTO_DECL 417 : void 418 : install_parser_service( 419 : context& ctx, 420 : parser::config_base const& cfg); 421 : 422 : } // http_proto 423 : } // boost 424 : 425 : #include <boost/http_proto/impl/parser.hpp> 426 : 427 : #endif