Merge pull request #5376

8a5c951 [REST] make selection of output-format mandatory, support dot url syntax (Jonas Schnelli)
This commit is contained in:
Wladimir J. van der Laan 2014-12-02 15:31:08 +01:00
commit fec0d100d5
No known key found for this signature in database
GPG Key ID: 74810B012346C9A6
1 changed files with 57 additions and 32 deletions

View File

@ -18,6 +18,7 @@ using namespace std;
using namespace json_spirit; using namespace json_spirit;
enum RetFormat { enum RetFormat {
RF_UNDEF,
RF_BINARY, RF_BINARY,
RF_HEX, RF_HEX,
RF_JSON, RF_JSON,
@ -25,14 +26,16 @@ enum RetFormat {
static const struct { static const struct {
enum RetFormat rf; enum RetFormat rf;
const char *name; const char* name;
} rf_names[] = { } rf_names[] = {
{ RF_BINARY, "binary" }, // default, if match not found {RF_UNDEF, ""},
{ RF_HEX, "hex" }, {RF_BINARY, "bin"},
{ RF_JSON, "json" }, {RF_HEX, "hex"},
{RF_JSON, "json"},
}; };
class RestErr { class RestErr
{
public: public:
enum HTTPStatusCode status; enum HTTPStatusCode status;
string message; string message;
@ -49,15 +52,34 @@ static RestErr RESTERR(enum HTTPStatusCode status, string message)
return re; 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++) boost::split(params, strReq, boost::is_any_of("."));
if (format == rf_names[i].name) if (params.size() > 1) {
return rf_names[i].rf; 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; 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) static bool ParseHashStr(const string& strReq, uint256& v)
{ {
if (!IsHex(strReq) || (strReq.size() != 64)) if (!IsHex(strReq) || (strReq.size() != 64))
@ -67,15 +89,13 @@ static bool ParseHashStr(const string& strReq, uint256& v)
return true; return true;
} }
static bool rest_block(AcceptedConnection *conn, static bool rest_block(AcceptedConnection* conn,
string& strReq, string& strReq,
map<string, string>& mapHeaders, map<string, string>& mapHeaders,
bool fRun) bool fRun)
{ {
vector<string> params; vector<string> params;
boost::split(params, strReq, boost::is_any_of("/")); enum RetFormat rf = ParseDataFormat(params, strReq);
enum RetFormat rf = ParseDataFormat(params.size() > 1 ? params[1] : string(""));
string hashStr = params[0]; string hashStr = params[0];
uint256 hash; uint256 hash;
@ -105,7 +125,7 @@ static bool rest_block(AcceptedConnection *conn,
} }
case RF_HEX: { 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; conn->stream() << HTTPReply(HTTP_OK, strHex, fRun, false, "text/plain") << std::flush;
return true; return true;
} }
@ -115,22 +135,24 @@ static bool rest_block(AcceptedConnection *conn,
string strJSON = write_string(Value(objBlock), false) + "\n"; string strJSON = write_string(Value(objBlock), false) + "\n";
conn->stream() << HTTPReply(HTTP_OK, strJSON, fRun) << std::flush; conn->stream() << HTTPReply(HTTP_OK, strJSON, fRun) << std::flush;
return true; return true;
} }
default: {
throw RESTERR(HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
}
} }
// not reached // 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, static bool rest_tx(AcceptedConnection* conn,
string& strReq, string& strReq,
map<string, string>& mapHeaders, map<string, string>& mapHeaders,
bool fRun) bool fRun)
{ {
vector<string> params; vector<string> params;
boost::split(params, strReq, boost::is_any_of("/")); enum RetFormat rf = ParseDataFormat(params, strReq);
enum RetFormat rf = ParseDataFormat(params.size() > 1 ? params[1] : string(""));
string hashStr = params[0]; string hashStr = params[0];
uint256 hash; uint256 hash;
@ -153,7 +175,7 @@ static bool rest_tx(AcceptedConnection *conn,
} }
case RF_HEX: { 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; conn->stream() << HTTPReply(HTTP_OK, strHex, fRun, false, "text/plain") << std::flush;
return true; return true;
} }
@ -165,33 +187,37 @@ static bool rest_tx(AcceptedConnection *conn,
conn->stream() << HTTPReply(HTTP_OK, strJSON, fRun) << std::flush; conn->stream() << HTTPReply(HTTP_OK, strJSON, fRun) << std::flush;
return true; return true;
} }
default: {
throw RESTERR(HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")");
}
} }
// not reached // 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 { static const struct {
const char *prefix; const char* prefix;
bool (*handler)(AcceptedConnection *conn, bool (*handler)(AcceptedConnection* conn,
string& strURI, string& strURI,
map<string, string>& mapHeaders, map<string, string>& mapHeaders,
bool fRun); bool fRun);
} uri_prefixes[] = { } uri_prefixes[] = {
{ "/rest/tx/", rest_tx }, {"/rest/tx/", rest_tx},
{ "/rest/block/", rest_block }, {"/rest/block/", rest_block},
}; };
bool HTTPReq_REST(AcceptedConnection *conn, bool HTTPReq_REST(AcceptedConnection* conn,
string& strURI, string& strURI,
map<string, string>& mapHeaders, map<string, string>& mapHeaders,
bool fRun) bool fRun)
{ {
try { try {
std::string statusmessage; std::string statusmessage;
if(RPCIsInWarmup(&statusmessage)) if (RPCIsInWarmup(&statusmessage))
throw RESTERR(HTTP_SERVICE_UNAVAILABLE, "Service temporarily unavailable: "+statusmessage); throw RESTERR(HTTP_SERVICE_UNAVAILABLE, "Service temporarily unavailable: " + statusmessage);
for (unsigned int i = 0; i < ARRAYLEN(uri_prefixes); i++) { for (unsigned int i = 0; i < ARRAYLEN(uri_prefixes); i++) {
unsigned int plen = strlen(uri_prefixes[i].prefix); unsigned int plen = strlen(uri_prefixes[i].prefix);
if (strURI.substr(0, plen) == uri_prefixes[i].prefix) { if (strURI.substr(0, plen) == uri_prefixes[i].prefix) {
@ -199,8 +225,7 @@ bool HTTPReq_REST(AcceptedConnection *conn,
return uri_prefixes[i].handler(conn, strReq, mapHeaders, fRun); return uri_prefixes[i].handler(conn, strReq, mapHeaders, fRun);
} }
} }
} } catch (RestErr& re) {
catch (RestErr& re) {
conn->stream() << HTTPReply(re.status, re.message + "\r\n", false, false, "text/plain") << std::flush; conn->stream() << HTTPReply(re.status, re.message + "\r\n", false, false, "text/plain") << std::flush;
return false; return false;
} }