In this tip, you will see a diagram and code to show a reverse proxy developed with BOOST async ASIO calls.
Background
C++ Boost Reverse Proxy
In a client server application, server is a central machine and one or more clients connect to the server. Clients connect to the server through a port which is usually a number or port number. A port number configured on the server side which is also called server port. All clients connect using the server port.
What is Proxy?
It is an intermediate application that connects between client and server. Proxy acts both as a server and as a client. It is also called proxy server and proxy client. Original clients communicate directly with the Proxy servers with and proxy client will communicate with the original server. Proxy Server and proxy client exchange information among themselves. The original client connects to the proxy server through a port also called a proxy port.
There are two types of proxy:
- Forward Proxy
- Reverse Proxy
Forward Proxy is the one that resided on every client machine. Hence, it need to be installed in every client machine.
A reverse proxy is a proxy that acts as a bridge between the client and server. Instead of client directly communicating with the server and server directly communicating with the client, they communicate directly with reverse proxy. A simple Reverse proxy is just a packet exchange between the client and server. Most proxies perform advanced tasks.
Reverse proxies themselves act as a server and client: server that listens to the external client, and client that connects to the external server. Normally, reverse proxy resides on the external server side. Internal server and internal client themselves communicate with each other for packet exchange. In the code, I have referred to the internal server as server and internal client as client.
Mostly, the reverse proxy looks as shown below. I will try to add more description or modify the diagram, since I don't have time, I am unable to do it now.
Using the Code
The latest version of BOOST needs to be installed.
Most of the things are self explanatory.
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <tchar.h>
#include <strsafe.h>
#include <Winerror.h>
#include <iostream>
using namespace std;
#include <boost/asio.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/thread.hpp>
#include <boost/lexical_cast.hpp>
using namespace boost;
using namespace boost::asio;
using namespace boost::asio::ip;
char szServerBuffer[4096]; char szClientBuffer[4096];
boost::mutex g_Mutex;
void writeclient_handler(const boost::system::error_code& ec,
std::size_t bytes_transferred)
{
g_Mutex.lock();
cout << "Proxy Client Sent: " << bytes_transferred << endl;
g_Mutex.unlock();
}
void write_handler(const boost::system::error_code& ec, std::size_t bytes_transferred)
{
g_Mutex.lock();
cout << "Proxy Server Sent: " << bytes_transferred << endl;
g_Mutex.unlock();
}
void readclient_handler(const boost::system::error_code& ec,
std::size_t bytes_transferred,
boost::shared_ptr<tcp::socket>& pClientSocket,
boost::shared_ptr<tcp::socket>& pPeerSocket)
{
if (!ec || bytes_transferred != 0)
{
g_Mutex.lock();
cout << "Proxy Client Received: " << bytes_transferred << endl;
g_Mutex.unlock();
pPeerSocket->async_write_some(boost::asio::buffer
(szClientBuffer, bytes_transferred),
boost::bind(write_handler, boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
pClientSocket->async_read_some(boost::asio::buffer
(szClientBuffer, sizeof(szClientBuffer)),
boost::bind(readclient_handler, boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred,
pClientSocket, pPeerSocket));
}
else
{
g_Mutex.lock();
cout << "Proxy Client receive error: " << ec << endl;
g_Mutex.unlock();
}
}
void connect_handler(const boost::system::error_code& ec,
boost::shared_ptr<tcp::socket>& pClientSocket,
boost::shared_ptr<tcp::socket>& pPeerSocket)
{
if (!ec)
{
g_Mutex.lock();
cout << "Proxy CLient connected:" << endl;
g_Mutex.unlock();
pClientSocket->async_read_some(boost::asio::buffer
(szClientBuffer, sizeof(szClientBuffer)),
boost::bind(readclient_handler, boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred,
pClientSocket, pPeerSocket));
}
else
{
g_Mutex.lock();
cout << "Proxy Client error: " << ec << endl;
g_Mutex.unlock();
}
}
void read_handler(const boost::system::error_code& ec, std::size_t bytes_transferred,
boost::shared_ptr<tcp::socket>& pPeerSocket,
boost::shared_ptr<tcp::socket>& pClientSocket)
{
if (!ec || bytes_transferred != 0)
{
g_Mutex.lock();
cout << "Proxy Server Received: " << bytes_transferred << endl;
g_Mutex.unlock();
pClientSocket->async_write_some(boost::asio::buffer
(szServerBuffer, bytes_transferred),
boost::bind(writeclient_handler, boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
pPeerSocket->async_read_some(boost::asio::buffer
(szServerBuffer, sizeof(szServerBuffer)),
boost::bind(read_handler, boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred,
pPeerSocket, pClientSocket));
}
else
{
g_Mutex.lock();
cout << "Proxy Server receive error: " << ec << endl;
g_Mutex.unlock();
}
}
void accept_handler(const boost::system::error_code& ec,
boost::shared_ptr<io_context>& pIOContext,
boost::shared_ptr<tcp::acceptor>& pAcceptor,
boost::shared_ptr<tcp::socket>& pPeerSocket)
{
if (!ec)
{
g_Mutex.lock();
cout << "Proxy Server accepted:" << endl;
g_Mutex.unlock();
boost::shared_ptr<tcp::socket> pNewPeerSocket(new tcp::socket(*pIOContext));
pAcceptor->async_accept(*pNewPeerSocket,
boost::bind(accept_handler, boost::asio::placeholders::error,
pIOContext, pAcceptor, pNewPeerSocket));
boost::shared_ptr<tcp::socket> pCientSocket1(new tcp::socket(*pIOContext));
boost::shared_ptr<tcp::resolver>
pClientResolver(new tcp::resolver(*pIOContext));
boost::shared_ptr<tcp::resolver::query> pClientResolverQuery(
new tcp::resolver::query("127.0.0.1", boost::lexical_cast<string>(30000)));
tcp::resolver::iterator itrEndPoint =
pClientResolver->resolve(*pClientResolverQuery);
pCientSocket1->async_connect(*itrEndPoint,
boost::bind(connect_handler, boost::asio::placeholders::error,
pCientSocket1, pPeerSocket));
Sleep(2000);
pPeerSocket->async_read_some(boost::asio::buffer
(szServerBuffer, sizeof(szServerBuffer)),
boost::bind(read_handler, boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred,
pPeerSocket, pCientSocket1));
}
else
{
g_Mutex.lock();
cout << "Server accept error: " << ec << endl;
g_Mutex.unlock();
}
}
void ReverProxyThread(boost::shared_ptr<io_context>& pIOContext)
{
while (1)
{
try
{
boost::system::error_code ec;
pIOContext->run(ec);
break;
}
catch (std::runtime_error& ex)
{
}
}
}
void _tmain()
{
boost::shared_ptr<io_context> pIOContext1(new io_context());
boost::shared_ptr<io_context::strand>
pStrand(new io_context::strand(*pIOContext1));
boost::shared_ptr<io_context::work> pWork(new io_context::work(*pIOContext1));
boost::shared_ptr<boost::thread_group> pTG(new boost::thread_group());
for (int i = 0; i < 2; i++)
{
pTG->create_thread(boost::bind(ReverProxyThread, pIOContext1));
}
boost::shared_ptr<tcp::resolver> pResolver(new tcp::resolver(*pIOContext1));
boost::shared_ptr<tcp::resolver::query>
pQuery1(new tcp::resolver::query(tcp::v4(),
boost::lexical_cast<string>(20000)));
tcp::resolver::iterator pIter1 = pResolver->resolve(*pQuery1);
boost::shared_ptr<tcp::acceptor> pAcceptor(new tcp::acceptor(*pIOContext1));
tcp::endpoint Endpoint = (*pIter1);
pAcceptor->open(Endpoint.protocol());
pAcceptor->bind(*pIter1);
pAcceptor->listen();
boost::shared_ptr<tcp::socket> pPeerSocket(new tcp::socket(*pIOContext1));
pAcceptor->async_accept(*pPeerSocket,
boost::bind(accept_handler, boost::asio::placeholders::error,
pIOContext1, pAcceptor, pPeerSocket));
cin.get();
system::error_code ec;
pPeerSocket->close(ec);
pPeerSocket->shutdown(tcp::socket::shutdown_both, ec);
pIOContext1->stop();
pTG->join_all();
}
History
- 9th May, 2021: Initial version
- 21st March, 2022: Article updated