diff --git a/CMakeLists.txt b/CMakeLists.txt index b1faffa..9f67e14 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,5 +21,6 @@ add_executable(application src/main.cpp src/Session.h src/Session.cpp src/Listener.h - src/Listener.cpp) + src/Listener.cpp + src/RequestHandlers/BasicRequestHandler.h) target_link_libraries(application PRIVATE Threads::Threads) \ No newline at end of file diff --git a/src/Listener.cpp b/src/Listener.cpp index e7dd2ad..6397ba1 100644 --- a/src/Listener.cpp +++ b/src/Listener.cpp @@ -12,21 +12,21 @@ Listener::Listener(boost::asio::io_context& ioc, acceptor_.open(endpoint.protocol(), ec); if (ec) { - uad::fail(ec, "open"); + uad::Fail(ec, "open"); return; } acceptor_.set_option(net::socket_base::reuse_address(true), ec); if (ec) { - uad::fail(ec, "set_option"); + uad::Fail(ec, "set_option"); return; } acceptor_.bind(endpoint, ec); if (ec) { - uad::fail(ec, "bind"); + uad::Fail(ec, "bind"); return; } @@ -34,14 +34,14 @@ Listener::Listener(boost::asio::io_context& ioc, net::socket_base::max_listen_connections, ec); if (ec) { - uad::fail(ec, "listen"); + uad::Fail(ec, "listen"); return; } } void Listener::Run() { - do_accept(); + DoAccept(); } void Listener::DoAccept() @@ -49,7 +49,7 @@ void Listener::DoAccept() acceptor_.async_accept( net::make_strand(ioc_), beast::bind_front_handler( - &Listener::on_accept, + &Listener::OnAccept, shared_from_this())); } @@ -57,16 +57,16 @@ void Listener::OnAccept(beast::error_code ec, tcp::socket socket) { if (ec) { - uad::fail(ec, "accept"); + uad::Fail(ec, "accept"); return; } else { - std::make_shared( + std::make_shared( std::move(socket), - doc_root_)->run(); + doc_root_)->Run(); } - do_accept(); + DoAccept(); } } diff --git a/src/RequestHandlers/BasicRequestHandler.h b/src/RequestHandlers/BasicRequestHandler.h new file mode 100644 index 0000000..97e8287 --- /dev/null +++ b/src/RequestHandlers/BasicRequestHandler.h @@ -0,0 +1,112 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "./../helpers.h" + +namespace uad +{ +namespace beast = boost::beast; +namespace http = beast::http; +namespace net = boost::asio; + +template +http::message_generator HandleRequest( + beast::string_view doc_root, + http::request>&& req) +{ + auto const bad_request = + [&req](beast::string_view why) + { + http::response res {http::status::bad_request, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/html"); + res.keep_alive(req.keep_alive()); + res.body() = std::string(why); + res.prepare_payload(); + return res; + }; + + auto const not_found = + [&req](beast::string_view target) + { + http::response res {http::status::not_found, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/html"); + res.keep_alive(req.keep_alive()); + res.body() = "The resource '" + std::string(target) + "' was not found."; + res.prepare_payload(); + return res; + }; + + auto const server_error = + [&req](beast::string_view what) + { + http::response res {http::status::internal_server_error, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/html"); + res.keep_alive(req.keep_alive()); + res.body() = "An error occurred: '" + std::string(what) + "'"; + res.prepare_payload(); + return res; + }; + + if (req.method() != http::verb::get && + req.method() != http::verb::head) + return bad_request("Unknown HTTP-method"); + + if (req.target().empty() || + req.target()[0] != '/' || + req.target().find("..") != beast::string_view::npos) + return bad_request("Illegal request-target"); + + std::string path = PathCat(doc_root, req.target()); + if (req.target().back() == '/') + path.append("index.html"); + + beast::error_code ec; + http::file_body::value_type body; + body.open(path.c_str(), beast::file_mode::scan, ec); + + if (ec == beast::errc::no_such_file_or_directory) + return not_found(req.target()); + + if (ec) + return server_error(ec.message()); + + auto const size = body.size(); + + if (req.method() == http::verb::head) + { + http::response res {http::status::ok, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, MimeType(path)); + res.content_length(size); + res.keep_alive(req.keep_alive()); + return res; + } + + http::response res { + std::piecewise_construct, + std::make_tuple(std::move(body)), + std::make_tuple(http::status::ok, req.version())}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, MimeType(path)); + res.content_length(size); + res.keep_alive(req.keep_alive()); + return res; +} +} diff --git a/src/Session.cpp b/src/Session.cpp index 448202d..9da81b6 100644 --- a/src/Session.cpp +++ b/src/Session.cpp @@ -1,3 +1,4 @@ +#include "RequestHandlers/BasicRequestHandler.h" #include "Session.h" namespace uad diff --git a/src/helpers.h b/src/helpers.h index ae20537..dea2def 100644 --- a/src/helpers.h +++ b/src/helpers.h @@ -20,100 +20,10 @@ namespace uad namespace beast = boost::beast; namespace http = beast::http; namespace net = boost::asio; -using tcp = boost::asio::ip::tcp; beast::string_view MimeType(beast::string_view path); -std::string PathCat( - beast::string_view base, - beast::string_view path); - -template -http::message_generator HandleRequest( - beast::string_view doc_root, - http::request>&& req) -{ - auto const bad_request = - [&req](beast::string_view why) - { - http::response res {http::status::bad_request, req.version()}; - res.set(http::field::server, BOOST_BEAST_VERSION_STRING); - res.set(http::field::content_type, "text/html"); - res.keep_alive(req.keep_alive()); - res.body() = std::string(why); - res.prepare_payload(); - return res; - }; - - auto const not_found = - [&req](beast::string_view target) - { - http::response res {http::status::not_found, req.version()}; - res.set(http::field::server, BOOST_BEAST_VERSION_STRING); - res.set(http::field::content_type, "text/html"); - res.keep_alive(req.keep_alive()); - res.body() = "The resource '" + std::string(target) + "' was not found."; - res.prepare_payload(); - return res; - }; - - auto const server_error = - [&req](beast::string_view what) - { - http::response res {http::status::internal_server_error, req.version()}; - res.set(http::field::server, BOOST_BEAST_VERSION_STRING); - res.set(http::field::content_type, "text/html"); - res.keep_alive(req.keep_alive()); - res.body() = "An error occurred: '" + std::string(what) + "'"; - res.prepare_payload(); - return res; - }; - - if (req.method() != http::verb::get && - req.method() != http::verb::head) - return bad_request("Unknown HTTP-method"); - - if (req.target().empty() || - req.target()[0] != '/' || - req.target().find("..") != beast::string_view::npos) - return bad_request("Illegal request-target"); - - std::string path = PathCat(doc_root, req.target()); - if (req.target().back() == '/') - path.append("index.html"); - - beast::error_code ec; - http::file_body::value_type body; - body.open(path.c_str(), beast::file_mode::scan, ec); - - if (ec == beast::errc::no_such_file_or_directory) - return not_found(req.target()); - - if (ec) - return server_error(ec.message()); - - auto const size = body.size(); - - if (req.method() == http::verb::head) - { - http::response res {http::status::ok, req.version()}; - res.set(http::field::server, BOOST_BEAST_VERSION_STRING); - res.set(http::field::content_type, MimeType(path)); - res.content_length(size); - res.keep_alive(req.keep_alive()); - return res; - } - - http::response res { - std::piecewise_construct, - std::make_tuple(std::move(body)), - std::make_tuple(http::status::ok, req.version())}; - res.set(http::field::server, BOOST_BEAST_VERSION_STRING); - res.set(http::field::content_type, MimeType(path)); - res.content_length(size); - res.keep_alive(req.keep_alive()); - return res; -} +std::string PathCat(beast::string_view base, beast::string_view path); void Fail(beast::error_code ec, char const* what); } diff --git a/src/main.cpp b/src/main.cpp index bbe305c..f9bf2e5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -10,6 +10,7 @@ #include #include +#include "RequestHandlers/BasicRequestHandler.h" #include "Listener.h" namespace beast = boost::beast;