diff --git a/I2PControl.cpp b/I2PControl.cpp index 1e8546ac..6d6a5ee3 100644 --- a/I2PControl.cpp +++ b/I2PControl.cpp @@ -2,6 +2,8 @@ #include #include #include +#include +#include #include #include @@ -14,7 +16,6 @@ #include "Crypto.h" #include "FS.h" #include "Log.h" -#include "HTTP.h" #include "Config.h" #include "NetDb.h" #include "RouterContext.h" @@ -189,66 +190,71 @@ namespace client if (ecode) { LogPrint (eLogError, "I2PControl: read error: ", ecode.message ()); return; - } - /* try to parse received data */ - std::stringstream json; - std::string response; - bool isHTTP = false; - if (memcmp (buf->data (), "POST", 4) == 0) { - long int remains = 0; - isHTTP = true; - i2p::http::HTTPReq req; - std::size_t len = req.parse(buf->data(), bytes_transferred); - if (len <= 0) { - LogPrint(eLogError, "I2PControl: incomplete/malformed POST request"); - return; - } - /* append to json chunk of data from 1st request */ - json.write(buf->data() + len, bytes_transferred - len); - remains = req.content_length(); - /* if request has Content-Length header, fetch rest of data and store to json buffer */ - while (remains > 0) { - len = ((long int) buf->size() < remains) ? buf->size() : remains; - bytes_transferred = boost::asio::read (*socket, boost::asio::buffer (buf->data (), len)); - json.write(buf->data(), bytes_transferred); - remains -= bytes_transferred; - } } else { - json.write(buf->data(), bytes_transferred); - } - //LogPrint(eLogDebug, "I2PControl: json from request: ", json.str()); + try + { + bool isHtml = !memcmp (buf->data (), "POST", 4); + std::stringstream ss; + ss.write (buf->data (), bytes_transferred); + if (isHtml) + { + std::string header; + size_t contentLength = 0; + while (!ss.eof () && header != "\r") + { + std::getline(ss, header); + auto colon = header.find (':'); + if (colon != std::string::npos && header.substr (0, colon) == "Content-Length") + contentLength = std::stoi (header.substr (colon + 1)); + } + if (ss.eof ()) + { + LogPrint (eLogError, "I2PControl: malformed request, HTTP header expected"); + return; // TODO: + } + std::streamoff rem = contentLength + ss.tellg () - bytes_transferred; // more bytes to read + if (rem > 0) + { + bytes_transferred = boost::asio::read (*socket, boost::asio::buffer (buf->data (), rem)); + ss.write (buf->data (), bytes_transferred); + } + } + std::ostringstream response; #if GCC47_BOOST149 - LogPrint (eLogError, "I2PControl: json_read is not supported due bug in boost 1.49 with gcc 4.7"); - BuildErrorResponse(response, 32603, "JSON requests is not supported with this version of boost"); + LogPrint (eLogError, "I2PControl: json_read is not supported due bug in boost 1.49 with gcc 4.7"); + response << "{\"id\":null,\"error\":"; + response << "{\"code\":-32603,\"message\":\"JSON requests is not supported with this version of boost\"},"; + response << "\"jsonrpc\":\"2.0\"}"; #else - /* now try to parse json itself */ - std::string j_str = json.str(); - std::stringstream _json(j_str); - try { - boost::property_tree::ptree pt; - boost::property_tree::read_json (_json, pt); - - std::string id = pt.get("id"); - std::string method = pt.get("method"); - auto it = m_MethodHandlers.find (method); - if (it != m_MethodHandlers.end ()) { - std::ostringstream ss; - ss << "{\"id\":" << id << ",\"result\":{"; - (this->*(it->second))(pt.get_child ("params"), ss); - ss << "},\"jsonrpc\":\"2.0\"}"; - response = ss.str(); - } else { - LogPrint (eLogWarning, "I2PControl: unknown method ", method); - BuildErrorResponse(response, 32601, "Method not found"); + boost::property_tree::ptree pt; + boost::property_tree::read_json (ss, pt); + + std::string id = pt.get("id"); + std::string method = pt.get("method"); + auto it = m_MethodHandlers.find (method); + if (it != m_MethodHandlers.end ()) + { + response << "{\"id\":" << id << ",\"result\":{"; + (this->*(it->second))(pt.get_child ("params"), response); + response << "},\"jsonrpc\":\"2.0\"}"; + } else { + LogPrint (eLogWarning, "I2PControl: unknown method ", method); + response << "{\"id\":null,\"error\":"; + response << "{\"code\":-32601,\"message\":\"Method not found\"},"; + response << "\"jsonrpc\":\"2.0\"}"; + } +#endif + SendResponse (socket, buf, response, isHtml); + } + catch (std::exception& ex) + { + LogPrint (eLogError, "I2PControl: exception when handle request: ", ex.what ()); + } + catch (...) + { + LogPrint (eLogError, "I2PControl: handle request unknown exception"); } - } catch (std::exception& ex) { - LogPrint (eLogError, "I2PControl: exception when handle request: ", ex.what ()); - BuildErrorResponse(response, 32603, ex.what()); - } catch (...) { - LogPrint (eLogError, "I2PControl: handle request unknown exception"); } -#endif - SendResponse (socket, buf, response, isHTTP); } void I2PControlService::InsertParam (std::ostringstream& ss, const std::string& name, int value) const @@ -270,28 +276,27 @@ namespace client ss << "\"" << name << "\":" << std::fixed << std::setprecision(2) << value; } - void I2PControlService::BuildErrorResponse (std::string & content, int code, const char *message) { - std::stringstream ss; - ss << "{\"id\":null,\"error\":"; - ss << "{\"code\":" << -code << ",\"message\":\"" << message << "\"},"; - ss << "\"jsonrpc\":\"2.0\"}"; - content = ss.str(); - } - void I2PControlService::SendResponse (std::shared_ptr socket, - std::shared_ptr buf, std::string& content, bool isHTTP) - { - if (isHTTP) { - i2p::http::HTTPRes res; - res.code = 200; - res.add_header("Content-Type", "application/json"); - res.add_header("Connection", "close"); - res.body = content; - std::string tmp = res.to_string(); - content = tmp; + std::shared_ptr buf, std::ostringstream& response, bool isHtml) + { + size_t len = response.str ().length (), offset = 0; + if (isHtml) + { + std::ostringstream header; + header << "HTTP/1.1 200 OK\r\n"; + header << "Connection: close\r\n"; + header << "Content-Length: " << boost::lexical_cast(len) << "\r\n"; + header << "Content-Type: application/json\r\n"; + header << "Date: "; + auto facet = new boost::local_time::local_time_facet ("%a, %d %b %Y %H:%M:%S GMT"); + header.imbue(std::locale (header.getloc(), facet)); + header << boost::posix_time::second_clock::local_time() << "\r\n"; + header << "\r\n"; + offset = header.str ().size (); + memcpy (buf->data (), header.str ().c_str (), offset); } - std::copy(content.begin(), content.end(), buf->begin()); - boost::asio::async_write (*socket, boost::asio::buffer (buf->data (), content.length()), + memcpy (buf->data () + offset, response.str ().c_str (), len); + boost::asio::async_write (*socket, boost::asio::buffer (buf->data (), offset + len), boost::asio::transfer_all (), std::bind(&I2PControlService::HandleResponseSent, this, std::placeholders::_1, std::placeholders::_2, socket, buf)); @@ -318,7 +323,7 @@ namespace client } InsertParam (results, "API", api); results << ","; - std::string token = std::to_string(i2p::util::GetSecondsSinceEpoch ()); + std::string token = boost::lexical_cast(i2p::util::GetSecondsSinceEpoch ()); m_Tokens.insert (token); InsertParam (results, "Token", token); } @@ -344,10 +349,9 @@ namespace client (this->*(it1->second))(it.second.data ()); InsertParam (results, it.first, ""); } - else { + else LogPrint (eLogError, "I2PControl: I2PControl unknown request: ", it.first); - } - } + } } void I2PControlService::PasswordHandler (const std::string& value) @@ -361,20 +365,17 @@ namespace client void I2PControlService::RouterInfoHandler (const boost::property_tree::ptree& params, std::ostringstream& results) { - for (auto it = params.begin (); it != params.end (); ++it) + for (auto it = params.begin (); it != params.end (); it++) { LogPrint (eLogDebug, "I2PControl: RouterInfo request: ", it->first); - if (it != params.begin ()) results << ","; auto it1 = m_RouterInfoHandlers.find (it->first); if (it1 != m_RouterInfoHandlers.end ()) { + if (it != params.begin ()) results << ","; (this->*(it1->second))(results); } else - { - InsertParam(results, it->first, ""); LogPrint (eLogError, "I2PControl: RouterInfo unknown request ", it->first); - } } } @@ -440,20 +441,15 @@ namespace client void I2PControlService::RouterManagerHandler (const boost::property_tree::ptree& params, std::ostringstream& results) { - for (auto it = params.begin (); it != params.end (); ++it) + for (auto it = params.begin (); it != params.end (); it++) { - if (it != params.begin ()) results << ","; + if (it != params.begin ()) results << ","; LogPrint (eLogDebug, "I2PControl: RouterManager request: ", it->first); auto it1 = m_RouterManagerHandlers.find (it->first); - if (it1 != m_RouterManagerHandlers.end ()) - { - (this->*(it1->second))(results); - } - else - { - InsertParam(results, it->first, ""); + if (it1 != m_RouterManagerHandlers.end ()) { + (this->*(it1->second))(results); + } else LogPrint (eLogError, "I2PControl: RouterManager unknown request: ", it->first); - } } } @@ -494,20 +490,15 @@ namespace client // network setting void I2PControlService::NetworkSettingHandler (const boost::property_tree::ptree& params, std::ostringstream& results) { - for (auto it = params.begin (); it != params.end (); ++it) + for (auto it = params.begin (); it != params.end (); it++) { if (it != params.begin ()) results << ","; LogPrint (eLogDebug, "I2PControl: NetworkSetting request: ", it->first); auto it1 = m_NetworkSettingHandlers.find (it->first); - if (it1 != m_NetworkSettingHandlers.end ()) - { - (this->*(it1->second))(it->second.data (), results); - } - else - { - InsertParam(results, it->first, ""); + if (it1 != m_NetworkSettingHandlers.end ()) { + (this->*(it1->second))(it->second.data (), results); + } else LogPrint (eLogError, "I2PControl: NetworkSetting unknown request: ", it->first); - } } } diff --git a/I2PControl.h b/I2PControl.h index bd5b9bad..047c2fe2 100644 --- a/I2PControl.h +++ b/I2PControl.h @@ -45,9 +45,8 @@ namespace client 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); - void BuildErrorResponse (std::string & content, int code, const char *message); void SendResponse (std::shared_ptr socket, - std::shared_ptr buf, std::string& response, bool isHtml); + std::shared_ptr buf, std::ostringstream& response, bool isHtml); void HandleResponseSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, std::shared_ptr socket, std::shared_ptr buf); @@ -120,4 +119,3 @@ namespace client } #endif -