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:
Vinnie Falco
2014-06-03 14:48:34 -07:00
parent 39a387b54c
commit 4f1d1d2a8a
774 changed files with 6924 additions and 10355 deletions

View 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

View 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

View 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

View 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

View 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);
}
}

View 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

View 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

View 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

View 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

View 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

View 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

View 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

File diff suppressed because it is too large Load Diff

View 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

View 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

View 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

View 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

View 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

View 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

View 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