mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
Reorganize source file hierarchy:
* Rename unity files * Move some modules to new subdirectories * Remove obsolete Visual Studio project files * Remove obsolete coding style and TODO list
This commit is contained in:
621
src/ripple/module/net/basics/HTTPClient.cpp
Normal file
621
src/ripple/module/net/basics/HTTPClient.cpp
Normal file
@@ -0,0 +1,621 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <boost/regex.hpp>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
//
|
||||
// Fetch a web page via http or https.
|
||||
//
|
||||
|
||||
SETUP_LOG (HTTPClient)
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class HTTPClientSSLContext
|
||||
{
|
||||
public:
|
||||
HTTPClientSSLContext ()
|
||||
: m_context (boost::asio::ssl::context::sslv23)
|
||||
{
|
||||
boost::system::error_code ec;
|
||||
|
||||
if (getConfig().SSL_VERIFY_FILE.empty ())
|
||||
{
|
||||
m_context.set_default_verify_paths (ec);
|
||||
|
||||
if (ec && getConfig().SSL_VERIFY_DIR.empty ())
|
||||
throw std::runtime_error (boost::str (
|
||||
boost::format ("Failed to set_default_verify_paths: %s") % ec.message ()));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_context.load_verify_file (getConfig().SSL_VERIFY_FILE);
|
||||
}
|
||||
|
||||
if (! getConfig().SSL_VERIFY_DIR.empty ())
|
||||
{
|
||||
m_context.add_verify_path (getConfig().SSL_VERIFY_DIR, ec);
|
||||
|
||||
if (ec)
|
||||
throw std::runtime_error (boost::str (
|
||||
boost::format ("Failed to add verify path: %s") % ec.message ()));
|
||||
}
|
||||
}
|
||||
|
||||
boost::asio::ssl::context& context()
|
||||
{
|
||||
return m_context;
|
||||
}
|
||||
|
||||
private:
|
||||
boost::asio::ssl::context m_context;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// VFALCO NOTE I moved the SSL_CONTEXT out of the Config and into this
|
||||
// singleton to eliminate the asio dependency in the headers.
|
||||
//
|
||||
void HTTPClient::initializeSSLContext ()
|
||||
{
|
||||
beast::SharedSingleton <HTTPClientSSLContext>::get();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class HTTPClientImp
|
||||
: public boost::enable_shared_from_this <HTTPClientImp>
|
||||
, public HTTPClient
|
||||
, beast::LeakChecked <HTTPClientImp>
|
||||
{
|
||||
public:
|
||||
HTTPClientImp (boost::asio::io_service& io_service,
|
||||
const unsigned short port,
|
||||
std::size_t responseMax)
|
||||
: mSocket (io_service, beast::SharedSingleton <HTTPClientSSLContext>::get()->context())
|
||||
, mResolver (io_service)
|
||||
, mHeader (maxClientHeaderBytes)
|
||||
, mPort (port)
|
||||
, mResponseMax (responseMax)
|
||||
, mDeadline (io_service)
|
||||
{
|
||||
if (!getConfig ().SSL_VERIFY)
|
||||
mSocket.SSLSocket ().set_verify_mode (boost::asio::ssl::verify_none);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
void makeGet (const std::string& strPath, boost::asio::streambuf& sb,
|
||||
const std::string& strHost)
|
||||
{
|
||||
std::ostream osRequest (&sb);
|
||||
|
||||
osRequest <<
|
||||
"GET " << strPath << " HTTP/1.0\r\n"
|
||||
"Host: " << strHost << "\r\n"
|
||||
"Accept: */*\r\n" // YYY Do we need this line?
|
||||
"Connection: close\r\n\r\n";
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
void request (
|
||||
bool bSSL,
|
||||
std::deque<std::string> deqSites,
|
||||
std::function<void (boost::asio::streambuf& sb, const std::string& strHost)> build,
|
||||
boost::posix_time::time_duration timeout,
|
||||
std::function<bool (const boost::system::error_code& ecResult,
|
||||
int iStatus, const std::string& strData)> complete)
|
||||
{
|
||||
mSSL = bSSL;
|
||||
mDeqSites = deqSites;
|
||||
mBuild = build;
|
||||
mComplete = complete;
|
||||
mTimeout = timeout;
|
||||
|
||||
httpsNext ();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
void get (
|
||||
bool bSSL,
|
||||
std::deque<std::string> deqSites,
|
||||
const std::string& strPath,
|
||||
boost::posix_time::time_duration timeout,
|
||||
std::function<bool (const boost::system::error_code& ecResult, int iStatus,
|
||||
const std::string& strData)> complete)
|
||||
{
|
||||
|
||||
mComplete = complete;
|
||||
mTimeout = timeout;
|
||||
|
||||
request (
|
||||
bSSL,
|
||||
deqSites,
|
||||
std::bind (&HTTPClientImp::makeGet, shared_from_this (), strPath,
|
||||
std::placeholders::_1, std::placeholders::_2),
|
||||
timeout,
|
||||
complete);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
void httpsNext ()
|
||||
{
|
||||
WriteLog (lsTRACE, HTTPClient) << "Fetch: " << mDeqSites[0];
|
||||
|
||||
boost::shared_ptr <boost::asio::ip::tcp::resolver::query> query (
|
||||
new boost::asio::ip::tcp::resolver::query (
|
||||
mDeqSites[0],
|
||||
beast::lexicalCast <std::string> (mPort),
|
||||
boost::asio::ip::resolver_query_base::numeric_service));
|
||||
mQuery = query;
|
||||
|
||||
mDeadline.expires_from_now (mTimeout, mShutdown);
|
||||
|
||||
WriteLog (lsTRACE, HTTPClient) << "expires_from_now: " << mShutdown.message ();
|
||||
|
||||
if (!mShutdown)
|
||||
{
|
||||
mDeadline.async_wait (
|
||||
boost::bind (
|
||||
&HTTPClientImp::handleDeadline,
|
||||
shared_from_this (),
|
||||
boost::asio::placeholders::error));
|
||||
}
|
||||
|
||||
if (!mShutdown)
|
||||
{
|
||||
WriteLog (lsTRACE, HTTPClient) << "Resolving: " << mDeqSites[0];
|
||||
|
||||
mResolver.async_resolve (*mQuery,
|
||||
boost::bind (
|
||||
&HTTPClientImp::handleResolve,
|
||||
shared_from_this (),
|
||||
boost::asio::placeholders::error,
|
||||
boost::asio::placeholders::iterator));
|
||||
}
|
||||
|
||||
if (mShutdown)
|
||||
invokeComplete (mShutdown);
|
||||
}
|
||||
|
||||
void handleDeadline (const boost::system::error_code& ecResult)
|
||||
{
|
||||
if (ecResult == boost::asio::error::operation_aborted)
|
||||
{
|
||||
// Timer canceled because deadline no longer needed.
|
||||
WriteLog (lsTRACE, HTTPClient) << "Deadline cancelled.";
|
||||
|
||||
nothing (); // Aborter is done.
|
||||
}
|
||||
else if (ecResult)
|
||||
{
|
||||
WriteLog (lsTRACE, HTTPClient) << "Deadline error: " << mDeqSites[0] << ": " << ecResult.message ();
|
||||
|
||||
// Can't do anything sound.
|
||||
abort ();
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLog (lsTRACE, HTTPClient) << "Deadline arrived.";
|
||||
|
||||
// Mark us as shutting down.
|
||||
// XXX Use our own error code.
|
||||
mShutdown = boost::system::error_code (boost::system::errc::bad_address, boost::system::system_category ());
|
||||
|
||||
// Cancel any resolving.
|
||||
mResolver.cancel ();
|
||||
|
||||
// Stop the transaction.
|
||||
mSocket.async_shutdown (boost::bind (
|
||||
&HTTPClientImp::handleShutdown,
|
||||
shared_from_this (),
|
||||
boost::asio::placeholders::error));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void handleShutdown (
|
||||
const boost::system::error_code& ecResult
|
||||
)
|
||||
{
|
||||
if (ecResult)
|
||||
{
|
||||
WriteLog (lsTRACE, HTTPClient) << "Shutdown error: " << mDeqSites[0] << ": " << ecResult.message ();
|
||||
}
|
||||
}
|
||||
|
||||
void handleResolve (
|
||||
const boost::system::error_code& ecResult,
|
||||
boost::asio::ip::tcp::resolver::iterator itrEndpoint
|
||||
)
|
||||
{
|
||||
if (!mShutdown)
|
||||
mShutdown = ecResult;
|
||||
|
||||
if (mShutdown)
|
||||
{
|
||||
WriteLog (lsTRACE, HTTPClient) << "Resolve error: " << mDeqSites[0] << ": " << mShutdown.message ();
|
||||
|
||||
invokeComplete (mShutdown);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLog (lsTRACE, HTTPClient) << "Resolve complete.";
|
||||
|
||||
boost::asio::async_connect (
|
||||
mSocket.lowest_layer (),
|
||||
itrEndpoint,
|
||||
boost::bind (
|
||||
&HTTPClientImp::handleConnect,
|
||||
shared_from_this (),
|
||||
boost::asio::placeholders::error));
|
||||
}
|
||||
}
|
||||
|
||||
void handleConnect (const boost::system::error_code& ecResult)
|
||||
{
|
||||
if (!mShutdown)
|
||||
mShutdown = ecResult;
|
||||
|
||||
if (mShutdown)
|
||||
{
|
||||
WriteLog (lsTRACE, HTTPClient) << "Connect error: " << mShutdown.message ();
|
||||
}
|
||||
|
||||
if (!mShutdown)
|
||||
{
|
||||
WriteLog (lsTRACE, HTTPClient) << "Connected.";
|
||||
|
||||
if (getConfig ().SSL_VERIFY)
|
||||
{
|
||||
mShutdown = mSocket.verify (mDeqSites[0]);
|
||||
|
||||
if (mShutdown)
|
||||
{
|
||||
WriteLog (lsTRACE, HTTPClient) << "set_verify_callback: " << mDeqSites[0] << ": " << mShutdown.message ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mShutdown)
|
||||
{
|
||||
invokeComplete (mShutdown);
|
||||
}
|
||||
else if (mSSL)
|
||||
{
|
||||
mSocket.async_handshake (
|
||||
AutoSocket::ssl_socket::client,
|
||||
boost::bind (
|
||||
&HTTPClientImp::handleRequest,
|
||||
shared_from_this (),
|
||||
boost::asio::placeholders::error));
|
||||
}
|
||||
else
|
||||
{
|
||||
handleRequest (ecResult);
|
||||
}
|
||||
}
|
||||
|
||||
void handleRequest (const boost::system::error_code& ecResult)
|
||||
{
|
||||
if (!mShutdown)
|
||||
mShutdown = ecResult;
|
||||
|
||||
if (mShutdown)
|
||||
{
|
||||
WriteLog (lsTRACE, HTTPClient) << "Handshake error:" << mShutdown.message ();
|
||||
|
||||
invokeComplete (mShutdown);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLog (lsTRACE, HTTPClient) << "Session started.";
|
||||
|
||||
mBuild (mRequest, mDeqSites[0]);
|
||||
|
||||
mSocket.async_write (
|
||||
mRequest,
|
||||
boost::bind (&HTTPClientImp::handleWrite,
|
||||
shared_from_this (),
|
||||
boost::asio::placeholders::error,
|
||||
boost::asio::placeholders::bytes_transferred));
|
||||
}
|
||||
}
|
||||
|
||||
void handleWrite (const boost::system::error_code& ecResult, std::size_t bytes_transferred)
|
||||
{
|
||||
if (!mShutdown)
|
||||
mShutdown = ecResult;
|
||||
|
||||
if (mShutdown)
|
||||
{
|
||||
WriteLog (lsTRACE, HTTPClient) << "Write error: " << mShutdown.message ();
|
||||
|
||||
invokeComplete (mShutdown);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLog (lsTRACE, HTTPClient) << "Wrote.";
|
||||
|
||||
mSocket.async_read_until (
|
||||
mHeader,
|
||||
"\r\n\r\n",
|
||||
boost::bind (&HTTPClientImp::handleHeader,
|
||||
shared_from_this (),
|
||||
boost::asio::placeholders::error,
|
||||
boost::asio::placeholders::bytes_transferred));
|
||||
}
|
||||
}
|
||||
|
||||
void handleHeader (const boost::system::error_code& ecResult, std::size_t bytes_transferred)
|
||||
{
|
||||
std::string strHeader ((std::istreambuf_iterator<char> (&mHeader)), std::istreambuf_iterator<char> ());
|
||||
WriteLog (lsTRACE, HTTPClient) << "Header: \"" << strHeader << "\"";
|
||||
|
||||
static boost::regex reStatus ("\\`HTTP/1\\S+ (\\d{3}) .*\\'"); // HTTP/1.1 200 OK
|
||||
static boost::regex reSize ("\\`.*\\r\\nContent-Length:\\s+([0-9]+).*\\'");
|
||||
static boost::regex reBody ("\\`.*\\r\\n\\r\\n(.*)\\'");
|
||||
|
||||
boost::smatch smMatch;
|
||||
|
||||
bool bMatch = boost::regex_match (strHeader, smMatch, reStatus); // Match status code.
|
||||
|
||||
if (!bMatch)
|
||||
{
|
||||
// XXX Use our own error code.
|
||||
WriteLog (lsTRACE, HTTPClient) << "No status code";
|
||||
invokeComplete (boost::system::error_code (boost::system::errc::bad_address, boost::system::system_category ()));
|
||||
return;
|
||||
}
|
||||
|
||||
mStatus = beast::lexicalCastThrow <int> (std::string (smMatch[1]));
|
||||
|
||||
if (boost::regex_match (strHeader, smMatch, reBody)) // we got some body
|
||||
mBody = smMatch[1];
|
||||
|
||||
if (boost::regex_match (strHeader, smMatch, reSize))
|
||||
{
|
||||
int size = beast::lexicalCastThrow <int> (std::string(smMatch[1]));
|
||||
|
||||
if (size < mResponseMax)
|
||||
mResponseMax = size;
|
||||
}
|
||||
|
||||
if (mResponseMax == 0)
|
||||
{
|
||||
// no body wanted or available
|
||||
invokeComplete (ecResult, mStatus);
|
||||
}
|
||||
else if (mBody.size () >= mResponseMax)
|
||||
{
|
||||
// we got the whole thing
|
||||
invokeComplete (ecResult, mStatus, mBody);
|
||||
}
|
||||
else
|
||||
{
|
||||
mSocket.async_read (
|
||||
mResponse.prepare (mResponseMax - mBody.size ()),
|
||||
boost::asio::transfer_all (),
|
||||
boost::bind (&HTTPClientImp::handleData,
|
||||
shared_from_this (),
|
||||
boost::asio::placeholders::error,
|
||||
boost::asio::placeholders::bytes_transferred));
|
||||
}
|
||||
}
|
||||
|
||||
void handleData (const boost::system::error_code& ecResult, std::size_t bytes_transferred)
|
||||
{
|
||||
if (!mShutdown)
|
||||
mShutdown = ecResult;
|
||||
|
||||
if (mShutdown && mShutdown != boost::asio::error::eof)
|
||||
{
|
||||
WriteLog (lsTRACE, HTTPClient) << "Read error: " << mShutdown.message ();
|
||||
|
||||
invokeComplete (mShutdown);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mShutdown)
|
||||
{
|
||||
WriteLog (lsTRACE, HTTPClient) << "Complete.";
|
||||
|
||||
nothing ();
|
||||
}
|
||||
else
|
||||
{
|
||||
mResponse.commit (bytes_transferred);
|
||||
std::string strBody ((std::istreambuf_iterator<char> (&mResponse)), std::istreambuf_iterator<char> ());
|
||||
invokeComplete (ecResult, mStatus, mBody + strBody);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Call cancel the deadline timer and invoke the completion routine.
|
||||
void invokeComplete (const boost::system::error_code& ecResult, int iStatus = 0, const std::string& strData = "")
|
||||
{
|
||||
boost::system::error_code ecCancel;
|
||||
|
||||
(void) mDeadline.cancel (ecCancel);
|
||||
|
||||
if (ecCancel)
|
||||
{
|
||||
WriteLog (lsTRACE, HTTPClient) << "invokeComplete: Deadline cancel error: " << ecCancel.message ();
|
||||
}
|
||||
|
||||
WriteLog (lsDEBUG, HTTPClient) << "invokeComplete: Deadline popping: " << mDeqSites.size ();
|
||||
|
||||
if (!mDeqSites.empty ())
|
||||
{
|
||||
mDeqSites.pop_front ();
|
||||
}
|
||||
|
||||
bool bAgain = true;
|
||||
|
||||
if (mDeqSites.empty () || !ecResult)
|
||||
{
|
||||
// ecResult: !0 = had an error, last entry
|
||||
// iStatus: result, if no error
|
||||
// strData: data, if no error
|
||||
bAgain = mComplete && mComplete (ecResult ? ecResult : ecCancel, iStatus, strData);
|
||||
}
|
||||
|
||||
if (!mDeqSites.empty () && bAgain)
|
||||
{
|
||||
httpsNext ();
|
||||
}
|
||||
}
|
||||
|
||||
static bool onSMSResponse (const boost::system::error_code& ecResult, int iStatus, const std::string& strData)
|
||||
{
|
||||
WriteLog (lsINFO, HTTPClient) << "SMS: Response:" << iStatus << " :" << strData;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
typedef boost::shared_ptr<HTTPClient> pointer;
|
||||
|
||||
bool mSSL;
|
||||
AutoSocket mSocket;
|
||||
boost::asio::ip::tcp::resolver mResolver;
|
||||
boost::shared_ptr<boost::asio::ip::tcp::resolver::query> mQuery;
|
||||
boost::asio::streambuf mRequest;
|
||||
boost::asio::streambuf mHeader;
|
||||
boost::asio::streambuf mResponse;
|
||||
std::string mBody;
|
||||
const unsigned short mPort;
|
||||
int mResponseMax;
|
||||
int mStatus;
|
||||
std::function<void (boost::asio::streambuf& sb, const std::string& strHost)> mBuild;
|
||||
std::function<bool (const boost::system::error_code& ecResult, int iStatus, const std::string& strData)> mComplete;
|
||||
|
||||
boost::asio::deadline_timer mDeadline;
|
||||
|
||||
// If not success, we are shutting down.
|
||||
boost::system::error_code mShutdown;
|
||||
|
||||
std::deque<std::string> mDeqSites;
|
||||
boost::posix_time::time_duration mTimeout;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void HTTPClient::get (
|
||||
bool bSSL,
|
||||
boost::asio::io_service& io_service,
|
||||
std::deque<std::string> deqSites,
|
||||
const unsigned short port,
|
||||
const std::string& strPath,
|
||||
std::size_t responseMax,
|
||||
boost::posix_time::time_duration timeout,
|
||||
std::function<bool (const boost::system::error_code& ecResult, int iStatus,
|
||||
const std::string& strData)> complete)
|
||||
{
|
||||
boost::shared_ptr <HTTPClientImp> client (
|
||||
new HTTPClientImp (io_service, port, responseMax));
|
||||
|
||||
client->get (bSSL, deqSites, strPath, timeout, complete);
|
||||
}
|
||||
|
||||
void HTTPClient::get (
|
||||
bool bSSL,
|
||||
boost::asio::io_service& io_service,
|
||||
std::string strSite,
|
||||
const unsigned short port,
|
||||
const std::string& strPath,
|
||||
std::size_t responseMax,
|
||||
boost::posix_time::time_duration timeout,
|
||||
std::function<bool (const boost::system::error_code& ecResult, int iStatus,
|
||||
const std::string& strData)> complete)
|
||||
{
|
||||
std::deque<std::string> deqSites (1, strSite);
|
||||
|
||||
boost::shared_ptr <HTTPClientImp> client (
|
||||
new HTTPClientImp (io_service, port, responseMax));
|
||||
|
||||
client->get (bSSL, deqSites, strPath, timeout, complete);
|
||||
}
|
||||
|
||||
void HTTPClient::request (
|
||||
bool bSSL,
|
||||
boost::asio::io_service& io_service,
|
||||
std::string strSite,
|
||||
const unsigned short port,
|
||||
std::function<void (boost::asio::streambuf& sb, const std::string& strHost)> setRequest,
|
||||
std::size_t responseMax,
|
||||
boost::posix_time::time_duration timeout,
|
||||
std::function<bool (const boost::system::error_code& ecResult, int iStatus,
|
||||
const std::string& strData)> complete)
|
||||
{
|
||||
std::deque<std::string> deqSites (1, strSite);
|
||||
|
||||
boost::shared_ptr <HTTPClientImp> client (
|
||||
new HTTPClientImp (io_service, port, responseMax));
|
||||
|
||||
client->request (bSSL, deqSites, setRequest, timeout, complete);
|
||||
}
|
||||
|
||||
void HTTPClient::sendSMS (boost::asio::io_service& io_service, const std::string& strText)
|
||||
{
|
||||
std::string strScheme;
|
||||
std::string strDomain;
|
||||
int iPort;
|
||||
std::string strPath;
|
||||
|
||||
if (getConfig ().SMS_URL == "" || !parseUrl (getConfig ().SMS_URL, strScheme, strDomain, iPort, strPath))
|
||||
{
|
||||
WriteLog (lsWARNING, HTTPClient) << "SMSRequest: Bad URL:" << getConfig ().SMS_URL;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool const bSSL = strScheme == "https";
|
||||
|
||||
std::deque<std::string> deqSites (1, strDomain);
|
||||
std::string strURI =
|
||||
boost::str (boost::format ("%s?from=%s&to=%s&api_key=%s&api_secret=%s&text=%s")
|
||||
% (strPath.empty () ? "/" : strPath)
|
||||
% getConfig ().SMS_FROM
|
||||
% getConfig ().SMS_TO
|
||||
% getConfig ().SMS_KEY
|
||||
% getConfig ().SMS_SECRET
|
||||
% urlEncode (strText));
|
||||
|
||||
// WriteLog (lsINFO) << "SMS: Request:" << strURI;
|
||||
WriteLog (lsINFO, HTTPClient) << "SMS: Request: '" << strText << "'";
|
||||
|
||||
if (iPort < 0)
|
||||
iPort = bSSL ? 443 : 80;
|
||||
|
||||
boost::shared_ptr <HTTPClientImp> client (
|
||||
new HTTPClientImp (io_service, iPort, maxClientHeaderBytes));
|
||||
|
||||
client->get (bSSL, deqSites, strURI, boost::posix_time::seconds (smsTimeoutSeconds),
|
||||
std::bind (&HTTPClientImp::onSMSResponse,
|
||||
std::placeholders::_1, std::placeholders::_2,
|
||||
std::placeholders::_3));
|
||||
}
|
||||
}
|
||||
|
||||
} // ripple
|
||||
77
src/ripple/module/net/basics/HTTPClient.h
Normal file
77
src/ripple/module/net/basics/HTTPClient.h
Normal file
@@ -0,0 +1,77 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_NET_BASICS_HTTPCLIENT_H_INCLUDED
|
||||
#define RIPPLE_NET_BASICS_HTTPCLIENT_H_INCLUDED
|
||||
|
||||
namespace ripple {
|
||||
|
||||
/** Provides an asynchronous HTTP client implementation with optional SSL.
|
||||
*/
|
||||
class HTTPClient
|
||||
{
|
||||
public:
|
||||
enum
|
||||
{
|
||||
maxClientHeaderBytes = 32 * 1024
|
||||
};
|
||||
|
||||
static void initializeSSLContext ();
|
||||
|
||||
static void get (
|
||||
bool bSSL,
|
||||
boost::asio::io_service& io_service,
|
||||
std::deque <std::string> deqSites,
|
||||
const unsigned short port,
|
||||
const std::string& strPath,
|
||||
std::size_t responseMax,
|
||||
boost::posix_time::time_duration timeout,
|
||||
std::function <bool (const boost::system::error_code& ecResult, int iStatus, const std::string& strData)> complete);
|
||||
|
||||
static void get (
|
||||
bool bSSL,
|
||||
boost::asio::io_service& io_service,
|
||||
std::string strSite,
|
||||
const unsigned short port,
|
||||
const std::string& strPath,
|
||||
std::size_t responseMax,
|
||||
boost::posix_time::time_duration timeout,
|
||||
std::function <bool (const boost::system::error_code& ecResult, int iStatus, const std::string& strData)> complete);
|
||||
|
||||
static void request (
|
||||
bool bSSL,
|
||||
boost::asio::io_service& io_service,
|
||||
std::string strSite,
|
||||
const unsigned short port,
|
||||
std::function <void (boost::asio::streambuf& sb, const std::string& strHost)> build,
|
||||
std::size_t responseMax,
|
||||
boost::posix_time::time_duration timeout,
|
||||
std::function <bool (const boost::system::error_code& ecResult, int iStatus, const std::string& strData)> complete);
|
||||
|
||||
enum
|
||||
{
|
||||
smsTimeoutSeconds = 30
|
||||
};
|
||||
|
||||
static void sendSMS (boost::asio::io_service& io_service, const std::string& strText);
|
||||
};
|
||||
|
||||
} // ripple
|
||||
|
||||
#endif
|
||||
133
src/ripple/module/net/basics/HTTPRequest.cpp
Normal file
133
src/ripple/module/net/basics/HTTPRequest.cpp
Normal file
@@ -0,0 +1,133 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <beast/module/core/text/LexicalCast.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
SETUP_LOG (HTTPRequest)
|
||||
|
||||
// Logic to handle incoming HTTP reqests
|
||||
|
||||
void HTTPRequest::reset ()
|
||||
{
|
||||
mHeaders.clear ();
|
||||
sRequestBody.clear ();
|
||||
sAuthorization.clear ();
|
||||
iDataSize = 0;
|
||||
bShouldClose = true;
|
||||
eState = await_request;
|
||||
}
|
||||
|
||||
HTTPRequest::Action HTTPRequest::requestDone (bool forceClose)
|
||||
{
|
||||
if (forceClose || bShouldClose)
|
||||
return haCLOSE_CONN;
|
||||
|
||||
reset ();
|
||||
return haREAD_LINE;
|
||||
}
|
||||
|
||||
std::string HTTPRequest::getReplyHeaders (bool forceClose)
|
||||
{
|
||||
if (forceClose || bShouldClose)
|
||||
return "Connection: close\r\n";
|
||||
else
|
||||
return "Connection: Keep-Alive\r\n";
|
||||
}
|
||||
|
||||
HTTPRequest::Action HTTPRequest::consume (boost::asio::streambuf& buf)
|
||||
{
|
||||
std::string line;
|
||||
std::istream is (&buf);
|
||||
std::getline (is, line);
|
||||
boost::trim (line);
|
||||
|
||||
// WriteLog (lsTRACE, HTTPRequest) << "HTTPRequest line: " << line;
|
||||
|
||||
if (eState == await_request)
|
||||
{
|
||||
// VERB URL PROTO
|
||||
if (line.empty ())
|
||||
return haREAD_LINE;
|
||||
|
||||
sRequest = line;
|
||||
bShouldClose = sRequest.find ("HTTP/1.1") == std::string::npos;
|
||||
|
||||
eState = await_header;
|
||||
return haREAD_LINE;
|
||||
}
|
||||
|
||||
if (eState == await_header)
|
||||
{
|
||||
// HEADER_NAME: HEADER_BODY
|
||||
if (line.empty ()) // empty line or bare \r
|
||||
{
|
||||
if (iDataSize == 0)
|
||||
{
|
||||
// no body
|
||||
eState = do_request;
|
||||
return haDO_REQUEST;
|
||||
}
|
||||
|
||||
eState = getting_body;
|
||||
return haREAD_RAW;
|
||||
}
|
||||
|
||||
size_t colon = line.find (':');
|
||||
|
||||
if (colon != std::string::npos)
|
||||
{
|
||||
std::string headerName = line.substr (0, colon);
|
||||
boost::trim (headerName);
|
||||
boost::to_lower (headerName);
|
||||
|
||||
std::string headerValue = line.substr (colon + 1);
|
||||
boost::trim (headerValue);
|
||||
|
||||
mHeaders[headerName] += headerValue;
|
||||
|
||||
if (headerName == "connection")
|
||||
{
|
||||
boost::to_lower (headerValue);
|
||||
|
||||
if ((headerValue == "keep-alive") || (headerValue == "keepalive"))
|
||||
bShouldClose = false;
|
||||
|
||||
if (headerValue == "close")
|
||||
bShouldClose = true;
|
||||
}
|
||||
|
||||
if (headerName == "content-length")
|
||||
iDataSize = beast::lexicalCastThrow <int> (headerValue);
|
||||
|
||||
if (headerName == "authorization")
|
||||
sAuthorization = headerValue;
|
||||
}
|
||||
|
||||
return haREAD_LINE;
|
||||
}
|
||||
|
||||
assert (false);
|
||||
return haERROR;
|
||||
}
|
||||
|
||||
} // ripple
|
||||
106
src/ripple/module/net/basics/HTTPRequest.h
Normal file
106
src/ripple/module/net/basics/HTTPRequest.h
Normal file
@@ -0,0 +1,106 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_NET_BASICS_HTTPREQUEST_H_INCLUDED
|
||||
#define RIPPLE_NET_BASICS_HTTPREQUEST_H_INCLUDED
|
||||
|
||||
namespace ripple {
|
||||
|
||||
/** An HTTP request we are handling from a client. */
|
||||
class HTTPRequest
|
||||
{
|
||||
public:
|
||||
enum Action
|
||||
{
|
||||
// What the application code needs to do
|
||||
haERROR = 0,
|
||||
haREAD_LINE = 1,
|
||||
haREAD_RAW = 2,
|
||||
haDO_REQUEST = 3,
|
||||
haCLOSE_CONN = 4
|
||||
};
|
||||
|
||||
HTTPRequest () : eState (await_request), iDataSize (0), bShouldClose (true)
|
||||
{
|
||||
;
|
||||
}
|
||||
void reset ();
|
||||
|
||||
std::string& peekBody ()
|
||||
{
|
||||
return sRequestBody;
|
||||
}
|
||||
std::string getBody ()
|
||||
{
|
||||
return sRequestBody;
|
||||
}
|
||||
std::string& peekRequest ()
|
||||
{
|
||||
return sRequest;
|
||||
}
|
||||
std::string getRequest ()
|
||||
{
|
||||
return sRequest;
|
||||
}
|
||||
std::string& peekAuth ()
|
||||
{
|
||||
return sAuthorization;
|
||||
}
|
||||
std::string getAuth ()
|
||||
{
|
||||
return sAuthorization;
|
||||
}
|
||||
|
||||
std::map<std::string, std::string>& peekHeaders ()
|
||||
{
|
||||
return mHeaders;
|
||||
}
|
||||
std::string getReplyHeaders (bool forceClose);
|
||||
|
||||
Action consume (boost::asio::streambuf&);
|
||||
Action requestDone (bool forceClose); // call after reply is sent
|
||||
|
||||
int getDataSize ()
|
||||
{
|
||||
return iDataSize;
|
||||
}
|
||||
|
||||
private:
|
||||
enum state
|
||||
{
|
||||
await_request, // We are waiting for the request line
|
||||
await_header, // We are waiting for request headers
|
||||
getting_body, // We are waiting for the body
|
||||
do_request, // We are waiting for the request to complete
|
||||
};
|
||||
|
||||
state eState;
|
||||
std::string sRequest; // VERB URL PROTO
|
||||
std::string sRequestBody;
|
||||
std::string sAuthorization;
|
||||
|
||||
std::map<std::string, std::string> mHeaders;
|
||||
|
||||
int iDataSize;
|
||||
bool bShouldClose;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
143
src/ripple/module/net/basics/RPCDoor.cpp
Normal file
143
src/ripple/module/net/basics/RPCDoor.cpp
Normal file
@@ -0,0 +1,143 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/common/RippleSSLContext.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
SETUP_LOG (RPCDoor)
|
||||
|
||||
class RPCDoorImp : public RPCDoor, public beast::LeakChecked <RPCDoorImp>
|
||||
{
|
||||
public:
|
||||
RPCDoorImp (boost::asio::io_service& io_service, RPCServer::Handler& handler)
|
||||
: m_rpcServerHandler (handler)
|
||||
, mAcceptor (io_service,
|
||||
boost::asio::ip::tcp::endpoint (boost::asio::ip::address::from_string (getConfig ().getRpcIP ()), getConfig ().getRpcPort ()))
|
||||
, mDelayTimer (io_service)
|
||||
, m_sslContext ((getConfig ().RPC_SECURE == 0) ?
|
||||
RippleSSLContext::createBare () :
|
||||
RippleSSLContext::createAuthenticated (
|
||||
getConfig ().RPC_SSL_KEY,
|
||||
getConfig ().RPC_SSL_CERT,
|
||||
getConfig ().RPC_SSL_CHAIN))
|
||||
{
|
||||
WriteLog (lsINFO, RPCDoor) << "RPC port: " << getConfig ().getRpcAddress().toRawUTF8() << " allow remote: " << getConfig ().RPC_ALLOW_REMOTE;
|
||||
|
||||
startListening ();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
~RPCDoorImp ()
|
||||
{
|
||||
WriteLog (lsINFO, RPCDoor) <<
|
||||
"RPC port: " << getConfig ().getRpcAddress().toRawUTF8() <<
|
||||
" allow remote: " << getConfig ().RPC_ALLOW_REMOTE;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
void startListening ()
|
||||
{
|
||||
RPCServerImp::pointer new_connection (boost::make_shared <RPCServerImp> (
|
||||
boost::ref (mAcceptor.get_io_service ()),
|
||||
boost::ref (m_sslContext->get ()),
|
||||
boost::ref (m_rpcServerHandler)));
|
||||
|
||||
mAcceptor.set_option (boost::asio::ip::tcp::acceptor::reuse_address (true));
|
||||
|
||||
mAcceptor.async_accept (new_connection->getRawSocket (),
|
||||
new_connection->getRemoteEndpoint (),
|
||||
boost::bind (&RPCDoorImp::handleConnect, this,
|
||||
new_connection, boost::asio::placeholders::error));
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
bool isClientAllowed (const std::string& ip)
|
||||
{
|
||||
if (getConfig ().RPC_ALLOW_REMOTE)
|
||||
return true;
|
||||
|
||||
// VFALCO TODO Represent ip addresses as a structure. Use isLoopback() member here
|
||||
//
|
||||
if (ip == "127.0.0.1")
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
void handleConnect (RPCServerImp::pointer new_connection,
|
||||
boost::system::error_code const& error)
|
||||
{
|
||||
bool delay = false;
|
||||
|
||||
if (!error)
|
||||
{
|
||||
// Restrict callers by IP
|
||||
std::string client_ip (
|
||||
new_connection->getRemoteEndpoint ().address ().to_string ());
|
||||
|
||||
if (! isClientAllowed (client_ip))
|
||||
{
|
||||
startListening ();
|
||||
return;
|
||||
}
|
||||
|
||||
new_connection->getSocket ().async_handshake (AutoSocket::ssl_socket::server,
|
||||
boost::bind (&RPCServer::connected, new_connection));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (error == boost::system::errc::too_many_files_open)
|
||||
delay = true;
|
||||
|
||||
WriteLog (lsINFO, RPCDoor) << "RPCDoorImp::handleConnect Error: " << error;
|
||||
}
|
||||
|
||||
if (delay)
|
||||
{
|
||||
mDelayTimer.expires_from_now (boost::posix_time::milliseconds (1000));
|
||||
mDelayTimer.async_wait (boost::bind (&RPCDoorImp::startListening, this));
|
||||
}
|
||||
else
|
||||
{
|
||||
startListening ();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
RPCServer::Handler& m_rpcServerHandler;
|
||||
boost::asio::ip::tcp::acceptor mAcceptor;
|
||||
boost::asio::deadline_timer mDelayTimer;
|
||||
std::unique_ptr <RippleSSLContext> m_sslContext;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// VFALCO TODO Return std::unique_ptr here
|
||||
RPCDoor* RPCDoor::New (boost::asio::io_service& io_service, RPCServer::Handler& handler)
|
||||
{
|
||||
return new RPCDoorImp (io_service, handler);
|
||||
}
|
||||
|
||||
}
|
||||
37
src/ripple/module/net/basics/RPCDoor.h
Normal file
37
src/ripple/module/net/basics/RPCDoor.h
Normal file
@@ -0,0 +1,37 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_NET_BASICS_RPCDOOR_H_INCLUDED
|
||||
#define RIPPLE_NET_BASICS_RPCDOOR_H_INCLUDED
|
||||
|
||||
namespace ripple {
|
||||
|
||||
/** Listening socket for RPC requests.
|
||||
*/
|
||||
class RPCDoor
|
||||
{
|
||||
public:
|
||||
static RPCDoor* New (boost::asio::io_service& io_service, RPCServer::Handler& handler);
|
||||
|
||||
virtual ~RPCDoor () { }
|
||||
};
|
||||
|
||||
} // ripple
|
||||
|
||||
#endif
|
||||
68
src/ripple/module/net/basics/RPCServer.h
Normal file
68
src/ripple/module/net/basics/RPCServer.h
Normal file
@@ -0,0 +1,68 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_NET_BASICS_RPCSERVER_H_INCLUDED
|
||||
#define RIPPLE_NET_BASICS_RPCSERVER_H_INCLUDED
|
||||
|
||||
namespace ripple {
|
||||
|
||||
/** Provides RPC services to a client.
|
||||
Each client has a separate instance of this object.
|
||||
*/
|
||||
class RPCServer
|
||||
{
|
||||
public:
|
||||
/** Handles a RPC client request.
|
||||
*/
|
||||
class Handler
|
||||
{
|
||||
public:
|
||||
virtual ~Handler () { }
|
||||
|
||||
/** Construct a HTTP response string.
|
||||
*/
|
||||
virtual std::string createResponse (int statusCode, std::string const& description) = 0;
|
||||
|
||||
/** Determine if the connection is authorized.
|
||||
*/
|
||||
virtual bool isAuthorized (std::map <std::string, std::string> const& headers) = 0;
|
||||
|
||||
/** Produce a response for a given request.
|
||||
|
||||
@param request The RPC request string.
|
||||
@return The server's response.
|
||||
*/
|
||||
virtual std::string processRequest (std::string const& request,
|
||||
beast::IP::Endpoint const& remoteIPAddress) = 0;
|
||||
};
|
||||
|
||||
virtual ~RPCServer () { }
|
||||
|
||||
/** Called when the connection is established.
|
||||
*/
|
||||
virtual void connected () = 0;
|
||||
|
||||
// VFALCO TODO Remove these since they expose boost
|
||||
virtual boost::asio::ip::tcp::socket& getRawSocket () = 0;
|
||||
virtual boost::asio::ip::tcp::socket::endpoint_type& getRemoteEndpoint () = 0;
|
||||
};
|
||||
|
||||
} // ripple
|
||||
|
||||
#endif
|
||||
379
src/ripple/module/net/basics/SNTPClient.cpp
Normal file
379
src/ripple/module/net/basics/SNTPClient.cpp
Normal file
@@ -0,0 +1,379 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <beast/threads/Thread.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
SETUP_LOG (SNTPClient)
|
||||
|
||||
// #define SNTP_DEBUG
|
||||
|
||||
static uint8_t SNTPQueryData[48] =
|
||||
{ 0x1B, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
|
||||
// NTP query frequency - 4 minutes
|
||||
#define NTP_QUERY_FREQUENCY (4 * 60)
|
||||
|
||||
// NTP minimum interval to query same servers - 3 minutes
|
||||
#define NTP_MIN_QUERY (3 * 60)
|
||||
|
||||
// NTP sample window (should be odd)
|
||||
#define NTP_SAMPLE_WINDOW 9
|
||||
|
||||
// NTP timestamp constant
|
||||
#define NTP_UNIX_OFFSET 0x83AA7E80
|
||||
|
||||
// NTP timestamp validity
|
||||
#define NTP_TIMESTAMP_VALID ((NTP_QUERY_FREQUENCY + NTP_MIN_QUERY) * 2)
|
||||
|
||||
// SNTP packet offsets
|
||||
#define NTP_OFF_INFO 0
|
||||
#define NTP_OFF_ROOTDELAY 1
|
||||
#define NTP_OFF_ROOTDISP 2
|
||||
#define NTP_OFF_REFERENCEID 3
|
||||
#define NTP_OFF_REFTS_INT 4
|
||||
#define NTP_OFF_REFTS_FRAC 5
|
||||
#define NTP_OFF_ORGTS_INT 6
|
||||
#define NTP_OFF_ORGTS_FRAC 7
|
||||
#define NTP_OFF_RECVTS_INT 8
|
||||
#define NTP_OFF_RECVTS_FRAC 9
|
||||
#define NTP_OFF_XMITTS_INT 10
|
||||
#define NTP_OFF_XMITTS_FRAC 11
|
||||
|
||||
class SNTPClientImp
|
||||
: public SNTPClient
|
||||
, public beast::Thread
|
||||
, public beast::LeakChecked <SNTPClientImp>
|
||||
{
|
||||
public:
|
||||
class SNTPQuery
|
||||
{
|
||||
public:
|
||||
bool mReceivedReply;
|
||||
time_t mLocalTimeSent;
|
||||
std::uint32_t mQueryNonce;
|
||||
|
||||
SNTPQuery (time_t j = (time_t) - 1) : mReceivedReply (false), mLocalTimeSent (j)
|
||||
{
|
||||
;
|
||||
}
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
explicit SNTPClientImp (Stoppable& parent)
|
||||
: SNTPClient (parent)
|
||||
, Thread ("SNTPClient")
|
||||
, mSocket (m_io_service)
|
||||
, mTimer (m_io_service)
|
||||
, mResolver (m_io_service)
|
||||
, mOffset (0)
|
||||
, mLastOffsetUpdate ((time_t) - 1)
|
||||
, mReceiveBuffer (256)
|
||||
{
|
||||
mSocket.open (boost::asio::ip::udp::v4 ());
|
||||
|
||||
mSocket.async_receive_from (boost::asio::buffer (mReceiveBuffer, 256),
|
||||
mReceiveEndpoint, boost::bind (
|
||||
&SNTPClientImp::receivePacket, this,
|
||||
boost::asio::placeholders::error,
|
||||
boost::asio::placeholders::bytes_transferred));
|
||||
|
||||
mTimer.expires_from_now (boost::posix_time::seconds (NTP_QUERY_FREQUENCY));
|
||||
mTimer.async_wait (boost::bind (&SNTPClientImp::timerEntry, this, boost::asio::placeholders::error));
|
||||
}
|
||||
|
||||
~SNTPClientImp ()
|
||||
{
|
||||
stopThread ();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
void onStart ()
|
||||
{
|
||||
startThread ();
|
||||
}
|
||||
|
||||
void onStop ()
|
||||
{
|
||||
// HACK!
|
||||
m_io_service.stop ();
|
||||
}
|
||||
|
||||
void run ()
|
||||
{
|
||||
m_io_service.run ();
|
||||
|
||||
stopped ();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
void init (const std::vector<std::string>& servers)
|
||||
{
|
||||
std::vector<std::string>::const_iterator it = servers.begin ();
|
||||
|
||||
if (it == servers.end ())
|
||||
{
|
||||
WriteLog (lsINFO, SNTPClient) << "SNTP: no server specified";
|
||||
return;
|
||||
}
|
||||
|
||||
BOOST_FOREACH (const std::string & it, servers)
|
||||
addServer (it);
|
||||
queryAll ();
|
||||
}
|
||||
|
||||
void addServer (const std::string& server)
|
||||
{
|
||||
ScopedLockType sl (mLock);
|
||||
mServers.push_back (std::make_pair (server, (time_t) - 1));
|
||||
}
|
||||
|
||||
void queryAll ()
|
||||
{
|
||||
while (doQuery ())
|
||||
nothing ();
|
||||
}
|
||||
|
||||
bool getOffset (int& offset)
|
||||
{
|
||||
ScopedLockType sl (mLock);
|
||||
|
||||
if ((mLastOffsetUpdate == (time_t) - 1) || ((mLastOffsetUpdate + NTP_TIMESTAMP_VALID) < time (nullptr)))
|
||||
return false;
|
||||
|
||||
offset = mOffset;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool doQuery ()
|
||||
{
|
||||
ScopedLockType sl (mLock);
|
||||
std::vector< std::pair<std::string, time_t> >::iterator best = mServers.end ();
|
||||
|
||||
for (std::vector< std::pair<std::string, time_t> >::iterator it = mServers.begin (), end = best;
|
||||
it != end; ++it)
|
||||
if ((best == end) || (it->second == (time_t) - 1) || (it->second < best->second))
|
||||
best = it;
|
||||
|
||||
if (best == mServers.end ())
|
||||
{
|
||||
WriteLog (lsTRACE, SNTPClient) << "SNTP: No server to query";
|
||||
return false;
|
||||
}
|
||||
|
||||
time_t now = time (nullptr);
|
||||
|
||||
if ((best->second != (time_t) - 1) && ((best->second + NTP_MIN_QUERY) >= now))
|
||||
{
|
||||
WriteLog (lsTRACE, SNTPClient) << "SNTP: All servers recently queried";
|
||||
return false;
|
||||
}
|
||||
|
||||
best->second = now;
|
||||
|
||||
boost::asio::ip::udp::resolver::query query (boost::asio::ip::udp::v4 (), best->first, "ntp");
|
||||
mResolver.async_resolve (query,
|
||||
boost::bind (&SNTPClientImp::resolveComplete, this,
|
||||
boost::asio::placeholders::error, boost::asio::placeholders::iterator));
|
||||
#ifdef SNTP_DEBUG
|
||||
WriteLog (lsTRACE, SNTPClient) << "SNTP: Resolve pending for " << best->first;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
void resolveComplete (const boost::system::error_code& error, boost::asio::ip::udp::resolver::iterator it)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
boost::asio::ip::udp::resolver::iterator sel = it;
|
||||
int i = 1;
|
||||
|
||||
while (++it != boost::asio::ip::udp::resolver::iterator ())
|
||||
if ((rand () % ++i) == 0)
|
||||
sel = it;
|
||||
|
||||
if (sel != boost::asio::ip::udp::resolver::iterator ())
|
||||
{
|
||||
ScopedLockType sl (mLock);
|
||||
SNTPQuery& query = mQueries[*sel];
|
||||
time_t now = time (nullptr);
|
||||
|
||||
if ((query.mLocalTimeSent == now) || ((query.mLocalTimeSent + 1) == now))
|
||||
{
|
||||
// This can happen if the same IP address is reached through multiple names
|
||||
WriteLog (lsTRACE, SNTPClient) << "SNTP: Redundant query suppressed";
|
||||
return;
|
||||
}
|
||||
|
||||
query.mReceivedReply = false;
|
||||
query.mLocalTimeSent = now;
|
||||
RandomNumbers::getInstance ().fill (&query.mQueryNonce);
|
||||
reinterpret_cast<std::uint32_t*> (SNTPQueryData)[NTP_OFF_XMITTS_INT] = static_cast<std::uint32_t> (time (nullptr)) + NTP_UNIX_OFFSET;
|
||||
reinterpret_cast<std::uint32_t*> (SNTPQueryData)[NTP_OFF_XMITTS_FRAC] = query.mQueryNonce;
|
||||
mSocket.async_send_to (boost::asio::buffer (SNTPQueryData, 48), *sel,
|
||||
boost::bind (&SNTPClientImp::sendComplete, this,
|
||||
boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void receivePacket (const boost::system::error_code& error, std::size_t bytes_xferd)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
ScopedLockType sl (mLock);
|
||||
#ifdef SNTP_DEBUG
|
||||
WriteLog (lsTRACE, SNTPClient) << "SNTP: Packet from " << mReceiveEndpoint;
|
||||
#endif
|
||||
std::map<boost::asio::ip::udp::endpoint, SNTPQuery>::iterator query = mQueries.find (mReceiveEndpoint);
|
||||
|
||||
if (query == mQueries.end ())
|
||||
WriteLog (lsDEBUG, SNTPClient) << "SNTP: Reply from " << mReceiveEndpoint << " found without matching query";
|
||||
else if (query->second.mReceivedReply)
|
||||
WriteLog (lsDEBUG, SNTPClient) << "SNTP: Duplicate response from " << mReceiveEndpoint;
|
||||
else
|
||||
{
|
||||
query->second.mReceivedReply = true;
|
||||
|
||||
if (time (nullptr) > (query->second.mLocalTimeSent + 1))
|
||||
WriteLog (lsWARNING, SNTPClient) << "SNTP: Late response from " << mReceiveEndpoint;
|
||||
else if (bytes_xferd < 48)
|
||||
WriteLog (lsWARNING, SNTPClient) << "SNTP: Short reply from " << mReceiveEndpoint
|
||||
<< " (" << bytes_xferd << ") " << mReceiveBuffer.size ();
|
||||
else if (reinterpret_cast<std::uint32_t*> (&mReceiveBuffer[0])[NTP_OFF_ORGTS_FRAC] != query->second.mQueryNonce)
|
||||
WriteLog (lsWARNING, SNTPClient) << "SNTP: Reply from " << mReceiveEndpoint << "had wrong nonce";
|
||||
else
|
||||
processReply ();
|
||||
}
|
||||
}
|
||||
|
||||
mSocket.async_receive_from (boost::asio::buffer (mReceiveBuffer, 256), mReceiveEndpoint,
|
||||
boost::bind (&SNTPClientImp::receivePacket, this, boost::asio::placeholders::error,
|
||||
boost::asio::placeholders::bytes_transferred));
|
||||
}
|
||||
|
||||
void sendComplete (const boost::system::error_code& error, std::size_t)
|
||||
{
|
||||
CondLog (error, lsWARNING, SNTPClient) << "SNTP: Send error";
|
||||
}
|
||||
|
||||
void processReply ()
|
||||
{
|
||||
assert (mReceiveBuffer.size () >= 48);
|
||||
std::uint32_t* recvBuffer = reinterpret_cast<std::uint32_t*> (&mReceiveBuffer.front ());
|
||||
|
||||
unsigned info = ntohl (recvBuffer[NTP_OFF_INFO]);
|
||||
int64_t timev = ntohl (recvBuffer[NTP_OFF_RECVTS_INT]);
|
||||
unsigned stratum = (info >> 16) & 0xff;
|
||||
|
||||
if ((info >> 30) == 3)
|
||||
{
|
||||
WriteLog (lsINFO, SNTPClient) << "SNTP: Alarm condition " << mReceiveEndpoint;
|
||||
return;
|
||||
}
|
||||
|
||||
if ((stratum == 0) || (stratum > 14))
|
||||
{
|
||||
WriteLog (lsINFO, SNTPClient) << "SNTP: Unreasonable stratum (" << stratum << ") from " << mReceiveEndpoint;
|
||||
return;
|
||||
}
|
||||
|
||||
std::int64_t now = static_cast<int> (time (nullptr));
|
||||
timev -= now;
|
||||
timev -= NTP_UNIX_OFFSET;
|
||||
|
||||
// add offset to list, replacing oldest one if appropriate
|
||||
mOffsetList.push_back (timev);
|
||||
|
||||
if (mOffsetList.size () >= NTP_SAMPLE_WINDOW)
|
||||
mOffsetList.pop_front ();
|
||||
|
||||
mLastOffsetUpdate = now;
|
||||
|
||||
// select median time
|
||||
std::list<int> offsetList = mOffsetList;
|
||||
offsetList.sort ();
|
||||
int j = offsetList.size ();
|
||||
std::list<int>::iterator it = offsetList.begin ();
|
||||
|
||||
for (int i = 0; i < (j / 2); ++i)
|
||||
++it;
|
||||
|
||||
mOffset = *it;
|
||||
|
||||
if ((j % 2) == 0)
|
||||
mOffset = (mOffset + (*--it)) / 2;
|
||||
|
||||
if ((mOffset == -1) || (mOffset == 1)) // small corrections likely do more harm than good
|
||||
mOffset = 0;
|
||||
|
||||
CondLog (timev || mOffset, lsTRACE, SNTPClient) << "SNTP: Offset is " << timev << ", new system offset is " << mOffset;
|
||||
}
|
||||
|
||||
void timerEntry (const boost::system::error_code& error)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
doQuery ();
|
||||
mTimer.expires_from_now (boost::posix_time::seconds (NTP_QUERY_FREQUENCY));
|
||||
mTimer.async_wait (boost::bind (&SNTPClientImp::timerEntry, this, boost::asio::placeholders::error));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
typedef RippleMutex LockType;
|
||||
typedef std::lock_guard <LockType> ScopedLockType;
|
||||
LockType mLock;
|
||||
|
||||
boost::asio::io_service m_io_service;
|
||||
std::map <boost::asio::ip::udp::endpoint, SNTPQuery> mQueries;
|
||||
|
||||
boost::asio::ip::udp::socket mSocket;
|
||||
boost::asio::deadline_timer mTimer;
|
||||
boost::asio::ip::udp::resolver mResolver;
|
||||
|
||||
std::vector< std::pair<std::string, time_t> > mServers;
|
||||
|
||||
int mOffset;
|
||||
time_t mLastOffsetUpdate;
|
||||
std::list<int> mOffsetList;
|
||||
|
||||
std::vector<uint8_t> mReceiveBuffer;
|
||||
boost::asio::ip::udp::endpoint mReceiveEndpoint;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
SNTPClient::SNTPClient (Stoppable& parent)
|
||||
: Stoppable ("SNTPClient", parent)
|
||||
{
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
SNTPClient* SNTPClient::New (Stoppable& parent)
|
||||
{
|
||||
return new SNTPClientImp (parent);
|
||||
}
|
||||
|
||||
} // ripple
|
||||
41
src/ripple/module/net/basics/SNTPClient.h
Normal file
41
src/ripple/module/net/basics/SNTPClient.h
Normal file
@@ -0,0 +1,41 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_NET_BASICS_SNTPCLIENT_H_INCLUDED
|
||||
#define RIPPLE_NET_BASICS_SNTPCLIENT_H_INCLUDED
|
||||
|
||||
namespace ripple {
|
||||
|
||||
class SNTPClient : public beast::Stoppable
|
||||
{
|
||||
protected:
|
||||
explicit SNTPClient (beast::Stoppable& parent);
|
||||
|
||||
public:
|
||||
static SNTPClient* New (beast::Stoppable& parent);
|
||||
virtual ~SNTPClient() { }
|
||||
virtual void init (std::vector <std::string> const& servers) = 0;
|
||||
virtual void addServer (std::string const& mServer) = 0;
|
||||
virtual void queryAll () = 0;
|
||||
virtual bool getOffset (int& offset) = 0;
|
||||
};
|
||||
|
||||
} // ripple
|
||||
|
||||
#endif
|
||||
264
src/ripple/module/net/basics/impl/RPCServerImp.h
Normal file
264
src/ripple/module/net/basics/impl/RPCServerImp.h
Normal file
@@ -0,0 +1,264 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <beast/asio/IPAddressConversion.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
SETUP_LOG (RPCServer)
|
||||
|
||||
class RPCServerImp
|
||||
: public RPCServer
|
||||
, public boost::enable_shared_from_this <RPCServerImp>
|
||||
, public beast::LeakChecked <RPCServerImp>
|
||||
{
|
||||
public:
|
||||
typedef boost::shared_ptr <RPCServerImp> pointer;
|
||||
|
||||
RPCServerImp (
|
||||
boost::asio::io_service& io_service,
|
||||
boost::asio::ssl::context& context,
|
||||
Handler& handler)
|
||||
: m_handler (handler)
|
||||
, mStrand (io_service)
|
||||
, mSocket (io_service, context)
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
enum
|
||||
{
|
||||
maxQueryBytes = 1024 * 1024
|
||||
};
|
||||
|
||||
void connected ()
|
||||
{
|
||||
boost::asio::async_read_until (
|
||||
mSocket,
|
||||
mLineBuffer,
|
||||
"\r\n",
|
||||
mStrand.wrap (boost::bind (
|
||||
&RPCServerImp::handle_read_line,
|
||||
boost::static_pointer_cast <RPCServerImp> (shared_from_this ()),
|
||||
boost::asio::placeholders::error)));
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
void handle_write (const boost::system::error_code& e)
|
||||
{
|
||||
if (!e)
|
||||
{
|
||||
HTTPRequest::Action action = mHTTPRequest.requestDone (false);
|
||||
|
||||
if (action == HTTPRequest::haCLOSE_CONN)
|
||||
{
|
||||
mSocket.async_shutdown (mStrand.wrap (boost::bind (
|
||||
&RPCServerImp::handle_shutdown,
|
||||
boost::static_pointer_cast <RPCServerImp> (shared_from_this()),
|
||||
boost::asio::placeholders::error)));
|
||||
}
|
||||
else
|
||||
{
|
||||
boost::asio::async_read_until (
|
||||
mSocket,
|
||||
mLineBuffer,
|
||||
"\r\n",
|
||||
mStrand.wrap (boost::bind (
|
||||
&RPCServerImp::handle_read_line,
|
||||
boost::static_pointer_cast <RPCServerImp> (shared_from_this()),
|
||||
boost::asio::placeholders::error)));
|
||||
}
|
||||
}
|
||||
|
||||
if (e != boost::asio::error::operation_aborted)
|
||||
{
|
||||
// VFALCO TODO What is this for? It was commented out.
|
||||
//
|
||||
//connection_manager_.stop (shared_from_this ());
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
void handle_read_line (const boost::system::error_code& e)
|
||||
{
|
||||
if (! e)
|
||||
{
|
||||
HTTPRequest::Action action = mHTTPRequest.consume (mLineBuffer);
|
||||
|
||||
if (action == HTTPRequest::haDO_REQUEST)
|
||||
{
|
||||
// request with no body
|
||||
WriteLog (lsWARNING, RPCServer) << "RPC HTTP request with no body";
|
||||
|
||||
mSocket.async_shutdown (mStrand.wrap (boost::bind (
|
||||
&RPCServerImp::handle_shutdown,
|
||||
boost::static_pointer_cast <RPCServerImp> (shared_from_this ()),
|
||||
boost::asio::placeholders::error)));
|
||||
}
|
||||
else if (action == HTTPRequest::haREAD_LINE)
|
||||
{
|
||||
boost::asio::async_read_until (
|
||||
mSocket,
|
||||
mLineBuffer,
|
||||
"\r\n",
|
||||
mStrand.wrap (boost::bind (
|
||||
&RPCServerImp::handle_read_line,
|
||||
boost::static_pointer_cast <RPCServerImp> (shared_from_this ()),
|
||||
boost::asio::placeholders::error)));
|
||||
}
|
||||
else if (action == HTTPRequest::haREAD_RAW)
|
||||
{
|
||||
int rLen = mHTTPRequest.getDataSize ();
|
||||
|
||||
if ((rLen < 0) || (rLen > maxQueryBytes))
|
||||
{
|
||||
WriteLog (lsWARNING, RPCServer) << "Illegal RPC request length " << rLen;
|
||||
|
||||
mSocket.async_shutdown (mStrand.wrap (boost::bind (
|
||||
&RPCServerImp::handle_shutdown,
|
||||
boost::static_pointer_cast <RPCServerImp> (shared_from_this ()),
|
||||
boost::asio::placeholders::error)));
|
||||
}
|
||||
else
|
||||
{
|
||||
int alreadyHave = mLineBuffer.size ();
|
||||
|
||||
if (alreadyHave < rLen)
|
||||
{
|
||||
mQueryVec.resize (rLen - alreadyHave);
|
||||
|
||||
boost::asio::async_read (
|
||||
mSocket,
|
||||
boost::asio::buffer (mQueryVec),
|
||||
mStrand.wrap (boost::bind (
|
||||
&RPCServerImp::handle_read_req,
|
||||
boost::static_pointer_cast <RPCServerImp> (shared_from_this ()),
|
||||
boost::asio::placeholders::error)));
|
||||
|
||||
WriteLog (lsTRACE, RPCServer) << "Waiting for completed request: " << rLen;
|
||||
}
|
||||
else
|
||||
{
|
||||
// we have the whole thing
|
||||
mQueryVec.resize (0);
|
||||
|
||||
handle_read_req (e);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mSocket.async_shutdown (mStrand.wrap (boost::bind (
|
||||
&RPCServerImp::handle_shutdown,
|
||||
boost::static_pointer_cast <RPCServerImp> (shared_from_this ()),
|
||||
boost::asio::placeholders::error)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
void handle_read_req (const boost::system::error_code& ec)
|
||||
{
|
||||
std::string req;
|
||||
|
||||
if (mLineBuffer.size ())
|
||||
{
|
||||
req.assign (boost::asio::buffer_cast <const char*> (mLineBuffer.data ()), mLineBuffer.size ());
|
||||
|
||||
mLineBuffer.consume (mLineBuffer.size ());
|
||||
}
|
||||
|
||||
req += strCopy (mQueryVec);
|
||||
|
||||
if (! m_handler.isAuthorized (mHTTPRequest.peekHeaders ()))
|
||||
{
|
||||
mReplyStr = m_handler.createResponse (403, "Forbidden");
|
||||
}
|
||||
else
|
||||
{
|
||||
mReplyStr = handleRequest (req);
|
||||
}
|
||||
|
||||
boost::asio::async_write (
|
||||
mSocket,
|
||||
boost::asio::buffer (mReplyStr),
|
||||
mStrand.wrap (boost::bind (
|
||||
&RPCServerImp::handle_write,
|
||||
boost::static_pointer_cast <RPCServerImp> (shared_from_this ()),
|
||||
boost::asio::placeholders::error)));
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
void handle_shutdown (const boost::system::error_code& ec)
|
||||
{
|
||||
// nothing to do, we just keep the object alive
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
// JSON-RPC request must contain "method", "params", and "id" fields.
|
||||
//
|
||||
std::string handleRequest (const std::string& request)
|
||||
{
|
||||
WriteLog (lsTRACE, RPCServer) << "handleRequest " << request;
|
||||
|
||||
return m_handler.processRequest (request,
|
||||
beast::IPAddressConversion::from_asio (
|
||||
m_remote_endpoint.address()));
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
AutoSocket& getSocket ()
|
||||
{
|
||||
return mSocket;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
boost::asio::ip::tcp::socket& getRawSocket ()
|
||||
{
|
||||
return mSocket.PlainSocket ();
|
||||
}
|
||||
|
||||
boost::asio::ip::tcp::socket::endpoint_type& getRemoteEndpoint ()
|
||||
{
|
||||
return m_remote_endpoint;
|
||||
}
|
||||
|
||||
private:
|
||||
Handler& m_handler;
|
||||
|
||||
boost::asio::io_service::strand mStrand;
|
||||
AutoSocket mSocket;
|
||||
AutoSocket::endpoint_type m_remote_endpoint;
|
||||
|
||||
boost::asio::streambuf mLineBuffer;
|
||||
Blob mQueryVec;
|
||||
std::string mReplyStr;
|
||||
|
||||
HTTPRequest mHTTPRequest;
|
||||
};
|
||||
|
||||
} // ripple
|
||||
101
src/ripple/module/net/rpc/InfoSub.cpp
Normal file
101
src/ripple/module/net/rpc/InfoSub.cpp
Normal file
@@ -0,0 +1,101 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
namespace ripple {
|
||||
|
||||
// This is the primary interface into the "client" portion of the program.
|
||||
// Code that wants to do normal operations on the network such as
|
||||
// creating and monitoring accounts, creating transactions, and so on
|
||||
// should use this interface. The RPC code will primarily be a light wrapper
|
||||
// over this code.
|
||||
|
||||
// Eventually, it will check the node's operating mode (synched, unsynched,
|
||||
// etectera) and defer to the correct means of processing. The current
|
||||
// code assumes this node is synched (and will continue to do so until
|
||||
// there's a functional network.
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
InfoSub::Source::Source (char const* name, Stoppable& parent)
|
||||
: Stoppable (name, parent)
|
||||
{
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
InfoSub::InfoSub (Source& source, Consumer consumer)
|
||||
: m_consumer (consumer)
|
||||
, m_source (source)
|
||||
{
|
||||
static beast::Atomic <int> s_seq_id;
|
||||
mSeq = ++s_seq_id;
|
||||
}
|
||||
|
||||
InfoSub::~InfoSub ()
|
||||
{
|
||||
m_source.unsubTransactions (mSeq);
|
||||
m_source.unsubRTTransactions (mSeq);
|
||||
m_source.unsubLedger (mSeq);
|
||||
m_source.unsubServer (mSeq);
|
||||
m_source.unsubAccount (mSeq, mSubAccountInfo, true);
|
||||
m_source.unsubAccount (mSeq, mSubAccountInfo, false);
|
||||
}
|
||||
|
||||
Resource::Consumer& InfoSub::getConsumer()
|
||||
{
|
||||
return m_consumer;
|
||||
}
|
||||
|
||||
void InfoSub::send (const Json::Value& jvObj, const std::string& sObj, bool broadcast)
|
||||
{
|
||||
send (jvObj, broadcast);
|
||||
}
|
||||
|
||||
std::uint64_t InfoSub::getSeq ()
|
||||
{
|
||||
return mSeq;
|
||||
}
|
||||
|
||||
void InfoSub::onSendEmpty ()
|
||||
{
|
||||
}
|
||||
|
||||
void InfoSub::insertSubAccountInfo (RippleAddress addr, std::uint32_t uLedgerIndex)
|
||||
{
|
||||
ScopedLockType sl (mLock);
|
||||
|
||||
mSubAccountInfo.insert (addr);
|
||||
}
|
||||
|
||||
void InfoSub::clearPathRequest ()
|
||||
{
|
||||
mPathRequest.reset ();
|
||||
}
|
||||
|
||||
void InfoSub::setPathRequest (const boost::shared_ptr<PathRequest>& req)
|
||||
{
|
||||
mPathRequest = req;
|
||||
}
|
||||
|
||||
const boost::shared_ptr<PathRequest>& InfoSub::getPathRequest ()
|
||||
{
|
||||
return mPathRequest;
|
||||
}
|
||||
|
||||
} // ripple
|
||||
142
src/ripple/module/net/rpc/InfoSub.h
Normal file
142
src/ripple/module/net/rpc/InfoSub.h
Normal file
@@ -0,0 +1,142 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_NET_RPC_INFOSUB_H_INCLUDED
|
||||
#define RIPPLE_NET_RPC_INFOSUB_H_INCLUDED
|
||||
|
||||
namespace ripple {
|
||||
|
||||
// Operations that clients may wish to perform against the network
|
||||
// Master operational handler, server sequencer, network tracker
|
||||
|
||||
class PathRequest;
|
||||
|
||||
/** Manages a client's subscription to data feeds.
|
||||
*/
|
||||
class InfoSub
|
||||
: public CountedObject <InfoSub>
|
||||
{
|
||||
public:
|
||||
static char const* getCountedObjectName () { return "InfoSub"; }
|
||||
|
||||
typedef boost::shared_ptr<InfoSub> pointer;
|
||||
|
||||
// VFALCO TODO Standardize on the names of weak / strong pointer typedefs.
|
||||
typedef boost::weak_ptr<InfoSub> wptr;
|
||||
|
||||
typedef const boost::shared_ptr<InfoSub>& ref;
|
||||
|
||||
typedef Resource::Consumer Consumer;
|
||||
|
||||
public:
|
||||
/** Abstracts the source of subscription data.
|
||||
*/
|
||||
class Source : public beast::Stoppable
|
||||
{
|
||||
protected:
|
||||
Source (char const* name, beast::Stoppable& parent);
|
||||
|
||||
public:
|
||||
// VFALCO TODO Rename the 'rt' parameters to something meaningful.
|
||||
virtual void subAccount (ref ispListener,
|
||||
const boost::unordered_set<RippleAddress>& vnaAccountIDs,
|
||||
std::uint32_t uLedgerIndex, bool rt) = 0;
|
||||
|
||||
virtual void unsubAccount (std::uint64_t uListener,
|
||||
const boost::unordered_set<RippleAddress>& vnaAccountIDs,
|
||||
bool rt) = 0;
|
||||
|
||||
// VFALCO TODO Document the bool return value
|
||||
virtual bool subLedger (ref ispListener,
|
||||
Json::Value& jvResult) = 0;
|
||||
|
||||
virtual bool unsubLedger (std::uint64_t uListener) = 0;
|
||||
|
||||
virtual bool subServer (ref ispListener,
|
||||
Json::Value& jvResult) = 0;
|
||||
|
||||
virtual bool unsubServer (std::uint64_t uListener) = 0;
|
||||
|
||||
virtual bool subBook (ref ispListener,
|
||||
RippleCurrency const& currencyPays, RippleCurrency const& currencyGets,
|
||||
RippleIssuer const& issuerPays, RippleIssuer const& issuerGets) = 0;
|
||||
|
||||
virtual bool unsubBook (std::uint64_t uListener,
|
||||
RippleCurrency const& currencyPays, RippleCurrency const& currencyGets,
|
||||
RippleIssuer const& issuerPays, RippleIssuer const& issuerGets) = 0;
|
||||
|
||||
virtual bool subTransactions (ref ispListener) = 0;
|
||||
|
||||
virtual bool unsubTransactions (std::uint64_t uListener) = 0;
|
||||
|
||||
virtual bool subRTTransactions (ref ispListener) = 0;
|
||||
|
||||
virtual bool unsubRTTransactions (std::uint64_t uListener) = 0;
|
||||
|
||||
// VFALCO TODO Remove
|
||||
// This was added for one particular partner, it
|
||||
// "pushes" subscription data to a particular URL.
|
||||
//
|
||||
virtual pointer findRpcSub (const std::string& strUrl) = 0;
|
||||
|
||||
virtual pointer addRpcSub (const std::string& strUrl, ref rspEntry) = 0;
|
||||
};
|
||||
|
||||
public:
|
||||
InfoSub (Source& source, Consumer consumer);
|
||||
|
||||
virtual ~InfoSub ();
|
||||
|
||||
Consumer& getConsumer();
|
||||
|
||||
virtual void send (const Json::Value & jvObj, bool broadcast) = 0;
|
||||
|
||||
// VFALCO NOTE Why is this virtual?
|
||||
virtual void send (const Json::Value & jvObj, const std::string & sObj, bool broadcast);
|
||||
|
||||
std::uint64_t getSeq ();
|
||||
|
||||
void onSendEmpty ();
|
||||
|
||||
void insertSubAccountInfo (RippleAddress addr, std::uint32_t uLedgerIndex);
|
||||
|
||||
void clearPathRequest ();
|
||||
|
||||
void setPathRequest (const boost::shared_ptr<PathRequest>& req);
|
||||
|
||||
boost::shared_ptr <PathRequest> const& getPathRequest ();
|
||||
|
||||
protected:
|
||||
typedef RippleMutex LockType;
|
||||
typedef std::lock_guard <LockType> ScopedLockType;
|
||||
LockType mLock;
|
||||
|
||||
private:
|
||||
Consumer m_consumer;
|
||||
Source& m_source;
|
||||
boost::unordered_set <RippleAddress> mSubAccountInfo;
|
||||
boost::unordered_set <RippleAddress> mSubAccountTransaction;
|
||||
boost::shared_ptr <PathRequest> mPathRequest;
|
||||
|
||||
std::uint64_t mSeq;
|
||||
};
|
||||
|
||||
} // ripple
|
||||
|
||||
#endif
|
||||
1122
src/ripple/module/net/rpc/RPCCall.cpp
Normal file
1122
src/ripple/module/net/rpc/RPCCall.cpp
Normal file
File diff suppressed because it is too large
Load Diff
51
src/ripple/module/net/rpc/RPCCall.h
Normal file
51
src/ripple/module/net/rpc/RPCCall.h
Normal file
@@ -0,0 +1,51 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_NET_RPC_RPCCALL_H_INCLUDED
|
||||
#define RIPPLE_NET_RPC_RPCCALL_H_INCLUDED
|
||||
|
||||
namespace ripple {
|
||||
|
||||
//
|
||||
// This a trusted interface, the user is expected to provide valid input to perform valid requests.
|
||||
// Error catching and reporting is not a requirement of this command line interface.
|
||||
//
|
||||
// Improvements to be more strict and to provide better diagnostics are welcome.
|
||||
//
|
||||
|
||||
/** Processes Ripple RPC calls.
|
||||
*/
|
||||
class RPCCall
|
||||
{
|
||||
public:
|
||||
|
||||
static int fromCommandLine (const std::vector<std::string>& vCmd);
|
||||
|
||||
static void fromNetwork (
|
||||
boost::asio::io_service& io_service,
|
||||
const std::string& strIp, const int iPort,
|
||||
const std::string& strUsername, const std::string& strPassword,
|
||||
const std::string& strPath, const std::string& strMethod,
|
||||
const Json::Value& jvParams, const bool bSSL,
|
||||
std::function<void (const Json::Value& jvInput)> callbackFuncP = std::function<void (const Json::Value& jvInput)> ());
|
||||
};
|
||||
|
||||
} // ripple
|
||||
|
||||
#endif
|
||||
52
src/ripple/module/net/rpc/RPCErr.cpp
Normal file
52
src/ripple/module/net/rpc/RPCErr.cpp
Normal file
@@ -0,0 +1,52 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
namespace ripple {
|
||||
|
||||
struct RPCErr;
|
||||
|
||||
SETUP_LOG (RPCErr)
|
||||
|
||||
// VFALCO NOTE Deprecated function
|
||||
Json::Value rpcError (int iError, Json::Value jvResult)
|
||||
{
|
||||
RPC::inject_error (iError, jvResult);
|
||||
return jvResult;
|
||||
}
|
||||
|
||||
// VFALCO NOTE Deprecated function
|
||||
bool isRpcError (Json::Value jvResult)
|
||||
{
|
||||
return jvResult.isObject () && jvResult.isMember ("error");
|
||||
}
|
||||
|
||||
Json::Value const& logRPCError (Json::Value const& json)
|
||||
{
|
||||
if (RPC::contains_error (json))
|
||||
{
|
||||
WriteLog (lsDEBUG, RPCErr) <<
|
||||
"rpcError: " << json ["error"] <<
|
||||
": " << json ["error_message"];
|
||||
}
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
} // ripple
|
||||
|
||||
34
src/ripple/module/net/rpc/RPCErr.h
Normal file
34
src/ripple/module/net/rpc/RPCErr.h
Normal file
@@ -0,0 +1,34 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_NET_RPC_RPCERR_H_INCLUDED
|
||||
#define RIPPLE_NET_RPC_RPCERR_H_INCLUDED
|
||||
|
||||
namespace ripple {
|
||||
|
||||
Json::Value const& logRPCError (Json::Value const& json);
|
||||
|
||||
// VFALCO NOTE these are deprecated
|
||||
bool isRpcError (Json::Value jvResult);
|
||||
Json::Value rpcError (int iError,
|
||||
Json::Value jvResult = Json::Value (Json::objectValue));
|
||||
|
||||
} // ripple
|
||||
|
||||
#endif
|
||||
213
src/ripple/module/net/rpc/RPCSub.cpp
Normal file
213
src/ripple/module/net/rpc/RPCSub.cpp
Normal file
@@ -0,0 +1,213 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
namespace ripple {
|
||||
|
||||
SETUP_LOG (RPCSub)
|
||||
|
||||
// Subscription object for JSON-RPC
|
||||
class RPCSubImp
|
||||
: public RPCSub
|
||||
, public beast::LeakChecked <RPCSub>
|
||||
{
|
||||
public:
|
||||
RPCSubImp (InfoSub::Source& source, boost::asio::io_service& io_service,
|
||||
JobQueue& jobQueue, const std::string& strUrl, const std::string& strUsername,
|
||||
const std::string& strPassword)
|
||||
: RPCSub (source)
|
||||
, m_io_service (io_service)
|
||||
, m_jobQueue (jobQueue)
|
||||
, mUrl (strUrl)
|
||||
, mSSL (false)
|
||||
, mUsername (strUsername)
|
||||
, mPassword (strPassword)
|
||||
, mSending (false)
|
||||
{
|
||||
std::string strScheme;
|
||||
|
||||
if (!parseUrl (strUrl, strScheme, mIp, mPort, mPath))
|
||||
{
|
||||
throw std::runtime_error ("Failed to parse url.");
|
||||
}
|
||||
else if (strScheme == "https")
|
||||
{
|
||||
mSSL = true;
|
||||
}
|
||||
else if (strScheme != "http")
|
||||
{
|
||||
throw std::runtime_error ("Only http and https is supported.");
|
||||
}
|
||||
|
||||
mSeq = 1;
|
||||
|
||||
if (mPort < 0)
|
||||
mPort = mSSL ? 443 : 80;
|
||||
|
||||
WriteLog (lsINFO, RPCSub) <<
|
||||
"RPCCall::fromNetwork sub: ip=" << mIp <<
|
||||
" port=" << mPort <<
|
||||
" ssl= "<< (mSSL ? "yes" : "no") <<
|
||||
" path='" << mPath << "'";
|
||||
}
|
||||
|
||||
~RPCSubImp ()
|
||||
{
|
||||
}
|
||||
|
||||
void send (const Json::Value& jvObj, bool broadcast)
|
||||
{
|
||||
ScopedLockType sl (mLock);
|
||||
|
||||
if (mDeque.size () >= eventQueueMax)
|
||||
{
|
||||
// Drop the previous event.
|
||||
WriteLog (lsWARNING, RPCSub) << "RPCCall::fromNetwork drop";
|
||||
mDeque.pop_back ();
|
||||
}
|
||||
|
||||
WriteLog (broadcast ? lsDEBUG : lsINFO, RPCSub) <<
|
||||
"RPCCall::fromNetwork push: " << jvObj;
|
||||
|
||||
mDeque.push_back (std::make_pair (mSeq++, jvObj));
|
||||
|
||||
if (!mSending)
|
||||
{
|
||||
// Start a sending thread.
|
||||
mSending = true;
|
||||
|
||||
WriteLog (lsINFO, RPCSub) << "RPCCall::fromNetwork start";
|
||||
|
||||
m_jobQueue.addJob (
|
||||
jtCLIENT, "RPCSub::sendThread", std::bind (&RPCSubImp::sendThread, this));
|
||||
}
|
||||
}
|
||||
|
||||
void setUsername (const std::string& strUsername)
|
||||
{
|
||||
ScopedLockType sl (mLock);
|
||||
|
||||
mUsername = strUsername;
|
||||
}
|
||||
|
||||
void setPassword (const std::string& strPassword)
|
||||
{
|
||||
ScopedLockType sl (mLock);
|
||||
|
||||
mPassword = strPassword;
|
||||
}
|
||||
|
||||
private:
|
||||
// XXX Could probably create a bunch of send jobs in a single get of the lock.
|
||||
void sendThread ()
|
||||
{
|
||||
Json::Value jvEvent;
|
||||
bool bSend;
|
||||
|
||||
do
|
||||
{
|
||||
{
|
||||
// Obtain the lock to manipulate the queue and change sending.
|
||||
ScopedLockType sl (mLock);
|
||||
|
||||
if (mDeque.empty ())
|
||||
{
|
||||
mSending = false;
|
||||
bSend = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::pair<int, Json::Value> pEvent = mDeque.front ();
|
||||
|
||||
mDeque.pop_front ();
|
||||
|
||||
jvEvent = pEvent.second;
|
||||
jvEvent["seq"] = pEvent.first;
|
||||
|
||||
bSend = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Send outside of the lock.
|
||||
if (bSend)
|
||||
{
|
||||
// XXX Might not need this in a try.
|
||||
try
|
||||
{
|
||||
WriteLog (lsINFO, RPCSub) << "RPCCall::fromNetwork: " << mIp;
|
||||
|
||||
RPCCall::fromNetwork (
|
||||
m_io_service,
|
||||
mIp, mPort,
|
||||
mUsername, mPassword,
|
||||
mPath, "event",
|
||||
jvEvent,
|
||||
mSSL);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
WriteLog (lsINFO, RPCSub) << "RPCCall::fromNetwork exception: " << e.what ();
|
||||
}
|
||||
}
|
||||
}
|
||||
while (bSend);
|
||||
}
|
||||
|
||||
private:
|
||||
// VFALCO TODO replace this macro with a language constant
|
||||
enum
|
||||
{
|
||||
eventQueueMax = 32
|
||||
};
|
||||
|
||||
boost::asio::io_service& m_io_service;
|
||||
JobQueue& m_jobQueue;
|
||||
|
||||
std::string mUrl;
|
||||
std::string mIp;
|
||||
int mPort;
|
||||
bool mSSL;
|
||||
std::string mUsername;
|
||||
std::string mPassword;
|
||||
std::string mPath;
|
||||
|
||||
int mSeq; // Next id to allocate.
|
||||
|
||||
bool mSending; // Sending threead is active.
|
||||
|
||||
std::deque<std::pair<int, Json::Value> > mDeque;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
RPCSub::RPCSub (InfoSub::Source& source)
|
||||
: InfoSub (source, Consumer())
|
||||
{
|
||||
}
|
||||
|
||||
RPCSub::pointer RPCSub::New (InfoSub::Source& source,
|
||||
boost::asio::io_service& io_service, JobQueue& jobQueue,
|
||||
const std::string& strUrl, const std::string& strUsername,
|
||||
const std::string& strPassword)
|
||||
{
|
||||
return boost::make_shared <RPCSubImp> (boost::ref (source),
|
||||
boost::ref (io_service), boost::ref (jobQueue),
|
||||
strUrl, strUsername, strPassword);
|
||||
}
|
||||
|
||||
} // ripple
|
||||
46
src/ripple/module/net/rpc/RPCSub.h
Normal file
46
src/ripple/module/net/rpc/RPCSub.h
Normal file
@@ -0,0 +1,46 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_NET_RPC_RPCSUB_H_INCLUDED
|
||||
#define RIPPLE_NET_RPC_RPCSUB_H_INCLUDED
|
||||
|
||||
namespace ripple {
|
||||
|
||||
/** Subscription object for JSON RPC. */
|
||||
class RPCSub : public InfoSub
|
||||
{
|
||||
public:
|
||||
typedef boost::shared_ptr <RPCSub> pointer;
|
||||
typedef pointer const& ref;
|
||||
|
||||
static pointer New (InfoSub::Source& source,
|
||||
boost::asio::io_service& io_service, JobQueue& jobQueue,
|
||||
const std::string& strUrl, const std::string& strUsername,
|
||||
const std::string& strPassword);
|
||||
|
||||
virtual void setUsername (const std::string& strUsername) = 0;
|
||||
virtual void setPassword (const std::string& strPassword) = 0;
|
||||
|
||||
protected:
|
||||
explicit RPCSub (InfoSub::Source& source);
|
||||
};
|
||||
|
||||
} // ripple
|
||||
|
||||
#endif
|
||||
331
src/ripple/module/net/rpc/RPCUtil.cpp
Normal file
331
src/ripple/module/net/rpc/RPCUtil.cpp
Normal file
@@ -0,0 +1,331 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
namespace ripple {
|
||||
|
||||
// Used for logging
|
||||
struct RPCLog;
|
||||
|
||||
SETUP_LOGN (RPCLog, "RPC")
|
||||
|
||||
unsigned int const gMaxHTTPHeaderSize = 0x02000000;
|
||||
|
||||
std::string gFormatStr ("v1");
|
||||
|
||||
// VFALCO TODO clean up this nonsense
|
||||
std::string FormatFullVersion ()
|
||||
{
|
||||
return (gFormatStr);
|
||||
}
|
||||
|
||||
|
||||
Json::Value JSONRPCError (int code, const std::string& message)
|
||||
{
|
||||
Json::Value error (Json::objectValue);
|
||||
|
||||
error[jss::code] = Json::Value (code);
|
||||
error[jss::message] = Json::Value (message);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
//
|
||||
// HTTP protocol
|
||||
//
|
||||
// This ain't Apache. We're just using HTTP header for the length field
|
||||
// and to be compatible with other JSON-RPC implementations.
|
||||
//
|
||||
|
||||
std::string createHTTPPost (
|
||||
std::string const& strHost,
|
||||
std::string const& strPath,
|
||||
std::string const& strMsg,
|
||||
std::map<std::string, std::string> const& mapRequestHeaders)
|
||||
{
|
||||
std::ostringstream s;
|
||||
|
||||
// CHECKME this uses a different version than the replies below use. Is
|
||||
// this by design or an accident or should it be using
|
||||
// BuildInfo::getFullVersionString () as well?
|
||||
|
||||
s << "POST "
|
||||
<< (strPath.empty () ? "/" : strPath)
|
||||
<< " HTTP/1.0\r\n"
|
||||
<< "User-Agent: " SYSTEM_NAME "-json-rpc/" << FormatFullVersion () << "\r\n"
|
||||
<< "Host: " << strHost << "\r\n"
|
||||
<< "Content-Type: application/json\r\n"
|
||||
<< "Content-Length: " << strMsg.size () << "\r\n"
|
||||
<< "Accept: application/json\r\n";
|
||||
|
||||
for (auto const& item : mapRequestHeaders)
|
||||
s << item.first << ": " << item.second << "\r\n";
|
||||
|
||||
s << "\r\n" << strMsg;
|
||||
|
||||
return s.str ();
|
||||
}
|
||||
|
||||
std::string getHTTPHeaderTimestamp ()
|
||||
{
|
||||
// CHECKME This is probably called often enough that optimizing it makes
|
||||
// sense. There's no point in doing all this work if this function
|
||||
// gets called multiple times a second.
|
||||
char buffer[96];
|
||||
time_t now;
|
||||
time (&now);
|
||||
struct tm* now_gmt = gmtime (&now);
|
||||
std::string locale (setlocale (LC_TIME, nullptr));
|
||||
setlocale (LC_TIME, "C"); // we want posix (aka "C") weekday/month strings
|
||||
strftime (buffer, sizeof (buffer),
|
||||
"Date: %a, %d %b %Y %H:%M:%S +0000\r\n",
|
||||
now_gmt);
|
||||
setlocale (LC_TIME, locale.c_str ());
|
||||
return std::string (buffer);
|
||||
}
|
||||
|
||||
std::string HTTPReply (int nStatus, const std::string& strMsg)
|
||||
{
|
||||
if (ShouldLog (lsTRACE, RPCLog))
|
||||
{
|
||||
WriteLog (lsTRACE, RPCLog) << "HTTP Reply " << nStatus << " " << strMsg;
|
||||
}
|
||||
|
||||
std::string ret;
|
||||
|
||||
if (nStatus == 401)
|
||||
{
|
||||
ret.reserve (512);
|
||||
|
||||
ret.append ("HTTP/1.0 401 Authorization Required\r\n");
|
||||
ret.append (getHTTPHeaderTimestamp ());
|
||||
|
||||
// CHECKME this returns a different version than the replies below. Is
|
||||
// this by design or an accident or should it be using
|
||||
// BuildInfo::getFullVersionString () as well?
|
||||
ret.append ("Server: " SYSTEM_NAME "-json-rpc/");
|
||||
ret.append (FormatFullVersion ());
|
||||
ret.append ("\r\n");
|
||||
|
||||
// Be careful in modifying this! If you change the contents you MUST
|
||||
// update the Content-Length header as well to indicate the correct
|
||||
// size of the data.
|
||||
ret.append ("WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n"
|
||||
"Content-Type: text/html\r\n"
|
||||
"Content-Length: 296\r\n"
|
||||
"\r\n"
|
||||
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\r\n"
|
||||
"\"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd\">\r\n"
|
||||
"<HTML>\r\n"
|
||||
"<HEAD>\r\n"
|
||||
"<TITLE>Error</TITLE>\r\n"
|
||||
"<META HTTP-EQUIV='Content-Type' CONTENT='text/html; charset=ISO-8859-1'>\r\n"
|
||||
"</HEAD>\r\n"
|
||||
"<BODY><H1>401 Unauthorized.</H1></BODY>\r\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret.reserve(256 + strMsg.length());
|
||||
|
||||
switch (nStatus)
|
||||
{
|
||||
case 200: ret.append ("HTTP/1.1 200 OK\r\n"); break;
|
||||
case 400: ret.append ("HTTP/1.1 400 Bad Request\r\n"); break;
|
||||
case 403: ret.append ("HTTP/1.1 403 Forbidden\r\n"); break;
|
||||
case 404: ret.append ("HTTP/1.1 404 Not Found\r\n"); break;
|
||||
case 500: ret.append ("HTTP/1.1 500 Internal Server Error\r\n"); break;
|
||||
}
|
||||
|
||||
ret.append (getHTTPHeaderTimestamp ());
|
||||
|
||||
ret.append ("Connection: Keep-Alive\r\n");
|
||||
|
||||
if (getConfig ().RPC_ALLOW_REMOTE)
|
||||
ret.append ("Access-Control-Allow-Origin: *\r\n");
|
||||
|
||||
ret.append ("Content-Length: ");
|
||||
ret.append (std::to_string(strMsg.size () + 2));
|
||||
ret.append ("\r\n");
|
||||
|
||||
ret.append ("Content-Type: application/json; charset=UTF-8\r\n");
|
||||
|
||||
ret.append ("Server: " SYSTEM_NAME "-json-rpc/");
|
||||
ret.append (BuildInfo::getFullVersionString ());
|
||||
ret.append ("\r\n");
|
||||
|
||||
ret.append ("\r\n");
|
||||
ret.append (strMsg);
|
||||
ret.append ("\r\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ReadHTTPStatus (std::basic_istream<char>& stream)
|
||||
{
|
||||
std::string str;
|
||||
getline (stream, str);
|
||||
std::vector<std::string> vWords;
|
||||
boost::split (vWords, str, boost::is_any_of (" "));
|
||||
|
||||
if (vWords.size () < 2)
|
||||
return 500;
|
||||
|
||||
return atoi (vWords[1].c_str ());
|
||||
}
|
||||
|
||||
int ReadHTTPHeader (std::basic_istream<char>& stream, std::map<std::string, std::string>& mapHeadersRet)
|
||||
{
|
||||
int nLen = 0;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
std::string str;
|
||||
std::getline (stream, str);
|
||||
|
||||
if (str.empty () || str == "\r")
|
||||
break;
|
||||
|
||||
std::string::size_type nColon = str.find (":");
|
||||
|
||||
if (nColon != std::string::npos)
|
||||
{
|
||||
std::string strHeader = str.substr (0, nColon);
|
||||
boost::trim (strHeader);
|
||||
boost::to_lower (strHeader);
|
||||
std::string strValue = str.substr (nColon + 1);
|
||||
boost::trim (strValue);
|
||||
mapHeadersRet[strHeader] = strValue;
|
||||
|
||||
if (strHeader == "content-length")
|
||||
nLen = atoi (strValue.c_str ());
|
||||
}
|
||||
}
|
||||
|
||||
return nLen;
|
||||
}
|
||||
|
||||
int ReadHTTP (std::basic_istream<char>& stream, std::map<std::string, std::string>& mapHeadersRet,
|
||||
std::string& strMessageRet)
|
||||
{
|
||||
mapHeadersRet.clear ();
|
||||
strMessageRet = "";
|
||||
|
||||
// Read status
|
||||
int nStatus = ReadHTTPStatus (stream);
|
||||
|
||||
// Read header
|
||||
int nLen = ReadHTTPHeader (stream, mapHeadersRet);
|
||||
|
||||
if (nLen < 0 || nLen > gMaxHTTPHeaderSize)
|
||||
return 500;
|
||||
|
||||
// Read message
|
||||
if (nLen > 0)
|
||||
{
|
||||
std::vector<char> vch (nLen);
|
||||
stream.read (&vch[0], nLen);
|
||||
strMessageRet = std::string (vch.begin (), vch.end ());
|
||||
}
|
||||
|
||||
return nStatus;
|
||||
}
|
||||
|
||||
std::string DecodeBase64 (std::string s)
|
||||
{
|
||||
// FIXME: This performs badly
|
||||
BIO* b64, *bmem;
|
||||
|
||||
char* buffer = static_cast<char*> (calloc (s.size (), sizeof (char)));
|
||||
|
||||
b64 = BIO_new (BIO_f_base64 ());
|
||||
BIO_set_flags (b64, BIO_FLAGS_BASE64_NO_NL);
|
||||
bmem = BIO_new_mem_buf (const_cast<char*> (s.data ()), s.size ());
|
||||
bmem = BIO_push (b64, bmem);
|
||||
BIO_read (bmem, buffer, s.size ());
|
||||
BIO_free_all (bmem);
|
||||
|
||||
std::string result (buffer);
|
||||
free (buffer);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool HTTPAuthorized (const std::map<std::string, std::string>& mapHeaders)
|
||||
{
|
||||
std::map<std::string, std::string>::const_iterator it = mapHeaders.find ("authorization");
|
||||
|
||||
if ((it == mapHeaders.end ()) || (it->second.substr (0, 6) != "Basic "))
|
||||
return getConfig ().RPC_USER.empty () && getConfig ().RPC_PASSWORD.empty ();
|
||||
|
||||
std::string strUserPass64 = it->second.substr (6);
|
||||
boost::trim (strUserPass64);
|
||||
std::string strUserPass = DecodeBase64 (strUserPass64);
|
||||
std::string::size_type nColon = strUserPass.find (":");
|
||||
|
||||
if (nColon == std::string::npos)
|
||||
return false;
|
||||
|
||||
std::string strUser = strUserPass.substr (0, nColon);
|
||||
std::string strPassword = strUserPass.substr (nColon + 1);
|
||||
return (strUser == getConfig ().RPC_USER) && (strPassword == getConfig ().RPC_PASSWORD);
|
||||
}
|
||||
|
||||
//
|
||||
// JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility,
|
||||
// but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were
|
||||
// unspecified (HTTP errors and contents of 'error').
|
||||
//
|
||||
// 1.0 spec: http://json-rpc.org/wiki/specification
|
||||
// 1.2 spec: http://groups.google.com/group/json-rpc/web/json-rpc-over-http
|
||||
//
|
||||
|
||||
std::string JSONRPCRequest (const std::string& strMethod, const Json::Value& params, const Json::Value& id)
|
||||
{
|
||||
Json::Value request;
|
||||
request[jss::method] = strMethod;
|
||||
request[jss::params] = params;
|
||||
request[jss::id] = id;
|
||||
Json::FastWriter writer;
|
||||
return writer.write (request) + "\n";
|
||||
}
|
||||
|
||||
std::string JSONRPCReply (const Json::Value& result, const Json::Value& error, const Json::Value& id)
|
||||
{
|
||||
Json::Value reply (Json::objectValue);
|
||||
reply[jss::result] = result;
|
||||
//reply["error"]=error;
|
||||
//reply["id"]=id;
|
||||
Json::FastWriter writer;
|
||||
return writer.write (reply) + "\n";
|
||||
}
|
||||
|
||||
void ErrorReply (std::ostream& stream, const Json::Value& objError, const Json::Value& id)
|
||||
{
|
||||
// Send error reply from json-rpc error object
|
||||
int nStatus = 500;
|
||||
int code = objError[jss::code].asInt ();
|
||||
|
||||
if (code == -32600) nStatus = 400;
|
||||
else if (code == -32601) nStatus = 404;
|
||||
|
||||
std::string strReply = JSONRPCReply (Json::Value (), objError, id);
|
||||
stream << HTTPReply (nStatus, strReply) << std::flush;
|
||||
}
|
||||
|
||||
} // ripple
|
||||
|
||||
53
src/ripple/module/net/rpc/RPCUtil.h
Normal file
53
src/ripple/module/net/rpc/RPCUtil.h
Normal file
@@ -0,0 +1,53 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_NET_RPC_RPCUTIL_H_INCLUDED
|
||||
#define RIPPLE_NET_RPC_RPCUTIL_H_INCLUDED
|
||||
|
||||
namespace ripple {
|
||||
|
||||
// VFALCO TODO Wrap these up into a class. It looks like they just do some
|
||||
// convenience packaging of JSON data from the pieces. It looks
|
||||
// Ripple client protocol-specific.
|
||||
//
|
||||
extern std::string JSONRPCRequest (const std::string& strMethod, const Json::Value& params,
|
||||
const Json::Value& id);
|
||||
|
||||
extern std::string JSONRPCReply (const Json::Value& result, const Json::Value& error, const Json::Value& id);
|
||||
|
||||
extern Json::Value JSONRPCError (int code, const std::string& message);
|
||||
|
||||
extern std::string createHTTPPost (const std::string& strHost, const std::string& strPath, const std::string& strMsg,
|
||||
const std::map<std::string, std::string>& mapRequestHeaders);
|
||||
|
||||
extern std::string HTTPReply (int nStatus, const std::string& strMsg);
|
||||
|
||||
// VFALCO TODO Create a HTTPHeaders class with a nice interface instead of the std::map
|
||||
//
|
||||
extern bool HTTPAuthorized (std::map <std::string, std::string> const& mapHeaders);
|
||||
|
||||
// VFALCO NOTE This one looks like it does some sort of stream i/o
|
||||
//
|
||||
extern int ReadHTTP (std::basic_istream<char>& stream,
|
||||
std::map<std::string, std::string>& mapHeadersRet,
|
||||
std::string& strMessageRet);
|
||||
|
||||
} // ripple
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user