delete unnecessary
This commit is contained in:
parent
ed3bcc6f4a
commit
dbbb04264d
|
@ -23,13 +23,5 @@ configure_file(config.json ${CMAKE_BUILD_RPATH} config.json COPYONLY)
|
||||||
|
|
||||||
add_executable(hello_async
|
add_executable(hello_async
|
||||||
main.cpp
|
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)
|
target_link_libraries(hello_async PRIVATE Threads::Threads)
|
||||||
|
|
104
main.cpp
104
main.cpp
|
@ -6,109 +6,7 @@
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#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<http::string_body>;
|
|
||||||
// Ответ, тело которого представлено в виде строки
|
|
||||||
using StringResponse = http::response<http::string_body>;
|
|
||||||
|
|
||||||
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<typename Fn>
|
|
||||||
void RunWorkers(unsigned n, const Fn& fn)
|
|
||||||
{
|
|
||||||
n = std::max(1u, n);
|
|
||||||
std::vector<std::jthread> workers;
|
|
||||||
workers.reserve(n - 1);
|
|
||||||
// Запускаем n-1 рабочих потоков, выполняющих функцию fn
|
|
||||||
while (--n)
|
|
||||||
{
|
|
||||||
workers.emplace_back(fn);
|
|
||||||
}
|
|
||||||
fn();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
const unsigned num_threads = std::thread::hardware_concurrency();
|
return EXIT_SUCCESS
|
||||||
|
|
||||||
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<decltype(req)>(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();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
#include "http_server.h"
|
|
||||||
|
|
||||||
#include <boost/asio/dispatch.hpp>
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
namespace http_server
|
|
||||||
{
|
|
||||||
}
|
|
|
@ -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 <boost/asio/ip/tcp.hpp>
|
|
||||||
#include <boost/asio/strand.hpp>
|
|
||||||
#include <boost/beast/core.hpp>
|
|
||||||
#include <boost/beast/http.hpp>
|
|
||||||
|
|
||||||
#include "listener.h"
|
|
||||||
#include "session.h"
|
|
||||||
|
|
||||||
namespace http_server
|
|
||||||
{
|
|
||||||
template<typename RequestHandler>
|
|
||||||
void ServeHttp(net::io_context& ioc,
|
|
||||||
const tcp::endpoint& endpoint,
|
|
||||||
RequestHandler&& handler)
|
|
||||||
{
|
|
||||||
using MyListener = Listener<std::decay_t < RequestHandler>>;
|
|
||||||
|
|
||||||
std::make_shared<MyListener>(ioc,
|
|
||||||
endpoint,
|
|
||||||
std::forward<RequestHandler>(handler))->Run();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
#include "http_server.h"
|
|
||||||
|
|
||||||
#include <boost/asio/dispatch.hpp>
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
namespace http_server
|
|
||||||
{
|
|
||||||
} // namespace http_server
|
|
|
@ -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 <boost/asio/ip/tcp.hpp>
|
|
||||||
#include <boost/asio/strand.hpp>
|
|
||||||
#include <boost/beast/core.hpp>
|
|
||||||
#include <boost/beast/http.hpp>
|
|
||||||
|
|
||||||
#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<typename RequestHandler>
|
|
||||||
class Listener : public std::enable_shared_from_this<Listener<RequestHandler>>
|
|
||||||
{
|
|
||||||
net::io_context& ioc_;
|
|
||||||
tcp::acceptor acceptor_;
|
|
||||||
RequestHandler request_handler_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
template<class Handler>
|
|
||||||
Listener(net::io_context& ioc,
|
|
||||||
const tcp::endpoint& endpoint,
|
|
||||||
Handler&& request_handler)
|
|
||||||
: ioc_(ioc),
|
|
||||||
acceptor_(net::make_strand(ioc)),
|
|
||||||
request_handler_(std::forward<Handler>(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<Session<RequestHandler>>(std::move(socket),
|
|
||||||
request_handler_)->Run();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace http_server
|
|
|
@ -1,8 +0,0 @@
|
||||||
#include "http_server.h"
|
|
||||||
|
|
||||||
#include <boost/asio/dispatch.hpp>
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
namespace http_server
|
|
||||||
{
|
|
||||||
} // namespace http_server
|
|
|
@ -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 <boost/asio/ip/tcp.hpp>
|
|
||||||
#include <boost/asio/strand.hpp>
|
|
||||||
#include <boost/beast/core.hpp>
|
|
||||||
#include <boost/beast/http.hpp>
|
|
||||||
|
|
||||||
#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<typename RequestHandler>
|
|
||||||
class Session
|
|
||||||
: public SessionBase,
|
|
||||||
public std::enable_shared_from_this<Session<RequestHandler>>
|
|
||||||
{
|
|
||||||
RequestHandler request_handler_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
template<class Handler>
|
|
||||||
Session(tcp::socket&& socket, Handler&& request_handler)
|
|
||||||
: SessionBase(std::move(socket)), request_handler_(request_handler) {}
|
|
||||||
|
|
||||||
std::shared_ptr<SessionBase> 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
|
|
|
@ -1,73 +0,0 @@
|
||||||
#include "http_server.h"
|
|
||||||
#include "session_base.h"
|
|
||||||
|
|
||||||
#include <boost/asio/dispatch.hpp>
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
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
|
|
|
@ -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 <iostream>
|
|
||||||
#include <string_view>
|
|
||||||
|
|
||||||
#include <boost/asio/ip/tcp.hpp>
|
|
||||||
#include <boost/asio/strand.hpp>
|
|
||||||
#include <boost/beast/core.hpp>
|
|
||||||
#include <boost/beast/http.hpp>
|
|
||||||
|
|
||||||
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<http::string_body>;
|
|
||||||
|
|
||||||
virtual ~SessionBase() = default;
|
|
||||||
|
|
||||||
public:
|
|
||||||
SessionBase(const SessionBase&) = delete;
|
|
||||||
SessionBase& operator =(const SessionBase&) = delete;
|
|
||||||
|
|
||||||
void Run();
|
|
||||||
void Read();
|
|
||||||
|
|
||||||
virtual std::shared_ptr<SessionBase> GetSharedThis() = 0;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
explicit SessionBase(tcp::socket&& socket);
|
|
||||||
|
|
||||||
template<typename Body, typename Fields>
|
|
||||||
void Write(http::response<Body, Fields>&& 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
|
|
Loading…
Reference in New Issue
Block a user