Skip to content

Commit

Permalink
Add route macros and update test cases
Browse files Browse the repository at this point in the history
  • Loading branch information
riccardodebenedictis committed Feb 8, 2024
1 parent 3ca193c commit cac76a7
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 35 deletions.
5 changes: 5 additions & 0 deletions include/async_server.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
#include <queue>
#include "base_server.hpp"

#define GET(server, target, handler) server.add_route(boost::beast::http::verb::get, target, std::function{handler})
#define POST(server, target, handler) server.add_route(boost::beast::http::verb::post, target, std::function{handler})
#define PUT(server, target, handler) server.add_route(boost::beast::http::verb::put, target, std::function{handler})
#define DELETE(server, target, handler) server.add_route(boost::beast::http::verb::delete_, target, std::function{handler})

namespace network::async
{
class plain_session;
Expand Down
36 changes: 15 additions & 21 deletions include/ws_client.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ namespace network
inline std::function<void()> default_on_close_handler = []() {};

template <class Derived>
class base_ws_client
class base_ws_client : public std::enable_shared_from_this<Derived>
{
friend class ws_client;
#ifdef USE_SSL
Expand All @@ -32,24 +32,19 @@ namespace network
Derived &derived() { return static_cast<Derived &>(*this); }

public:
base_ws_client(const std::string &host = SERVER_ADDRESS, const std::string &port = SERVER_PORT, const std::string &path = "/ws", std::function<void()> on_connect_handler = default_on_connect_handler, std::function<void(const std::string &)> on_message_handler = default_on_message_handler, std::function<void(boost::beast::error_code)> on_error_handler = default_on_error_handler, std::function<void()> on_close_handler = default_on_close_handler) : host(host), port(port), path(path), on_connect_handler(on_connect_handler), on_message_handler(on_message_handler), on_error_handler(on_error_handler), on_close_handler(on_close_handler)
{
#ifdef SIGQUIT
signals.add(SIGQUIT);
#endif
signals.async_wait([this](boost::beast::error_code, int)
{ close(); });
}
base_ws_client(const std::string &host = SERVER_ADDRESS, const std::string &port = SERVER_PORT, const std::string &path = "/ws", std::function<void()> on_connect_handler = default_on_connect_handler, std::function<void(const std::string &)> on_message_handler = default_on_message_handler, std::function<void(boost::beast::error_code)> on_error_handler = default_on_error_handler, std::function<void()> on_close_handler = default_on_close_handler) : host(host), port(port), path(path), on_connect_handler(on_connect_handler), on_message_handler(on_message_handler), on_error_handler(on_error_handler), on_close_handler(on_close_handler) {}
virtual ~base_ws_client() = default;

void connect() { do_resolve(); }

void send(const std::string &&msg) { enqueue(std::make_shared<const std::string>(std::move(msg))); }

void send(const std::shared_ptr<const std::string> &msg) { boost::asio::post(derived().get_stream().get_executor(), boost::beast::bind_front_handler(&base_ws_client::enqueue, this, msg)); }
void send(const std::shared_ptr<const std::string> &msg) { boost::asio::post(derived().get_stream().get_executor(), boost::beast::bind_front_handler(&base_ws_client::enqueue, this->shared_from_this(), msg)); }

void close() { derived().get_stream().async_close(boost::beast::websocket::close_code::normal, boost::beast::bind_front_handler(&base_ws_client::on_close, this)); }
void disconnect() { derived().get_stream().async_close(boost::beast::websocket::close_code::normal, boost::beast::bind_front_handler(&base_ws_client::on_close, this->shared_from_this())); }

private:
void do_resolve() { resolver.async_resolve(host, port, boost::beast::bind_front_handler(&base_ws_client::on_resolve, this)); }
void do_resolve() { resolver.async_resolve(host, port, boost::beast::bind_front_handler(&base_ws_client::on_resolve, this->shared_from_this())); }

void on_resolve(boost::beast::error_code ec, boost::asio::ip::tcp::resolver::results_type results)
{
Expand All @@ -60,7 +55,7 @@ namespace network
boost::beast::get_lowest_layer(derived().get_stream()).expires_after(std::chrono::seconds(30));

// Make the connection on the IP address we get from a lookup
boost::beast::get_lowest_layer(derived().get_stream()).async_connect(results, boost::beast::bind_front_handler(&base_ws_client::on_connect, this));
boost::beast::get_lowest_layer(derived().get_stream()).async_connect(results, boost::beast::bind_front_handler(&base_ws_client::on_connect, this->shared_from_this()));
}

virtual void on_connect(boost::beast::error_code ec, boost::asio::ip::tcp::resolver::results_type::endpoint_type ep) = 0;
Expand All @@ -86,7 +81,7 @@ namespace network
do_write();
}

void do_read() { derived().get_stream().async_read(buffer, boost::beast::bind_front_handler(&base_ws_client::on_read, this)); }
void do_read() { derived().get_stream().async_read(buffer, boost::beast::bind_front_handler(&base_ws_client::on_read, this->shared_from_this())); }
void on_read(boost::beast::error_code ec, [[maybe_unused]] std::size_t bytes_transferred)
{
if (ec)
Expand All @@ -95,7 +90,7 @@ namespace network
on_message_handler(boost::beast::buffers_to_string(buffer.data()));
}

void do_write() { derived().get_stream().async_write(boost::asio::buffer(*send_queue.front()), boost::asio::bind_executor(derived().get_stream().get_executor(), boost::beast::bind_front_handler(&base_ws_client::on_write, this))); }
void do_write() { derived().get_stream().async_write(boost::asio::buffer(*send_queue.front()), boost::asio::bind_executor(derived().get_stream().get_executor(), boost::beast::bind_front_handler(&base_ws_client::on_write, this->shared_from_this()))); }
void on_write(boost::beast::error_code ec, [[maybe_unused]] std::size_t bytes_transferred)
{
if (ec)
Expand All @@ -120,7 +115,6 @@ namespace network
std::string port;
std::string path;
boost::asio::strand<boost::asio::system_executor> strand{boost::asio::system_executor()};
boost::asio::signal_set signals{strand, SIGINT, SIGTERM};
boost::asio::ip::tcp::resolver resolver{strand};
boost::beast::flat_buffer buffer;
std::function<void()> on_connect_handler;
Expand All @@ -135,7 +129,7 @@ namespace network
class ws_client : public base_ws_client<ws_client>
{
public:
ws_client(const std::string &host = SERVER_ADDRESS, const std::string &port = SERVER_PORT, const std::string &path = "/ws", std::function<void()> on_connect_handler = default_on_connect_handler, std::function<void(const std::string &)> on_message_handler = default_on_message_handler, std::function<void(boost::beast::error_code)> on_error_handler = default_on_error_handler, std::function<void()> on_close_handler = default_on_close_handler) : base_ws_client(host, port, path, on_connect_handler, on_message_handler, on_error_handler, on_close_handler) { do_resolve(); }
ws_client(const std::string &host = SERVER_ADDRESS, const std::string &port = SERVER_PORT, const std::string &path = "/ws", std::function<void()> on_connect_handler = default_on_connect_handler, std::function<void(const std::string &)> on_message_handler = default_on_message_handler, std::function<void(boost::beast::error_code)> on_error_handler = default_on_error_handler, std::function<void()> on_close_handler = default_on_close_handler) : base_ws_client(host, port, path, on_connect_handler, on_message_handler, on_error_handler, on_close_handler) {}

boost::beast::websocket::stream<boost::beast::tcp_stream> &get_stream() { return stream; }

Expand All @@ -161,7 +155,7 @@ namespace network
host += ':' + std::to_string(ep.port());

// Perform the websocket handshake
stream.async_handshake(host, path, boost::beast::bind_front_handler(&base_ws_client::on_handshake, this));
stream.async_handshake(host, path, boost::beast::bind_front_handler(&base_ws_client::on_handshake, this->shared_from_this()));
}

private:
Expand All @@ -172,7 +166,7 @@ namespace network
class wss_client : public base_ws_client<wss_client>
{
public:
wss_client(const std::string &host = SERVER_ADDRESS, const std::string &port = SERVER_PORT, const std::string &path = "/wss", std::function<void()> on_connect_handler = default_on_connect_handler, std::function<void(const std::string &)> on_message_handler = default_on_message_handler, std::function<void(boost::beast::error_code)> on_error_handler = default_on_error_handler, std::function<void()> on_close_handler = default_on_close_handler) : base_ws_client(host, port, path, on_connect_handler, on_message_handler, on_error_handler, on_close_handler) { do_resolve(); }
wss_client(const std::string &host = SERVER_ADDRESS, const std::string &port = SERVER_PORT, const std::string &path = "/wss", std::function<void()> on_connect_handler = default_on_connect_handler, std::function<void(const std::string &)> on_message_handler = default_on_message_handler, std::function<void(boost::beast::error_code)> on_error_handler = default_on_error_handler, std::function<void()> on_close_handler = default_on_close_handler) : base_ws_client(host, port, path, on_connect_handler, on_message_handler, on_error_handler, on_close_handler) {}

boost::beast::websocket::stream<boost::beast::ssl_stream<boost::beast::tcp_stream>> &get_stream() { return stream; }

Expand All @@ -198,7 +192,7 @@ namespace network
host += ':' + std::to_string(ep.port());

// Perform the SSL handshake
stream.next_layer().async_handshake(boost::asio::ssl::stream_base::client, boost::beast::bind_front_handler(&wss_client::on_ssl_handshake, this));
stream.next_layer().async_handshake(boost::asio::ssl::stream_base::client, boost::beast::bind_front_handler(&wss_client::on_ssl_handshake, this->shared_from_this()));
}

void on_ssl_handshake(boost::beast::error_code ec)
Expand All @@ -218,7 +212,7 @@ namespace network
{ req.set(boost::beast::http::field::user_agent, "ratioNet wss_client"); }));

// Perform the websocket handshake
stream.async_handshake(host, path, boost::beast::bind_front_handler(&base_ws_client::on_handshake, this));
stream.async_handshake(host, path, boost::beast::bind_front_handler(&base_ws_client::on_handshake, this->shared_from_this()));
}

private:
Expand Down
24 changes: 23 additions & 1 deletion tests/test_client.cpp
Original file line number Diff line number Diff line change
@@ -1,11 +1,33 @@
#include <iostream>
#include "async_server.hpp"
#include "client.hpp"

using string_req = boost::beast::http::request<boost::beast::http::string_body>;
using string_res = boost::beast::http::response<boost::beast::http::string_body>;

int main(int argc, char const *argv[])
{
network::client client;
// we create a server to test the client
network::async::server server;
server.set_log_handler([](const std::string &msg)
{ std::cout << msg << std::endl; });
server.set_error_handler([](const std::string &msg)
{ std::cerr << msg << std::endl; });
GET(server, "/", [](const string_req &, string_res &res)
{
res.set(boost::beast::http::field::content_type, "html");
res.body() = R"(<html><body><h1>Hello, world!</h1></body></html>)"; });

std::thread t{[&server]
{ server.start(); }};
std::this_thread::sleep_for(std::chrono::seconds(5));

// we create a client
network::client client{"localhost"};
auto res = client.get<boost::beast::http::string_body>("/");
std::cout << res << std::endl;

server.stop();
t.join();
return 0;
}
14 changes: 3 additions & 11 deletions tests/test_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,6 @@ void test_plain_async_server()
res.set(boost::beast::http::field::content_type, "html");
res.body() = R"(<html><body><h1>Hello, world!</h1></body></html>)"; });

server.ws("/ws")
.on_open([](network::websocket_session &)
{ std::cout << "New connection" << std::endl; })
.on_message([](network::websocket_session &, const std::string &msg)
{ std::cout << "Received message: " << msg << std::endl; })
.on_close([](network::websocket_session &, const boost::beast::websocket::close_reason)
{ std::cout << "Connection closed" << std::endl; });

std::thread t{[&server]
{ server.start(); }};

Expand Down Expand Up @@ -59,8 +51,7 @@ void test_ws_server()

server.ws("/ws")
.on_open([](network::websocket_session &session)
{
std::cout << "New connection" << std::endl; })
{ std::cout << "New connection" << std::endl; })
.on_message([](network::websocket_session &, const std::string &msg)
{ std::cout << "Received message: " << msg << std::endl; })
.on_close([](network::websocket_session &, const boost::beast::websocket::close_reason)
Expand All @@ -69,13 +60,14 @@ void test_ws_server()
std::thread t{[&server]
{ server.start(); }};

std::this_thread::sleep_for(std::chrono::seconds(1000));
std::this_thread::sleep_for(std::chrono::seconds(10));
server.stop();
t.join();
}

int main(int argc, char const *argv[])
{
test_plain_async_server();
test_ws_server();

return 0;
Expand Down
48 changes: 46 additions & 2 deletions tests/test_ws.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,48 @@
#include <iostream>
#include <thread>
#include "async_server.hpp"
#include "ws_client.hpp"

using string_req = boost::beast::http::request<boost::beast::http::string_body>;
using string_res = boost::beast::http::response<boost::beast::http::string_body>;

int main(int argc, char const *argv[])
{
network::ws_client client(
// we create a server to test the client
network::async::server server;
server.set_log_handler([](const std::string &msg)
{ std::cout << msg << std::endl; });
server.set_error_handler([](const std::string &msg)
{ std::cerr << msg << std::endl; });
GET(server, "/", [](const string_req &, string_res &res)
{
res.set(boost::beast::http::field::content_type, "html");
res.body() = R"(
<html>
<body>
<h1>Hello, world!</h1>
<script>
var ws = new WebSocket("ws://" + window.location.host + "/ws");
ws.onopen = function() { ws.send("Hello, server!"); };
ws.onmessage = function(event) { alert("Message from server: " + event.data); };
</script>
</body>
</html>)"; });

server.ws("/ws")
.on_open([](network::websocket_session &session)
{ std::cout << "New connection" << std::endl; })
.on_message([](network::websocket_session &, const std::string &msg)
{ std::cout << "Received message: " << msg << std::endl; })
.on_close([](network::websocket_session &, const boost::beast::websocket::close_reason)
{ std::cout << "Connection closed" << std::endl; });

std::thread t{[&server]
{ server.start(); }};
std::this_thread::sleep_for(std::chrono::seconds(5));

// we create a client
auto client = std::make_shared<network::ws_client>(
"localhost", SERVER_PORT, "/ws", []()
{ std::cout << "Connected" << std::endl; },
[](const std::string &message)
Expand All @@ -13,8 +51,14 @@ int main(int argc, char const *argv[])
{ std::cerr << ec.message() << std::endl; },
[]()
{ std::cout << "Connection closed" << std::endl; });

client->connect();
std::this_thread::sleep_for(std::chrono::seconds(5));
client.send("Hello, world!");
client->send("Hello, world!");
std::this_thread::sleep_for(std::chrono::seconds(5));

client->disconnect();
server.stop();
t.join();
return 0;
}

0 comments on commit cac76a7

Please sign in to comment.