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 0 : 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. 142 : */ 143 : BOOST_HTTP_PROTO_DECL 144 : parser(parser&&) noexcept; 145 : 146 : //-------------------------------------------- 147 : // 148 : // Observers 149 : // 150 : //-------------------------------------------- 151 : 152 : #if 0 153 : /** Return true if any input was committed. 154 : */ 155 : bool 156 : got_some() const noexcept 157 : { 158 : return st_ != state::need_start; 159 : } 160 : #endif 161 : 162 : /** Return true if the complete header was parsed. 163 : */ 164 : bool 165 57707 : got_header() const noexcept 166 : { 167 57707 : return st_ > state::header; 168 : } 169 : 170 : /** Returns `true` if a complete message has been parsed. 171 : 172 : Calling @ref reset prepares the parser 173 : to process the next message in the stream. 174 : 175 : */ 176 : bool 177 48814 : is_complete() const noexcept 178 : { 179 48814 : return st_ == state::complete; 180 : } 181 : 182 : /** Returns `true` if the end of the stream was reached. 183 : 184 : The end of the stream is encountered 185 : when one of the following conditions 186 : occurs: 187 : 188 : @li @ref commit_eof was called and there 189 : is no more data left to parse, or 190 : 191 : @li An unrecoverable error occurred 192 : during parsing. 193 : 194 : When the end of stream is reached, the 195 : function @ref reset must be called 196 : to start parsing a new stream. 197 : */ 198 : bool 199 13550 : is_end_of_stream() const noexcept 200 : { 201 : return 202 26240 : st_ == state::reset || 203 12690 : ( st_ == state::complete && 204 26234 : got_eof_); 205 : } 206 : 207 : //-------------------------------------------- 208 : // 209 : // Modifiers 210 : // 211 : //-------------------------------------------- 212 : 213 : /** Prepare for a new stream. 214 : */ 215 : BOOST_HTTP_PROTO_DECL 216 : void 217 : reset() noexcept; 218 : 219 : private: 220 : // New message on the current stream 221 : BOOST_HTTP_PROTO_DECL void 222 : start_impl(bool head_response); 223 : public: 224 : 225 : /** Return the input buffer 226 : */ 227 : BOOST_HTTP_PROTO_DECL 228 : mutable_buffers_type 229 : prepare(); 230 : 231 : /** Commit bytes to the input buffer 232 : */ 233 : BOOST_HTTP_PROTO_DECL 234 : void 235 : commit( 236 : std::size_t n); 237 : 238 : /** Indicate there will be no more input 239 : */ 240 : BOOST_HTTP_PROTO_DECL 241 : void 242 : commit_eof(); 243 : 244 : /** Parse pending input data 245 : */ 246 : BOOST_HTTP_PROTO_DECL 247 : void 248 : parse( 249 : system::error_code& ec); 250 : 251 : /** Attach a body 252 : */ 253 : // VFALCO Should this function have 254 : // error_code& ec and call parse? 255 : #ifndef BOOST_HTTP_PROTO_DOCS 256 : template< 257 : class DynamicBuffer 258 : , class = typename std::enable_if< 259 : buffers::is_dynamic_buffer< 260 : DynamicBuffer>::value 261 : >::type 262 : > 263 : #else 264 : template<class DynamicBuffer> 265 : #endif 266 : typename std::decay< 267 : DynamicBuffer>::type& 268 : set_body(DynamicBuffer&& b); 269 : 270 : /** Attach a body 271 : */ 272 : template<class Sink> 273 : #ifndef BOOST_HTTP_PROTO_DOCS 274 : typename std::enable_if< 275 : is_sink<Sink>::value, 276 : typename std::decay<Sink>::type 277 : >::type 278 : #else 279 : typename std::decay<Sink>::type 280 : #endif 281 : set_body(Sink&& sink); 282 : 283 : /** Return the available body data and consume it. 284 : 285 : The buffer referenced by the string view 286 : will be invalidated if any member function 287 : of the parser is called. 288 : */ 289 : BOOST_HTTP_PROTO_DECL 290 : const_buffers_type 291 : pull_some(); 292 : 293 : /** Return the complete body as a contiguous character buffer. 294 : */ 295 : BOOST_HTTP_PROTO_DECL 296 : core::string_view 297 : body() const noexcept; 298 : 299 : //-------------------------------------------- 300 : 301 : /** Return any leftover data 302 : 303 : This is used to forward unconsumed data 304 : that could lie past the last message. 305 : For example on a CONNECT request there 306 : could be additional protocol-dependent 307 : data that we want to retrieve. 308 : */ 309 : BOOST_HTTP_PROTO_DECL 310 : core::string_view 311 : release_buffered_data() noexcept; 312 : 313 : private: 314 : friend class request_parser; 315 : friend class response_parser; 316 : 317 : detail::header const* 318 : safe_get_header() const; 319 : bool is_plain() const noexcept; 320 : void on_headers(system::error_code&); 321 : 322 : BOOST_HTTP_PROTO_DECL void on_set_body(); 323 : 324 : static constexpr unsigned buffers_N = 8; 325 : 326 : enum class state 327 : { 328 : // order matters 329 : reset, 330 : start, 331 : header, 332 : body, 333 : body_set, 334 : complete, 335 : }; 336 : 337 : enum class how 338 : { 339 : in_place, 340 : dynamic, 341 : sink, 342 : pull 343 : }; 344 : 345 : context& ctx_; 346 : parser_service& svc_; 347 : detail::workspace ws_; 348 : detail::header h_; 349 : std::uint64_t body_avail_; // in body_buf_ 350 : std::uint64_t body_total_; // total output 351 : std::uint64_t payload_remain_; // if known 352 : 353 : buffers::flat_buffer fb_; 354 : buffers::circular_buffer cb0_; 355 : buffers::circular_buffer cb1_; 356 : buffers::circular_buffer* body_buf_; 357 : buffers::mutable_buffer_pair mbp_; 358 : buffers::any_dynamic_buffer* dyn_; 359 : filter* filt_; 360 : sink* sink_; 361 : 362 : state st_; 363 : how how_; 364 : bool got_eof_; 365 : bool head_response_; 366 : }; 367 : 368 : //------------------------------------------------ 369 : 370 : /** Install the parser service. 371 : */ 372 : BOOST_HTTP_PROTO_DECL 373 : void 374 : install_parser_service( 375 : context& ctx, 376 : parser::config_base const& cfg); 377 : 378 : } // http_proto 379 : } // boost 380 : 381 : #include <boost/http_proto/impl/parser.hpp> 382 : 383 : #endif