remove global vars

This commit is contained in:
jie 2021-07-14 14:16:00 +08:00
parent 11c681acf3
commit 41e0fe4c11
7 changed files with 120 additions and 89 deletions

View File

@ -13,7 +13,6 @@
#include <chrono>
#include <exception>
#include "router.hpp"
#include "logging.hpp"
namespace bserv {
@ -24,6 +23,9 @@ namespace asio = boost::asio;
namespace json = boost::json;
using asio::ip::tcp;
using request_type = http::request<http::string_body>;
using response_type = http::response<http::string_body>;
class request_failed_exception
: public std::exception {
private:
@ -37,9 +39,9 @@ public:
// sends one async request to a remote server
template <typename ResponseType>
class client_session
class http_client_session
: public std::enable_shared_from_this<
client_session<ResponseType>> {
http_client_session<ResponseType>> {
private:
tcp::resolver resolver_;
beast::tcp_stream stream_;
@ -54,7 +56,7 @@ private:
request_failed_exception{what + ": " + ec.message()}));
}
public:
client_session(
http_client_session(
asio::io_context& ioc,
const http::request<http::string_body>& req)
: resolver_{asio::make_strand(ioc)},
@ -65,15 +67,15 @@ public:
resolver_.async_resolve(
host, port,
beast::bind_front_handler(
&client_session::on_resolve,
client_session<ResponseType>::shared_from_this()));
&http_client_session::on_resolve,
http_client_session<ResponseType>::shared_from_this()));
return promise_.get_future();
}
void on_resolve(
beast::error_code ec,
tcp::resolver::results_type results) {
if (ec) {
failed(ec, "client_session::resolver resolve");
failed(ec, "http_client_session::resolver resolve");
return;
}
// sets a timeout on the operation
@ -82,14 +84,14 @@ public:
stream_.async_connect(
results,
beast::bind_front_handler(
&client_session::on_connect,
client_session<ResponseType>::shared_from_this()));
&http_client_session::on_connect,
http_client_session<ResponseType>::shared_from_this()));
}
void on_connect(
beast::error_code ec,
tcp::resolver::results_type::endpoint_type) {
if (ec) {
failed(ec, "client_session::stream connect");
failed(ec, "http_client_session::stream connect");
return;
}
// sets a timeout on the operation
@ -98,23 +100,23 @@ public:
http::async_write(
stream_, req_,
beast::bind_front_handler(
&client_session::on_write,
client_session<ResponseType>::shared_from_this()));
&http_client_session::on_write,
http_client_session<ResponseType>::shared_from_this()));
}
void on_write(
beast::error_code ec,
std::size_t bytes_transferred) {
boost::ignore_unused(bytes_transferred);
if (ec) {
failed(ec, "client_session::stream write");
failed(ec, "http_client_session::stream write");
return;
}
// receives the HTTP response
http::async_read(
stream_, buffer_, res_,
beast::bind_front_handler(
&client_session::on_read,
client_session<ResponseType>::shared_from_this()));
&http_client_session::on_read,
http_client_session<ResponseType>::shared_from_this()));
}
static_assert(std::is_same<ResponseType, http::response<http::string_body>>::value
|| std::is_same<ResponseType, boost::json::value>::value,
@ -124,7 +126,7 @@ public:
std::size_t bytes_transferred) {
boost::ignore_unused(bytes_transferred);
if (ec) {
failed(ec, "client_session::stream read");
failed(ec, "http_client_session::stream read");
return;
}
if constexpr (std::is_same<ResponseType, http::response<http::string_body>>::value) {
@ -141,25 +143,41 @@ public:
// `not_connected` happens sometimes so don't bother reporting it
if (ec && ec != beast::errc::not_connected) {
// reports the error to the log!
fail(ec, "client_session::stream::socket shutdown");
fail(ec, "http_client_session::stream::socket shutdown");
return;
}
// if we get here then the connection is closed gracefully
}
};
class client {
request_type get_request(
const std::string& host,
const std::string& target,
const http::verb& method,
const boost::json::object& obj) {
request_type req;
req.method(method);
req.target(target);
req.set(http::field::host, host);
req.set(http::field::user_agent, NAME);
req.set(http::field::content_type, "application/json");
req.body() = boost::json::serialize(obj);
req.prepare_payload();
return req;
}
class http_client {
private:
asio::io_context& ioc_;
public:
client(asio::io_context& ioc)
http_client(asio::io_context& ioc)
: ioc_{ioc} {}
std::future<http::response<http::string_body>> request(
const std::string& host,
const std::string& port,
const http::request<http::string_body>& req) {
return std::make_shared<
client_session<http::response<http::string_body>>
http_client_session<http::response<http::string_body>>
>(ioc_, req)->send(host, port);
}
std::future<boost::json::value> request_for_object(
@ -167,30 +185,9 @@ public:
const std::string& port,
const http::request<http::string_body>& req) {
return std::make_shared<
client_session<boost::json::value>
http_client_session<boost::json::value>
>(ioc_, req)->send(host, port);
}
};
std::shared_ptr<client> client_ptr;
namespace request {
request_type get_request(
const std::string& host,
const std::string& target,
const http::verb& method,
const boost::json::object& obj) {
request_type req;
req.method(method);
req.target(target);
req.set(http::field::host, host);
req.set(http::field::user_agent, NAME);
req.set(http::field::content_type, "application/json");
req.body() = boost::json::serialize(obj);
req.prepare_payload();
return req;
}
std::future<response_type> send(
const std::string& host,
@ -199,9 +196,8 @@ namespace request {
const http::verb& method,
const boost::json::object& obj) {
request_type req = get_request(host, target, method, obj);
return client_ptr->request(host, port, req);
return request(host, port, req);
}
std::future<boost::json::value> send_for_object(
const std::string& host,
const std::string& port,
@ -209,9 +205,8 @@ namespace request {
const http::verb& method,
const boost::json::object& obj) {
request_type req = get_request(host, target, method, obj);
return client_ptr->request_for_object(host, port, req);
return request_for_object(host, port, req);
}
std::future<response_type> get(
const std::string& host,
const std::string& port,
@ -219,7 +214,6 @@ namespace request {
const boost::json::object& obj) {
return send(host, port, target, http::verb::get, obj);
}
std::future<boost::json::value> get_for_object(
const std::string& host,
const std::string& port,
@ -227,7 +221,6 @@ namespace request {
const boost::json::object& obj) {
return send_for_object(host, port, target, http::verb::get, obj);
}
std::future<response_type> put(
const std::string& host,
const std::string& port,
@ -235,7 +228,6 @@ namespace request {
const boost::json::object& obj) {
return send(host, port, target, http::verb::put, obj);
}
std::future<boost::json::value> put_for_object(
const std::string& host,
const std::string& port,
@ -243,7 +235,6 @@ namespace request {
const boost::json::object& obj) {
return send_for_object(host, port, target, http::verb::put, obj);
}
std::future<response_type> post(
const std::string& host,
const std::string& port,
@ -251,7 +242,6 @@ namespace request {
const boost::json::object& obj) {
return send(host, port, target, http::verb::post, obj);
}
std::future<boost::json::value> post_for_object(
const std::string& host,
const std::string& port,
@ -259,7 +249,6 @@ namespace request {
const boost::json::object& obj) {
return send_for_object(host, port, target, http::verb::post, obj);
}
std::future<response_type> delete_(
const std::string& host,
const std::string& port,
@ -267,7 +256,6 @@ namespace request {
const boost::json::object& obj) {
return send(host, port, target, http::verb::delete_, obj);
}
std::future<boost::json::value> delete_for_object(
const std::string& host,
const std::string& port,
@ -275,8 +263,7 @@ namespace request {
const boost::json::object& obj) {
return send_for_object(host, port, target, http::verb::delete_, obj);
}
} // request
};
} // bserv

View File

@ -88,8 +88,6 @@ db_connection::~db_connection() {
mgr_.counter_lock_.unlock();
}
std::shared_ptr<db_connection_manager> db_conn_mgr;
// **************************************************************************
class db_parameter {

View File

@ -14,6 +14,7 @@
#include <pqxx/pqxx>
#include "client.hpp"
#include "database.hpp"
#include "session.hpp"
#include "utils.hpp"
@ -24,8 +25,11 @@ namespace bserv {
namespace beast = boost::beast;
namespace http = beast::http;
using request_type = http::request<http::string_body>;
using response_type = http::response<http::string_body>;
struct server_resources {
std::shared_ptr<session_manager_base> session_mgr;
std::shared_ptr<db_connection_manager> db_conn_mgr;
std::shared_ptr<http_client> http_client_ptr;
};
namespace placeholders {
@ -55,7 +59,9 @@ constexpr placeholder<-3> response;
// boost::json::object&&
constexpr placeholder<-4> json_params;
// std::shared_ptr<bserv::db_connection>
constexpr placeholder<-5> transaction;
constexpr placeholder<-5> db_connection_ptr;
// std::shared_ptr<bserv::http_client>
constexpr placeholder<-6> http_client_ptr;
} // placeholders
@ -116,6 +122,7 @@ struct get_parameter<0, Head, Tail...> {
template <typename Type>
Type&& get_parameter_data(
server_resources&,
const std::vector<std::string>&,
request_type&, response_type&, Type&& val) {
return static_cast<Type&&>(val);
@ -123,6 +130,7 @@ Type&& get_parameter_data(
template <int N, std::enable_if_t<(N >= 0), int> = 0>
const std::string& get_parameter_data(
server_resources&,
const std::vector<std::string>& url_params,
request_type&, response_type&,
placeholders::placeholder<N>) {
@ -130,6 +138,7 @@ const std::string& get_parameter_data(
}
std::shared_ptr<session_type> get_parameter_data(
server_resources& resources,
const std::vector<std::string>&,
request_type& request, response_type& response,
placeholders::placeholder<-1>) {
@ -142,13 +151,14 @@ std::shared_ptr<session_type> get_parameter_data(
session_id = cookie_dict[SESSION_NAME];
}
std::shared_ptr<session_type> session_ptr;
if (session_mgr->get_or_create(session_id, session_ptr)) {
if (resources.session_mgr->get_or_create(session_id, session_ptr)) {
response.set(http::field::set_cookie, SESSION_NAME + "=" + session_id);
}
return session_ptr;
}
request_type& get_parameter_data(
server_resources&,
const std::vector<std::string>&,
request_type& request, response_type&,
placeholders::placeholder<-2>) {
@ -156,6 +166,7 @@ request_type& get_parameter_data(
}
response_type& get_parameter_data(
server_resources&,
const std::vector<std::string>&,
request_type&, response_type& response,
placeholders::placeholder<-3>) {
@ -163,6 +174,7 @@ response_type& get_parameter_data(
}
boost::json::object get_parameter_data(
server_resources&,
const std::vector<std::string>&,
request_type& request, response_type&,
placeholders::placeholder<-4>) {
@ -195,10 +207,19 @@ boost::json::object get_parameter_data(
}
std::shared_ptr<db_connection> get_parameter_data(
server_resources& resources,
const std::vector<std::string>&,
request_type&, response_type&,
placeholders::placeholder<-5>) {
return db_conn_mgr->get_or_block();
return resources.db_conn_mgr->get_or_block();
}
std::shared_ptr<http_client> get_parameter_data(
server_resources& resources,
const std::vector<std::string>&,
request_type&, response_type&,
placeholders::placeholder<-6>) {
return resources.http_client_ptr;
}
template <int Idx, typename Func, typename Params, typename ...Args>
@ -206,15 +227,16 @@ struct path_handler;
template <int Idx, typename Ret, typename ...Args, typename ...Params>
struct path_handler<Idx, Ret (*)(Args ...), parameter_pack<Params...>> {
Ret invoke(Ret (*pf)(Args ...), parameter_pack<Params...>& params,
Ret invoke(server_resources& resources,
Ret (*pf)(Args ...), parameter_pack<Params...>& params,
const std::vector<std::string>& url_params,
request_type& request, response_type& response) {
if constexpr (Idx == 0) return (*pf)();
else return static_cast<path_handler<
Idx - 1, Ret (*)(Args ...), parameter_pack<Params...>,
typename get_parameter<Idx - 1, Params...>::type>*
>(this)->invoke2(pf, params, url_params, request, response,
get_parameter_data(url_params, request, response,
>(this)->invoke2(resources, pf, params, url_params, request, response,
get_parameter_data(resources, url_params, request, response,
get_parameter_value<Idx - 1>(params)));
}
};
@ -228,7 +250,8 @@ struct path_handler<Idx, Ret (*)(Args ...),
template <
typename Head2, typename ...Tail2,
std::enable_if_t<sizeof...(Tail2) == sizeof...(Tail), int> = 0>
Ret invoke2(Ret (*pf)(Args ...), parameter_pack<Params...>& params,
Ret invoke2(server_resources& resources,
Ret (*pf)(Args ...), parameter_pack<Params...>& params,
const std::vector<std::string>& url_params,
request_type& request, response_type& response,
Head2&& head2, Tail2&& ...tail2) {
@ -238,8 +261,8 @@ struct path_handler<Idx, Ret (*)(Args ...),
else return static_cast<path_handler<
Idx - 1, Ret (*)(Args ...), parameter_pack<Params...>,
typename get_parameter<Idx - 1, Params...>::type, Head, Tail...>*
>(this)->invoke2(pf, params, url_params, request, response,
get_parameter_data(url_params, request, response,
>(this)->invoke2(resources, pf, params, url_params, request, response,
get_parameter_data(resources, url_params, request, response,
get_parameter_value<Idx - 1>(params)),
static_cast<Head2&&>(head2), static_cast<Tail2&&>(tail2)...);
}
@ -265,6 +288,7 @@ struct path_holder : std::enable_shared_from_this<path_holder> {
const std::string&,
std::vector<std::string>&) const = 0;
virtual std::optional<boost::json::value> invoke(
server_resources&,
const std::vector<std::string>&,
request_type&, response_type&) = 0;
};
@ -295,9 +319,11 @@ public:
return matched;
}
std::optional<boost::json::value> invoke(
server_resources& resources,
const std::vector<std::string>& url_params,
request_type& request, response_type& response) {
return handler_.invoke(
resources,
pf_, params_, url_params,
request, response);
}
@ -335,15 +361,19 @@ class router {
private:
using path_holder_type = std::shared_ptr<router_internal::path_holder>;
std::vector<path_holder_type> paths_;
std::shared_ptr<server_resources> resources_;
public:
router(const std::initializer_list<path_holder_type>& paths)
: paths_{paths} {}
void set_resources(std::shared_ptr<server_resources> resources) {
resources_ = resources;
}
std::optional<boost::json::value> operator()(
const std::string& url, request_type& request, response_type& response) {
std::vector<std::string> url_params;
for (auto& ptr : paths_) {
if (ptr->match(url, url_params))
return ptr->invoke(url_params, request, response);
return ptr->invoke(*resources_, url_params, request, response);
}
throw url_not_found_exception{};
}

View File

@ -320,22 +320,32 @@ private:
// io_context for all I/O
asio::io_context ioc_;
router routes_;
std::shared_ptr<session_manager_base> session_mgr_;
std::shared_ptr<db_connection_manager> db_conn_mgr_;
std::shared_ptr<http_client> http_client_ptr_;
public:
server(const server_config& config, router&& routes)
: ioc_{config.get_num_threads()}, routes_{routes} {
: ioc_{config.get_num_threads()},
routes_{std::move(routes)} {
init_logging(config);
// database connection
try {
db_conn_mgr = std::make_shared<
db_conn_mgr_ = std::make_shared<
db_connection_manager>(config.get_db_conn_str(), config.get_num_db_conn());
} catch (const std::exception& e) {
lgfatal << "db connection initialization failed: " << e.what() << std::endl;
exit(EXIT_FAILURE);
}
session_mgr = std::make_shared<memory_session>();
session_mgr_ = std::make_shared<memory_session_manager>();
http_client_ptr_ = std::make_shared<http_client>(ioc_);
client_ptr = std::make_shared<client>(ioc_);
std::shared_ptr<server_resources> resources_ptr = std::make_shared<server_resources>();
resources_ptr->session_mgr = session_mgr_;
resources_ptr->db_conn_mgr = db_conn_mgr_;
resources_ptr->http_client_ptr = http_client_ptr_;
routes_.set_resources(resources_ptr);
// creates and launches a listening port
std::make_shared<listener>(

View File

@ -23,9 +23,9 @@ const std::string SESSION_NAME = "bsessionid";
// using session_type = std::map<std::string, boost::json::value>;
using session_type = boost::json::object;
struct session_base
: std::enable_shared_from_this<session_base> {
virtual ~session_base() = default;
struct session_manager_base
: std::enable_shared_from_this<session_manager_base> {
virtual ~session_manager_base() = default;
// if `key` refers to an existing session, that session will be placed in
// `session_ptr` and this function will return `false`.
// otherwise, this function will create a new session, place the created
@ -39,9 +39,7 @@ struct session_base
std::shared_ptr<session_type>& session_ptr) = 0;
};
std::shared_ptr<session_base> session_mgr;
class memory_session : public session_base {
class memory_session_manager : public session_manager_base {
private:
using time_point = std::chrono::steady_clock::time_point;
std::mt19937 rng_;
@ -58,7 +56,7 @@ private:
std::set<std::pair<time_point, std::size_t>> queue_;
mutable std::mutex lock_;
public:
memory_session()
memory_session_manager()
: rng_{utils::internal::get_rd_value()},
dist_{0, std::numeric_limits<std::size_t>::max()} {}
bool get_or_create(

View File

@ -209,10 +209,10 @@ boost::json::object user_logout(
};
}
boost::json::object send_request() {
boost::json::object send_request(std::shared_ptr<bserv::http_client> client_ptr) {
// post for response:
// auto res = bserv::request::post(
// "localhost", "8081", "/test", {{"msg", "request"}}
// auto res = client_ptr->post(
// "localhost", "8080", "/echo", {{"msg", "request"}}
// ).get();
// return {{"response", boost::json::parse(res.body())}};
// -------------------------------------------------------
@ -221,10 +221,15 @@ boost::json::object send_request() {
// -------------------------------------------------------
// post for json response (json value, rather than json
// object, is returned):
auto obj = bserv::request::post_for_object(
"localhost", "8081", "/test", {{"msg", "request"}}
auto obj = client_ptr->post_for_object(
"localhost", "8080", "/echo", {{"msg", "request"}}
).get();
return {{"response", obj}};
}
boost::json::object echo(
boost::json::object&& params) {
return params;
}
#endif // _HANDLERS_HPP

View File

@ -108,18 +108,21 @@ int main(int argc, char* argv[]) {
bserv::make_path("/register", &user_register,
bserv::placeholders::request,
bserv::placeholders::json_params,
bserv::placeholders::transaction),
bserv::placeholders::db_connection_ptr),
bserv::make_path("/login", &user_login,
bserv::placeholders::request,
bserv::placeholders::json_params,
bserv::placeholders::transaction,
bserv::placeholders::db_connection_ptr,
bserv::placeholders::session),
bserv::make_path("/logout", &user_logout,
bserv::placeholders::session),
bserv::make_path("/find/<str>", &find_user,
bserv::placeholders::transaction,
bserv::placeholders::db_connection_ptr,
bserv::placeholders::_1),
bserv::make_path("/send", &send_request)
bserv::make_path("/send", &send_request,
bserv::placeholders::http_client_ptr),
bserv::make_path("/echo", &echo,
bserv::placeholders::json_params)
}};
return EXIT_SUCCESS;