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