]> Git Repo - VerusCoin.git/blob - src/rest.cpp
Merge pull request #5421
[VerusCoin.git] / src / rest.cpp
1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2014 The Bitcoin developers
3 // Distributed under the MIT software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5
6 #include "primitives/block.h"
7 #include "primitives/transaction.h"
8 #include "main.h"
9 #include "rpcserver.h"
10 #include "streams.h"
11 #include "sync.h"
12 #include "utilstrencodings.h"
13 #include "version.h"
14
15 #include <boost/algorithm/string.hpp>
16
17 using namespace std;
18 using namespace json_spirit;
19
20 enum RetFormat {
21     RF_UNDEF,
22     RF_BINARY,
23     RF_HEX,
24     RF_JSON,
25 };
26
27 static const struct {
28     enum RetFormat rf;
29     const char* name;
30 } rf_names[] = {
31       {RF_UNDEF, ""},
32       {RF_BINARY, "bin"},
33       {RF_HEX, "hex"},
34       {RF_JSON, "json"},
35 };
36
37 class RestErr
38 {
39 public:
40     enum HTTPStatusCode status;
41     string message;
42 };
43
44 extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry);
45 extern Object blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool txDetails = false);
46
47 static RestErr RESTERR(enum HTTPStatusCode status, string message)
48 {
49     RestErr re;
50     re.status = status;
51     re.message = message;
52     return re;
53 }
54
55 static enum RetFormat ParseDataFormat(vector<string>& params, const string strReq)
56 {
57     boost::split(params, strReq, boost::is_any_of("."));
58     if (params.size() > 1) {
59         for (unsigned int i = 0; i < ARRAYLEN(rf_names); i++)
60             if (params[1] == rf_names[i].name)
61                 return rf_names[i].rf;
62     }
63
64     return rf_names[0].rf;
65 }
66
67 static string AvailableDataFormatsString()
68 {
69     string formats = "";
70     for (unsigned int i = 0; i < ARRAYLEN(rf_names); i++)
71         if (strlen(rf_names[i].name) > 0) {
72             formats.append(".");
73             formats.append(rf_names[i].name);
74             formats.append(", ");
75         }
76
77     if (formats.length() > 0)
78         return formats.substr(0, formats.length() - 2);
79
80     return formats;
81 }
82
83 static bool ParseHashStr(const string& strReq, uint256& v)
84 {
85     if (!IsHex(strReq) || (strReq.size() != 64))
86         return false;
87
88     v.SetHex(strReq);
89     return true;
90 }
91
92 static bool rest_headers(AcceptedConnection* conn,
93                          const std::string& strReq,
94                          const std::map<std::string, std::string>& mapHeaders,
95                          bool fRun)
96 {
97     vector<string> params;
98     enum RetFormat rf = ParseDataFormat(params, strReq);
99     vector<string> path;
100     boost::split(path, params[0], boost::is_any_of("/"));
101
102     if (path.size() != 2)
103         throw RESTERR(HTTP_BAD_REQUEST, "No header count specified. Use /rest/headers/<count>/<hash>.<ext>.");
104
105     long count = strtol(path[0].c_str(), NULL, 10);
106     if (count < 1 || count > 2000)
107         throw RESTERR(HTTP_BAD_REQUEST, "Header count out of range: " + path[0]);
108
109     string hashStr = path[1];
110     uint256 hash;
111     if (!ParseHashStr(hashStr, hash))
112         throw RESTERR(HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
113
114     std::vector<CBlockHeader> headers;
115     headers.reserve(count);
116     {
117         LOCK(cs_main);
118         BlockMap::const_iterator it = mapBlockIndex.find(hash);
119         const CBlockIndex *pindex = (it != mapBlockIndex.end()) ? it->second : NULL;
120         while (pindex != NULL && chainActive.Contains(pindex)) {
121             headers.push_back(pindex->GetBlockHeader());
122             if (headers.size() == (unsigned long)count)
123                 break;
124             pindex = chainActive.Next(pindex);
125         }
126     }
127
128     CDataStream ssHeader(SER_NETWORK, PROTOCOL_VERSION);
129     BOOST_FOREACH(const CBlockHeader &header, headers) {
130         ssHeader << header;
131     }
132
133     switch (rf) {
134     case RF_BINARY: {
135         string binaryHeader = ssHeader.str();
136         conn->stream() << HTTPReplyHeader(HTTP_OK, fRun, binaryHeader.size(), "application/octet-stream") << binaryHeader << std::flush;
137         return true;
138     }
139
140     case RF_HEX: {
141         string strHex = HexStr(ssHeader.begin(), ssHeader.end()) + "\n";
142         conn->stream() << HTTPReply(HTTP_OK, strHex, fRun, false, "text/plain") << std::flush;
143         return true;
144     }
145
146     default: {
147         throw RESTERR(HTTP_NOT_FOUND, "output format not found (available: .bin, .hex)");
148     }
149     }
150
151     // not reached
152     return true; // continue to process further HTTP reqs on this cxn
153 }
154
155 static bool rest_block(AcceptedConnection* conn,
156                        const std::string& strReq,
157                        const std::map<std::string, std::string>& mapHeaders,
158                        bool fRun,
159                        bool showTxDetails)
160 {
161     vector<string> params;
162     enum RetFormat rf = ParseDataFormat(params, strReq);
163
164     string hashStr = params[0];
165     uint256 hash;
166     if (!ParseHashStr(hashStr, hash))
167         throw RESTERR(HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
168
169     CBlock block;
170     CBlockIndex* pblockindex = NULL;
171     {
172         LOCK(cs_main);
173         if (mapBlockIndex.count(hash) == 0)
174             throw RESTERR(HTTP_NOT_FOUND, hashStr + " not found");
175
176         pblockindex = mapBlockIndex[hash];
177         if (!ReadBlockFromDisk(block, pblockindex))
178             throw RESTERR(HTTP_NOT_FOUND, hashStr + " not found");
179     }
180
181     CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION);
182     ssBlock << block;
183
184     switch (rf) {
185     case RF_BINARY: {
186         string binaryBlock = ssBlock.str();
187         conn->stream() << HTTPReplyHeader(HTTP_OK, fRun, binaryBlock.size(), "application/octet-stream") << binaryBlock << std::flush;
188         return true;
189     }
190
191     case RF_HEX: {
192         string strHex = HexStr(ssBlock.begin(), ssBlock.end()) + "\n";
193         conn->stream() << HTTPReply(HTTP_OK, strHex, fRun, false, "text/plain") << std::flush;
194         return true;
195     }
196
197     case RF_JSON: {
198         Object objBlock = blockToJSON(block, pblockindex, showTxDetails);
199         string strJSON = write_string(Value(objBlock), false) + "\n";
200         conn->stream() << HTTPReply(HTTP_OK, strJSON, fRun) << std::flush;
201         return true;
202     }
203
204     default: {
205         throw RESTERR(HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
206     }
207     }
208
209     // not reached
210     return true; // continue to process further HTTP reqs on this cxn
211 }
212
213 static bool rest_block_extended(AcceptedConnection* conn,
214                        const std::string& strReq,
215                        const std::map<std::string, std::string>& mapHeaders,
216                        bool fRun)
217 {
218     return rest_block(conn, strReq, mapHeaders, fRun, true);
219 }
220
221 static bool rest_block_notxdetails(AcceptedConnection* conn,
222                        const std::string& strReq,
223                        const std::map<std::string, std::string>& mapHeaders,
224                        bool fRun)
225 {
226     return rest_block(conn, strReq, mapHeaders, fRun, false);
227 }
228
229 static bool rest_tx(AcceptedConnection* conn,
230                     const std::string& strReq,
231                     const std::map<std::string, std::string>& mapHeaders,
232                     bool fRun)
233 {
234     vector<string> params;
235     enum RetFormat rf = ParseDataFormat(params, strReq);
236
237     string hashStr = params[0];
238     uint256 hash;
239     if (!ParseHashStr(hashStr, hash))
240         throw RESTERR(HTTP_BAD_REQUEST, "Invalid hash: " + hashStr);
241
242     CTransaction tx;
243     uint256 hashBlock = 0;
244     if (!GetTransaction(hash, tx, hashBlock, true))
245         throw RESTERR(HTTP_NOT_FOUND, hashStr + " not found");
246
247     CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
248     ssTx << tx;
249
250     switch (rf) {
251     case RF_BINARY: {
252         string binaryTx = ssTx.str();
253         conn->stream() << HTTPReplyHeader(HTTP_OK, fRun, binaryTx.size(), "application/octet-stream") << binaryTx << std::flush;
254         return true;
255     }
256
257     case RF_HEX: {
258         string strHex = HexStr(ssTx.begin(), ssTx.end()) + "\n";
259         conn->stream() << HTTPReply(HTTP_OK, strHex, fRun, false, "text/plain") << std::flush;
260         return true;
261     }
262
263     case RF_JSON: {
264         Object objTx;
265         TxToJSON(tx, hashBlock, objTx);
266         string strJSON = write_string(Value(objTx), false) + "\n";
267         conn->stream() << HTTPReply(HTTP_OK, strJSON, fRun) << std::flush;
268         return true;
269     }
270
271     default: {
272         throw RESTERR(HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
273     }
274     }
275
276     // not reached
277     return true; // continue to process further HTTP reqs on this cxn
278 }
279
280 static const struct {
281     const char* prefix;
282     bool (*handler)(AcceptedConnection* conn,
283                     const std::string& strURI,
284                     const std::map<std::string, std::string>& mapHeaders,
285                     bool fRun);
286 } uri_prefixes[] = {
287       {"/rest/tx/", rest_tx},
288       {"/rest/block/notxdetails/", rest_block_notxdetails},
289       {"/rest/block/", rest_block_extended},
290       {"/rest/headers/", rest_headers},
291 };
292
293 bool HTTPReq_REST(AcceptedConnection* conn,
294                   const std::string& strURI,
295                   const std::map<std::string, std::string>& mapHeaders,
296                   bool fRun)
297 {
298     try {
299         std::string statusmessage;
300         if (RPCIsInWarmup(&statusmessage))
301             throw RESTERR(HTTP_SERVICE_UNAVAILABLE, "Service temporarily unavailable: " + statusmessage);
302
303         for (unsigned int i = 0; i < ARRAYLEN(uri_prefixes); i++) {
304             unsigned int plen = strlen(uri_prefixes[i].prefix);
305             if (strURI.substr(0, plen) == uri_prefixes[i].prefix) {
306                 string strReq = strURI.substr(plen);
307                 return uri_prefixes[i].handler(conn, strReq, mapHeaders, fRun);
308             }
309         }
310     } catch (const RestErr& re) {
311         conn->stream() << HTTPReply(re.status, re.message + "\r\n", false, false, "text/plain") << std::flush;
312         return false;
313     }
314
315     conn->stream() << HTTPError(HTTP_NOT_FOUND, false) << std::flush;
316     return false;
317 }
This page took 0.04899 seconds and 4 git commands to generate.