diff options
Diffstat (limited to 'src/rest.cpp')
| -rw-r--r-- | src/rest.cpp | 444 |
1 files changed, 257 insertions, 187 deletions
diff --git a/src/rest.cpp b/src/rest.cpp index 1b7954bbf..226e237fc 100644 --- a/src/rest.cpp +++ b/src/rest.cpp @@ -3,9 +3,11 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include "chain.h" #include "primitives/block.h" #include "primitives/transaction.h" #include "main.h" +#include "httpserver.h" #include "rpcserver.h" #include "streams.h" #include "sync.h" @@ -16,10 +18,11 @@ #include <boost/algorithm/string.hpp> #include <boost/dynamic_bitset.hpp> +#include "univalue/univalue.h" + using namespace std; -using namespace json_spirit; -static const int MAX_GETUTXOS_OUTPOINTS = 100; //allow a max of 100 outpoints to be queried at once +static const size_t MAX_GETUTXOS_OUTPOINTS = 15; //allow a max of 15 outpoints to be queried at once enum RetFormat { RF_UNDEF, @@ -54,34 +57,38 @@ struct CCoin { } }; -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, bool txDetails = false); -extern void ScriptPubKeyToJSON(const CScript& scriptPubKey, Object& out, bool fIncludeHex); +extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry); +extern UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool txDetails = false); +extern UniValue mempoolInfoToJSON(); +extern UniValue mempoolToJSON(bool fVerbose = false); +extern void ScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex); +extern UniValue blockheaderToJSON(const CBlockIndex* blockindex); -static RestErr RESTERR(enum HTTPStatusCode status, string message) +static bool RESTERR(HTTPRequest* req, enum HTTPStatusCode status, string message) { - RestErr re; - re.status = status; - re.message = message; - return re; + req->WriteHeader("Content-Type", "text/plain"); + req->WriteReply(status, message + "\r\n"); + return false; } -static enum RetFormat ParseDataFormat(vector<string>& params, const string strReq) +static enum RetFormat ParseDataFormat(std::string& param, const std::string& strReq) { - 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; + const std::string::size_type pos = strReq.rfind('.'); + if (pos == std::string::npos) + { + param = strReq; + return rf_names[0].rf; } + param = strReq.substr(0, pos); + const std::string suff(strReq, pos + 1); + + for (unsigned int i = 0; i < ARRAYLEN(rf_names); i++) + if (suff == rf_names[i].name) + return rf_names[i].rf; + + /* If no suffix is found, return original string. */ + param = strReq; return rf_names[0].rf; } @@ -110,37 +117,44 @@ static bool ParseHashStr(const string& strReq, uint256& v) return true; } -static bool rest_headers(AcceptedConnection* conn, - const std::string& strURIPart, - const std::string& strRequest, - const std::map<std::string, std::string>& mapHeaders, - bool fRun) +static bool CheckWarmup(HTTPRequest* req) { - vector<string> params; - const RetFormat rf = ParseDataFormat(params, strURIPart); + std::string statusmessage; + if (RPCIsInWarmup(&statusmessage)) + return RESTERR(req, HTTP_SERVICE_UNAVAILABLE, "Service temporarily unavailable: " + statusmessage); + return true; +} + +static bool rest_headers(HTTPRequest* req, + const std::string& strURIPart) +{ + if (!CheckWarmup(req)) + return false; + std::string param; + const RetFormat rf = ParseDataFormat(param, strURIPart); vector<string> path; - boost::split(path, params[0], boost::is_any_of("/")); + boost::split(path, param, boost::is_any_of("/")); if (path.size() != 2) - throw RESTERR(HTTP_BAD_REQUEST, "No header count specified. Use /rest/headers/<count>/<hash>.<ext>."); + return RESTERR(req, 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]); + return RESTERR(req, 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); + return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr); - std::vector<CBlockHeader> headers; + std::vector<const CBlockIndex *> 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()); + headers.push_back(pindex); if (headers.size() == (unsigned long)count) break; pindex = chainActive.Next(pindex); @@ -148,25 +162,36 @@ static bool rest_headers(AcceptedConnection* conn, } CDataStream ssHeader(SER_NETWORK, PROTOCOL_VERSION); - BOOST_FOREACH(const CBlockHeader &header, headers) { - ssHeader << header; + BOOST_FOREACH(const CBlockIndex *pindex, headers) { + ssHeader << pindex->GetBlockHeader(); } switch (rf) { case RF_BINARY: { string binaryHeader = ssHeader.str(); - conn->stream() << HTTPReplyHeader(HTTP_OK, fRun, binaryHeader.size(), "application/octet-stream") << binaryHeader << std::flush; + req->WriteHeader("Content-Type", "application/octet-stream"); + req->WriteReply(HTTP_OK, binaryHeader); 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; + req->WriteHeader("Content-Type", "text/plain"); + req->WriteReply(HTTP_OK, strHex); + return true; + } + case RF_JSON: { + UniValue jsonHeaders(UniValue::VARR); + BOOST_FOREACH(const CBlockIndex *pindex, headers) { + jsonHeaders.push_back(blockheaderToJSON(pindex)); + } + string strJSON = jsonHeaders.write() + "\n"; + req->WriteHeader("Content-Type", "application/json"); + req->WriteReply(HTTP_OK, strJSON); return true; } - default: { - throw RESTERR(HTTP_NOT_FOUND, "output format not found (available: .bin, .hex)"); + return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: .bin, .hex)"); } } @@ -174,34 +199,32 @@ static bool rest_headers(AcceptedConnection* conn, return true; // continue to process further HTTP reqs on this cxn } -static bool rest_block(AcceptedConnection* conn, +static bool rest_block(HTTPRequest* req, const std::string& strURIPart, - const std::string& strRequest, - const std::map<std::string, std::string>& mapHeaders, - bool fRun, bool showTxDetails) { - vector<string> params; - const RetFormat rf = ParseDataFormat(params, strURIPart); + if (!CheckWarmup(req)) + return false; + std::string hashStr; + const RetFormat rf = ParseDataFormat(hashStr, strURIPart); - string hashStr = params[0]; uint256 hash; if (!ParseHashStr(hashStr, hash)) - throw RESTERR(HTTP_BAD_REQUEST, "Invalid hash: " + hashStr); + return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr); CBlock block; CBlockIndex* pblockindex = NULL; { LOCK(cs_main); if (mapBlockIndex.count(hash) == 0) - throw RESTERR(HTTP_NOT_FOUND, hashStr + " not found"); + return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found"); pblockindex = mapBlockIndex[hash]; if (fHavePruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0) - throw RESTERR(HTTP_NOT_FOUND, hashStr + " not available (pruned data)"); + return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not available (pruned data)"); if (!ReadBlockFromDisk(block, pblockindex)) - throw RESTERR(HTTP_NOT_FOUND, hashStr + " not found"); + return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found"); } CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION); @@ -210,25 +233,28 @@ static bool rest_block(AcceptedConnection* conn, switch (rf) { case RF_BINARY: { string binaryBlock = ssBlock.str(); - conn->stream() << HTTPReplyHeader(HTTP_OK, fRun, binaryBlock.size(), "application/octet-stream") << binaryBlock << std::flush; + req->WriteHeader("Content-Type", "application/octet-stream"); + req->WriteReply(HTTP_OK, binaryBlock); return true; } case RF_HEX: { string strHex = HexStr(ssBlock.begin(), ssBlock.end()) + "\n"; - conn->stream() << HTTPReply(HTTP_OK, strHex, fRun, false, "text/plain") << std::flush; + req->WriteHeader("Content-Type", "text/plain"); + req->WriteReply(HTTP_OK, strHex); return true; } case RF_JSON: { - Object objBlock = blockToJSON(block, pblockindex, showTxDetails); - string strJSON = write_string(Value(objBlock), false) + "\n"; - conn->stream() << HTTPReply(HTTP_OK, strJSON, fRun) << std::flush; + UniValue objBlock = blockToJSON(block, pblockindex, showTxDetails); + string strJSON = objBlock.write() + "\n"; + req->WriteHeader("Content-Type", "application/json"); + req->WriteReply(HTTP_OK, strJSON); return true; } default: { - throw RESTERR(HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")"); + return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")"); } } @@ -236,69 +262,106 @@ static bool rest_block(AcceptedConnection* conn, return true; // continue to process further HTTP reqs on this cxn } -static bool rest_block_extended(AcceptedConnection* conn, - const std::string& strURIPart, - const std::string& strRequest, - const std::map<std::string, std::string>& mapHeaders, - bool fRun) +static bool rest_block_extended(HTTPRequest* req, const std::string& strURIPart) { - return rest_block(conn, strURIPart, strRequest, mapHeaders, fRun, true); + return rest_block(req, strURIPart, true); } -static bool rest_block_notxdetails(AcceptedConnection* conn, - const std::string& strURIPart, - const std::string& strRequest, - const std::map<std::string, std::string>& mapHeaders, - bool fRun) +static bool rest_block_notxdetails(HTTPRequest* req, const std::string& strURIPart) +{ + return rest_block(req, strURIPart, false); +} + +static bool rest_chaininfo(HTTPRequest* req, const std::string& strURIPart) { - return rest_block(conn, strURIPart, strRequest, mapHeaders, fRun, false); + if (!CheckWarmup(req)) + return false; + std::string param; + const RetFormat rf = ParseDataFormat(param, strURIPart); + + switch (rf) { + case RF_JSON: { + UniValue rpcParams(UniValue::VARR); + UniValue chainInfoObject = getblockchaininfo(rpcParams, false); + string strJSON = chainInfoObject.write() + "\n"; + req->WriteHeader("Content-Type", "application/json"); + req->WriteReply(HTTP_OK, strJSON); + return true; + } + default: { + return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: json)"); + } + } + + // not reached + return true; // continue to process further HTTP reqs on this cxn } -static bool rest_chaininfo(AcceptedConnection* conn, - const std::string& strURIPart, - const std::string& strRequest, - const std::map<std::string, std::string>& mapHeaders, - bool fRun) +static bool rest_mempool_info(HTTPRequest* req, const std::string& strURIPart) { - vector<string> params; - const RetFormat rf = ParseDataFormat(params, strURIPart); - + if (!CheckWarmup(req)) + return false; + std::string param; + const RetFormat rf = ParseDataFormat(param, strURIPart); + 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; + UniValue mempoolInfoObject = mempoolInfoToJSON(); + + string strJSON = mempoolInfoObject.write() + "\n"; + req->WriteHeader("Content-Type", "application/json"); + req->WriteReply(HTTP_OK, strJSON); return true; } default: { - throw RESTERR(HTTP_NOT_FOUND, "output format not found (available: json)"); + return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: json)"); } } - + // not reached return true; // continue to process further HTTP reqs on this cxn } -static bool rest_tx(AcceptedConnection* conn, - const std::string& strURIPart, - const std::string& strRequest, - const std::map<std::string, std::string>& mapHeaders, - bool fRun) +static bool rest_mempool_contents(HTTPRequest* req, const std::string& strURIPart) { - vector<string> params; - const RetFormat rf = ParseDataFormat(params, strURIPart); + if (!CheckWarmup(req)) + return false; + std::string param; + const RetFormat rf = ParseDataFormat(param, strURIPart); + + switch (rf) { + case RF_JSON: { + UniValue mempoolObject = mempoolToJSON(true); + + string strJSON = mempoolObject.write() + "\n"; + req->WriteHeader("Content-Type", "application/json"); + req->WriteReply(HTTP_OK, strJSON); + return true; + } + default: { + return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: json)"); + } + } + + // not reached + return true; // continue to process further HTTP reqs on this cxn +} + +static bool rest_tx(HTTPRequest* req, const std::string& strURIPart) +{ + if (!CheckWarmup(req)) + return false; + std::string hashStr; + const RetFormat rf = ParseDataFormat(hashStr, strURIPart); - string hashStr = params[0]; uint256 hash; if (!ParseHashStr(hashStr, hash)) - throw RESTERR(HTTP_BAD_REQUEST, "Invalid hash: " + hashStr); + return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr); CTransaction tx; uint256 hashBlock = uint256(); if (!GetTransaction(hash, tx, hashBlock, true)) - throw RESTERR(HTTP_NOT_FOUND, hashStr + " not found"); + return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found"); CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); ssTx << tx; @@ -306,26 +369,29 @@ static bool rest_tx(AcceptedConnection* conn, switch (rf) { case RF_BINARY: { string binaryTx = ssTx.str(); - conn->stream() << HTTPReplyHeader(HTTP_OK, fRun, binaryTx.size(), "application/octet-stream") << binaryTx << std::flush; + req->WriteHeader("Content-Type", "application/octet-stream"); + req->WriteReply(HTTP_OK, binaryTx); return true; } case RF_HEX: { string strHex = HexStr(ssTx.begin(), ssTx.end()) + "\n"; - conn->stream() << HTTPReply(HTTP_OK, strHex, fRun, false, "text/plain") << std::flush; + req->WriteHeader("Content-Type", "text/plain"); + req->WriteReply(HTTP_OK, strHex); return true; } case RF_JSON: { - Object objTx; + UniValue objTx(UniValue::VOBJ); TxToJSON(tx, hashBlock, objTx); - string strJSON = write_string(Value(objTx), false) + "\n"; - conn->stream() << HTTPReply(HTTP_OK, strJSON, fRun) << std::flush; + string strJSON = objTx.write() + "\n"; + req->WriteHeader("Content-Type", "application/json"); + req->WriteReply(HTTP_OK, strJSON); return true; } default: { - throw RESTERR(HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")"); + return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")"); } } @@ -333,86 +399,99 @@ static bool rest_tx(AcceptedConnection* conn, return true; // continue to process further HTTP reqs on this cxn } -static bool rest_getutxos(AcceptedConnection* conn, - const std::string& strURIPart, - const std::string& strRequest, - const std::map<std::string, std::string>& mapHeaders, - bool fRun) +static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart) { - vector<string> params; - enum RetFormat rf = ParseDataFormat(params, strURIPart); + if (!CheckWarmup(req)) + return false; + std::string param; + const RetFormat rf = ParseDataFormat(param, strURIPart); + + vector<string> uriParts; + if (param.length() > 1) + { + std::string strUriParams = param.substr(1); + boost::split(uriParts, strUriParams, boost::is_any_of("/")); + } // throw exception in case of a empty request - if (strRequest.length() == 0) - throw RESTERR(HTTP_INTERNAL_SERVER_ERROR, "Error: empty request"); + std::string strRequestMutable = req->ReadBody(); + if (strRequestMutable.length() == 0 && uriParts.size() == 0) + return RESTERR(req, HTTP_INTERNAL_SERVER_ERROR, "Error: empty request"); + bool fInputParsed = false; bool fCheckMemPool = false; vector<COutPoint> vOutPoints; // parse/deserialize input // input-format = output-format, rest/getutxos/bin requires binary input, gives binary output, ... - - string strRequestMutable = strRequest; //convert const string to string for allowing hex to bin converting - + + if (uriParts.size() > 0) + { + + //inputs is sent over URI scheme (/rest/getutxos/checkmempool/txid1-n/txid2-n/...) + if (uriParts.size() > 0 && uriParts[0] == "checkmempool") + fCheckMemPool = true; + + for (size_t i = (fCheckMemPool) ? 1 : 0; i < uriParts.size(); i++) + { + uint256 txid; + int32_t nOutput; + std::string strTxid = uriParts[i].substr(0, uriParts[i].find("-")); + std::string strOutput = uriParts[i].substr(uriParts[i].find("-")+1); + + if (!ParseInt32(strOutput, &nOutput) || !IsHex(strTxid)) + return RESTERR(req, HTTP_INTERNAL_SERVER_ERROR, "Parse error"); + + txid.SetHex(strTxid); + vOutPoints.push_back(COutPoint(txid, (uint32_t)nOutput)); + } + + if (vOutPoints.size() > 0) + fInputParsed = true; + else + return RESTERR(req, HTTP_INTERNAL_SERVER_ERROR, "Error: empty request"); + } + switch (rf) { case RF_HEX: { // convert hex to bin, continue then with bin part - std::vector<unsigned char> strRequestV = ParseHex(strRequest); + std::vector<unsigned char> strRequestV = ParseHex(strRequestMutable); strRequestMutable.assign(strRequestV.begin(), strRequestV.end()); } case RF_BINARY: { try { - //deserialize - CDataStream oss(SER_NETWORK, PROTOCOL_VERSION); - oss << strRequestMutable; - oss >> fCheckMemPool; - oss >> vOutPoints; + //deserialize only if user sent a request + if (strRequestMutable.size() > 0) + { + if (fInputParsed) //don't allow sending input over URI and HTTP RAW DATA + return RESTERR(req, HTTP_INTERNAL_SERVER_ERROR, "Combination of URI scheme inputs and raw post data is not allowed"); + + CDataStream oss(SER_NETWORK, PROTOCOL_VERSION); + oss << strRequestMutable; + oss >> fCheckMemPool; + oss >> vOutPoints; + } } catch (const std::ios_base::failure& e) { // abort in case of unreadable binary data - throw RESTERR(HTTP_INTERNAL_SERVER_ERROR, "Parse error"); + return RESTERR(req, HTTP_INTERNAL_SERVER_ERROR, "Parse error"); } break; } case RF_JSON: { - try { - // parse json request - Value valRequest; - if (!read_string(strRequest, valRequest)) - throw RESTERR(HTTP_INTERNAL_SERVER_ERROR, "Parse error"); - - Object jsonObject = valRequest.get_obj(); - const Value& checkMempoolValue = find_value(jsonObject, "checkmempool"); - - if (!checkMempoolValue.is_null()) { - fCheckMemPool = checkMempoolValue.get_bool(); - } - const Value& outpointsValue = find_value(jsonObject, "outpoints"); - if (!outpointsValue.is_null()) { - Array outPoints = outpointsValue.get_array(); - BOOST_FOREACH (const Value& outPoint, outPoints) { - Object outpointObject = outPoint.get_obj(); - uint256 txid = ParseHashO(outpointObject, "txid"); - Value nValue = find_value(outpointObject, "n"); - int nOutput = nValue.get_int(); - vOutPoints.push_back(COutPoint(txid, nOutput)); - } - } - } catch (...) { - // return HTTP 500 if there was a json parsing error - throw RESTERR(HTTP_INTERNAL_SERVER_ERROR, "Parse error"); - } + if (!fInputParsed) + return RESTERR(req, HTTP_INTERNAL_SERVER_ERROR, "Error: empty request"); break; } default: { - throw RESTERR(HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")"); + return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")"); } } // limit max outpoints if (vOutPoints.size() > MAX_GETUTXOS_OUTPOINTS) - throw RESTERR(HTTP_INTERNAL_SERVER_ERROR, strprintf("Error: max outpoints exceeded (max: %d, tried: %d)", MAX_GETUTXOS_OUTPOINTS, vOutPoints.size())); + return RESTERR(req, HTTP_INTERNAL_SERVER_ERROR, strprintf("Error: max outpoints exceeded (max: %d, tried: %d)", MAX_GETUTXOS_OUTPOINTS, vOutPoints.size())); // check spentness and form a bitmap (as well as a JSON capable human-readble string representation) vector<unsigned char> bitmap; @@ -462,7 +541,8 @@ static bool rest_getutxos(AcceptedConnection* conn, ssGetUTXOResponse << chainActive.Height() << chainActive.Tip()->GetBlockHash() << bitmap << outs; string ssGetUTXOResponseString = ssGetUTXOResponse.str(); - conn->stream() << HTTPReplyHeader(HTTP_OK, fRun, ssGetUTXOResponseString.size(), "application/octet-stream") << ssGetUTXOResponseString << std::flush; + req->WriteHeader("Content-Type", "application/octet-stream"); + req->WriteReply(HTTP_OK, ssGetUTXOResponseString); return true; } @@ -471,12 +551,13 @@ static bool rest_getutxos(AcceptedConnection* conn, ssGetUTXOResponse << chainActive.Height() << chainActive.Tip()->GetBlockHash() << bitmap << outs; string strHex = HexStr(ssGetUTXOResponse.begin(), ssGetUTXOResponse.end()) + "\n"; - conn->stream() << HTTPReply(HTTP_OK, strHex, fRun, false, "text/plain") << std::flush; + req->WriteHeader("Content-Type", "text/plain"); + req->WriteReply(HTTP_OK, strHex); return true; } case RF_JSON: { - Object objGetUTXOResponse; + UniValue objGetUTXOResponse(UniValue::VOBJ); // pack in some essentials // use more or less the same output as mentioned in Bip64 @@ -484,15 +565,15 @@ static bool rest_getutxos(AcceptedConnection* conn, objGetUTXOResponse.push_back(Pair("chaintipHash", chainActive.Tip()->GetBlockHash().GetHex())); objGetUTXOResponse.push_back(Pair("bitmap", bitmapStringRepresentation)); - Array utxos; + UniValue utxos(UniValue::VARR); BOOST_FOREACH (const CCoin& coin, outs) { - Object utxo; + UniValue utxo(UniValue::VOBJ); utxo.push_back(Pair("txvers", (int32_t)coin.nTxVer)); utxo.push_back(Pair("height", (int32_t)coin.nHeight)); utxo.push_back(Pair("value", ValueFromAmount(coin.out.nValue))); // include the script in a json output - Object o; + UniValue o(UniValue::VOBJ); ScriptPubKeyToJSON(coin.out.scriptPubKey, o, true); utxo.push_back(Pair("scriptPubKey", o)); utxos.push_back(utxo); @@ -500,12 +581,13 @@ static bool rest_getutxos(AcceptedConnection* conn, objGetUTXOResponse.push_back(Pair("utxos", utxos)); // return json string - string strJSON = write_string(Value(objGetUTXOResponse), false) + "\n"; - conn->stream() << HTTPReply(HTTP_OK, strJSON, fRun) << std::flush; + string strJSON = objGetUTXOResponse.write() + "\n"; + req->WriteHeader("Content-Type", "application/json"); + req->WriteReply(HTTP_OK, strJSON); return true; } default: { - throw RESTERR(HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")"); + return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")"); } } @@ -515,43 +597,31 @@ static bool rest_getutxos(AcceptedConnection* conn, static const struct { const char* prefix; - bool (*handler)(AcceptedConnection* conn, - const std::string& strURIPart, - const std::string& strRequest, - const std::map<std::string, std::string>& mapHeaders, - bool fRun); + bool (*handler)(HTTPRequest* req, const std::string& strReq); } uri_prefixes[] = { {"/rest/tx/", rest_tx}, {"/rest/block/notxdetails/", rest_block_notxdetails}, {"/rest/block/", rest_block_extended}, {"/rest/chaininfo", rest_chaininfo}, + {"/rest/mempool/info", rest_mempool_info}, + {"/rest/mempool/contents", rest_mempool_contents}, {"/rest/headers/", rest_headers}, {"/rest/getutxos", rest_getutxos}, }; -bool HTTPReq_REST(AcceptedConnection* conn, - const std::string& strURI, - const string& strRequest, - const std::map<std::string, std::string>& mapHeaders, - bool fRun) +bool StartREST() { - 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) { - string strURIPart = strURI.substr(plen); - return uri_prefixes[i].handler(conn, strURIPart, strRequest, mapHeaders, fRun); - } - } - } catch (const RestErr& re) { - conn->stream() << HTTPReply(re.status, re.message + "\r\n", false, false, "text/plain") << std::flush; - return false; - } + for (unsigned int i = 0; i < ARRAYLEN(uri_prefixes); i++) + RegisterHTTPHandler(uri_prefixes[i].prefix, false, uri_prefixes[i].handler); + return true; +} - conn->stream() << HTTPError(HTTP_NOT_FOUND, false) << std::flush; - return false; +void InterruptREST() +{ +} + +void StopREST() +{ + for (unsigned int i = 0; i < ARRAYLEN(uri_prefixes); i++) + UnregisterHTTPHandler(uri_prefixes[i].prefix, false); } |