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