diff --git a/CMakeLists.txt b/CMakeLists.txt index 62d04fa..db97e3f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,13 +23,5 @@ configure_file(config.json ${CMAKE_BUILD_RPATH} config.json COPYONLY) add_executable(hello_async main.cpp - src/http_server.cpp - src/http_server.h - src/session.h - src/session.cpp - src/session_base.h - src/session_base.cpp - src/listener.h - src/listener.cpp - src/sdk.h) +) target_link_libraries(hello_async PRIVATE Threads::Threads) diff --git a/main.cpp b/main.cpp index 68c7635..bb8118c 100644 --- a/main.cpp +++ b/main.cpp @@ -6,109 +6,7 @@ #include #include -#include "src/http_server.h" - -namespace -{ -namespace net = boost::asio; -using namespace std::literals; -namespace sys = boost::system; -namespace http = boost::beast::http; -using namespace std; -using net::ip::tcp; - -// Запрос, тело которого представлено в виде строки -using StringRequest = http::request; -// Ответ, тело которого представлено в виде строки -using StringResponse = http::response; - -struct content_type -{ - content_type() = delete; - constexpr static std::string_view k_TextHTML = "text/html"sv; - // При необходимости внутрь content_type можно добавить и другие типы контента -}; - -// Создаёт StringResponse с заданными параметрами -StringResponse MakeStringResponse(http::status status, - std::string_view body, - unsigned http_version, - bool keep_alive, - std::string_view content_type = content_type::k_TextHTML) -{ - StringResponse response(status, http_version); - response.set(http::field::content_type, content_type); - response.body() = body; - response.content_length(body.size()); - response.keep_alive(keep_alive); - return response; -} - -StringResponse HandleRequest(StringRequest&& req) -{ - auto route = req.target(); - - return MakeStringResponse(http::status::ok, - "Hello, World!"sv, - req.version(), - req.keep_alive()); -} - -// Запускает функцию fn на n потоках, включая текущий -template -void RunWorkers(unsigned n, const Fn& fn) -{ - n = std::max(1u, n); - std::vector workers; - workers.reserve(n - 1); - // Запускаем n-1 рабочих потоков, выполняющих функцию fn - while (--n) - { - workers.emplace_back(fn); - } - fn(); -} - -} // namespace - int main() { - const unsigned num_threads = std::thread::hardware_concurrency(); - - net::io_context ioc(num_threads); - tcp::acceptor acceptor(net::make_strand(ioc)); - - // Подписываемся на сигналы и при их получении завершаем работу сервера - net::signal_set signals(ioc, SIGINT, SIGTERM); - signals.async_wait([&ioc](const sys::error_code& ec, - [[maybe_unused]] int signal_number) - { - if (!ec) - { - cout << "Signal "sv << signal_number << " received"sv - << endl; - ioc.stop(); - } - }); - - const auto address = net::ip::make_address("0.0.0.0"); - constexpr net::ip::port_type port = 8080; - http_server::ServeHttp(ioc, {address, port}, [](auto&& req, auto&& sender) - { - sender(HandleRequest(std::forward(req))); - }); - - net::steady_timer t {ioc, 30s}; - - t.async_wait([](sys::error_code ec) - { - cout << "Timer expired"sv << endl; - }); - - std::cout << "Server has started..."sv << std::endl; - - RunWorkers(num_threads, [&ioc] - { - ioc.run(); - }); + return EXIT_SUCCESS } diff --git a/src/http_server.cpp b/src/http_server.cpp deleted file mode 100644 index ee86a25..0000000 --- a/src/http_server.cpp +++ /dev/null @@ -1,8 +0,0 @@ -#include "http_server.h" - -#include -#include - -namespace http_server -{ -} diff --git a/src/http_server.h b/src/http_server.h deleted file mode 100644 index 089cca9..0000000 --- a/src/http_server.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once -#include "sdk.h" -// boost.beast будет использовать std::string_view вместо boost::string_view -#define BOOST_BEAST_USE_STD_STRING_VIEW - -#include -#include -#include -#include - -#include "listener.h" -#include "session.h" - -namespace http_server -{ -template -void ServeHttp(net::io_context& ioc, - const tcp::endpoint& endpoint, - RequestHandler&& handler) -{ - using MyListener = Listener>; - - std::make_shared(ioc, - endpoint, - std::forward(handler))->Run(); -} -} diff --git a/src/listener.cpp b/src/listener.cpp deleted file mode 100644 index 73a9ed7..0000000 --- a/src/listener.cpp +++ /dev/null @@ -1,8 +0,0 @@ -#include "http_server.h" - -#include -#include - -namespace http_server -{ -} // namespace http_server diff --git a/src/listener.h b/src/listener.h deleted file mode 100644 index eb78643..0000000 --- a/src/listener.h +++ /dev/null @@ -1,74 +0,0 @@ -#pragma once -#include "sdk.h" -// boost.beast будет использовать std::string_view вместо boost::string_view -#define BOOST_BEAST_USE_STD_STRING_VIEW - -#include -#include -#include -#include - -#include "session.h" - -namespace http_server -{ -namespace net = boost::asio; -namespace sys = boost::system; -using tcp = net::ip::tcp; -namespace beast = boost::beast; -namespace http = beast::http; - -template -class Listener : public std::enable_shared_from_this> -{ - net::io_context& ioc_; - tcp::acceptor acceptor_; - RequestHandler request_handler_; - - public: - template - Listener(net::io_context& ioc, - const tcp::endpoint& endpoint, - Handler&& request_handler) - : ioc_(ioc), - acceptor_(net::make_strand(ioc)), - request_handler_(std::forward(request_handler)) - { - acceptor_.open(endpoint.protocol()); - acceptor_.set_option(net::socket_base::reuse_address(true)); - acceptor_.bind(endpoint); - acceptor_.listen(net::socket_base::max_listen_connections); - } - - void Run() - { - DoAccept(); - } - - private: - void DoAccept() - { - acceptor_.async_accept(net::make_strand(ioc_), - beast::bind_front_handler(&Listener::OnAccept, - this->shared_from_this())); - } - - void OnAccept(sys::error_code ec, tcp::socket socket) - { - if (ec) - { - return ReportError(ec, "accept"sv); - } - - AsyncRunSession(std::move(socket)); - - DoAccept(); - } - - void AsyncRunSession(tcp::socket&& socket) - { - std::make_shared>(std::move(socket), - request_handler_)->Run(); - } -}; -} // namespace http_server \ No newline at end of file diff --git a/src/sdk.h b/src/sdk.h deleted file mode 100644 index c1ac92b..0000000 --- a/src/sdk.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -#ifdef WIN32 -#include -#endif diff --git a/src/session.cpp b/src/session.cpp deleted file mode 100644 index 73a9ed7..0000000 --- a/src/session.cpp +++ /dev/null @@ -1,8 +0,0 @@ -#include "http_server.h" - -#include -#include - -namespace http_server -{ -} // namespace http_server diff --git a/src/session.h b/src/session.h deleted file mode 100644 index 4f8c613..0000000 --- a/src/session.h +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once -#include "sdk.h" -// boost.beast будет использовать std::string_view вместо boost::string_view -#define BOOST_BEAST_USE_STD_STRING_VIEW - -#include -#include -#include -#include - -#include "session_base.h" - -namespace http_server -{ - -namespace net = boost::asio; -using tcp = net::ip::tcp; -namespace beast = boost::beast; -namespace http = beast::http; - -template -class Session - : public SessionBase, - public std::enable_shared_from_this> -{ - RequestHandler request_handler_; - - public: - template - Session(tcp::socket&& socket, Handler&& request_handler) - : SessionBase(std::move(socket)), request_handler_(request_handler) {} - - std::shared_ptr GetSharedThis() override - { - return this->shared_from_this(); - } - - private: - void HandleRequest(HttpRequest&& request) override - { - // Захватываем умный указатель на текущий объект Session в лямбде, - // чтобы продлить время жизни сессии до вызова лямбды. - // Используется generic-лямбда функция, способная принять response произвольного типа - request_handler_(std::move(request), - [self = this->shared_from_this()](auto&& response) - { - self->Write(std::move(response)); - }); - } -}; -} // namespace http_server \ No newline at end of file diff --git a/src/session_base.cpp b/src/session_base.cpp deleted file mode 100644 index 01a264b..0000000 --- a/src/session_base.cpp +++ /dev/null @@ -1,73 +0,0 @@ -#include "http_server.h" -#include "session_base.h" - -#include -#include - -namespace http_server -{ -void ReportError(beast::error_code ec, std::string_view what) -{ - std::cerr << what << ": "sv << ec.message() << std::endl; -} -SessionBase::SessionBase(tcp::socket&& socket) - : stream_(std::move(socket)) -{ - -} -void SessionBase::Run() -{ - net::dispatch(stream_.get_executor(), - beast::bind_front_handler(&SessionBase::Read, - GetSharedThis())); -} -void SessionBase::Read() -{ - using namespace std::literals; - request_ = {}; - stream_.expires_after(30s); - http::async_read(stream_, buffer_, request_, - beast::bind_front_handler(&SessionBase::OnRead, - GetSharedThis())); -} -void SessionBase::OnRead(beast::error_code ec, size_t bytes_read) -{ - if (ec == http::error::end_of_stream) - { - return Close(); - } - - if (ec) - { - return ReportError(ec, "read"sv); - } - - HandleRequest(std::move(request_)); -} -void SessionBase::Close() -{ - beast::error_code ec; - - stream_.socket().shutdown(tcp::socket::shutdown_send, ec); -} - -void SessionBase::OnWrite(bool close, - boost::beast::error_code ec, - std::size_t bytes_written) -{ - if (ec) - { - return ReportError(ec, "write"sv); - } - - if (close) - { - // Семантика ответа требует закрыть соединение - return Close(); - } - - // Считываем следующий запрос - Read(); -} - -} // namespace http_server diff --git a/src/session_base.h b/src/session_base.h deleted file mode 100644 index 57e3942..0000000 --- a/src/session_base.h +++ /dev/null @@ -1,77 +0,0 @@ -#pragma once -#include "sdk.h" -// boost.beast будет использовать std::string_view вместо boost::string_view -#define BOOST_BEAST_USE_STD_STRING_VIEW - -#include -#include - -#include -#include -#include -#include - -namespace http_server -{ -namespace net = boost::asio; -using tcp = net::ip::tcp; -namespace beast = boost::beast; -namespace http = beast::http; - -using namespace std::literals; - -void ReportError(beast::error_code ec, std::string_view what); - -class SessionBase -{ - protected: - using HttpRequest = http::request; - - virtual ~SessionBase() = default; - - public: - SessionBase(const SessionBase&) = delete; - SessionBase& operator =(const SessionBase&) = delete; - - void Run(); - void Read(); - - virtual std::shared_ptr GetSharedThis() = 0; - - protected: - explicit SessionBase(tcp::socket&& socket); - - template - void Write(http::response&& response) - { - // Запись выполняется асинхронно, поэтому response перемещаем в область кучи - auto safe_response = std::make_shared < http::response - < Body, Fields>>(std::move(response)); - - auto self = GetSharedThis(); - http::async_write(stream_, *safe_response, - [safe_response, self](beast::error_code ec, - std::size_t bytes_written) - { - self->OnWrite(safe_response->need_eof(), - ec, - bytes_written); - }); - } - - private: - beast::tcp_stream stream_; - beast::flat_buffer buffer_; - HttpRequest request_; - - void OnRead(beast::error_code ec, [[maybe_unused]] size_t bytes_read); - - void Close(); - - virtual void HandleRequest(HttpRequest&& request) = 0; - - void OnWrite(bool close, - beast::error_code ec, - [[maybe_unused]] std::size_t bytes_written); -}; -} // namespace http_server \ No newline at end of file