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
|
||||
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)
|
||||
|
|
104
main.cpp
104
main.cpp
|
@ -6,109 +6,7 @@
|
|||
#include <thread>
|
||||
#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()
|
||||
{
|
||||
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<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();
|
||||
});
|
||||
return EXIT_SUCCESS
|
||||
}
|
||||
|
|
|
@ -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