diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..a8dde64 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,33 @@ +cmake_minimum_required(VERSION 3.11) + +project(HelloAsync CXX) +set(CMAKE_CXX_STANDARD 20) + +set(Boost_USE_STATIC_LIBS ON) +set(Boost_USE_MULTITHREADED ON) +set(CMAKE_INCLUDE_PATH ${CMAKE_INCLUDE_PATH} "/home/oem/Libs/boost/1.82.0/include") +set(CMAKE_LIBRARY_PATH ${CMAKE_LIBRARY_PATH} "/home/oem/Libs/boost/1.82.0/lib") + +#include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake) +# conan_basic_setup() + +find_package(Boost 1.78.0 REQUIRED) +if(Boost_FOUND) + include_directories(${Boost_INCLUDE_DIRS}) +endif() + +set(THREADS_PREFER_PTHREAD_FLAG ON) +find_package(Threads REQUIRED) + +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 new file mode 100644 index 0000000..fd5bd86 --- /dev/null +++ b/main.cpp @@ -0,0 +1,98 @@ +#include "src/sdk.h" +// +#include +#include +#include +#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 StringRequest = http::request; +// Ответ, тело которого представлено в виде строки +using StringResponse = http::response; + +struct ContentType +{ + ContentType() = delete; + constexpr static std::string_view + TEXT_HTML = "text/html"sv; + // При необходимости внутрь ContentType можно добавить и другие типы контента +}; + +// Создаёт StringResponse с заданными параметрами +StringResponse MakeStringResponse(http::status status, std::string_view body, unsigned http_version, + bool keep_alive, + std::string_view content_type = ContentType::TEXT_HTML) +{ + 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) +{ + // Подставьте сюда код из синхронной версии HTTP-сервера + return MakeStringResponse(http::status::ok, "OK"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); + + // Подписываемся на сигналы и при их получении завершаем работу сервера + net::signal_set signals(ioc, SIGINT, SIGTERM); + signals.async_wait([&ioc](const sys::error_code& ec, [[maybe_unused]] int signal_number) + { + if (!ec) + { + 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))); + }); + + // Эта надпись сообщает тестам о том, что сервер запущен и готов обрабатывать запросы + std::cout << "Server has started..."sv << std::endl; + + RunWorkers(num_threads, [&ioc] + { + ioc.run(); + }); +} diff --git a/src/http_server.cpp b/src/http_server.cpp new file mode 100644 index 0000000..ee86a25 --- /dev/null +++ b/src/http_server.cpp @@ -0,0 +1,8 @@ +#include "http_server.h" + +#include +#include + +namespace http_server +{ +} diff --git a/src/http_server.h b/src/http_server.h new file mode 100644 index 0000000..308eb3f --- /dev/null +++ b/src/http_server.h @@ -0,0 +1,21 @@ +#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) +{ + // Напишите недостающий код, используя информацию из урока +} +} diff --git a/src/listener.cpp b/src/listener.cpp new file mode 100644 index 0000000..73a9ed7 --- /dev/null +++ b/src/listener.cpp @@ -0,0 +1,8 @@ +#include "http_server.h" + +#include +#include + +namespace http_server +{ +} // namespace http_server diff --git a/src/listener.h b/src/listener.h new file mode 100644 index 0000000..85cffee --- /dev/null +++ b/src/listener.h @@ -0,0 +1,23 @@ +#pragma once +#include "sdk.h" +// boost.beast будет использовать std::string_view вместо boost::string_view +#define BOOST_BEAST_USE_STD_STRING_VIEW + +#include +#include +#include +#include + +namespace http_server +{ +namespace net = boost::asio; +using tcp = net::ip::tcp; +namespace beast = boost::beast; +namespace http = beast::http; + +template +class Listener : public std::enable_shared_from_this> +{ + // Напишите недостающий код, используя информацию из урока +}; +} // namespace http_server \ No newline at end of file diff --git a/src/sdk.h b/src/sdk.h new file mode 100644 index 0000000..c1ac92b --- /dev/null +++ b/src/sdk.h @@ -0,0 +1,4 @@ +#pragma once +#ifdef WIN32 +#include +#endif diff --git a/src/session.cpp b/src/session.cpp new file mode 100644 index 0000000..73a9ed7 --- /dev/null +++ b/src/session.cpp @@ -0,0 +1,8 @@ +#include "http_server.h" + +#include +#include + +namespace http_server +{ +} // namespace http_server diff --git a/src/session.h b/src/session.h new file mode 100644 index 0000000..8ea5839 --- /dev/null +++ b/src/session.h @@ -0,0 +1,24 @@ +#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> { + // Напишите недостающий код, используя информацию из урока +}; +} // namespace http_server \ No newline at end of file diff --git a/src/session_base.cpp b/src/session_base.cpp new file mode 100644 index 0000000..73a9ed7 --- /dev/null +++ b/src/session_base.cpp @@ -0,0 +1,8 @@ +#include "http_server.h" + +#include +#include + +namespace http_server +{ +} // namespace http_server diff --git a/src/session_base.h b/src/session_base.h new file mode 100644 index 0000000..300685c --- /dev/null +++ b/src/session_base.h @@ -0,0 +1,20 @@ +#pragma once +#include "sdk.h" +// boost.beast будет использовать std::string_view вместо boost::string_view +#define BOOST_BEAST_USE_STD_STRING_VIEW + +#include +#include +#include +#include + +namespace http_server { + +namespace net = boost::asio; +using tcp = net::ip::tcp; +namespace beast = boost::beast; +namespace http = beast::http; +class SessionBase { + // Напишите недостающий код, используя информацию из урока +}; +} // namespace http_server \ No newline at end of file