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_IMPL_PARSER_IPP 11 : #define BOOST_HTTP_PROTO_IMPL_PARSER_IPP 12 : 13 : #include <boost/http_proto/parser.hpp> 14 : #include <boost/http_proto/context.hpp> 15 : #include <boost/http_proto/error.hpp> 16 : #include <boost/http_proto/service/zlib_service.hpp> 17 : #include <boost/http_proto/detail/except.hpp> 18 : #include <boost/buffers/buffer_copy.hpp> 19 : #include <boost/url/grammar/ci_string.hpp> 20 : #include <boost/assert.hpp> 21 : #include <boost/none.hpp> 22 : #include <memory> 23 : 24 : namespace boost { 25 : namespace http_proto { 26 : 27 : //------------------------------------------------ 28 : /* 29 : Four body styles for `parser` 30 : * Specify a DynamicBuffer 31 : * Specify a Sink 32 : * Read from a parser::stream 33 : * in-place 34 : 35 : Buffer Usage 36 : 37 : | | begin 38 : | H | p | | f | read headers 39 : | H | p | | T | f | set T body 40 : | H | p | | C | T | f | make codec C 41 : | H | p | b | C | T | f | decode p into b 42 : | H | p | b | C | T | f | read/parse loop 43 : | H | | T | f | destroy codec 44 : | H | | T | f | finished 45 : 46 : H headers 47 : C codec 48 : T body 49 : f table 50 : p partial payload 51 : b body data 52 : 53 : - We can compact the headers: 54 : move the table downwards to 55 : squeeze out the unused space 56 : 57 : */ 58 : //------------------------------------------------ 59 : 60 : struct parser_service 61 : : service 62 : { 63 : parser::config_base cfg; 64 : std::size_t space_needed = 0; 65 : zlib::deflate_decoder_service const* 66 : deflate_svc = nullptr; 67 : 68 : parser_service( 69 : context& ctx, 70 : parser::config_base const& cfg_); 71 : }; 72 : 73 6 : parser_service:: 74 : parser_service( 75 : context& ctx, 76 6 : parser::config_base const& cfg_) 77 6 : : cfg(cfg_) 78 : { 79 : /* 80 : | fb | cb0 | cb1 | C | T | f | 81 : 82 : fb flat_buffer headers.max_size 83 : cb0 circular_buffer min_in_place_body 84 : cb1 circular_buffer min_in_place_body 85 : C codec max_codec 86 : T body max_type_erase 87 : f table max_table_space 88 : 89 : */ 90 : // validate 91 : //if(cfg.min_prepare > cfg.max_prepare) 92 : //detail::throw_invalid_argument(); 93 : 94 : // fb, f 95 6 : space_needed += 96 6 : cfg.headers.valid_space_needed(); 97 : 98 : // cb0, cb1 99 6 : space_needed += 100 6 : 2 * cfg.min_in_place_body; 101 : 102 : // T 103 6 : space_needed += cfg.max_type_erase; 104 : 105 : // max(C...) 106 6 : std::size_t C = 0; 107 : { 108 6 : if(cfg.apply_deflate_decoder) 109 : { 110 0 : deflate_svc = &ctx.get_service< 111 0 : zlib::deflate_decoder_service>(); 112 : auto const n = 113 0 : deflate_svc->space_needed(); 114 0 : if( C < n) 115 0 : C = n; 116 : } 117 : } 118 6 : space_needed += C; 119 6 : } 120 : 121 : void 122 6 : install_parser_service( 123 : context& ctx, 124 : parser::config_base const& cfg) 125 : { 126 : ctx.make_service< 127 6 : parser_service>(cfg); 128 6 : } 129 : 130 : //------------------------------------------------ 131 : 132 736 : parser:: 133 : parser( 134 : context& ctx, 135 736 : detail::kind k) 136 : : ctx_(ctx) 137 : , svc_(ctx.get_service< 138 1472 : parser_service>()) 139 736 : , h_(detail::empty{k}) 140 : { 141 736 : auto const n = 142 736 : svc_.space_needed; 143 736 : ws_.allocate(n); 144 736 : h_.cap = n; 145 736 : reset(); 146 736 : } 147 : 148 : //------------------------------------------------ 149 : // 150 : // Special Members 151 : // 152 : //------------------------------------------------ 153 : 154 736 : parser:: 155 736 : ~parser() 156 : { 157 736 : } 158 : 159 : parser:: 160 : parser( 161 : parser&&) noexcept = default; 162 : 163 : //------------------------------------------------ 164 : // 165 : // Modifiers 166 : // 167 : //------------------------------------------------ 168 : 169 : // prepare for a new stream 170 : void 171 736 : parser:: 172 : reset() noexcept 173 : { 174 736 : ws_.clear(); 175 736 : st_ = state::need_start; 176 736 : body_ = body::in_place; 177 736 : head_response_ = false; 178 736 : got_eof_ = false; 179 736 : } 180 : 181 : void 182 767 : parser:: 183 : start_impl( 184 : bool head_response) 185 : { 186 767 : std::size_t leftover = 0; 187 767 : switch(st_) 188 : { 189 734 : default: 190 : case state::need_start: 191 734 : BOOST_ASSERT(h_.size == 0); 192 734 : BOOST_ASSERT(fb_.size() == 0); 193 734 : BOOST_ASSERT(! got_eof_); 194 734 : break; 195 : 196 0 : case state::headers: 197 : // can't call start() twice 198 0 : detail::throw_logic_error(); 199 : 200 0 : case state::headers_done: 201 : case state::body: 202 : // previous message was unfinished 203 0 : detail::throw_logic_error(); 204 : 205 33 : case state::complete: 206 33 : if(fb_.size() > 0) 207 : { 208 : // headers with no body 209 33 : BOOST_ASSERT(h_.size > 0); 210 33 : fb_.consume(h_.size); 211 33 : leftover = fb_.size(); 212 : // move unused octets to front 213 33 : buffers::buffer_copy( 214 33 : buffers::mutable_buffer( 215 : ws_.data(), 216 : leftover), 217 66 : fb_.data()); 218 : } 219 : else 220 : { 221 : // leftover data after body 222 : } 223 33 : break; 224 : } 225 : /* 226 : | fb | cb0 | cb1 | C | T | f | 227 : */ 228 : // start with fb+cb0 229 767 : fb_ = { 230 : ws_.data(), // VFALCO this might need an offset 231 767 : svc_.cfg.headers.max_size + 232 767 : svc_.cfg.min_in_place_body, 233 : leftover }; 234 : 235 1534 : h_ = detail::header( 236 767 : detail::empty{h_.kind}); 237 767 : h_.buf = reinterpret_cast< 238 767 : char*>(ws_.data()); 239 767 : h_.cbuf = h_.buf; 240 767 : h_.cap = ws_.size(); 241 : 242 767 : st_ = state::headers; 243 767 : body_ = body::in_place; 244 : 245 767 : BOOST_ASSERT(! head_response || 246 : h_.kind == detail::kind::response); 247 767 : head_response_ = head_response; 248 767 : } 249 : 250 : auto 251 3185 : parser:: 252 : prepare() -> 253 : mutable_buffers_type 254 : { 255 3185 : switch(st_) 256 : { 257 0 : default: 258 : case state::need_start: 259 : // forgot to call start() 260 0 : detail::throw_logic_error(); 261 : 262 3185 : case state::headers: 263 : { 264 3185 : BOOST_ASSERT(h_.size < 265 : svc_.cfg.headers.max_size); 266 3185 : auto n = fb_.capacity() - fb_.size(); 267 3185 : if( n > svc_.cfg.max_prepare) 268 0 : n = svc_.cfg.max_prepare; 269 : return { 270 3185 : fb_.prepare(n), 271 3185 : buffers::mutable_buffer{} }; 272 : } 273 : 274 0 : case state::headers_done: 275 : { 276 : // reserve headers 277 0 : ws_.reserve_front(h_.size); 278 : 279 : // reserve table at the back 280 0 : ws_.reserve_back(h_.table_space()); 281 : 282 0 : st_ = state::body; 283 : // VFALCO set up body buffer 284 : BOOST_FALLTHROUGH; 285 : } 286 : 287 0 : case state::body: 288 : { 289 : //if(body_ == body::dynamic) 290 0 : auto n = cb0_.capacity() - 291 0 : cb0_.size(); 292 0 : if( n > svc_.cfg.max_prepare) 293 0 : n = svc_.cfg.max_prepare; 294 0 : return cb0_.prepare(n); 295 : } 296 : 297 0 : case state::complete: 298 : // forgot to call start() 299 0 : detail::throw_logic_error(); 300 : } 301 : } 302 : 303 : void 304 3185 : parser:: 305 : commit( 306 : std::size_t n) 307 : { 308 : // Can't commit after eof 309 3185 : if(got_eof_) 310 0 : detail::throw_logic_error(); 311 : 312 3185 : switch(st_) 313 : { 314 0 : default: 315 : case state::need_start: 316 : // forgot to call start() 317 0 : detail::throw_logic_error(); 318 : 319 3185 : case state::headers: 320 3185 : fb_.commit(n); 321 3185 : break; 322 : 323 0 : case state::headers_done: 324 : // forgot to call prepare() 325 0 : detail::throw_logic_error(); 326 : 327 0 : case state::body: 328 0 : cb0_.commit(n); 329 0 : break; 330 : 331 0 : case state::complete: 332 : // forgot to call start() 333 0 : detail::throw_logic_error(); 334 : } 335 3185 : } 336 : 337 : void 338 0 : parser:: 339 : commit_eof() 340 : { 341 0 : switch(st_) 342 : { 343 0 : default: 344 : case state::need_start: 345 : // forgot to call prepare() 346 0 : detail::throw_logic_error(); 347 : 348 0 : case state::headers: 349 0 : got_eof_ = true; 350 0 : break; 351 : 352 0 : case state::headers_done: 353 : // forgot to call prepare() 354 0 : detail::throw_logic_error(); 355 : 356 0 : case state::body: 357 0 : got_eof_ = true; 358 0 : break; 359 : 360 0 : case state::complete: 361 : // Can't commit eof when 362 : // message is complete. 363 0 : detail::throw_logic_error(); 364 : } 365 0 : } 366 : 367 : //----------------------------------------------- 368 : 369 : // process input data then 370 : // eof if input data runs out. 371 : void 372 3185 : parser:: 373 : parse( 374 : error_code& ec) 375 : { 376 3185 : switch(st_) 377 : { 378 0 : default: 379 : case state::need_start: 380 : // forgot to call start() 381 0 : detail::throw_logic_error(); 382 : 383 3185 : case state::headers: 384 : { 385 3185 : BOOST_ASSERT(h_.buf == ws_.data()); 386 3185 : BOOST_ASSERT(h_.cbuf == ws_.data()); 387 3185 : auto const new_size = fb_.size(); 388 3185 : h_.parse(new_size, svc_.cfg.headers, ec); 389 3185 : if(! ec.failed()) 390 : { 391 584 : if( h_.md.payload != payload::none && 392 3 : ! head_response_) 393 : { 394 3 : if(h_.md.payload == payload::size) 395 2 : remain_ = h_.md.payload_size; 396 : 397 : // Deliver headers to caller 398 3 : st_ = state::headers_done; 399 3 : break; 400 : } 401 : // no payload 402 581 : st_ = state::complete; 403 581 : break; 404 : } 405 2601 : if(ec == grammar::error::need_more) 406 : { 407 2473 : if(! got_eof_) 408 2473 : break; 409 0 : if(h_.size > 0) 410 : { 411 : // Connection closed before 412 : // message is complete. 413 0 : ec = BOOST_HTTP_PROTO_ERR( 414 : error::incomplete); 415 0 : return; 416 : } 417 : 418 : // Connection closed 419 : // cleanly. 420 0 : ec = BOOST_HTTP_PROTO_ERR( 421 : error::end_of_stream); 422 0 : return; 423 : } 424 128 : BOOST_ASSERT(ec.failed()); 425 128 : return; 426 : } 427 : 428 0 : case state::headers_done: 429 : { 430 : // This is a no-op 431 : // VFALCO Is this right? 432 0 : ec = {}; 433 0 : break; 434 : } 435 : 436 0 : case state::body: 437 : { 438 0 : parse_body(ec); 439 0 : if(ec.failed()) 440 0 : return; 441 0 : st_ = state::complete; 442 0 : break; 443 : } 444 : 445 0 : case state::complete: 446 0 : break; 447 : } 448 : } 449 : 450 : //------------------------------------------------ 451 : 452 : auto 453 0 : parser:: 454 : get_stream() -> 455 : stream 456 : { 457 : // body type already chosen 458 0 : if(body_ != body::in_place) 459 0 : detail::throw_logic_error(); 460 : 461 0 : body_ = body::stream; 462 0 : return stream(*this); 463 : } 464 : 465 : //------------------------------------------------ 466 : 467 : string_view 468 0 : parser:: 469 : release_buffered_data() noexcept 470 : { 471 0 : return {}; 472 : } 473 : 474 : //------------------------------------------------ 475 : // 476 : // Implementation 477 : // 478 : //------------------------------------------------ 479 : 480 : auto 481 37 : parser:: 482 : safe_get_header() const -> 483 : detail::header const* 484 : { 485 37 : switch(st_) 486 : { 487 0 : default: 488 : case state::need_start: 489 : case state::headers: 490 : // Headers not received yet 491 0 : detail::throw_logic_error(); 492 : 493 3 : case state::headers_done: 494 3 : break; 495 : 496 0 : case state::body: 497 : // Headers received and discarded 498 0 : detail::throw_logic_error(); 499 : 500 34 : case state::complete: 501 : // VFALCO Could be OK 502 34 : break; 503 : } 504 37 : return &h_; 505 : } 506 : 507 : void 508 0 : parser:: 509 : on_set_body() 510 : { 511 0 : } 512 : 513 : void 514 0 : parser:: 515 : parse_body( 516 : error_code& ec) 517 : { 518 0 : if(body_ == body::in_place) 519 : { 520 0 : ec = {}; 521 0 : return; 522 : } 523 0 : if(body_ == body::dynamic) 524 : { 525 0 : ec = {}; 526 0 : return; 527 : } 528 0 : if(body_ == body::sink) 529 : { 530 0 : ec = {}; 531 0 : return; 532 : } 533 0 : if(body_ == body::stream) 534 : { 535 0 : ec = {}; 536 0 : return; 537 : } 538 : } 539 : 540 : void 541 0 : parser:: 542 : parse_chunk( 543 : error_code& ec) 544 : { 545 0 : ec = {}; 546 0 : } 547 : 548 : } // http_proto 549 : } // boost 550 : 551 : #endif