restructure project
This commit is contained in:
parent
ce481ac336
commit
11c681acf3
|
@ -13,7 +13,7 @@ set(CMAKE_CXX_FLAGS "-Wall -Wextra")
|
||||||
set(CMAKE_CXX_FLAGS_DEBUG "-g")
|
set(CMAKE_CXX_FLAGS_DEBUG "-g")
|
||||||
set(CMAKE_CXX_FLAGS_RELEASE "-O3")
|
set(CMAKE_CXX_FLAGS_RELEASE "-O3")
|
||||||
|
|
||||||
add_executable(bserv bserv.cpp)
|
add_executable(bserv main.cpp)
|
||||||
target_link_libraries(bserv
|
target_link_libraries(bserv
|
||||||
pthread
|
pthread
|
||||||
boost_thread
|
boost_thread
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <exception>
|
#include <exception>
|
||||||
|
|
||||||
#include "config.hpp"
|
#include "router.hpp"
|
||||||
#include "logging.hpp"
|
#include "logging.hpp"
|
||||||
|
|
||||||
namespace bserv {
|
namespace bserv {
|
||||||
|
@ -77,7 +77,7 @@ public:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// sets a timeout on the operation
|
// sets a timeout on the operation
|
||||||
stream_.expires_after(std::chrono::seconds(30));
|
stream_.expires_after(std::chrono::seconds(EXPIRY_TIME));
|
||||||
// makes the connection on the IP address we get from a lookup
|
// makes the connection on the IP address we get from a lookup
|
||||||
stream_.async_connect(
|
stream_.async_connect(
|
||||||
results,
|
results,
|
||||||
|
@ -93,7 +93,7 @@ public:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// sets a timeout on the operation
|
// sets a timeout on the operation
|
||||||
stream_.expires_after(std::chrono::seconds(30));
|
stream_.expires_after(std::chrono::seconds(EXPIRY_TIME));
|
||||||
// sends the HTTP request to the remote host
|
// sends the HTTP request to the remote host
|
||||||
http::async_write(
|
http::async_write(
|
||||||
stream_, req_,
|
stream_, req_,
|
|
@ -2,10 +2,12 @@
|
||||||
#define _COMMON_HPP
|
#define _COMMON_HPP
|
||||||
|
|
||||||
#include "client.hpp"
|
#include "client.hpp"
|
||||||
|
#include "config.hpp"
|
||||||
#include "database.hpp"
|
#include "database.hpp"
|
||||||
#include "session.hpp"
|
|
||||||
#include "router.hpp"
|
|
||||||
#include "utils.hpp"
|
|
||||||
#include "logging.hpp"
|
#include "logging.hpp"
|
||||||
|
#include "router.hpp"
|
||||||
|
#include "server.hpp"
|
||||||
|
#include "session.hpp"
|
||||||
|
#include "utils.hpp"
|
||||||
|
|
||||||
#endif // _COMMON_HPP
|
#endif // _COMMON_HPP
|
|
@ -0,0 +1,48 @@
|
||||||
|
#ifndef _CONFIG_HPP
|
||||||
|
#define _CONFIG_HPP
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
namespace bserv {
|
||||||
|
|
||||||
|
const std::string NAME = "bserv";
|
||||||
|
|
||||||
|
const unsigned short PORT = 8080;
|
||||||
|
const int NUM_THREADS = 4;
|
||||||
|
|
||||||
|
const std::size_t PAYLOAD_LIMIT = 1 * 1024 * 1024;
|
||||||
|
const int EXPIRY_TIME = 30; // seconds
|
||||||
|
|
||||||
|
const std::size_t LOG_ROTATION_SIZE = 4 * 1024 * 1024;
|
||||||
|
const std::string LOG_PATH = "./log/" + NAME;
|
||||||
|
|
||||||
|
const int NUM_DB_CONN = 10;
|
||||||
|
const std::string DB_CONN_STR = "dbname=bserv";
|
||||||
|
|
||||||
|
#define decl_field(type, name, default_value) \
|
||||||
|
private: \
|
||||||
|
std::optional<type> name##_; \
|
||||||
|
public: \
|
||||||
|
void set_##name(std::optional<type>&& name) { name##_ = std::move(name); } \
|
||||||
|
type get_##name() const { return name##_.has_value() ? name##_.value() : default_value; }
|
||||||
|
|
||||||
|
struct server_config {
|
||||||
|
decl_field(std::string, name, NAME)
|
||||||
|
decl_field(unsigned short, port, PORT)
|
||||||
|
decl_field(int, num_threads, NUM_THREADS)
|
||||||
|
decl_field(std::size_t, log_rotation_size, LOG_ROTATION_SIZE)
|
||||||
|
decl_field(std::string, log_path, LOG_PATH)
|
||||||
|
decl_field(int, num_db_conn, NUM_DB_CONN)
|
||||||
|
decl_field(std::string, db_conn_str, DB_CONN_STR)
|
||||||
|
public:
|
||||||
|
server_config() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
#undef decl_field
|
||||||
|
|
||||||
|
} // bserv
|
||||||
|
|
||||||
|
#endif // _CONFIG_HPP
|
|
@ -8,6 +8,9 @@
|
||||||
#include <boost/log/trivial.hpp>
|
#include <boost/log/trivial.hpp>
|
||||||
#include <boost/log/utility/setup.hpp>
|
#include <boost/log/utility/setup.hpp>
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include "config.hpp"
|
#include "config.hpp"
|
||||||
|
|
||||||
namespace bserv {
|
namespace bserv {
|
||||||
|
@ -16,12 +19,11 @@ namespace logging = boost::log;
|
||||||
namespace keywords = boost::log::keywords;
|
namespace keywords = boost::log::keywords;
|
||||||
namespace src = boost::log::sources;
|
namespace src = boost::log::sources;
|
||||||
|
|
||||||
// this function should be called in `main`
|
// this function should be called before logging is used
|
||||||
// right after the configurations are loaded.
|
void init_logging(const server_config& config) {
|
||||||
void init_logging() {
|
|
||||||
logging::add_file_log(
|
logging::add_file_log(
|
||||||
keywords::file_name = LOG_PATH + NAME + "_%Y%m%d_%H-%M-%S.%N.log",
|
keywords::file_name = config.get_log_path() + "_%Y%m%d_%H-%M-%S.%N.log",
|
||||||
keywords::rotation_size = LOG_ROTATION_SIZE,
|
keywords::rotation_size = config.get_log_rotation_size(),
|
||||||
keywords::format = "[%Severity%][%TimeStamp%][%ThreadID%]: %Message%"
|
keywords::format = "[%Severity%][%TimeStamp%][%ThreadID%]: %Message%"
|
||||||
);
|
);
|
||||||
logging::core::get()->set_filter(
|
logging::core::get()->set_filter(
|
|
@ -7,6 +7,9 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifndef _SERVER_HPP
|
||||||
|
#define _SERVER_HPP
|
||||||
|
|
||||||
#include <boost/beast.hpp>
|
#include <boost/beast.hpp>
|
||||||
#include <boost/asio.hpp>
|
#include <boost/asio.hpp>
|
||||||
#include <boost/optional.hpp>
|
#include <boost/optional.hpp>
|
||||||
|
@ -25,8 +28,9 @@
|
||||||
#include "config.hpp"
|
#include "config.hpp"
|
||||||
#include "logging.hpp"
|
#include "logging.hpp"
|
||||||
#include "utils.hpp"
|
#include "utils.hpp"
|
||||||
#include "routing.hpp"
|
#include "router.hpp"
|
||||||
#include "database.hpp"
|
#include "database.hpp"
|
||||||
|
#include "session.hpp"
|
||||||
#include "client.hpp"
|
#include "client.hpp"
|
||||||
|
|
||||||
namespace bserv {
|
namespace bserv {
|
||||||
|
@ -45,7 +49,7 @@ using asio::ip::tcp;
|
||||||
template <class Body, class Allocator, class Send>
|
template <class Body, class Allocator, class Send>
|
||||||
void handle_request(
|
void handle_request(
|
||||||
http::request<Body, http::basic_fields<Allocator>>&& req,
|
http::request<Body, http::basic_fields<Allocator>>&& req,
|
||||||
Send&& send) {
|
Send&& send, router& routes) {
|
||||||
|
|
||||||
const auto bad_request = [&req](beast::string_view why) {
|
const auto bad_request = [&req](beast::string_view why) {
|
||||||
http::response<http::string_body> res{
|
http::response<http::string_body> res{
|
||||||
|
@ -118,12 +122,12 @@ void handle_request(
|
||||||
send(std::move(res));
|
send(std::move(res));
|
||||||
}
|
}
|
||||||
|
|
||||||
// std::string get_address(const tcp::socket& socket) {
|
std::string get_address(const tcp::socket& socket) {
|
||||||
// tcp::endpoint end_point = socket.remote_endpoint();
|
tcp::endpoint end_point = socket.remote_endpoint();
|
||||||
// std::string addr = end_point.address().to_string()
|
std::string addr = end_point.address().to_string()
|
||||||
// + ':' + std::to_string(end_point.port());
|
+ ':' + std::to_string(end_point.port());
|
||||||
// return addr;
|
return addr;
|
||||||
// }
|
}
|
||||||
|
|
||||||
// handles an HTTP server connection
|
// handles an HTTP server connection
|
||||||
class http_session
|
class http_session
|
||||||
|
@ -162,7 +166,8 @@ private:
|
||||||
boost::optional<
|
boost::optional<
|
||||||
http::request_parser<http::string_body>> parser_;
|
http::request_parser<http::string_body>> parser_;
|
||||||
std::shared_ptr<void> res_;
|
std::shared_ptr<void> res_;
|
||||||
// const std::string address_;
|
router& routes_;
|
||||||
|
const std::string address_;
|
||||||
void do_read() {
|
void do_read() {
|
||||||
// constructs a new parser for each message
|
// constructs a new parser for each message
|
||||||
parser_.emplace();
|
parser_.emplace();
|
||||||
|
@ -170,7 +175,7 @@ private:
|
||||||
// of the body in bytes to prevent abuse.
|
// of the body in bytes to prevent abuse.
|
||||||
parser_->body_limit(PAYLOAD_LIMIT);
|
parser_->body_limit(PAYLOAD_LIMIT);
|
||||||
// sets the timeout.
|
// sets the timeout.
|
||||||
stream_.expires_after(std::chrono::seconds(30));
|
stream_.expires_after(std::chrono::seconds(EXPIRY_TIME));
|
||||||
// reads a request using the parser-oriented interface
|
// reads a request using the parser-oriented interface
|
||||||
http::async_read(
|
http::async_read(
|
||||||
stream_, buffer_, *parser_,
|
stream_, buffer_, *parser_,
|
||||||
|
@ -182,7 +187,7 @@ private:
|
||||||
beast::error_code ec,
|
beast::error_code ec,
|
||||||
std::size_t bytes_transferred) {
|
std::size_t bytes_transferred) {
|
||||||
boost::ignore_unused(bytes_transferred);
|
boost::ignore_unused(bytes_transferred);
|
||||||
// lgtrace << "received " << bytes_transferred << " byte(s) from: " << address_;
|
lgtrace << "received " << bytes_transferred << " byte(s) from: " << address_;
|
||||||
// this means they closed the connection
|
// this means they closed the connection
|
||||||
if (ec == http::error::end_of_stream) {
|
if (ec == http::error::end_of_stream) {
|
||||||
do_close();
|
do_close();
|
||||||
|
@ -193,7 +198,7 @@ private:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// handles the request and sends the response
|
// handles the request and sends the response
|
||||||
handle_request(parser_->release(), lambda_);
|
handle_request(parser_->release(), lambda_, routes_);
|
||||||
// at this point the parser can be reset
|
// at this point the parser can be reset
|
||||||
}
|
}
|
||||||
void on_write(
|
void on_write(
|
||||||
|
@ -206,7 +211,7 @@ private:
|
||||||
fail(ec, "http_session async_write");
|
fail(ec, "http_session async_write");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// lgtrace << "sent " << bytes_transferred << " byte(s) to: " << address_;
|
lgtrace << "sent " << bytes_transferred << " byte(s) to: " << address_;
|
||||||
if (close) {
|
if (close) {
|
||||||
// this means we should close the connection, usually because
|
// this means we should close the connection, usually because
|
||||||
// the response indicated the "Connection: close" semantic.
|
// the response indicated the "Connection: close" semantic.
|
||||||
|
@ -221,16 +226,16 @@ private:
|
||||||
beast::error_code ec;
|
beast::error_code ec;
|
||||||
stream_.socket().shutdown(tcp::socket::shutdown_send, ec);
|
stream_.socket().shutdown(tcp::socket::shutdown_send, ec);
|
||||||
// at this point the connection is closed gracefully
|
// at this point the connection is closed gracefully
|
||||||
// lgtrace << "socket connection closed: " << address_;
|
lgtrace << "socket connection closed: " << address_;
|
||||||
}
|
}
|
||||||
public:
|
public:
|
||||||
http_session(tcp::socket&& socket)
|
http_session(tcp::socket&& socket, router& routes)
|
||||||
: lambda_{*this}, stream_{std::move(socket)} /*,
|
: lambda_{*this}, stream_{std::move(socket)}, routes_{routes},
|
||||||
address_{get_address(stream_.socket())} */ {
|
address_{get_address(stream_.socket())} {
|
||||||
// lgtrace << "http session opened: " << address_;
|
lgtrace << "http session opened: " << address_;
|
||||||
}
|
}
|
||||||
~http_session() {
|
~http_session() {
|
||||||
// lgtrace << "http session closed: " << address_;
|
lgtrace << "http session closed: " << address_;
|
||||||
}
|
}
|
||||||
void run() {
|
void run() {
|
||||||
asio::dispatch(
|
asio::dispatch(
|
||||||
|
@ -247,6 +252,7 @@ class listener
|
||||||
private:
|
private:
|
||||||
asio::io_context& ioc_;
|
asio::io_context& ioc_;
|
||||||
tcp::acceptor acceptor_;
|
tcp::acceptor acceptor_;
|
||||||
|
router& routes_;
|
||||||
void do_accept() {
|
void do_accept() {
|
||||||
acceptor_.async_accept(
|
acceptor_.async_accept(
|
||||||
asio::make_strand(ioc_),
|
asio::make_strand(ioc_),
|
||||||
|
@ -258,39 +264,45 @@ private:
|
||||||
if (ec) {
|
if (ec) {
|
||||||
fail(ec, "listener::acceptor async_accept");
|
fail(ec, "listener::acceptor async_accept");
|
||||||
} else {
|
} else {
|
||||||
// lgtrace << "listener accepts: " << get_address(socket);
|
lgtrace << "listener accepts: " << get_address(socket);
|
||||||
std::make_shared<http_session>(
|
std::make_shared<http_session>(
|
||||||
std::move(socket))->run();
|
std::move(socket), routes_)->run();
|
||||||
}
|
}
|
||||||
do_accept();
|
do_accept();
|
||||||
}
|
}
|
||||||
public:
|
public:
|
||||||
listener(
|
listener(
|
||||||
asio::io_context& ioc,
|
asio::io_context& ioc,
|
||||||
tcp::endpoint endpoint)
|
tcp::endpoint endpoint,
|
||||||
|
router& routes)
|
||||||
: ioc_{ioc},
|
: ioc_{ioc},
|
||||||
acceptor_{asio::make_strand(ioc)} {
|
acceptor_{asio::make_strand(ioc)},
|
||||||
|
routes_{routes} {
|
||||||
beast::error_code ec;
|
beast::error_code ec;
|
||||||
acceptor_.open(endpoint.protocol(), ec);
|
acceptor_.open(endpoint.protocol(), ec);
|
||||||
if (ec) {
|
if (ec) {
|
||||||
fail(ec, "listener::acceptor open");
|
fail(ec, "listener::acceptor open");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
acceptor_.set_option(
|
acceptor_.set_option(
|
||||||
asio::socket_base::reuse_address(true), ec);
|
asio::socket_base::reuse_address(true), ec);
|
||||||
if (ec) {
|
if (ec) {
|
||||||
fail(ec, "listener::acceptor set_option");
|
fail(ec, "listener::acceptor set_option");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
acceptor_.bind(endpoint, ec);
|
acceptor_.bind(endpoint, ec);
|
||||||
if (ec) {
|
if (ec) {
|
||||||
fail(ec, "listener::acceptor bind");
|
fail(ec, "listener::acceptor bind");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
acceptor_.listen(
|
acceptor_.listen(
|
||||||
asio::socket_base::max_listen_connections, ec);
|
asio::socket_base::max_listen_connections, ec);
|
||||||
if (ec) {
|
if (ec) {
|
||||||
fail(ec, "listener::acceptor listen");
|
fail(ec, "listener::acceptor listen");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -303,69 +315,59 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void show_config() {
|
class server {
|
||||||
lginfo << NAME << " config:"
|
private:
|
||||||
<< "\nport: " << PORT
|
// io_context for all I/O
|
||||||
<< "\nthreads: " << NUM_THREADS
|
asio::io_context ioc_;
|
||||||
<< "\ndb-conn: " << NUM_DB_CONN
|
router routes_;
|
||||||
<< "\npayload: " << PAYLOAD_LIMIT / 1024 / 1024
|
public:
|
||||||
<< "\nrotation: " << LOG_ROTATION_SIZE / 1024 / 1024
|
server(const server_config& config, router&& routes)
|
||||||
<< "\nlog path: " << LOG_PATH
|
: ioc_{config.get_num_threads()}, routes_{routes} {
|
||||||
<< "\nconn-str: " << DB_CONN_STR << std::endl;
|
init_logging(config);
|
||||||
}
|
|
||||||
|
// database connection
|
||||||
|
try {
|
||||||
|
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>();
|
||||||
|
|
||||||
|
client_ptr = std::make_shared<client>(ioc_);
|
||||||
|
|
||||||
|
// creates and launches a listening port
|
||||||
|
std::make_shared<listener>(
|
||||||
|
ioc_, tcp::endpoint{tcp::v4(), config.get_port()}, routes_)->run();
|
||||||
|
|
||||||
|
// captures SIGINT and SIGTERM to perform a clean shutdown
|
||||||
|
asio::signal_set signals{ioc_, SIGINT, SIGTERM};
|
||||||
|
signals.async_wait(
|
||||||
|
[&](const boost::system::error_code&, int) {
|
||||||
|
// stops the `io_context`. This will cause `run()`
|
||||||
|
// to return immediately, eventually destroying the
|
||||||
|
// `io_context` and all of the sockets in it.
|
||||||
|
ioc_.stop();
|
||||||
|
});
|
||||||
|
|
||||||
|
lginfo << config.get_name() << " started";
|
||||||
|
|
||||||
|
// runs the I/O service on the requested number of threads
|
||||||
|
std::vector<std::thread> v;
|
||||||
|
v.reserve(config.get_num_threads() - 1);
|
||||||
|
for (int i = 1; i < config.get_num_threads(); ++i)
|
||||||
|
v.emplace_back([&]{ ioc_.run(); });
|
||||||
|
ioc_.run();
|
||||||
|
|
||||||
|
// if we get here, it means we got a SIGINT or SIGTERM
|
||||||
|
lginfo << "exiting " << config.get_name();
|
||||||
|
|
||||||
|
// blocks until all the threads exit
|
||||||
|
for (auto & t : v) t.join();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
} // bserv
|
} // bserv
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
#endif // _SERVER_HPP
|
||||||
using namespace bserv;
|
|
||||||
if (parse_arguments(argc, argv))
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
init_logging();
|
|
||||||
show_config();
|
|
||||||
|
|
||||||
// io_context for all I/O
|
|
||||||
asio::io_context ioc{NUM_THREADS};
|
|
||||||
|
|
||||||
// some initializations must be done after parsing the arguments
|
|
||||||
// e.g. database connection
|
|
||||||
try {
|
|
||||||
db_conn_mgr = std::make_shared<
|
|
||||||
db_connection_manager>(DB_CONN_STR, NUM_DB_CONN);
|
|
||||||
} catch (const std::exception& e) {
|
|
||||||
lgfatal << "db connection initialization failed: " << e.what() << std::endl;
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
session_mgr = std::make_shared<memory_session>();
|
|
||||||
|
|
||||||
client_ptr = std::make_shared<client>(ioc);
|
|
||||||
|
|
||||||
// creates and launches a listening port
|
|
||||||
std::make_shared<listener>(
|
|
||||||
ioc, tcp::endpoint{tcp::v4(), PORT})->run();
|
|
||||||
|
|
||||||
// captures SIGINT and SIGTERM to perform a clean shutdown
|
|
||||||
asio::signal_set signals{ioc, SIGINT, SIGTERM};
|
|
||||||
signals.async_wait(
|
|
||||||
[&](const boost::system::error_code&, int) {
|
|
||||||
// stops the `io_context`. This will cause `run()`
|
|
||||||
// to return immediately, eventually destroying the
|
|
||||||
// `io_context` and all of the sockets in it.
|
|
||||||
ioc.stop();
|
|
||||||
});
|
|
||||||
|
|
||||||
lginfo << NAME << " started";
|
|
||||||
|
|
||||||
// runs the I/O service on the requested number of threads
|
|
||||||
std::vector<std::thread> v;
|
|
||||||
v.reserve(NUM_THREADS - 1);
|
|
||||||
for (int i = 1; i < NUM_THREADS; ++i)
|
|
||||||
v.emplace_back([&]{ ioc.run(); });
|
|
||||||
ioc.run();
|
|
||||||
|
|
||||||
// if we get here, it means we got a SIGINT or SIGTERM
|
|
||||||
lginfo << "exiting " << NAME;
|
|
||||||
|
|
||||||
// blocks until all the threads exit
|
|
||||||
for (auto & t : v) t.join();
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
115
config.hpp
115
config.hpp
|
@ -1,115 +0,0 @@
|
||||||
#ifndef _CONFIG_HPP
|
|
||||||
#define _CONFIG_HPP
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <string>
|
|
||||||
#include <cstddef>
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
namespace bserv {
|
|
||||||
|
|
||||||
const char* NAME = "bserv";
|
|
||||||
|
|
||||||
unsigned short PORT = 8080;
|
|
||||||
int NUM_THREADS = 4;
|
|
||||||
int NUM_DB_CONN = 10;
|
|
||||||
|
|
||||||
std::size_t PAYLOAD_LIMIT = 1 * 1024 * 1024;
|
|
||||||
|
|
||||||
std::size_t LOG_ROTATION_SIZE = 4 * 1024 * 1024;
|
|
||||||
std::string LOG_PATH = "./log/";
|
|
||||||
std::string DB_CONN_STR = "dbname=bserv";
|
|
||||||
|
|
||||||
void show_usage() {
|
|
||||||
std::cout << "Usage: " << NAME << " [OPTION...]\n"
|
|
||||||
<< NAME << " is a C++ Boost-based HTTP server.\n\n"
|
|
||||||
"Example:\n"
|
|
||||||
<< " " << NAME << " -p 8081 --threads 2\n\n"
|
|
||||||
"Option:\n"
|
|
||||||
" -h, --help show help and exit\n"
|
|
||||||
" -p, --port port (default: 8080)\n"
|
|
||||||
" --threads number of threads (default: 4)\n"
|
|
||||||
" --num-conn number of database connections (default: 10)\n"
|
|
||||||
" --payload payload limit for request in mega bytes (default: 1)\n"
|
|
||||||
" --rotation log rotation size in mega bytes (default: 4)\n"
|
|
||||||
" --log-path log path (default: ./log/)\n"
|
|
||||||
" -c, --conn-str connection string (default: dbname=bserv)"
|
|
||||||
<< std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
// returns `true` if error occurs
|
|
||||||
bool parse_arguments(int argc, char* argv[]) {
|
|
||||||
for (int i = 1; i < argc; ++i) {
|
|
||||||
if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
|
|
||||||
show_usage();
|
|
||||||
return true;
|
|
||||||
} else if (strcmp(argv[i], "-p") == 0 || strcmp(argv[i], "--port") == 0) {
|
|
||||||
if (i + 1 < argc) {
|
|
||||||
PORT = atoi(argv[i + 1]);
|
|
||||||
++i;
|
|
||||||
} else {
|
|
||||||
std::cerr << "Missing value after: " << argv[i] << std::endl;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else if (strcmp(argv[i], "--threads") == 0) {
|
|
||||||
if (i + 1 < argc) {
|
|
||||||
NUM_THREADS = atoi(argv[i + 1]);
|
|
||||||
++i;
|
|
||||||
} else {
|
|
||||||
std::cerr << "Missing value after: " << argv[i] << std::endl;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else if (strcmp(argv[i], "--num-conn") == 0) {
|
|
||||||
if (i + 1 < argc) {
|
|
||||||
NUM_DB_CONN = atoi(argv[i + 1]);
|
|
||||||
++i;
|
|
||||||
} else {
|
|
||||||
std::cerr << "Missing value after: " << argv[i] << std::endl;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else if (strcmp(argv[i], "--payload") == 0) {
|
|
||||||
if (i + 1 < argc) {
|
|
||||||
PAYLOAD_LIMIT = atoi(argv[i + 1]) * 1024 * 1024;
|
|
||||||
++i;
|
|
||||||
} else {
|
|
||||||
std::cerr << "Missing value after: " << argv[i] << std::endl;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else if (strcmp(argv[i], "--rotation") == 0) {
|
|
||||||
if (i + 1 < argc) {
|
|
||||||
LOG_ROTATION_SIZE = atoi(argv[i + 1]) * 1024 * 1024;
|
|
||||||
++i;
|
|
||||||
} else {
|
|
||||||
std::cerr << "Missing value after: " << argv[i] << std::endl;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else if (strcmp(argv[i], "--log-path") == 0) {
|
|
||||||
if (i + 1 < argc) {
|
|
||||||
LOG_PATH = argv[i + 1];
|
|
||||||
if (LOG_PATH.back() != '/')
|
|
||||||
LOG_PATH += '/';
|
|
||||||
++i;
|
|
||||||
} else {
|
|
||||||
std::cerr << "Missing value after: " << argv[i] << std::endl;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else if (strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--conn-str") == 0) {
|
|
||||||
if (i + 1 < argc) {
|
|
||||||
DB_CONN_STR = argv[i + 1];
|
|
||||||
++i;
|
|
||||||
} else {
|
|
||||||
std::cerr << "Missing value after: " << argv[i] << std::endl;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
std::cerr << "Unrecognized option: " << argv[i] << '\n' << std::endl;
|
|
||||||
show_usage();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // bserv
|
|
||||||
|
|
||||||
#endif // _CONFIG_HPP
|
|
|
@ -10,7 +10,7 @@
|
||||||
|
|
||||||
#include <pqxx/pqxx>
|
#include <pqxx/pqxx>
|
||||||
|
|
||||||
#include "common.hpp"
|
#include "bserv/common.hpp"
|
||||||
|
|
||||||
// register an orm mapping (to convert the db query results into
|
// register an orm mapping (to convert the db query results into
|
||||||
// json objects).
|
// json objects).
|
||||||
|
|
|
@ -0,0 +1,126 @@
|
||||||
|
#include <iostream>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
#include "bserv/common.hpp"
|
||||||
|
#include "handlers.hpp"
|
||||||
|
|
||||||
|
void show_usage(const bserv::server_config& config) {
|
||||||
|
std::cout << "Usage: " << config.get_name() << " [OPTION...]\n"
|
||||||
|
<< config.get_name() << " is a C++ Boost-based HTTP server.\n\n"
|
||||||
|
"Example:\n"
|
||||||
|
<< " " << config.get_name() << " -p 8081 --threads 2\n\n"
|
||||||
|
"Option:\n"
|
||||||
|
" -h, --help show help and exit\n"
|
||||||
|
" -p, --port port (default: 8080)\n"
|
||||||
|
" --threads number of threads (default: 4)\n"
|
||||||
|
" --rotation log rotation size in mega bytes (default: 4)\n"
|
||||||
|
" --log-path log path (default: ./log/bserv)\n"
|
||||||
|
" --num-conn number of database connections (default: 10)\n"
|
||||||
|
" -c, --conn-str connection string (default: dbname=bserv)"
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns `true` if error occurs
|
||||||
|
bool parse_arguments(int argc, char* argv[], bserv::server_config& config) {
|
||||||
|
for (int i = 1; i < argc; ++i) {
|
||||||
|
if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
|
||||||
|
show_usage(config);
|
||||||
|
return true;
|
||||||
|
} else if (strcmp(argv[i], "-p") == 0 || strcmp(argv[i], "--port") == 0) {
|
||||||
|
if (i + 1 < argc) {
|
||||||
|
config.set_port(atoi(argv[i + 1]));
|
||||||
|
++i;
|
||||||
|
} else {
|
||||||
|
std::cerr << "Missing value after: " << argv[i] << std::endl;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (strcmp(argv[i], "--threads") == 0) {
|
||||||
|
if (i + 1 < argc) {
|
||||||
|
config.set_num_threads(atoi(argv[i + 1]));
|
||||||
|
++i;
|
||||||
|
} else {
|
||||||
|
std::cerr << "Missing value after: " << argv[i] << std::endl;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (strcmp(argv[i], "--num-conn") == 0) {
|
||||||
|
if (i + 1 < argc) {
|
||||||
|
config.set_num_db_conn(atoi(argv[i + 1]));
|
||||||
|
++i;
|
||||||
|
} else {
|
||||||
|
std::cerr << "Missing value after: " << argv[i] << std::endl;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (strcmp(argv[i], "--rotation") == 0) {
|
||||||
|
if (i + 1 < argc) {
|
||||||
|
config.set_log_rotation_size(atoi(argv[i + 1]) * 1024 * 1024);
|
||||||
|
++i;
|
||||||
|
} else {
|
||||||
|
std::cerr << "Missing value after: " << argv[i] << std::endl;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (strcmp(argv[i], "--log-path") == 0) {
|
||||||
|
if (i + 1 < argc) {
|
||||||
|
config.set_log_path(argv[i + 1]);
|
||||||
|
++i;
|
||||||
|
} else {
|
||||||
|
std::cerr << "Missing value after: " << argv[i] << std::endl;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--conn-str") == 0) {
|
||||||
|
if (i + 1 < argc) {
|
||||||
|
config.set_db_conn_str(argv[i + 1]);
|
||||||
|
++i;
|
||||||
|
} else {
|
||||||
|
std::cerr << "Missing value after: " << argv[i] << std::endl;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
std::cerr << "Unrecognized option: " << argv[i] << '\n' << std::endl;
|
||||||
|
show_usage(config);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void show_config(const bserv::server_config& config) {
|
||||||
|
std::cout << config.get_name() << " config:"
|
||||||
|
<< "\nport: " << config.get_port()
|
||||||
|
<< "\nthreads: " << config.get_num_threads()
|
||||||
|
<< "\nrotation: " << config.get_log_rotation_size() / 1024 / 1024
|
||||||
|
<< "\nlog path: " << config.get_log_path()
|
||||||
|
<< "\ndb-conn: " << config.get_num_db_conn()
|
||||||
|
<< "\nconn-str: " << config.get_db_conn_str() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
bserv::server_config config;
|
||||||
|
|
||||||
|
if (parse_arguments(argc, argv, config))
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
|
||||||
|
show_config(config);
|
||||||
|
|
||||||
|
bserv::server{config, {
|
||||||
|
bserv::make_path("/", &hello,
|
||||||
|
bserv::placeholders::response,
|
||||||
|
bserv::placeholders::session),
|
||||||
|
bserv::make_path("/register", &user_register,
|
||||||
|
bserv::placeholders::request,
|
||||||
|
bserv::placeholders::json_params,
|
||||||
|
bserv::placeholders::transaction),
|
||||||
|
bserv::make_path("/login", &user_login,
|
||||||
|
bserv::placeholders::request,
|
||||||
|
bserv::placeholders::json_params,
|
||||||
|
bserv::placeholders::transaction,
|
||||||
|
bserv::placeholders::session),
|
||||||
|
bserv::make_path("/logout", &user_logout,
|
||||||
|
bserv::placeholders::session),
|
||||||
|
bserv::make_path("/find/<str>", &find_user,
|
||||||
|
bserv::placeholders::transaction,
|
||||||
|
bserv::placeholders::_1),
|
||||||
|
bserv::make_path("/send", &send_request)
|
||||||
|
}};
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
33
routing.hpp
33
routing.hpp
|
@ -1,33 +0,0 @@
|
||||||
#ifndef _ROUTING_HPP
|
|
||||||
#define _ROUTING_HPP
|
|
||||||
|
|
||||||
#include "router.hpp"
|
|
||||||
|
|
||||||
#include "handlers.hpp"
|
|
||||||
|
|
||||||
namespace bserv {
|
|
||||||
|
|
||||||
bserv::router routes{
|
|
||||||
bserv::make_path("/", &hello,
|
|
||||||
bserv::placeholders::response,
|
|
||||||
bserv::placeholders::session),
|
|
||||||
bserv::make_path("/register", &user_register,
|
|
||||||
bserv::placeholders::request,
|
|
||||||
bserv::placeholders::json_params,
|
|
||||||
bserv::placeholders::transaction),
|
|
||||||
bserv::make_path("/login", &user_login,
|
|
||||||
bserv::placeholders::request,
|
|
||||||
bserv::placeholders::json_params,
|
|
||||||
bserv::placeholders::transaction,
|
|
||||||
bserv::placeholders::session),
|
|
||||||
bserv::make_path("/logout", &user_logout,
|
|
||||||
bserv::placeholders::session),
|
|
||||||
bserv::make_path("/find/<str>", &find_user,
|
|
||||||
bserv::placeholders::transaction,
|
|
||||||
bserv::placeholders::_1),
|
|
||||||
bserv::make_path("/send", &send_request)
|
|
||||||
};
|
|
||||||
|
|
||||||
} // bserv
|
|
||||||
|
|
||||||
#endif // _ROUTING_HPP
|
|
Loading…
Reference in New Issue