RESTinio
Loading...
Searching...
No Matches
sample/sendfiles/main.cpp
#include <iostream>
#include <map>
#include <fmt/format.h>
#include <restinio-helpers/cmd_line_args_helpers.hpp>
const char *
content_type_by_file_extention( const restinio::string_view_t & ext )
{
// Incomplete list of mime types from here:
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Complete_list_of_MIME_types
if(ext == "aac" ) return "audio/aac";
if(ext == "abw" ) return "application/x-abiword";
if(ext == "arc" ) return "application/octet-stream";
if(ext == "avi" ) return "video/x-msvideo";
if(ext == "azw" ) return "application/vnd.amazon.ebook";
if(ext == "bin" ) return "application/octet-stream";
if(ext == "bz" ) return "application/x-bzip";
if(ext == "bz2" ) return "application/x-bzip2";
if(ext == "csh" ) return "application/x-csh";
if(ext == "css" ) return "text/css";
if(ext == "csv" ) return "text/csv";
if(ext == "doc" ) return "application/msword";
if(ext == "docx" ) return "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
if(ext == "eot" ) return "application/vnd.ms-fontobject";
if(ext == "epub" ) return "application/epub+zip";
if(ext == "gif" ) return "image/gif";
if(ext == "htm" || ext == "html" ) return "text/html";
if(ext == "ico" ) return "image/x-icon";
if(ext == "ics" ) return "text/calendar";
if(ext == "jar" ) return "application/java-archive";
if(ext == "jpeg" || ext == "jpg" ) return "image/jpeg";
if(ext == "js" ) return "application/javascript";
if(ext == "json" ) return "application/json";
if(ext == "mid" || ext == "midi" ) return "audio/midi";
if(ext == "mpeg" ) return "video/mpeg";
if(ext == "mpkg" ) return "application/vnd.apple.installer+xml";
if(ext == "odp" ) return "application/vnd.oasis.opendocument.presentation";
if(ext == "ods" ) return "application/vnd.oasis.opendocument.spreadsheet";
if(ext == "odt" ) return "application/vnd.oasis.opendocument.text";
if(ext == "oga" ) return "audio/ogg";
if(ext == "ogv" ) return "video/ogg";
if(ext == "ogx" ) return "application/ogg";
if(ext == "otf" ) return "font/otf";
if(ext == "png" ) return "image/png";
if(ext == "pdf" ) return "application/pdf";
if(ext == "ppt" ) return "application/vnd.ms-powerpoint";
if(ext == "pptx" ) return "application/vnd.openxmlformats-officedocument.presentationml.presentation";
if(ext == "rar" ) return "archive application/x-rar-compressed";
if(ext == "rtf" ) return "application/rtf";
if(ext == "sh" ) return "application/x-sh";
if(ext == "svg" ) return "image/svg+xml";
if(ext == "swf" ) return "application/x-shockwave-flash";
if(ext == "tar" ) return "application/x-tar";
if(ext == "tif" || ext == "tiff" ) return "image/tiff";
if(ext == "ts" ) return "application/typescript";
if(ext == "ttf" ) return "font/ttf";
if(ext == "vsd" ) return "application/vnd.visio";
if(ext == "wav" ) return "audio/x-wav";
if(ext == "weba" ) return "audio/webm";
if(ext == "webm" ) return "video/webm";
if(ext == "webp" ) return "image/webp";
if(ext == "woff" ) return "font/woff";
if(ext == "woff2" ) return "font/woff2";
if(ext == "xhtml" ) return "application/xhtml+xml";
if(ext == "xls" ) return "application/vnd.ms-excel";
if(ext == "xlsx" ) return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
if(ext == "xml" ) return "application/xml";
if(ext == "xul" ) return "application/vnd.mozilla.xul+xml";
if(ext == "zip" ) return "archive application/zip";
if(ext == "3gp" ) return "video/3gpp";
if(ext == "3g2" ) return "video/3gpp2";
if(ext == "7z" ) return "application/x-7z-compressed";
return "application/text";
}
namespace rr = restinio::router;
using router_t = rr::express_router_t<>;
auto server_handler( const std::string & root_dir )
{
auto router = std::make_unique< router_t >();
std::string server_root_dir;
if( root_dir.empty() )
{
server_root_dir = "./";
}
else if( root_dir.back() != '/' && root_dir.back() != '\\' )
{
server_root_dir = root_dir + '/';
}
else
{
server_root_dir = root_dir;
}
// GET request to homepage.
router->http_get(
R"(/:path(.*)\.:ext(.*))",
[ server_root_dir ]( auto req, auto params ){
auto path = req->header().path();
if( std::string::npos == path.find( ".." ) )
{
// A nice path.
const auto file_path =
server_root_dir +
std::string{ path.data(), path.size() };
try
{
auto sf = restinio::sendfile( file_path );
auto modified_at =
restinio::make_date_field_value( sf.meta().last_modified_at() );
auto expires_at =
std::chrono::system_clock::now() +
std::chrono::hours( 24 * 7 ) );
return
req->create_response()
.append_header(
restinio::http_field::server,
"RESTinio" )
.append_header_date_field()
.append_header(
restinio::http_field::last_modified,
std::move( modified_at ) )
.append_header(
restinio::http_field::expires,
std::move( expires_at ) )
.append_header(
restinio::http_field::content_type,
content_type_by_file_extention( params[ "ext" ] ) )
.set_body( std::move( sf ) )
.done();
}
catch( const std::exception & )
{
return
req->create_response( restinio::status_not_found() )
.append_header_date_field()
.connection_close()
.done();
}
}
else
{
// Bad path.
return
req->create_response( restinio::status_forbidden() )
.append_header_date_field()
.connection_close()
.done();
}
} );
router->non_matched_request_handler(
[]( auto req ){
if( restinio::http_method_get() == req->header().method() )
return
req->create_response( restinio::status_not_found() )
.append_header_date_field()
.connection_close()
.done();
return req->create_response( restinio::status_not_implemented() )
.append_header_date_field()
.connection_close()
.done();
} );
return router;
}
struct app_args_t
{
bool m_help{ false };
std::string m_address{ "localhost" };
std::uint16_t m_port{ 8080 };
std::size_t m_pool_size{ 1 };
std::string m_root_dir{ "." };
static app_args_t
parse( int argc, const char * argv[] )
{
using namespace restinio_helpers;
app_args_t result;
process_cmd_line_args( argc, argv, result,
cmd_line_arg_t{
result.m_address, "-a", "--address",
"address to listen (default: {})"
},
cmd_line_arg_t{
result.m_port, "-p", "--port",
"port to listen (default: {})"
},
cmd_line_arg_t{
result.m_pool_size, "-n", "--thread-pool-size",
"size of a thread pool to run server (default: {})"
},
cmd_line_arg_t{
result.m_root_dir,
"-r", "--root-dir",
"server root dir (default: '{}')"
} );
if( result.m_root_dir.empty() )
throw std::runtime_error{
"server root dir can't be empty"
};
return result;
}
};
int main( int argc, char const *argv[] )
{
using namespace std::chrono;
try
{
const auto args = app_args_t::parse( argc, argv );
if( !args.m_help )
{
using traits_t =
router_t >;
.address( "localhost" )
.port( args.m_port )
.request_handler( server_handler( args.m_root_dir ) )
.read_next_http_message_timelimit( 10s )
.write_http_response_timelimit( 1s )
.handle_request_timeout( 1s ) );
}
}
catch( const std::exception & ex )
{
std::cerr << "Error: " << ex.what() << std::endl;
return 1;
}
return 0;
}
Timer factory implementation using asio timers.
No operation logger.
Options for matching routes.
options_t & strict(bool p) &
Include all core header files in one.
token_list_t< Route_Param_Appender > parse(string_view_t route_sv, const options_t &options)
Parse a string for the raw tokens.
run_on_thread_pool_settings_t< Traits > on_thread_pool(std::size_t pool_size)
A special marker for the case when http_server must be run on an thread pool.
http_status_line_t status_not_implemented()
http_status_line_t status_forbidden()
std::string_view string_view_t
void run(asio_ns::io_context &ioctx, run_on_this_thread_settings_t< Traits > &&settings)
Helper function for running http server until ctrl+c is hit.
std::string make_date_field_value(std::time_t t)
Format a timepoint to a string of a propper format.
sendfile_t sendfile(file_descriptor_holder_t fd, file_meta_t meta, file_size_t chunk_size=sendfile_default_chunk_size) noexcept
Definition sendfile.hpp:481
http_status_line_t status_not_found()