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