aboutsummaryrefslogtreecommitdiff
path: root/src/rest.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/rest.cpp')
-rw-r--r--src/rest.cpp228
1 files changed, 183 insertions, 45 deletions
diff --git a/src/rest.cpp b/src/rest.cpp
index 122b36171..adc2d5628 100644
--- a/src/rest.cpp
+++ b/src/rest.cpp
@@ -1,10 +1,10 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2014 The Bitcoin developers
+// Copyright (c) 2009-2014 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include "core/block.h"
-#include "core/transaction.h"
+#include "primitives/block.h"
+#include "primitives/transaction.h"
#include "main.h"
#include "rpcserver.h"
#include "streams.h"
@@ -18,6 +18,7 @@ using namespace std;
using namespace json_spirit;
enum RetFormat {
+ RF_UNDEF,
RF_BINARY,
RF_HEX,
RF_JSON,
@@ -25,21 +26,23 @@ enum RetFormat {
static const struct {
enum RetFormat rf;
- const char *name;
+ const char* name;
} rf_names[] = {
- { RF_BINARY, "binary" }, // default, if match not found
- { RF_HEX, "hex" },
- { RF_JSON, "json" },
+ {RF_UNDEF, ""},
+ {RF_BINARY, "bin"},
+ {RF_HEX, "hex"},
+ {RF_JSON, "json"},
};
-class RestErr {
+class RestErr
+{
public:
enum HTTPStatusCode status;
string message;
};
extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry);
-extern Object blockToJSON(const CBlock& block, const CBlockIndex* blockindex);
+extern Object blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool txDetails = false);
static RestErr RESTERR(enum HTTPStatusCode status, string message)
{
@@ -49,15 +52,34 @@ static RestErr RESTERR(enum HTTPStatusCode status, string message)
return re;
}
-static enum RetFormat ParseDataFormat(const string& format)
+static enum RetFormat ParseDataFormat(vector<string>& params, const string strReq)
{
- for (unsigned int i = 0; i < ARRAYLEN(rf_names); i++)
- if (format == rf_names[i].name)
- return rf_names[i].rf;
+ boost::split(params, strReq, boost::is_any_of("."));
+ if (params.size() > 1) {
+ for (unsigned int i = 0; i < ARRAYLEN(rf_names); i++)
+ if (params[1] == rf_names[i].name)
+ return rf_names[i].rf;
+ }
return rf_names[0].rf;
}
+static string AvailableDataFormatsString()
+{
+ string formats = "";
+ for (unsigned int i = 0; i < ARRAYLEN(rf_names); i++)
+ if (strlen(rf_names[i].name) > 0) {
+ formats.append(".");
+ formats.append(rf_names[i].name);
+ formats.append(", ");
+ }
+
+ if (formats.length() > 0)
+ return formats.substr(0, formats.length() - 2);
+
+ return formats;
+}
+
static bool ParseHashStr(const string& strReq, uint256& v)
{
if (!IsHex(strReq) || (strReq.size() != 64))
@@ -67,15 +89,77 @@ static bool ParseHashStr(const string& strReq, uint256& v)
return true;
}
-static bool rest_block(AcceptedConnection *conn,
- string& strReq,
- map<string, string>& mapHeaders,
- bool fRun)
+static bool rest_headers(AcceptedConnection* conn,
+ const std::string& strReq,
+ const std::map<std::string, std::string>& mapHeaders,
+ bool fRun)
{
vector<string> params;
- boost::split(params, strReq, boost::is_any_of("/"));
+ const RetFormat rf = ParseDataFormat(params, strReq);
+ vector<string> path;
+ boost::split(path, params[0], boost::is_any_of("/"));
+
+ if (path.size() != 2)
+ throw RESTERR(HTTP_BAD_REQUEST, "No header count specified. Use /rest/headers/<count>/<hash>.<ext>.");
+
+ long count = strtol(path[0].c_str(), NULL, 10);
+ if (count < 1 || count > 2000)
+ throw RESTERR(HTTP_BAD_REQUEST, "Header count out of range: " + path[0]);
+
+ string hashStr = path[1];
+ uint256 hash;
+ if (!ParseHashStr(hashStr, hash))
+ throw RESTERR(HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
+
+ std::vector<CBlockHeader> headers;
+ headers.reserve(count);
+ {
+ LOCK(cs_main);
+ BlockMap::const_iterator it = mapBlockIndex.find(hash);
+ const CBlockIndex *pindex = (it != mapBlockIndex.end()) ? it->second : NULL;
+ while (pindex != NULL && chainActive.Contains(pindex)) {
+ headers.push_back(pindex->GetBlockHeader());
+ if (headers.size() == (unsigned long)count)
+ break;
+ pindex = chainActive.Next(pindex);
+ }
+ }
+
+ CDataStream ssHeader(SER_NETWORK, PROTOCOL_VERSION);
+ BOOST_FOREACH(const CBlockHeader &header, headers) {
+ ssHeader << header;
+ }
+
+ switch (rf) {
+ case RF_BINARY: {
+ string binaryHeader = ssHeader.str();
+ conn->stream() << HTTPReplyHeader(HTTP_OK, fRun, binaryHeader.size(), "application/octet-stream") << binaryHeader << std::flush;
+ return true;
+ }
+
+ case RF_HEX: {
+ string strHex = HexStr(ssHeader.begin(), ssHeader.end()) + "\n";
+ conn->stream() << HTTPReply(HTTP_OK, strHex, fRun, false, "text/plain") << std::flush;
+ return true;
+ }
+
+ default: {
+ throw RESTERR(HTTP_NOT_FOUND, "output format not found (available: .bin, .hex)");
+ }
+ }
- enum RetFormat rf = ParseDataFormat(params.size() > 1 ? params[1] : string(""));
+ // not reached
+ return true; // continue to process further HTTP reqs on this cxn
+}
+
+static bool rest_block(AcceptedConnection* conn,
+ const std::string& strReq,
+ const std::map<std::string, std::string>& mapHeaders,
+ bool fRun,
+ bool showTxDetails)
+{
+ vector<string> params;
+ const RetFormat rf = ParseDataFormat(params, strReq);
string hashStr = params[0];
uint256 hash;
@@ -100,37 +184,81 @@ static bool rest_block(AcceptedConnection *conn,
switch (rf) {
case RF_BINARY: {
string binaryBlock = ssBlock.str();
- conn->stream() << HTTPReply(HTTP_OK, binaryBlock, fRun, true, "application/octet-stream") << binaryBlock << std::flush;
+ conn->stream() << HTTPReplyHeader(HTTP_OK, fRun, binaryBlock.size(), "application/octet-stream") << binaryBlock << std::flush;
return true;
}
case RF_HEX: {
- string strHex = HexStr(ssBlock.begin(), ssBlock.end()) + "\n";;
+ string strHex = HexStr(ssBlock.begin(), ssBlock.end()) + "\n";
conn->stream() << HTTPReply(HTTP_OK, strHex, fRun, false, "text/plain") << std::flush;
return true;
}
case RF_JSON: {
- Object objBlock = blockToJSON(block, pblockindex);
+ Object objBlock = blockToJSON(block, pblockindex, showTxDetails);
string strJSON = write_string(Value(objBlock), false) + "\n";
conn->stream() << HTTPReply(HTTP_OK, strJSON, fRun) << std::flush;
return true;
- }
+ }
+
+ default: {
+ throw RESTERR(HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
+ }
}
// not reached
- return true; // continue to process further HTTP reqs on this cxn
+ return true; // continue to process further HTTP reqs on this cxn
}
-static bool rest_tx(AcceptedConnection *conn,
- string& strReq,
- map<string, string>& mapHeaders,
- bool fRun)
+static bool rest_block_extended(AcceptedConnection* conn,
+ const std::string& strReq,
+ const std::map<std::string, std::string>& mapHeaders,
+ bool fRun)
+{
+ return rest_block(conn, strReq, mapHeaders, fRun, true);
+}
+
+static bool rest_block_notxdetails(AcceptedConnection* conn,
+ const std::string& strReq,
+ const std::map<std::string, std::string>& mapHeaders,
+ bool fRun)
+{
+ return rest_block(conn, strReq, mapHeaders, fRun, false);
+}
+
+static bool rest_chaininfo(AcceptedConnection* conn,
+ const std::string& strReq,
+ const std::map<std::string, std::string>& mapHeaders,
+ bool fRun)
{
vector<string> params;
- boost::split(params, strReq, boost::is_any_of("/"));
+ const RetFormat rf = ParseDataFormat(params, strReq);
+
+ switch (rf) {
+ case RF_JSON: {
+ Array rpcParams;
+ Value chainInfoObject = getblockchaininfo(rpcParams, false);
+
+ string strJSON = write_string(chainInfoObject, false) + "\n";
+ conn->stream() << HTTPReply(HTTP_OK, strJSON, fRun) << std::flush;
+ return true;
+ }
+ default: {
+ throw RESTERR(HTTP_NOT_FOUND, "output format not found (available: json)");
+ }
+ }
+
+ // not reached
+ return true; // continue to process further HTTP reqs on this cxn
+}
- enum RetFormat rf = ParseDataFormat(params.size() > 1 ? params[1] : string(""));
+static bool rest_tx(AcceptedConnection* conn,
+ const std::string& strReq,
+ const std::map<std::string, std::string>& mapHeaders,
+ bool fRun)
+{
+ vector<string> params;
+ const RetFormat rf = ParseDataFormat(params, strReq);
string hashStr = params[0];
uint256 hash;
@@ -138,7 +266,7 @@ static bool rest_tx(AcceptedConnection *conn,
throw RESTERR(HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
CTransaction tx;
- uint256 hashBlock = 0;
+ uint256 hashBlock = uint256();
if (!GetTransaction(hash, tx, hashBlock, true))
throw RESTERR(HTTP_NOT_FOUND, hashStr + " not found");
@@ -148,12 +276,12 @@ static bool rest_tx(AcceptedConnection *conn,
switch (rf) {
case RF_BINARY: {
string binaryTx = ssTx.str();
- conn->stream() << HTTPReply(HTTP_OK, binaryTx, fRun, true, "application/octet-stream") << binaryTx << std::flush;
+ conn->stream() << HTTPReplyHeader(HTTP_OK, fRun, binaryTx.size(), "application/octet-stream") << binaryTx << std::flush;
return true;
}
case RF_HEX: {
- string strHex = HexStr(ssTx.begin(), ssTx.end()) + "\n";;
+ string strHex = HexStr(ssTx.begin(), ssTx.end()) + "\n";
conn->stream() << HTTPReply(HTTP_OK, strHex, fRun, false, "text/plain") << std::flush;
return true;
}
@@ -165,29 +293,40 @@ static bool rest_tx(AcceptedConnection *conn,
conn->stream() << HTTPReply(HTTP_OK, strJSON, fRun) << std::flush;
return true;
}
+
+ default: {
+ throw RESTERR(HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
+ }
}
// not reached
- return true; // continue to process further HTTP reqs on this cxn
+ return true; // continue to process further HTTP reqs on this cxn
}
static const struct {
- const char *prefix;
- bool (*handler)(AcceptedConnection *conn,
- string& strURI,
- map<string, string>& mapHeaders,
+ const char* prefix;
+ bool (*handler)(AcceptedConnection* conn,
+ const std::string& strURI,
+ const std::map<std::string, std::string>& mapHeaders,
bool fRun);
} uri_prefixes[] = {
- { "/rest/tx/", rest_tx },
- { "/rest/block/", rest_block },
+ {"/rest/tx/", rest_tx},
+ {"/rest/block/notxdetails/", rest_block_notxdetails},
+ {"/rest/block/", rest_block_extended},
+ {"/rest/chaininfo", rest_chaininfo},
+ {"/rest/headers/", rest_headers},
};
-bool HTTPReq_REST(AcceptedConnection *conn,
- string& strURI,
- map<string, string>& mapHeaders,
+bool HTTPReq_REST(AcceptedConnection* conn,
+ const std::string& strURI,
+ const std::map<std::string, std::string>& mapHeaders,
bool fRun)
{
try {
+ std::string statusmessage;
+ if (RPCIsInWarmup(&statusmessage))
+ throw RESTERR(HTTP_SERVICE_UNAVAILABLE, "Service temporarily unavailable: " + statusmessage);
+
for (unsigned int i = 0; i < ARRAYLEN(uri_prefixes); i++) {
unsigned int plen = strlen(uri_prefixes[i].prefix);
if (strURI.substr(0, plen) == uri_prefixes[i].prefix) {
@@ -195,8 +334,7 @@ bool HTTPReq_REST(AcceptedConnection *conn,
return uri_prefixes[i].handler(conn, strReq, mapHeaders, fRun);
}
}
- }
- catch (RestErr& re) {
+ } catch (const RestErr& re) {
conn->stream() << HTTPReply(re.status, re.message + "\r\n", false, false, "text/plain") << std::flush;
return false;
}