10 #ifndef SUPPORTLIB_WEBSOCKETSERVER_H
11 #define SUPPORTLIB_WEBSOCKETSERVER_H
14 #include <boost/asio/ip/tcp.hpp>
15 #include <boost/beast/core.hpp>
16 #include <boost/beast/websocket.hpp>
17 #include <boost/beast/websocket/ssl.hpp>
18 #include <boost/asio/bind_executor.hpp>
19 #include <boost/asio/strand.hpp>
20 #include <boost/asio/ssl/stream.hpp>
32 using tcp = boost::asio::ip::tcp;
33 namespace websocket = boost::beast::websocket;
34 namespace ssl = boost::asio::ssl;
43 using SPtr = std::shared_ptr<WebSocketServerException>;
44 using UPtr = std::unique_ptr<WebSocketServerException>;
45 using WPtr = std::weak_ptr<WebSocketServerException>;
65 explicit WebSocketSession(tcp::socket socket,
bool ssl,
const std::filesystem::path& cert,
const std::filesystem::path& key, boost::asio::io_context& ioc) :
66 m_Socket(
std::move(socket)),
68 m_Strand(boost::asio::make_strand(ioc))
72 m_Ctx.set_options(boost::asio::ssl::context::default_workarounds |
73 boost::asio::ssl::context::no_sslv2 |
74 boost::asio::ssl::context::no_sslv3 |
75 boost::asio::ssl::context::no_tlsv1 |
76 boost::asio::ssl::context::single_dh_use);
77 m_Ctx.use_certificate_chain_file(cert.string());
78 m_Ctx.use_private_key_file(key.string(), boost::asio::ssl::context::file_format::pem);
79 m_Wss = std::make_shared<websocket::stream<ssl::stream<tcp::socket&> >>(m_Socket, m_Ctx);
80 m_Wss->next_layer().set_verify_mode(ssl::verify_none);
85 m_Ws = std::make_shared<websocket::stream<tcp::socket&>>(m_Socket);
95 m_Wss->next_layer().async_handshake(ssl::stream_base::server,boost::asio::bind_executor(m_Strand,std::bind(&WebSocketSession::on_handshake,this->shared_from_this(),std::placeholders::_1)));
97 m_Ws->async_accept(boost::asio::bind_executor(m_Strand, std::bind(&WebSocketSession::on_accept, this->shared_from_this(), std::placeholders::_1)));
104 void send(
const std::string& msg){
107 {
if(m_Wss->is_open()){m_Wss->write(boost::asio::buffer(msg));}}
109 {
if(m_Ws->is_open()){m_Ws->write(boost::asio::buffer(msg));}}
133 return m_Socket.remote_endpoint().address().to_string();
139 return std::to_string(m_Socket.remote_endpoint().port());
147 {
if(m_Wss->is_open()){m_Wss->close(websocket::close_code::normal);}}
149 {
if(m_Ws->is_open()){m_Ws->close(websocket::close_code::normal);}}
151 using SPtr = std::shared_ptr<WebSocketSession>;
152 using UPtr = std::unique_ptr<WebSocketSession>;
153 using WPtr = std::weak_ptr<WebSocketSession>;
155 void on_read(boost::system::error_code ec, std::size_t bytes_transferred) {
156 boost::ignore_unused(bytes_transferred);
161 m_Message = boost::beast::buffers_to_string(m_Buffer.data());
163 m_Buffer.consume(m_Buffer.size());
168 void on_accept(boost::system::error_code ec) {
170 throw WebSocketServerException(
"Accept: " + ec.message());
175 {
if(m_Wss->is_open()){m_Wss->async_read(m_Buffer, boost::asio::bind_executor(m_Strand, std::bind(&WebSocketSession::on_read, this->shared_from_this(), std::placeholders::_1, std::placeholders::_2)));}}
177 {
if(m_Ws->is_open()){m_Ws->async_read(m_Buffer, boost::asio::bind_executor(m_Strand, std::bind(&WebSocketSession::on_read, this->shared_from_this(), std::placeholders::_1, std::placeholders::_2)));}}
179 void on_handshake(boost::system::error_code ec) {
181 throw WebSocketServerException(
"Handshake: " + ec.message());
182 m_Wss->async_accept(boost::asio::bind_executor(m_Strand, std::bind( &WebSocketSession::on_accept, this->shared_from_this(), std::placeholders::_1)));
184 tcp::socket m_Socket;
186 std::shared_ptr< websocket::stream<tcp::socket&> > m_Ws;
187 std::shared_ptr< websocket::stream<ssl::stream<tcp::socket&> > > m_Wss;
188 boost::asio::strand<boost::asio::io_context::executor_type> m_Strand;
189 boost::beast::multi_buffer m_Buffer;
190 std::string m_Message;
191 ssl::context m_Ctx{ssl::context::sslv23};
192 boost::system::error_code m_Ec;
257 WebSocketServer(
const std::string& address =
"0.0.0.0",
const std::string& port =
"80",
bool ssl =
false,
const size_t numThreads = 1,
const std::filesystem::path& cert =
"",
const std::filesystem::path& key =
"") :
258 m_Endpoint(boost::asio::ip::make_address(address),
259 std::atoi(port.c_str())),
262 m_NumThreads(numThreads),
268 boost::system::error_code ec;
269 m_Acceptor.open(m_Endpoint.protocol(), ec);
272 m_Acceptor.set_option(boost::asio::socket_base::reuse_address(
true));
275 m_Acceptor.bind(m_Endpoint, ec);
278 m_Acceptor.listen(boost::asio::socket_base::max_listen_connections, ec);
288 if(!m_Acceptor.is_open())
return;
291 m_Threads.reserve(m_NumThreads);
292 for(
auto i = m_NumThreads; i > 0; --i)
293 m_Threads.emplace_back([
this]{ m_Ioc.run();});
319 using SPtr = std::shared_ptr<WebSocketServer>;
320 using UPtr = std::unique_ptr<WebSocketServer>;
321 using WPtr = std::weak_ptr<WebSocketServer>;
324 m_Acceptor.async_accept(m_Socket, std::bind(&WebSocketServer::on_accept, this->shared_from_this(), std::placeholders::_1));
326 void on_accept(boost::system::error_code ec) {
328 throw WebSocketServerException(
"Accept: " + ec.message());
329 m_NewSession = std::make_shared<WebSocketSession>(std::move(m_Socket), m_SSL, m_Cert, m_Key, m_Ioc);
334 tcp::endpoint m_Endpoint;
335 boost::asio::io_context m_Ioc;
336 std::vector<std::thread> m_Threads;
339 tcp::acceptor m_Acceptor;
340 tcp::socket m_Socket;
341 WebSocketSession::SPtr m_NewSession;
342 std::filesystem::path m_Cert;
343 std::filesystem::path m_Key;
Base exception to inherit custom exceptions from.
Observer/Obersvable Pattern implementation.
Base exception to inherit custom exceptions from.
Definition: Exception.h:48
ExceptionBase(const std::string &msg="")
Definition: Exception.h:54
Observable class. Inherited classes can notify all classes which inherit from Observer.
Definition: Observer.h:99
void notify()
Definition: Observer.h:130
Exception to be thrown on websocket server errors.
Definition: WebSocketServer.h:40
Class representing a WebSocket Server.
Definition: WebSocketServer.h:244
void run()
Definition: WebSocketServer.h:287
bool getSSL() const
Definition: WebSocketServer.h:304
std::filesystem::path getKey() const
Definition: WebSocketServer.h:316
std::filesystem::path getCert() const
Definition: WebSocketServer.h:310
WebSocketServer(const std::string &address="0.0.0.0", const std::string &port="80", bool ssl=false, const size_t numThreads=1, const std::filesystem::path &cert="", const std::filesystem::path &key="")
Definition: WebSocketServer.h:257
WebSocketSession::SPtr getSession() const
Definition: WebSocketServer.h:298
Class representing one session/connection.
Definition: WebSocketServer.h:54
void close()
Definition: WebSocketServer.h:144
boost::system::error_code getError() const
Definition: WebSocketServer.h:126
std::string getClientPort() const
Definition: WebSocketServer.h:138
std::string getClientIP() const
Definition: WebSocketServer.h:132
WebSocketSession(tcp::socket socket, bool ssl, const std::filesystem::path &cert, const std::filesystem::path &key, boost::asio::io_context &ioc)
Definition: WebSocketServer.h:65
void run()
Definition: WebSocketServer.h:93
void send(const std::string &msg)
msg Message to send
Definition: WebSocketServer.h:104
bool getSSL() const
Definition: WebSocketServer.h:120
std::string getMessage() const
Definition: WebSocketServer.h:114
Namespace for giri's C++ support library.
Definition: Base64.h:47