Last active
May 11, 2020 13:57
-
-
Save illera88/e78407c4b36705f67522756b8a63f648 to your computer and use it in GitHub Desktop.
bridge between regular TCP/IP socket and websocketpp connection
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* This is just to get a context on how the bridge.h is used*/ | |
void ws_proxy::on_open(websocketpp::connection_hdl hdl) { | |
server::connection_ptr con = ws_server.get_con_from_hdl(hdl); | |
// Pause reading from the websocket until we are ready | |
auto ec = con->pause_reading(); | |
if (ec) { | |
throw websocketpp::exception(ec.message()); | |
} | |
internal::connector::create( | |
io_context_, | |
address_, | |
port_, | |
[con] (asio::ip::tcp::socket socket) { | |
// Start reading from websockets again | |
con->resume_reading(); | |
// Hand off this socket and websocket to the bridge | |
auto bridge = internal::bridge<server::connection_ptr>::create(std::move(socket), std::move(con)); | |
} | |
); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#ifndef ASIO_STANDALONE | |
#define ASIO_STANDALONE | |
#endif | |
#include <asio.hpp> | |
#include <websocketpp/config/asio.hpp> | |
#include <websocketpp/connection.hpp> | |
namespace wswrap { | |
typedef websocketpp::config::asio::message_type::ptr message_ptr; | |
typedef std::shared_ptr<asio::io_service::strand> strand_ptr; | |
} | |
namespace internal { | |
template<typename WSConnT> | |
class bridge : public std::enable_shared_from_this<bridge<WSConnT>> { | |
// Private constructor, use create() | |
bridge(asio::ip::tcp::socket socket, WSConnT wsconnection) | |
: socket_(std::move(socket)), wsconn_(std::move(wsconnection)), strand_ptr_(wsconn_->get_strand()), write_clear(true) | |
{ | |
if (wsconn_ == nullptr) { | |
throw std::invalid_argument("No websocket connection provided."); | |
} | |
} | |
public: | |
// Ah, C++ templating can be such a joy | |
template<typename ... T> | |
static std::shared_ptr<bridge> create(T&& ... all) { | |
// Can't use make_shared here because of visibility rules and | |
// all that fun jazz... | |
auto ptr = std::shared_ptr<bridge>(new bridge(std::forward<T>(all)...)); | |
ptr->start(); | |
return ptr; | |
} | |
private: | |
void start() { | |
if (wsconn_->get_state() != websocketpp::session::state::open) { | |
// Set an open handler for the websocket connection | |
wsconn_->set_open_handler( | |
strand_ptr_->wrap(std::bind( | |
&bridge::handle_ws_open, | |
this->shared_from_this(), | |
std::placeholders::_1 | |
)) | |
); | |
} else { | |
// Setup reading from the socket | |
socket_.async_read_some( | |
asio::buffer(socket_data_, max_data_length), | |
strand_ptr_->wrap(std::bind( | |
&bridge::handle_socket_read, | |
this->shared_from_this(), | |
std::placeholders::_1, | |
std::placeholders::_2 | |
)) | |
); | |
} | |
// Set a close handler for the websocket connection | |
wsconn_->set_close_handler( | |
strand_ptr_->wrap(std::bind( | |
&bridge::handle_ws_close, | |
this->shared_from_this(), | |
std::placeholders::_1 | |
)) | |
); | |
// Set a message handler for the websocket connection | |
wsconn_->set_message_handler( | |
strand_ptr_->wrap(std::bind( | |
&bridge::handle_ws_read, | |
this->shared_from_this(), | |
std::placeholders::_1, | |
std::placeholders::_2 | |
)) | |
); | |
wsconn_->set_interrupt_handler( | |
strand_ptr_->wrap(std::bind( | |
&bridge::handle_ws_interrupt, | |
this->shared_from_this(), | |
std::placeholders::_1 | |
)) | |
); | |
} | |
// This handler is called when the websocket handler is open, and | |
// we can start sending data into it | |
void handle_ws_open(websocketpp::connection_hdl hdl) { | |
// Start reading from the socket to forward it on | |
socket_.async_read_some( | |
asio::buffer(socket_data_, max_data_length), | |
strand_ptr_->wrap(std::bind( | |
&bridge::handle_socket_read, | |
this->shared_from_this(), | |
std::placeholders::_1, | |
std::placeholders::_2 | |
)) | |
); | |
} | |
// This handle will be called when the websocket connection is | |
// closed | |
void handle_ws_close(websocketpp::connection_hdl hdl) { | |
wsconn_->pause_reading(); | |
// Close out our other socket if the websocket goes away | |
close(); | |
} | |
// This handler is called over and over by websocketpp, we do not | |
// need to retrigger it | |
void handle_ws_read(websocketpp::connection_hdl hdl, wswrap::message_ptr msg) { | |
// Append the data to the queue of data to be sent, this copies | |
// the data because the lifetime of the payload is determined | |
// by websocketpp and we don't control it | |
ws_data_.push(std::string(msg->get_payload())); | |
// Trigger an async write on the socket as necessary | |
maybe_write_to_socket(); | |
} | |
// Unfortunately we can't just keep calling async_write, so we end | |
// up having to manually deal with this mess by using a queue and a | |
// flag to know when we are done writing. | |
void maybe_write_to_socket() { | |
if (!ws_data_.empty() && write_clear) { | |
write_clear = false; | |
auto& data = ws_data_.front(); | |
async_write(socket_, | |
asio::buffer(data), | |
strand_ptr_->wrap(std::bind(&bridge::handle_socket_write, | |
this->shared_from_this(), | |
std::placeholders::_1)) | |
); | |
} | |
} | |
// This handler is called once after being triggered, so we need to | |
// make sure to trigger it again | |
void handle_socket_read( | |
const asio::error_code& error, | |
const size_t& bytes_transferred | |
) { | |
if (!error) { | |
// We received some data, send it to the websocket | |
s_data_.push(std::string(reinterpret_cast<char*>(&socket_data_), bytes_transferred)); | |
// We received some data, send it to the websocket | |
wsconn_->interrupt(); | |
// Reset trigger so we get called again | |
socket_.async_read_some( | |
asio::buffer(socket_data_, max_data_length), | |
strand_ptr_->wrap(std::bind( | |
&bridge::handle_socket_read, | |
this->shared_from_this(), | |
std::placeholders::_1, | |
std::placeholders::_2 | |
)) | |
); | |
} else { | |
close(); | |
} | |
} | |
// This is triggered when we complete the write that came in from | |
// the websocket | |
void handle_socket_write(const asio::error_code& error) { | |
if (!error) { | |
// Remove the front-most entry | |
ws_data_.pop(); | |
// Clear the flag | |
write_clear = true; | |
// Do we have more to send? Do it. | |
maybe_write_to_socket(); | |
} else { | |
close(); | |
} | |
} | |
void handle_ws_interrupt(websocketpp::connection_hdl hdl) { | |
std::cerr << "[" << this << "] on_interrupt" << std::endl; | |
wsconn_->send(s_data_.front(), websocketpp::frame::opcode::binary); | |
s_data_.pop(); | |
if (!s_data_.empty()) { | |
std::cerr << "Not empty yet, interrupting again" << std::endl; | |
wsconn_->interrupt(); | |
} | |
} | |
void close() { | |
std::cerr << "Closing the sockets down" << std::endl; | |
if (socket_.is_open()) | |
{ | |
socket_.close(); | |
} | |
auto state = wsconn_->get_state(); | |
if (!( | |
state == websocketpp::session::state::closed || | |
state == websocketpp::session::state::closing) | |
) { | |
wsconn_->close(websocketpp::close::status::going_away, "socket shutdown"); | |
} | |
} | |
asio::ip::tcp::socket socket_; | |
WSConnT wsconn_; | |
wswrap::strand_ptr strand_ptr_; | |
static const int max_data_length = 8192; //8KB | |
std::array<unsigned char, max_data_length> socket_data_; | |
std::queue<std::string> ws_data_; | |
std::queue<std::string> s_data_; | |
bool write_clear; | |
std::mutex mutex_; | |
}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi, I tried adding the bridge.h file to my own program but I get the missing file error. I updated the boost to 1.73.0,I keep getting the error. Can you help me?
[build] /home/furkan/Desktop/ws2tcp/include/./bridge.h:7:10: fatal error: asio.hpp: No such file or directory
[build] #include <asio.hpp>
I assume this asio comes from boost and ı changed to
<boost/asio.hpp>
and then
[build] [ 50%] Building CXX object CMakeFiles/sx-node.dir/src/main.cpp.o
[build] In file included from /home/furkan/Desktop/ws2tcp/include/websocketpp/transport/asio/base.hpp:31:0,
[build] from /home/furkan/Desktop/ws2tcp/include/websocketpp/transport/asio/connection.hpp:31,
[build] from /home/furkan/Desktop/ws2tcp/include/websocketpp/transport/asio/endpoint.hpp:32,
[build] from /home/furkan/Desktop/ws2tcp/include/websocketpp/config/asio.hpp:32,
[build] from /home/furkan/Desktop/ws2tcp/include/./bridge.h:8,
[build] from /home/furkan/Desktop/ws2tcp/src/main.cpp:3:
[build] /home/furkan/Desktop/ws2tcp/include/websocketpp/common/asio.hpp:46:14: fatal error: asio/version.hpp: No such file or directory
[build] #include <asio/version.hpp>
but
<asio/version.hpp>
exist in/usr/lib/boost/boost/asio/version.hpp
I used the websocketpp as header only. And also I was compile and install to my
/usr/lib
. Both are not working.