1 /* 2 restinio 3 */ 4 5 /*! 6 Echo server. 7 */ 8 9 #include <catch2/catch.hpp> 10 11 #include <restinio/all.hpp> 12 #include <restinio/transforms/zlib.hpp> 13 14 #include <test/common/utest_logger.hpp> 15 #include <test/common/pub.hpp> 16 17 #include "../random_data_generators.ipp" 18 19 TEST_CASE( "restinio_controlled_output" , "[zlib][body_appender][restinio_controlled_output]" ) 20 { 21 std::srand( static_cast<unsigned int>(std::time( nullptr )) ); 22 23 const auto response_body = create_random_text( 128 * 1024, 16 ); 24 25 using router_t = restinio::router::express_router_t<>; 26 27 auto router = std::make_unique< router_t >(); 28 29 namespace rtz = restinio::transforms::zlib; 30 31 router->http_get( 32 R"-(/deflate/:level(-?\d+))-", __anon5ab37a5f0102( auto req, auto params )33 [ & ]( auto req, auto params ){ 34 auto resp = req->create_response(); 35 36 resp 37 .append_header( "Server", "RESTinio Benchmark" ) 38 .append_header_date_field() 39 .append_header( "Content-Type", "text/plain; charset=utf-8" ); 40 41 const auto level = restinio::cast_to< int >( params["level" ] ); 42 43 rtz::deflate_body_appender( resp, level ) 44 .append( response_body ) 45 .complete(); 46 47 return resp.done(); 48 } ); 49 50 router->http_get( 51 R"-(/gzip/:level(-?\d+))-", __anon5ab37a5f0202( auto req, auto params )52 [ & ]( auto req, auto params ){ 53 auto resp = req->create_response(); 54 55 resp 56 .append_header( "Server", "RESTinio Benchmark" ) 57 .append_header_date_field() 58 .append_header( "Content-Type", "text/plain; charset=utf-8" ); 59 60 const auto level = restinio::cast_to< int >( params["level" ] ); 61 62 rtz::gzip_body_appender( resp, level ) 63 .append( response_body ) 64 .complete(); 65 66 return resp.done(); 67 } ); 68 69 router->http_get( 70 R"-(/identity)-", __anon5ab37a5f0302( auto req, auto )71 [ & ]( auto req, auto ){ 72 auto resp = req->create_response(); 73 74 resp 75 .append_header( "Server", "RESTinio Benchmark" ) 76 .append_header_date_field() 77 .append_header( "Content-Type", "text/plain; charset=utf-8" ); 78 79 rtz::identity_body_appender( resp ) 80 .append( response_body ) 81 .complete(); 82 83 return resp.done(); 84 } ); 85 86 87 using http_server_t = 88 restinio::http_server_t< 89 restinio::traits_t< 90 restinio::asio_timer_manager_t, 91 utest_logger_t, 92 router_t > >; 93 94 http_server_t http_server{ 95 restinio::own_io_context(), __anon5ab37a5f0402( )96 [&]( auto & settings ){ 97 settings 98 .port( utest_default_port() ) 99 .address( "127.0.0.1" ) 100 .request_handler( std::move( router ) ); 101 } 102 }; 103 104 other_work_thread_for_server_t<http_server_t> other_thread{ http_server }; 105 other_thread.run(); 106 107 { 108 const std::string request{ 109 "GET /deflate/-1 HTTP/1.0\r\n" 110 "From: unit-test\r\n" 111 "User-Agent: unit-test\r\n" 112 "Content-Type: application/x-www-form-urlencoded\r\n" 113 "Connection: close\r\n" 114 "\r\n" 115 }; 116 std::string response; 117 118 REQUIRE_NOTHROW( response = do_request( request ) ); 119 120 REQUIRE_THAT( 121 response, 122 Catch::Matchers::Contains( "Content-Encoding: deflate\r\n" ) ); 123 124 REQUIRE_THAT( 125 response, 126 Catch::Matchers::EndsWith( 127 "\r\n\r\n" + 128 rtz::deflate_compress( response_body, -1 ) ) ); 129 } 130 131 { 132 const std::string request{ 133 "GET /gzip/-1 HTTP/1.0\r\n" 134 "From: unit-test\r\n" 135 "User-Agent: unit-test\r\n" 136 "Content-Type: application/x-www-form-urlencoded\r\n" 137 "Connection: close\r\n" 138 "\r\n" 139 }; 140 std::string response; 141 142 REQUIRE_NOTHROW( response = do_request( request ) ); 143 144 REQUIRE_THAT( 145 response, 146 Catch::Matchers::Contains( "Content-Encoding: gzip\r\n" ) ); 147 148 REQUIRE_THAT( 149 response, 150 Catch::Matchers::EndsWith( 151 "\r\n\r\n" + 152 rtz::gzip_compress( response_body, -1 ) ) ); 153 } 154 155 { 156 const std::string request{ 157 "GET /identity HTTP/1.0\r\n" 158 "From: unit-test\r\n" 159 "User-Agent: unit-test\r\n" 160 "Content-Type: application/x-www-form-urlencoded\r\n" 161 "Connection: close\r\n" 162 "\r\n" 163 }; 164 std::string response; 165 166 REQUIRE_NOTHROW( response = do_request( request ) ); 167 168 REQUIRE_THAT( 169 response, 170 Catch::Matchers::Contains( "Content-Encoding: identity\r\n" ) ); 171 172 REQUIRE_THAT( 173 response, 174 Catch::Matchers::EndsWith( "\r\n\r\n" + response_body ) ); 175 } 176 177 for( int i = 0; i <= 9; ++i ) 178 { 179 { 180 const std::string request = 181 fmt::format( 182 "GET /deflate/{} HTTP/1.0\r\n" 183 "From: unit-test\r\n" 184 "User-Agent: unit-test\r\n" 185 "Content-Type: application/x-www-form-urlencoded\r\n" 186 "Connection: close\r\n" 187 "\r\n", 188 i ); 189 190 std::string response; 191 192 REQUIRE_NOTHROW( response = do_request( request ) ); 193 194 REQUIRE_THAT( 195 response, 196 Catch::Matchers::Contains( "Content-Encoding: deflate\r\n" ) ); 197 198 REQUIRE_THAT( 199 response, 200 Catch::Matchers::EndsWith( 201 "\r\n\r\n" + 202 rtz::deflate_compress( response_body, i ) ) ); 203 } 204 205 { 206 const std::string request = 207 fmt::format( 208 "GET /gzip/{} HTTP/1.0\r\n" 209 "From: unit-test\r\n" 210 "User-Agent: unit-test\r\n" 211 "Content-Type: application/x-www-form-urlencoded\r\n" 212 "Connection: close\r\n" 213 "\r\n", 214 i ); 215 216 std::string response; 217 218 REQUIRE_NOTHROW( response = do_request( request ) ); 219 220 REQUIRE_THAT( 221 response, 222 Catch::Matchers::Contains( "Content-Encoding: gzip\r\n" ) ); 223 224 REQUIRE_THAT( 225 response, 226 Catch::Matchers::EndsWith( 227 "\r\n\r\n" + 228 rtz::gzip_compress( response_body, i ) ) ); 229 } 230 } 231 232 other_thread.stop_and_join(); 233 } 234 235 TEST_CASE( "user_controlled_output" , "[zlib][body_appender][user_controlled_output]" ) 236 { 237 std::srand( static_cast<unsigned int>(std::time( nullptr )) ); 238 239 const auto response_body = create_random_text( 128 * 1024, 16 ); 240 241 using router_t = restinio::router::express_router_t<>; 242 243 auto router = std::make_unique< router_t >(); 244 245 namespace rtz = restinio::transforms::zlib; 246 247 router->http_get( 248 R"-(/deflate/:level(-?\d+))-", __anon5ab37a5f0502( const restinio::request_handle_t& req, auto params )249 [ & ]( const restinio::request_handle_t& req, auto params ){ 250 auto resp = req->create_response< restinio::user_controlled_output_t >(); 251 252 resp 253 .append_header( "Server", "RESTinio Benchmark" ) 254 .append_header_date_field() 255 .append_header( "Content-Type", "text/plain; charset=utf-8" ); 256 257 const auto level = restinio::cast_to< int >( params["level" ] ); 258 259 auto ba = rtz::deflate_body_appender( resp, level ); 260 261 ba 262 .append( 263 restinio::string_view_t{ 264 response_body.data(), 265 response_body.size()/2 } ) 266 .flush(); 267 268 ba 269 .append( 270 restinio::string_view_t{ 271 response_body.data() + response_body.size()/2, 272 response_body.size() - response_body.size()/2 } ) 273 .complete(); 274 275 return resp.done(); 276 } ); 277 278 router->http_get( 279 R"-(/gzip/:level(-?\d+))-", __anon5ab37a5f0602( const restinio::request_handle_t& req, auto params )280 [ & ]( const restinio::request_handle_t& req, auto params ){ 281 auto resp = req->create_response< restinio::user_controlled_output_t >(); 282 283 resp 284 .append_header( "Server", "RESTinio Benchmark" ) 285 .append_header_date_field() 286 .append_header( "Content-Type", "text/plain; charset=utf-8" ); 287 288 const auto level = restinio::cast_to< int >( params["level" ] ); 289 290 auto ba = rtz::gzip_body_appender( resp, level ); 291 ba 292 .append( 293 restinio::string_view_t{ 294 response_body.data(), 295 response_body.size()/2 } ) 296 .flush(); 297 298 ba 299 .append( 300 restinio::string_view_t{ 301 response_body.data() + response_body.size()/2, 302 response_body.size() - response_body.size()/2 } ) 303 .complete(); 304 305 return resp.done(); 306 } ); 307 308 router->http_get( 309 R"-(/identity)-", __anon5ab37a5f0702( const restinio::request_handle_t& req, auto )310 [ & ]( const restinio::request_handle_t& req, auto ){ 311 auto resp = req->create_response< restinio::user_controlled_output_t >(); 312 313 resp 314 .append_header( "Server", "RESTinio Benchmark" ) 315 .append_header_date_field() 316 .append_header( "Content-Type", "text/plain; charset=utf-8" ); 317 318 auto ba = rtz::identity_body_appender( resp ); 319 ba 320 .append( 321 restinio::string_view_t{ 322 response_body.data(), 323 response_body.size()/2 } ) 324 .flush(); 325 326 ba 327 .append( 328 restinio::string_view_t{ 329 response_body.data() + response_body.size()/2, 330 response_body.size() - response_body.size()/2 } ) 331 .complete(); 332 333 return resp.done(); 334 } ); 335 336 using http_server_t = 337 restinio::http_server_t< 338 restinio::traits_t< 339 restinio::asio_timer_manager_t, 340 utest_logger_t, 341 router_t > >; 342 343 http_server_t http_server{ 344 restinio::own_io_context(), __anon5ab37a5f0802( )345 [&]( auto & settings ){ 346 settings 347 .port( utest_default_port() ) 348 .address( "127.0.0.1" ) 349 .request_handler( std::move( router ) ); 350 } 351 }; 352 353 other_work_thread_for_server_t<http_server_t> other_thread{ http_server }; 354 other_thread.run(); 355 356 { 357 const std::string request{ 358 "GET /deflate/-1 HTTP/1.0\r\n" 359 "From: unit-test\r\n" 360 "User-Agent: unit-test\r\n" 361 "Content-Type: application/x-www-form-urlencoded\r\n" 362 "Connection: close\r\n" 363 "\r\n" 364 }; 365 std::string response; 366 367 REQUIRE_NOTHROW( response = do_request( request ) ); 368 369 REQUIRE_THAT( 370 response, 371 Catch::Matchers::Contains( "Content-Encoding: deflate\r\n" ) ); 372 373 REQUIRE_THAT( 374 response, 375 Catch::Matchers::Contains( "\r\n\r\n" ) ); 376 377 const auto body_start = response.find( "\r\n\r\n" ) + 4; 378 379 REQUIRE( 380 response_body == 381 rtz::deflate_decompress( 382 restinio::string_view_t{ 383 response.data() + body_start, 384 response.size() - body_start } ) ); 385 } 386 387 388 { 389 const std::string request{ 390 "GET /gzip/-1 HTTP/1.0\r\n" 391 "From: unit-test\r\n" 392 "User-Agent: unit-test\r\n" 393 "Content-Type: application/x-www-form-urlencoded\r\n" 394 "Connection: close\r\n" 395 "\r\n" 396 }; 397 std::string response; 398 399 REQUIRE_NOTHROW( response = do_request( request ) ); 400 401 REQUIRE_THAT( 402 response, 403 Catch::Matchers::Contains( "Content-Encoding: gzip\r\n" ) ); 404 405 REQUIRE_THAT( 406 response, 407 Catch::Matchers::Contains( "\r\n\r\n" ) ); 408 409 const auto body_start = response.find( "\r\n\r\n" ) + 4; 410 411 REQUIRE( 412 response_body == 413 rtz::gzip_decompress( 414 restinio::string_view_t{ 415 response.data() + body_start, 416 response.size() - body_start } ) ); 417 } 418 419 420 { 421 const std::string request{ 422 "GET /identity HTTP/1.0\r\n" 423 "From: unit-test\r\n" 424 "User-Agent: unit-test\r\n" 425 "Content-Type: application/x-www-form-urlencoded\r\n" 426 "Connection: close\r\n" 427 "\r\n" 428 }; 429 std::string response; 430 431 REQUIRE_NOTHROW( response = do_request( request ) ); 432 433 REQUIRE_THAT( 434 response, 435 Catch::Matchers::Contains( "Content-Encoding: identity\r\n" ) ); 436 437 REQUIRE_THAT( 438 response, 439 Catch::Matchers::Contains( "\r\n\r\n" ) ); 440 441 const auto body_start = response.find( "\r\n\r\n" ) + 4; 442 443 REQUIRE( 444 response_body == 445 restinio::string_view_t{ 446 response.data() + body_start, 447 response.size() - body_start } ); 448 } 449 450 for( int i = 0; i <= 9; ++i ) 451 { 452 { 453 const std::string request = 454 fmt::format( 455 "GET /deflate/{} HTTP/1.0\r\n" 456 "From: unit-test\r\n" 457 "User-Agent: unit-test\r\n" 458 "Content-Type: application/x-www-form-urlencoded\r\n" 459 "Connection: close\r\n" 460 "\r\n", 461 i ); 462 463 std::string response; 464 465 REQUIRE_NOTHROW( response = do_request( request ) ); 466 467 REQUIRE_THAT( 468 response, 469 Catch::Matchers::Contains( "Content-Encoding: deflate\r\n" ) ); 470 471 REQUIRE_THAT( 472 response, 473 Catch::Matchers::Contains( "\r\n\r\n" ) ); 474 475 const auto body_start = response.find( "\r\n\r\n" ) + 4; 476 477 REQUIRE( 478 response_body == 479 rtz::deflate_decompress( 480 restinio::string_view_t{ 481 response.data() + body_start, 482 response.size() - body_start } ) ); 483 } 484 485 { 486 const std::string request = 487 fmt::format( 488 "GET /gzip/{} HTTP/1.0\r\n" 489 "From: unit-test\r\n" 490 "User-Agent: unit-test\r\n" 491 "Content-Type: application/x-www-form-urlencoded\r\n" 492 "Connection: close\r\n" 493 "\r\n", 494 i ); 495 496 std::string response; 497 498 REQUIRE_NOTHROW( response = do_request( request ) ); 499 500 REQUIRE_THAT( 501 response, 502 Catch::Matchers::Contains( "Content-Encoding: gzip\r\n" ) ); 503 504 REQUIRE_THAT( 505 response, 506 Catch::Matchers::Contains( "\r\n\r\n" ) ); 507 508 const auto body_start = response.find( "\r\n\r\n" ) + 4; 509 510 REQUIRE( 511 response_body == 512 rtz::gzip_decompress( 513 restinio::string_view_t{ 514 response.data() + body_start, 515 response.size() - body_start } ) ); 516 } 517 } 518 519 other_thread.stop_and_join(); 520 } 521 522 TEST_CASE( "chunked_output" , "[zlib][body_appender][chunked_output]" ) 523 { 524 std::srand( static_cast<unsigned int>(std::time( nullptr )) ); 525 526 const auto response_body = create_random_text( 128 * 1024, 16 ); 527 528 using router_t = restinio::router::express_router_t<>; 529 530 auto router = std::make_unique< router_t >(); 531 532 namespace rtz = restinio::transforms::zlib; 533 534 router->http_get( 535 R"-(/deflate/:level(-?\d+))-", __anon5ab37a5f0902( const restinio::request_handle_t& req, auto params )536 [ & ]( const restinio::request_handle_t& req, auto params ){ 537 auto resp = req->create_response< restinio::chunked_output_t >(); 538 539 resp 540 .append_header( "Server", "RESTinio Benchmark" ) 541 .append_header_date_field() 542 .append_header( "Content-Type", "text/plain; charset=utf-8" ); 543 544 const auto level = restinio::cast_to< int >( params["level" ] ); 545 546 auto ba = rtz::deflate_body_appender( resp, level ); 547 548 ba 549 .append( 550 restinio::string_view_t{ 551 response_body.data(), 552 response_body.size()/2 } ) 553 .make_chunk() 554 .flush(); 555 556 ba 557 .append( 558 restinio::string_view_t{ 559 response_body.data() + response_body.size()/2, 560 response_body.size() - response_body.size()/2 } ) 561 .make_chunk() 562 .complete(); 563 564 return resp.done(); 565 } ); 566 567 router->http_get( 568 R"-(/gzip/:level(-?\d+))-", __anon5ab37a5f0a02( const restinio::request_handle_t& req, auto params )569 [ & ]( const restinio::request_handle_t& req, auto params ){ 570 auto resp = req->create_response< restinio::chunked_output_t >(); 571 572 resp 573 .append_header( "Server", "RESTinio Benchmark" ) 574 .append_header_date_field() 575 .append_header( "Content-Type", "text/plain; charset=utf-8" ); 576 577 const auto level = restinio::cast_to< int >( params["level" ] ); 578 579 auto ba = rtz::gzip_body_appender( resp, level ); 580 581 ba 582 .append( 583 restinio::string_view_t{ 584 response_body.data(), 585 response_body.size()/2 } ) 586 .make_chunk() 587 .flush(); 588 589 ba 590 .append( 591 restinio::string_view_t{ 592 response_body.data() + response_body.size()/2, 593 response_body.size() - response_body.size()/2 } ) 594 .make_chunk() 595 .complete(); 596 597 return resp.done(); 598 } ); 599 600 router->http_get( 601 R"-(/identity)-", __anon5ab37a5f0b02( const restinio::request_handle_t& req, auto )602 [ & ]( const restinio::request_handle_t& req, auto ){ 603 auto resp = req->create_response< restinio::chunked_output_t >(); 604 605 resp 606 .append_header( "Server", "RESTinio Benchmark" ) 607 .append_header_date_field() 608 .append_header( "Content-Type", "text/plain; charset=utf-8" ); 609 610 auto ba = rtz::identity_body_appender( resp ); 611 612 ba 613 .append( 614 restinio::string_view_t{ 615 response_body.data(), 616 response_body.size()/2 } ) 617 .make_chunk() 618 .flush(); 619 620 ba 621 .append( 622 restinio::string_view_t{ 623 response_body.data() + response_body.size()/2, 624 response_body.size() - response_body.size()/2 } ) 625 .make_chunk() 626 .complete(); 627 628 return resp.done(); 629 } ); 630 631 632 using http_server_t = 633 restinio::http_server_t< 634 restinio::traits_t< 635 restinio::asio_timer_manager_t, 636 utest_logger_t, 637 router_t > >; 638 639 http_server_t http_server{ 640 restinio::own_io_context(), __anon5ab37a5f0c02( )641 [&]( auto & settings ){ 642 settings 643 .port( utest_default_port() ) 644 .address( "127.0.0.1" ) 645 .request_handler( std::move( router ) ); 646 } 647 }; 648 649 other_work_thread_for_server_t<http_server_t> other_thread{ http_server }; 650 other_thread.run(); 651 652 { 653 const std::string request{ 654 "GET /deflate/-1 HTTP/1.0\r\n" 655 "From: unit-test\r\n" 656 "User-Agent: unit-test\r\n" 657 "Content-Type: application/x-www-form-urlencoded\r\n" 658 "Connection: close\r\n" 659 "\r\n" 660 }; 661 std::string response; 662 663 REQUIRE_NOTHROW( response = do_request( request ) ); 664 665 REQUIRE_THAT( 666 response, 667 Catch::Matchers::Contains( "Content-Encoding: deflate\r\n" ) ); 668 } 669 670 671 { 672 const std::string request{ 673 "GET /gzip/-1 HTTP/1.0\r\n" 674 "From: unit-test\r\n" 675 "User-Agent: unit-test\r\n" 676 "Content-Type: application/x-www-form-urlencoded\r\n" 677 "Connection: close\r\n" 678 "\r\n" 679 }; 680 std::string response; 681 682 REQUIRE_NOTHROW( response = do_request( request ) ); 683 684 REQUIRE_THAT( 685 response, 686 Catch::Matchers::Contains( "Content-Encoding: gzip\r\n" ) ); 687 } 688 689 690 { 691 const std::string request{ 692 "GET /identity HTTP/1.0\r\n" 693 "From: unit-test\r\n" 694 "User-Agent: unit-test\r\n" 695 "Content-Type: application/x-www-form-urlencoded\r\n" 696 "Connection: close\r\n" 697 "\r\n" 698 }; 699 std::string response; 700 701 REQUIRE_NOTHROW( response = do_request( request ) ); 702 703 REQUIRE_THAT( 704 response, 705 Catch::Matchers::Contains( "Content-Encoding: identity\r\n" ) ); 706 } 707 708 other_thread.stop_and_join(); 709 } 710