diff --git a/I2PControl.cpp b/I2PControl.cpp index 260e8b80..efb425e2 100644 --- a/I2PControl.cpp +++ b/I2PControl.cpp @@ -1,5 +1,7 @@ +#include #include #include +#include "Log.h" #include "I2PControl.h" namespace i2p @@ -11,5 +13,103 @@ namespace client m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)) { } + + I2PControlService::~I2PControlService () + { + Stop (); + } + + void I2PControlService::Start () + { + if (!m_IsRunning) + { + Accept (); + m_IsRunning = true; + m_Thread = new std::thread (std::bind (&I2PControlService::Run, this)); + } + } + + void I2PControlService::Stop () + { + if (m_IsRunning) + { + m_IsRunning = false; + m_Acceptor.cancel (); + m_Service.stop (); + if (m_Thread) + { + m_Thread->join (); + delete m_Thread; + m_Thread = nullptr; + } + } + } + + void I2PControlService::Run () + { + while (m_IsRunning) + { + try + { + m_Service.run (); + } + catch (std::exception& ex) + { + LogPrint (eLogError, "I2PControl: ", ex.what ()); + } + } + } + + void I2PControlService::Accept () + { + auto newSocket = std::make_shared (m_Service); + m_Acceptor.async_accept (*newSocket, std::bind (&I2PControlService::HandleAccept, this, + std::placeholders::_1, newSocket)); + } + + void I2PControlService::HandleAccept(const boost::system::error_code& ecode, std::shared_ptr socket) + { + if (ecode != boost::asio::error::operation_aborted) + Accept (); + + if (!ecode) + { + LogPrint (eLogInfo, "New I2PControl request from ", socket->remote_endpoint ()); + ReadRequest (socket); + } + else + LogPrint (eLogError, "I2PControl accept error: ", ecode.message ()); + } + + void I2PControlService::ReadRequest (std::shared_ptr socket) + { + auto request = std::make_shared(); + socket->async_read_some (boost::asio::buffer (*request), + std::bind(&I2PControlService::HandleRequestReceived, this, + std::placeholders::_1, std::placeholders::_2, socket, request)); + } + + void I2PControlService::HandleRequestReceived (const boost::system::error_code& ecode, + size_t bytes_transferred, std::shared_ptr socket, + std::shared_ptr buf) + { + if (ecode) + { + LogPrint (eLogError, "I2PControl read error: ", ecode.message ()); + } + else + { + std::stringstream ss; + ss.write (buf->data (), bytes_transferred); + boost::property_tree::ptree pt; + boost::property_tree::read_json (ss, pt); + std::string method = pt.get(I2P_CONTROL_PROPERTY_METHOD); + auto it = m_MethodHanders.find (method); + if (it != m_MethodHanders.end ()) + (this->*(it->second))(); + else + LogPrint (eLogWarning, "Unknown I2PControl method ", method); + } + } } } diff --git a/I2PControl.h b/I2PControl.h index 1c0e90da..38dca6b1 100644 --- a/I2PControl.h +++ b/I2PControl.h @@ -1,18 +1,44 @@ #ifndef I2P_CONTROL_H__ #define I2P_CONTROL_H__ +#include #include +#include +#include +#include +#include #include namespace i2p { namespace client { + const size_t I2P_CONTROL_MAX_REQUEST_SIZE = 1024; + typedef std::array I2PControlBuffer; + + const char I2P_CONTROL_PROPERTY_ID[] = "id"; + const char I2P_CONTROL_PROPERTY_METHOD[] = "method"; + const char I2P_CONTROL_PROPERTY_TOKEN[] = "Token"; + const char I2P_CONTROL_PROPERTY_PARAMS[] = "params"; + class I2PControlService { public: I2PControlService (int port); + ~I2PControlService (); + + void Start (); + void Stop (); + + private: + + void Run (); + void Accept (); + void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr socket); + void ReadRequest (std::shared_ptr socket); + void HandleRequestReceived (const boost::system::error_code& ecode, size_t bytes_transferred, + std::shared_ptr socket, std::shared_ptr buf); private: @@ -20,7 +46,10 @@ namespace client std::thread * m_Thread; boost::asio::io_service m_Service; - boost::asio::ip::tcp::acceptor m_Acceptor; + boost::asio::ip::tcp::acceptor m_Acceptor; + + typedef void (I2PControlService::*MethodHandler)(); + std::map m_MethodHanders; }; } }