Merge branch 'master' of github.com:jedmccaleb/NewCoin

This commit is contained in:
jed
2012-10-18 14:49:18 -07:00
14 changed files with 351 additions and 836 deletions

View File

@@ -88,7 +88,7 @@ PROTO_SRCS = env.Protoc([], 'src/ripple.proto', PROTOCOUTDIR='obj', PROTOCPYTHON
env.Clean(PROTO_SRCS, 'site_scons/site_tools/protoc.pyc')
# Remove unused source files.
UNUSED_SRCS = ['src/HttpReply.cpp']
UNUSED_SRCS = []
for file in UNUSED_SRCS:
NEWCOIN_SRCS.remove(file)

View File

@@ -258,6 +258,15 @@ Amount.prototype.canonicalize = function() {
}
};
Amount.prototype.negate = function () {
if (this.is_native) {
this.value.negate();
}
else {
this.is_negative = !this.is_negative;
}
};
Amount.prototype.to_json = function() {
if (this.is_native) {
return this.to_text();
@@ -400,6 +409,12 @@ Amount.prototype.parse_json = function(j) {
return this;
};
Amount.prototype.parse_issuer = function (issuer) {
this.issuer.parse_json(issuer);
return this;
};
exports.setAccounts = setAccounts;
exports.Amount = Amount;
exports.Currency = Currency;

View File

@@ -16,8 +16,8 @@ var EventEmitter = require('events').EventEmitter;
// npm
var WebSocket = require('ws');
var amount = require('./amount.js');
var Amount = amount.Amount;
var Amount = require('./amount.js').Amount;
var UInt160 = require('./amount.js').UInt160;
// Request events emmitted:
// 'success' : Request successful.
@@ -63,10 +63,23 @@ Request.prototype.request_default = function () {
this.remote.request(this);
};
Request.prototype.ledger_choose = function (current) {
if (current)
{
this.message.ledger_index = this.remote.ledger_current_index;
}
else {
this.message.ledger = this.remote.ledger_closed;
}
return this;
};
// Set the ledger for a request.
// - ledger_entry
Request.prototype.ledger = function (ledger) {
this.message.ledger = ledger;
// - transaction_entry
Request.prototype.ledger_closed = function (ledger) {
this.message.ledger_closed = ledger;
return this;
};
@@ -104,6 +117,15 @@ Request.prototype.transaction = function (t) {
return this;
};
Request.prototype.ripple_state = function (account, issuer, currency) {
this.message.ripple_state = {
'accounts' : [ account, issuer ],
'currency' : currency
};
return this;
};
//
// Remote - access to a remote Ripple server via websocket.
//
@@ -463,9 +485,10 @@ Remote.prototype.request_ledger_entry = function (type) {
// Transparent caching:
request.on('request', function (remote) { // Intercept default request.
if (this.ledger_closed) {
// XXX Initial implementation no caching.
// XXX Add caching.
}
// else if (req.ledger_index)
// else if ('ripple_state' === this.type) // YYY Could be cached per ledger.
else if ('account_root' === this.type) {
var cache = self.ledgers.current.account_root;
@@ -507,17 +530,11 @@ Remote.prototype.request_ledger_entry = function (type) {
return request;
};
// --> ledger_closed : optional
Remote.prototype.request_transaction_entry = function (hash, ledger_closed) {
Remote.prototype.request_transaction_entry = function (hash) {
assert(this.trusted); // If not trusted, need to check proof, maybe talk packet protocol.
var request = new Request(this, 'transaction_entry');
request.message.transaction = hash;
if (ledger_closed)
request.message.ledger_closed = ledger_closed;
return request;
return (new Request(this, 'transaction_entry'))
.transaction(hash);
};
// Submit a transaction.
@@ -587,10 +604,8 @@ Remote.prototype.submit = function (transaction) {
Remote.prototype._server_subscribe = function () {
var self = this;
var request = new Request(this, 'server_subscribe');
request.
on('success', function (message) {
(new Request(this, 'server_subscribe'))
.on('success', function (message) {
self.stand_alone = !!message.stand_alone;
if (message.ledger_closed && message.ledger_current_index) {
@@ -614,9 +629,8 @@ Remote.prototype._server_subscribe = function () {
Remote.prototype.ledger_accept = function () {
if (this.stand_alone || undefined === this.stand_alone)
{
var request = new Request(this, 'ledger_accept');
request.request();
(new Request(this, 'ledger_accept'))
.request();
}
else {
this.emit('error', {
@@ -627,6 +641,17 @@ Remote.prototype.ledger_accept = function () {
return this;
};
// Return a request to refresh the account balance.
Remote.prototype.request_account_balance = function (account, current) {
return (this.request_ledger_entry('account_root'))
.account_root(account)
.ledger_choose(current)
.on('success', function (message) {
// If the caller also waits for 'success', they might run before this.
request.emit('account_balance', message.node.Balance);
});
};
// Return the next account sequence if possible.
// <-- undefined or Sequence
Remote.prototype.account_seq = function (account, advance) {
@@ -648,29 +673,19 @@ Remote.prototype.account_seq_cache = function (account, current) {
var self = this;
var request = this.request_ledger_entry('account_root');
request
return request
.account_root(account)
.ledger_choose(current)
.on('success', function (message) {
var seq = message.node.Sequence;
if (!self.accounts[account])
self.accounts[account] = {};
var seq = message.node.Sequence;
if (!self.accounts[account]) self.accounts[account] = {};
self.accounts[account].seq = seq;
self.accounts[account].seq = seq;
// If the caller also waits for 'success', they might run before this.
request.emit('success_account_seq_cache');
});
if (current)
{
request.ledger_index(this.ledger_current_index);
}
else {
request.ledger(this.ledger_closed);
}
return request;
// If the caller also waits for 'success', they might run before this.
request.emit('success_account_seq_cache');
});
};
// Mark an account's root node as dirty.
@@ -678,6 +693,37 @@ Remote.prototype.dirty_account_root = function (account) {
delete this.ledgers.current.account_root[account];
};
// Return a request to get a ripple balance.
//
// --> account: String
// --> issuer: String
// --> currency: String
// --> current: bool : true = current ledger
Remote.prototype.request_ripple_balance = function (account, issuer, currency, current) {
var src = this.remote.config.accounts[account] ? this.remote.config.accounts[account].account : account;
var dst = this.remote.config.accounts[issuer] ? this.remote.config.accounts[issuer].account : issuer;
return (this.request_ledger_entry('ripple_state')) // YYY Could be cached per ledger.
.ripple_state(src, dst, currency)
.ledger_choose(current)
.on('success', function (message) {
var node = message.node;
var flip = UInt160.from_json(src) == node.HighLimit.issuer;
var issuerLimit = flip ? node.LowLimit : node.HighLimit;
var accountLimit = flip ? node.HighLimit : node.LowLimit;
var issuerBalance = (flip ? node.Balance.clone().negate() : node.Balance.clone()).parse_issuer(dst);
var accountBalance = issuerBalance.clone().parse_issuer(dst);
// If the caller also waits for 'success', they might run before this.
request.emit('ripple_state', {
'issuer_balance' : issuerBalance, // Balance with dst as issuer.
'account_balance' : accountBalance, // Balance with src as issuer.
'issuer_limit' : issuerLimit.clone().parse_issuer(src), // Limit set by issuer with src as issuer.
'account_limit' : accountLimit.clone().parse_issuer(dst) // Limit set by account with dst as issuer.
});
});
}
Remote.prototype.transaction = function () {
return new Transaction(this);
};
@@ -849,7 +895,8 @@ Transaction.prototype.submit = function () {
var stop = false;
// XXX make sure self.hash is available.
self.remote.request_transaction_entry(self.hash, ledger_closed)
self.remote.request_transaction_entry(self.hash)
.ledger_closed(ledger_closed)
.on('success', function (message) {
self.set_state(message.metadata.TransactionResult);
self.emit('final', message);

102
src/HTTPRequest.cpp Normal file
View File

@@ -0,0 +1,102 @@
#include "HTTPRequest.h"
#include <iostream>
#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>
#include "Log.h"
SETUP_LOG();
void HTTPRequest::reset()
{
vHeaders.clear();
sRequestBody.clear();
sAuthorization.clear();
iDataSize = 0;
bShouldClose = true;
eState = await_request;
}
HTTPRequestAction 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";
}
HTTPRequestAction HTTPRequest::consume(boost::asio::streambuf& buf)
{
std::string line;
std::istream is(&buf);
std::getline(is, line);
boost::trim(line);
// cLog(lsTRACE) << "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;
}
vHeaders.push_back(line);
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);
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 = boost::lexical_cast<int>(headerValue);
if (headerName == "authorization")
sAuthorization = headerValue;
}
return haREAD_LINE;
}
assert(false);
return haERROR;
}

61
src/HTTPRequest.h Normal file
View File

@@ -0,0 +1,61 @@
#ifndef HTTPREQUEST__HPP
#define HTTPREQUEST__HPP
#include <string>
#include <vector>
#include <boost/asio/streambuf.hpp>
enum HTTPRequestAction
{ // What the application code needs to do
haERROR = 0,
haREAD_LINE = 1,
haREAD_RAW = 2,
haDO_REQUEST = 3,
haCLOSE_CONN = 4
};
class HTTPRequest
{ // an HTTP request in progress
protected:
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::vector<std::string> vHeaders;
int iDataSize;
bool bShouldClose;
public:
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::vector<std::string>& peekHeaders() { return vHeaders; }
std::string getReplyHeaders(bool forceClose);
HTTPRequestAction consume(boost::asio::streambuf&);
HTTPRequestAction requestDone(bool forceClose); // call after reply is sent
int getDataSize() { return iDataSize; }
};
#endif

View File

@@ -1,241 +0,0 @@
#include "HttpReply.h"
#include <string>
#include <vector>
#include <boost/lexical_cast.hpp>
namespace status_strings
{
const std::string ok =
"HTTP/1.0 200 OK\r\n";
const std::string created =
"HTTP/1.0 201 Created\r\n";
const std::string accepted =
"HTTP/1.0 202 Accepted\r\n";
const std::string no_content =
"HTTP/1.0 204 No Content\r\n";
const std::string multiple_choices =
"HTTP/1.0 300 Multiple Choices\r\n";
const std::string moved_permanently =
"HTTP/1.0 301 Moved Permanently\r\n";
const std::string moved_temporarily =
"HTTP/1.0 302 Moved Temporarily\r\n";
const std::string not_modified =
"HTTP/1.0 304 Not Modified\r\n";
const std::string bad_request =
"HTTP/1.0 400 Bad Request\r\n";
const std::string unauthorized =
"HTTP/1.0 401 Unauthorized\r\n";
const std::string forbidden =
"HTTP/1.0 403 Forbidden\r\n";
const std::string not_found =
"HTTP/1.0 404 Not Found\r\n";
const std::string internal_server_error =
"HTTP/1.0 500 Internal Server Error\r\n";
const std::string not_implemented =
"HTTP/1.0 501 Not Implemented\r\n";
const std::string bad_gateway =
"HTTP/1.0 502 Bad Gateway\r\n";
const std::string service_unavailable =
"HTTP/1.0 503 Service Unavailable\r\n";
boost::asio::const_buffer to_buffer(HttpReply::status_type status)
{
switch (status)
{
case HttpReply::ok:
return boost::asio::buffer(ok);
case HttpReply::created:
return boost::asio::buffer(created);
case HttpReply::accepted:
return boost::asio::buffer(accepted);
case HttpReply::no_content:
return boost::asio::buffer(no_content);
case HttpReply::multiple_choices:
return boost::asio::buffer(multiple_choices);
case HttpReply::moved_permanently:
return boost::asio::buffer(moved_permanently);
case HttpReply::moved_temporarily:
return boost::asio::buffer(moved_temporarily);
case HttpReply::not_modified:
return boost::asio::buffer(not_modified);
case HttpReply::bad_request:
return boost::asio::buffer(bad_request);
case HttpReply::unauthorized:
return boost::asio::buffer(unauthorized);
case HttpReply::forbidden:
return boost::asio::buffer(forbidden);
case HttpReply::not_found:
return boost::asio::buffer(not_found);
case HttpReply::internal_server_error:
return boost::asio::buffer(internal_server_error);
case HttpReply::not_implemented:
return boost::asio::buffer(not_implemented);
case HttpReply::bad_gateway:
return boost::asio::buffer(bad_gateway);
case HttpReply::service_unavailable:
return boost::asio::buffer(service_unavailable);
default:
return boost::asio::buffer(internal_server_error);
}
}
} // namespace status_strings
namespace misc_strings
{
const char name_value_separator[] = { ':', ' ' };
const char crlf[] = { '\r', '\n' };
} // namespace misc_strings
std::vector<boost::asio::const_buffer> HttpReply::to_buffers()
{
std::vector<boost::asio::const_buffer> buffers;
buffers.push_back(status_strings::to_buffer(status));
for (std::size_t i = 0; i < headers.size(); ++i)
{
HttpHeader& h = headers[i];
buffers.push_back(boost::asio::buffer(h.name));
buffers.push_back(boost::asio::buffer(misc_strings::name_value_separator));
buffers.push_back(boost::asio::buffer(h.value));
buffers.push_back(boost::asio::buffer(misc_strings::crlf));
}
buffers.push_back(boost::asio::buffer(misc_strings::crlf));
buffers.push_back(boost::asio::buffer(content));
return buffers;
}
namespace stock_replies {
const char ok[] = "";
const char created[] =
"<html>"
"<head><title>Created</title></head>"
"<body><h1>201 Created</h1></body>"
"</html>";
const char accepted[] =
"<html>"
"<head><title>Accepted</title></head>"
"<body><h1>202 Accepted</h1></body>"
"</html>";
const char no_content[] =
"<html>"
"<head><title>No Content</title></head>"
"<body><h1>204 Content</h1></body>"
"</html>";
const char multiple_choices[] =
"<html>"
"<head><title>Multiple Choices</title></head>"
"<body><h1>300 Multiple Choices</h1></body>"
"</html>";
const char moved_permanently[] =
"<html>"
"<head><title>Moved Permanently</title></head>"
"<body><h1>301 Moved Permanently</h1></body>"
"</html>";
const char moved_temporarily[] =
"<html>"
"<head><title>Moved Temporarily</title></head>"
"<body><h1>302 Moved Temporarily</h1></body>"
"</html>";
const char not_modified[] =
"<html>"
"<head><title>Not Modified</title></head>"
"<body><h1>304 Not Modified</h1></body>"
"</html>";
const char bad_request[] =
"<html>"
"<head><title>Bad Request</title></head>"
"<body><h1>400 Bad Request</h1></body>"
"</html>";
const char unauthorized[] =
"<html>"
"<head><title>Unauthorized</title></head>"
"<body><h1>401 Unauthorized</h1></body>"
"</html>";
const char forbidden[] =
"<html>"
"<head><title>Forbidden</title></head>"
"<body><h1>403 Forbidden</h1></body>"
"</html>";
const char not_found[] =
"<html>"
"<head><title>Not Found</title></head>"
"<body><h1>404 Not Found</h1></body>"
"</html>";
const char internal_server_error[] =
"<html>"
"<head><title>Internal Server Error</title></head>"
"<body><h1>500 Internal Server Error</h1></body>"
"</html>";
const char not_implemented[] =
"<html>"
"<head><title>Not Implemented</title></head>"
"<body><h1>501 Not Implemented</h1></body>"
"</html>";
const char bad_gateway[] =
"<html>"
"<head><title>Bad Gateway</title></head>"
"<body><h1>502 Bad Gateway</h1></body>"
"</html>";
const char service_unavailable[] =
"<html>"
"<head><title>Service Unavailable</title></head>"
"<body><h1>503 Service Unavailable</h1></body>"
"</html>";
std::string to_string(HttpReply::status_type status)
{
switch (status)
{
case HttpReply::ok:
return ok;
case HttpReply::created:
return created;
case HttpReply::accepted:
return accepted;
case HttpReply::no_content:
return no_content;
case HttpReply::multiple_choices:
return multiple_choices;
case HttpReply::moved_permanently:
return moved_permanently;
case HttpReply::moved_temporarily:
return moved_temporarily;
case HttpReply::not_modified:
return not_modified;
case HttpReply::bad_request:
return bad_request;
case HttpReply::unauthorized:
return unauthorized;
case HttpReply::forbidden:
return forbidden;
case HttpReply::not_found:
return not_found;
case HttpReply::internal_server_error:
return internal_server_error;
case HttpReply::not_implemented:
return not_implemented;
case HttpReply::bad_gateway:
return bad_gateway;
case HttpReply::service_unavailable:
return service_unavailable;
default:
return internal_server_error;
}
}
} // namespace stock_replies
HttpReply HttpReply::stock_reply(HttpReply::status_type status)
{
HttpReply rep;
rep.status = status;
rep.content = stock_replies::to_string(status);
rep.headers.resize(2);
rep.headers[0].name = "Content-Length";
rep.headers[0].value = boost::lexical_cast<std::string>(rep.content.size());
rep.headers[1].name = "Content-Type";
rep.headers[1].value = "text/html";
return rep;
}

View File

@@ -1,50 +0,0 @@
#ifndef HTTP_REPLY_HPP
#define HTTP_REPLY_HPP
#include <string>
#include <vector>
#include <boost/asio.hpp>
#include "HttpRequest.h"
/// A reply to be sent to a client.
class HttpReply
{
public:
/// The status of the reply.
enum status_type
{
ok = 200,
created = 201,
accepted = 202,
no_content = 204,
multiple_choices = 300,
moved_permanently = 301,
moved_temporarily = 302,
not_modified = 304,
bad_request = 400,
unauthorized = 401,
forbidden = 403,
not_found = 404,
internal_server_error = 500,
not_implemented = 501,
bad_gateway = 502,
service_unavailable = 503
} status;
/// The headers to be included in the reply.
std::vector<HttpHeader> headers;
/// The content to be sent in the reply.
std::string content;
/// Convert the reply into a vector of buffers. The buffers do not own the
/// underlying memory blocks, therefore the reply object must remain valid and
/// not be changed until the write operation has completed.
std::vector<boost::asio::const_buffer> to_buffers();
/// Get a stock reply.
static HttpReply stock_reply(status_type status);
};
#endif // HTTP_REPLY_HPP

View File

@@ -1,24 +0,0 @@
#ifndef HTTP_REQUEST_HPP
#define HTTP_REQUEST_HPP
#include <string>
#include <vector>
struct HttpHeader
{
std::string name;
std::string value;
};
/// A request received from a client.
struct HttpRequest
{
std::string method;
std::string uri;
int http_version_major;
int http_version_minor;
std::vector<HttpHeader> headers;
std::string mBody;
};
#endif // HTTP_REQUEST_HPP

View File

@@ -1,7 +1,5 @@
#include "RPCServer.h"
#include "RequestParser.h"
#include "HttpReply.h"
#include "HttpsClient.h"
#include "Application.h"
#include "RPC.h"
@@ -22,6 +20,7 @@
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/asio/read_until.hpp>
#include <openssl/md5.h>
@@ -30,6 +29,10 @@
SETUP_LOG();
#ifndef RPC_MAXIMUM_QUERY
#define RPC_MAXIMUM_QUERY (1024*1024)
#endif
RPCServer::RPCServer(boost::asio::io_service& io_service , NetworkOPs* nopNetwork)
: mNetOps(nopNetwork), mSocket(io_service)
{
@@ -107,45 +110,79 @@ Json::Value RPCServer::RPCError(int iError)
void RPCServer::connected()
{
//std::cout << "RPC request" << std::endl;
if (mSocket.remote_endpoint().address().to_string()=="127.0.0.1") mRole=ADMIN;
else mRole=GUEST;
if (mSocket.remote_endpoint().address().to_string()=="127.0.0.1") mRole = ADMIN;
else mRole = GUEST;
mSocket.async_read_some(boost::asio::buffer(mReadBuffer),
boost::bind(&RPCServer::Shandle_read, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
boost::asio::async_read_until(mSocket, mLineBuffer, "\r\n",
boost::bind(&RPCServer::handle_read_line, shared_from_this(), boost::asio::placeholders::error));
}
void RPCServer::handle_read(const boost::system::error_code& e,
std::size_t bytes_transferred)
void RPCServer::handle_read_req(const boost::system::error_code& e)
{
if (!e)
std::string req;
if (mLineBuffer.size())
{
boost::tribool result;
result = mRequestParser.parse(
mIncomingRequest, mReadBuffer.data(), mReadBuffer.data() + bytes_transferred);
req.assign(boost::asio::buffer_cast<const char*>(mLineBuffer.data()), mLineBuffer.size());
mLineBuffer.consume(mLineBuffer.size());
}
if (result)
req += strCopy(mQueryVec);
mReplyStr = handleRequest(req);
boost::asio::async_write(mSocket, boost::asio::buffer(mReplyStr),
boost::bind(&RPCServer::handle_write, shared_from_this(), boost::asio::placeholders::error));
}
void RPCServer::handle_read_line(const boost::system::error_code& e)
{
if (e)
return;
HTTPRequestAction action = mHTTPRequest.consume(mLineBuffer);
if (action == haDO_REQUEST)
{ // request with no body
cLog(lsWARNING) << "RPC HTTP request with no body";
boost::system::error_code ignore_ec;
mSocket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignore_ec);
return;
}
else if (action == haREAD_LINE)
{
boost::asio::async_read_until(mSocket, mLineBuffer, "\r\n",
boost::bind(&RPCServer::handle_read_line, shared_from_this(),
boost::asio::placeholders::error));
}
else if (action == haREAD_RAW)
{
int rLen = mHTTPRequest.getDataSize();
if ((rLen < 0) || (rLen > RPC_MAXIMUM_QUERY))
{
mReplyStr = handleRequest(mIncomingRequest.mBody);
sendReply();
cLog(lsWARNING) << "Illegal RPC request length " << rLen;
boost::system::error_code ignore_ec;
mSocket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignore_ec);
return;
}
else if (!result)
{ // bad request
std::cout << "bad request: " << mIncomingRequest.mBody <<std::endl;
int alreadyHave = mLineBuffer.size();
if (alreadyHave < rLen)
{
mQueryVec.resize(rLen - alreadyHave);
boost::asio::async_read(mSocket, boost::asio::buffer(mQueryVec),
boost::bind(&RPCServer::handle_read_req, shared_from_this(), boost::asio::placeholders::error));
cLog(lsTRACE) << "Waiting for completed request: " << rLen;
}
else
{ // not done keep reading
mSocket.async_read_some(boost::asio::buffer(mReadBuffer),
boost::bind(&RPCServer::Shandle_read, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
{ // we have the whole thing
mQueryVec.resize(0);
handle_read_req(e);
}
}
else if (e != boost::asio::error::operation_aborted)
else
{
boost::system::error_code ignore_ec;
mSocket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignore_ec);
}
}
@@ -2769,48 +2806,23 @@ Json::Value RPCServer::doCommand(const std::string& command, Json::Value& params
}
}
void RPCServer::sendReply()
{
//std::cout << "RPC reply: " << mReplyStr << std::endl;
boost::asio::async_write(mSocket, boost::asio::buffer(mReplyStr),
boost::bind(&RPCServer::Shandle_write, shared_from_this(),
boost::asio::placeholders::error));
}
void RPCServer::handle_write(const boost::system::error_code& e)
{
//std::cout << "async_write complete " << e << std::endl;
if (!e)
{
bool keep_alive = (mIncomingRequest.http_version_major == 1) && (mIncomingRequest.http_version_minor >= 1);
BOOST_FOREACH(HttpHeader& h, mIncomingRequest.headers)
{
if (boost::iequals(h.name, "connection"))
{
if (boost::iequals(h.value, "keep-alive"))
keep_alive = true;
if (boost::iequals(h.value, "close"))
keep_alive = false;
}
}
if (keep_alive)
{
mIncomingRequest.method.clear();
mIncomingRequest.uri.clear();
mIncomingRequest.mBody.clear();
mIncomingRequest.headers.clear();
mRequestParser.reset();
mSocket.async_read_some(boost::asio::buffer(mReadBuffer),
boost::bind(&RPCServer::Shandle_read, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
else
HTTPRequestAction action = mHTTPRequest.requestDone(false);
if (action == haCLOSE_CONN)
{
boost::system::error_code ignored_ec;
mSocket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec);
}
else
{
boost::asio::async_read_until(mSocket, mLineBuffer, "\r\n",
boost::bind(&RPCServer::handle_read_line, shared_from_this(), boost::asio::placeholders::error));
}
}
if (e != boost::asio::error::operation_aborted)

View File

@@ -8,8 +8,7 @@
#include "../json/value.h"
#include "HttpRequest.h"
#include "RequestParser.h"
#include "HTTPRequest.h"
#include "NewcoinAddress.h"
#include "NetworkOPs.h"
#include "SerializedLedger.h"
@@ -91,11 +90,12 @@ private:
NetworkOPs* mNetOps;
boost::asio::ip::tcp::socket mSocket;
boost::array<char, 8192> mReadBuffer;
boost::asio::streambuf mLineBuffer;
std::vector<unsigned char> mQueryVec;
std::string mReplyStr;
HttpRequest mIncomingRequest;
HttpRequestParser mRequestParser;
HTTPRequest mHTTPRequest;
enum { GUEST, USER, ADMIN };
int mRole;
@@ -106,15 +106,10 @@ private:
RPCServer& operator=(const RPCServer&); // no implementation
void handle_write(const boost::system::error_code& ec);
static void Shandle_write(pointer This, const boost::system::error_code& ec)
{ This->handle_write(ec); }
void handle_read(const boost::system::error_code& ec, std::size_t bytes_transferred);
static void Shandle_read(pointer This, const boost::system::error_code& ec, std::size_t bytes_transferred)
{ This->handle_read(ec, bytes_transferred); }
void handle_read_line(const boost::system::error_code& ec);
void handle_read_req(const boost::system::error_code& ec);
std::string handleRequest(const std::string& requestStr);
void sendReply();
Json::Value doCommand(const std::string& command, Json::Value& params);
int getParamCount(const Json::Value& params);

View File

@@ -1,330 +0,0 @@
#include "RequestParser.h"
#include "HttpRequest.h"
HttpRequestParser::HttpRequestParser()
: state_(method_start)
{
}
void HttpRequestParser::reset()
{
state_ = method_start;
}
//template <typename InputIterator>
boost::tribool HttpRequestParser::parse(HttpRequest& req,
char* begin, char* end)
{
while (begin != end)
{
boost::tribool result = consume(req, *begin++);
if (result || !result)
{
std::string temp(begin,end);
req.mBody=temp;
return result;
}
}
boost::tribool result = boost::indeterminate;
return result;
}
boost::tribool HttpRequestParser::consume(HttpRequest& req, char input)
{
switch (state_)
{
case method_start:
if (!is_char(input) || is_ctl(input) || is_tspecial(input))
{
return false;
}
else
{
state_ = method;
req.method.push_back(input);
return boost::indeterminate;
}
case method:
if (input == ' ')
{
state_ = uri;
return boost::indeterminate;
}
else if (!is_char(input) || is_ctl(input) || is_tspecial(input))
{
return false;
}
else
{
req.method.push_back(input);
return boost::indeterminate;
}
case uri_start:
if (is_ctl(input))
{
return false;
}
else
{
state_ = uri;
req.uri.push_back(input);
return boost::indeterminate;
}
case uri:
if (input == ' ')
{
state_ = http_version_h;
return boost::indeterminate;
}
else if (is_ctl(input))
{
return false;
}
else
{
req.uri.push_back(input);
return boost::indeterminate;
}
case http_version_h:
if (input == 'H')
{
state_ = http_version_t_1;
return boost::indeterminate;
}
else
{
return false;
}
case http_version_t_1:
if (input == 'T')
{
state_ = http_version_t_2;
return boost::indeterminate;
}
else
{
return false;
}
case http_version_t_2:
if (input == 'T')
{
state_ = http_version_p;
return boost::indeterminate;
}
else
{
return false;
}
case http_version_p:
if (input == 'P')
{
state_ = http_version_slash;
return boost::indeterminate;
}
else
{
return false;
}
case http_version_slash:
if (input == '/')
{
req.http_version_major = 0;
req.http_version_minor = 0;
state_ = http_version_major_start;
return boost::indeterminate;
}
else
{
return false;
}
case http_version_major_start:
if (is_digit(input))
{
req.http_version_major = req.http_version_major * 10 + input - '0';
state_ = http_version_major;
return boost::indeterminate;
}
else
{
return false;
}
case http_version_major:
if (input == '.')
{
state_ = http_version_minor_start;
return boost::indeterminate;
}
else if (is_digit(input))
{
req.http_version_major = req.http_version_major * 10 + input - '0';
return boost::indeterminate;
}
else
{
return false;
}
case http_version_minor_start:
if (is_digit(input))
{
req.http_version_minor = req.http_version_minor * 10 + input - '0';
state_ = http_version_minor;
return boost::indeterminate;
}
else
{
return false;
}
case http_version_minor:
if (input == '\r')
{
state_ = expecting_newline_1;
return boost::indeterminate;
}
else if (is_digit(input))
{
req.http_version_minor = req.http_version_minor * 10 + input - '0';
return boost::indeterminate;
}
else
{
return false;
}
case expecting_newline_1:
if (input == '\n')
{
state_ = header_line_start;
return boost::indeterminate;
}
else
{
return false;
}
case header_line_start:
if (input == '\r')
{
state_ = expecting_newline_3;
return boost::indeterminate;
}
else if (!req.headers.empty() && (input == ' ' || input == '\t'))
{
state_ = header_lws;
return boost::indeterminate;
}
else if (!is_char(input) || is_ctl(input) || is_tspecial(input))
{
return false;
}
else
{
req.headers.push_back(HttpHeader());
req.headers.back().name.push_back(input);
state_ = header_name;
return boost::indeterminate;
}
case header_lws:
if (input == '\r')
{
state_ = expecting_newline_2;
return boost::indeterminate;
}
else if (input == ' ' || input == '\t')
{
return boost::indeterminate;
}
else if (is_ctl(input))
{
return false;
}
else
{
state_ = header_value;
req.headers.back().value.push_back(input);
return boost::indeterminate;
}
case header_name:
if (input == ':')
{
state_ = space_before_header_value;
return boost::indeterminate;
}
else if (!is_char(input) || is_ctl(input) || is_tspecial(input))
{
return false;
}
else
{
req.headers.back().name.push_back(input);
return boost::indeterminate;
}
case space_before_header_value:
if (input == ' ')
{
state_ = header_value;
return boost::indeterminate;
}
else
{
return false;
}
case header_value:
if (input == '\r')
{
state_ = expecting_newline_2;
return boost::indeterminate;
}
else if (is_ctl(input))
{
return false;
}
else
{
req.headers.back().value.push_back(input);
return boost::indeterminate;
}
case expecting_newline_2:
if (input == '\n')
{
state_ = header_line_start;
return boost::indeterminate;
}
else
{
return false;
}
case expecting_newline_3:
return (input == '\n');
default:
return false;
}
}
bool HttpRequestParser::is_char(int c)
{
return c >= 0 && c <= 127;
}
bool HttpRequestParser::is_ctl(int c)
{
return (c >= 0 && c <= 31) || (c == 127);
}
bool HttpRequestParser::is_tspecial(int c)
{
switch (c)
{
case '(': case ')': case '<': case '>': case '@':
case ',': case ';': case ':': case '\\': case '"':
case '/': case '[': case ']': case '?': case '=':
case '{': case '}': case ' ': case '\t':
return true;
default:
return false;
}
}
bool HttpRequestParser::is_digit(int c)
{
return c >= '0' && c <= '9';
}

View File

@@ -1,72 +0,0 @@
#ifndef HTTP_REQUEST_PARSER_HPP
#define HTTP_REQUEST_PARSER_HPP
#include <boost/logic/tribool.hpp>
#include <boost/tuple/tuple.hpp>
struct HttpRequest;
/// Parser for incoming requests.
class HttpRequestParser
{
/// Handle the next character of input.
boost::tribool consume(HttpRequest& req, char input);
/// Check if a byte is an HTTP character.
static bool is_char(int c);
/// Check if a byte is an HTTP control character.
static bool is_ctl(int c);
/// Check if a byte is defined as an HTTP special character.
static bool is_tspecial(int c);
/// Check if a byte is a digit.
static bool is_digit(int c);
/// The current state of the parser.
enum state
{
method_start,
method,
uri_start,
uri,
http_version_h,
http_version_t_1,
http_version_t_2,
http_version_p,
http_version_slash,
http_version_major_start,
http_version_major,
http_version_minor_start,
http_version_minor,
expecting_newline_1,
header_line_start,
header_lws,
header_name,
space_before_header_value,
header_value,
expecting_newline_2,
expecting_newline_3
} state_;
public:
/// Construct ready to parse the request method.
HttpRequestParser();
/// Reset to initial parser state.
void reset();
/// Parse some data. The tribool return value is true when a complete request
/// has been parsed, false if the data is invalid, indeterminate when more
/// data is required. The InputIterator return value indicates how much of the
/// input has been consumed.
//template <typename InputIterator>
boost::tribool parse(HttpRequest& req, char*, char*);
};
#endif // HTTP_REQUEST_PARSER_HPP

View File

@@ -577,7 +577,7 @@ void WSConnection::doLedgerCurrent(Json::Value& jvResult, const Json::Value& jvR
void WSConnection::doLedgerEntry(Json::Value& jvResult, const Json::Value& jvRequest)
{
NetworkOPs& noNetwork = mNetwork;
uint256 uLedger = jvRequest.isMember("ledger") ? uint256(jvRequest["ledger"].asString()) : 0;
uint256 uLedger = jvRequest.isMember("ledger_closed") ? uint256(jvRequest["ledger_closed"].asString()) : 0;
uint32 uLedgerIndex = jvRequest.isMember("ledger_index") && jvRequest["ledger_index"].isNumeric() ? jvRequest["ledger_index"].asUInt() : 0;
Ledger::pointer lpLedger;

View File

@@ -80,7 +80,7 @@ buster.testCase("Remote functions", {
alpha
.request_ledger_entry('account_root')
.ledger(r.ledger_closed)
.ledger_closed(r.ledger_closed)
.account_root("rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh")
.on('success', function (r) {
// console.log("account_root: %s", JSON.stringify(r));
@@ -110,7 +110,7 @@ buster.testCase("Remote functions", {
alpha
.request_ledger_entry('account_root')
.ledger(r.ledger_closed)
.ledger_closed(r.ledger_closed)
.account_root("zHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh")
.on('success', function (r) {
// console.log("account_root: %s", JSON.stringify(r));
@@ -141,7 +141,7 @@ buster.testCase("Remote functions", {
alpha
.request_ledger_entry('account_root')
.ledger(r.ledger_closed)
.ledger_closed(r.ledger_closed)
.account_root(config.accounts.alice.account)
.on('success', function (r) {
// console.log("account_root: %s", JSON.stringify(r));
@@ -171,7 +171,7 @@ buster.testCase("Remote functions", {
alpha
.request_ledger_entry('index')
.ledger(r.ledger_closed)
.ledger_closed(r.ledger_closed)
.account_root(config.accounts.alice.account)
.index("2B6AC232AA4C4BE41BF49D2459FA4A0347E1B543A4C92FCEE0821C0201E2E9A8")
.on('success', function (r) {