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