1 /*
2 	Simple example using sendfile facility.
3 */
4 
5 #include <iostream>
6 
7 #include <restinio/all.hpp>
8 #include <restinio/tls.hpp>
9 
10 #include <fmt/format.h>
11 
12 #include <clara.hpp>
13 
14 //
15 // app_args_t
16 //
17 struct app_args_t
18 {
19 	bool m_help{ false };
20 	std::string m_address{ "localhost" };
21 	std::uint16_t m_port{ 8080 };
22 	std::size_t m_pool_size{ 1 };
23 	std::string m_certs_dir{ "." };
24 	std::string m_file;
25 	restinio::file_offset_t m_data_offset{ 0 };
26 	restinio::file_size_t m_data_size{ std::numeric_limits< restinio::file_size_t >::max() };
27 	std::string m_content_type{ "text/plain" };
28 	bool m_trace_server{ false };
29 
30 	static app_args_t
parseapp_args_t31 	parse( int argc, const char * argv[] )
32 	{
33 		using namespace clara;
34 
35 		app_args_t result;
36 
37 		auto cli =
38 			Opt( result.m_address, "address" )
39 					["-a"]["--address"]
40 					( fmt::format( "address to listen (default: {})", result.m_address ) )
41 			| Opt( result.m_port, "port" )
42 					["-p"]["--port"]
43 					( fmt::format( "port to listen (default: {})", result.m_port ) )
44 			| Opt( result.m_pool_size, "thread-pool size" )
45 					[ "-n" ][ "--thread-pool-size" ]
46 					( fmt::format(
47 						"The size of a thread pool to run server (default: {})",
48 						result.m_pool_size ) )
49 			| Opt( result.m_certs_dir, "dir" )
50 					[ "--certs-dir" ]
51 					( fmt::format(
52 						"A directory with server.pem, key.pem, dh2048.pem (default: {})",
53 						result.m_certs_dir ) )
54 			| Opt( result.m_data_offset, "offset" )
55 					["-o"]["--data-offset"]
56 					( fmt::format(
57 						"Offset of the data portion in file (default: {})",
58 						result.m_data_offset ) )
59 			| Opt( result.m_data_size, "size" )
60 					["-s"]["--data-size"]
61 					( "size of the data portion in file (default: to the end of file)" )
62 			| Opt( result.m_content_type, "content-type" )
63 					["--content-type"]
64 					( fmt::format(
65 						"A value of 'Content-Type' header field (default: {})",
66 						result.m_content_type ) )
67 			| Opt( result.m_trace_server )
68 					[ "-t" ][ "--trace" ]
69 					( "Enable trace server" )
70 			| Arg( result.m_file, "file" ).required()
71 					( "Path to a file that will be served as response" )
72 			| Help(result.m_help);
73 
74 		auto parse_result = cli.parse( Args(argc, argv) );
75 		if( !parse_result )
76 		{
77 			throw std::runtime_error{
78 				fmt::format(
79 					"Invalid command-line arguments: {}",
80 					parse_result.errorMessage() ) };
81 		}
82 
83 		if( result.m_help )
84 		{
85 			std::cout << cli << std::endl;
86 		}
87 
88 		return result;
89 	}
90 };
91 
92 template < typename Server_Traits >
run_server(const app_args_t & args)93 void run_server( const app_args_t & args )
94 {
95 	// Since RESTinio supports both stand-alone ASIO and boost::ASIO
96 	// we specify an alias for a concrete asio namesace.
97 	// That's makes it possible to compile the code in both cases.
98 	// Typicaly only one of ASIO variants would be used,
99 	// and so only asio::* or only boost::asio::* would be applied.
100 	namespace asio_ns = restinio::asio_ns;
101 
102 	asio_ns::ssl::context tls_context{ asio_ns::ssl::context::sslv23 };
103 	tls_context.set_options(
104 		asio_ns::ssl::context::default_workarounds
105 		| asio_ns::ssl::context::no_sslv2
106 		| asio_ns::ssl::context::single_dh_use );
107 
108 	tls_context.use_certificate_chain_file( args.m_certs_dir + "/server.pem" );
109 	tls_context.use_private_key_file(
110 		args.m_certs_dir + "/key.pem",
111 		asio_ns::ssl::context::pem );
112 	tls_context.use_tmp_dh_file( args.m_certs_dir + "/dh2048.pem" );
113 
114 	restinio::run(
115 		restinio::on_thread_pool< Server_Traits >( args.m_pool_size )
116 			.port( args.m_port )
117 			.address( args.m_address )
118 			.concurrent_accepts_count( args.m_pool_size )
119 			.tls_context( std::move( tls_context ) )
120 			.request_handler(
121 				[&]( auto req ){
122 					if( restinio::http_method_get() == req->header().method() &&
123 						req->header().request_target() == "/" )
124 					{
125 						try
126 						{
127 							auto sf = restinio::sendfile( args.m_file );
128 							sf.offset_and_size(
129 								args.m_data_offset,
130 								args.m_data_size );
131 
132 							return
133 								req->create_response()
134 									.append_header(
135 										restinio::http_field::server,
136 										"RESTinio hello world server" )
137 									.append_header_date_field()
138 									.append_header(
139 										restinio::http_field::content_type,
140 										args.m_content_type )
141 									.set_body( std::move( sf ) )
142 									.done();
143 						}
144 						catch( const std::exception & )
145 						{
146 							return req->create_response( restinio::status_not_found() )
147 								.connection_close()
148 								.append_header_date_field()
149 								.done();
150 						}
151 					}
152 
153 					return restinio::request_rejected();
154 			} ) );
155 }
156 
main(int argc,const char * argv[])157 int main( int argc, const char * argv[] )
158 {
159 	try
160 	{
161 		const auto args = app_args_t::parse( argc, argv );
162 
163 		if( !args.m_help )
164 		{
165 			if( args.m_trace_server )
166 			{
167 				using traits_t =
168 					restinio::tls_traits_t<
169 						restinio::asio_timer_manager_t,
170 						restinio::shared_ostream_logger_t >;
171 
172 				run_server< traits_t >( args );
173 			}
174 			else
175 			{
176 				run_server< restinio::default_tls_traits_t >( args );
177 			}
178 		}
179 	}
180 	catch( const std::exception & ex )
181 	{
182 		std::cerr << "Error: " << ex.what() << std::endl;
183 		return 1;
184 	}
185 
186 	return 0;
187 }
188