mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-09 10:43:19 -05:00
Merge pull request #4288
ed5769f
Move AcceptedConnection class to rpcserver.h. (Jeff Garzik)854d013
RPC code movement: separate out JSON-RPC execution logic from HTTP server logic (Jeff Garzik)c912e22
RPC cleanup: Improve HTTP server replies (Jeff Garzik)
This commit is contained in:
commit
d77f761be9
4 changed files with 99 additions and 71 deletions
|
@ -54,7 +54,8 @@ static string rfc1123Time()
|
|||
return DateTimeStrFormat("%a, %d %b %Y %H:%M:%S +0000", GetTime());
|
||||
}
|
||||
|
||||
string HTTPReply(int nStatus, const string& strMsg, bool keepalive)
|
||||
string HTTPReply(int nStatus, const string& strMsg, bool keepalive,
|
||||
bool headersOnly, const char *contentType)
|
||||
{
|
||||
if (nStatus == HTTP_UNAUTHORIZED)
|
||||
return strprintf("HTTP/1.0 401 Authorization Required\r\n"
|
||||
|
@ -73,6 +74,7 @@ string HTTPReply(int nStatus, const string& strMsg, bool keepalive)
|
|||
"</HEAD>\r\n"
|
||||
"<BODY><H1>401 Unauthorized.</H1></BODY>\r\n"
|
||||
"</HTML>\r\n", rfc1123Time(), FormatFullVersion());
|
||||
|
||||
const char *cStatus;
|
||||
if (nStatus == HTTP_OK) cStatus = "OK";
|
||||
else if (nStatus == HTTP_BAD_REQUEST) cStatus = "Bad Request";
|
||||
|
@ -80,12 +82,19 @@ string HTTPReply(int nStatus, const string& strMsg, bool keepalive)
|
|||
else if (nStatus == HTTP_NOT_FOUND) cStatus = "Not Found";
|
||||
else if (nStatus == HTTP_INTERNAL_SERVER_ERROR) cStatus = "Internal Server Error";
|
||||
else cStatus = "";
|
||||
|
||||
bool useInternalContent = false;
|
||||
if (nStatus != HTTP_OK) {
|
||||
contentType = "text/plain";
|
||||
useInternalContent = true;
|
||||
}
|
||||
|
||||
return strprintf(
|
||||
"HTTP/1.1 %d %s\r\n"
|
||||
"Date: %s\r\n"
|
||||
"Connection: %s\r\n"
|
||||
"Content-Length: %u\r\n"
|
||||
"Content-Type: application/json\r\n"
|
||||
"Content-Type: %s\r\n"
|
||||
"Server: bitcoin-json-rpc/%s\r\n"
|
||||
"\r\n"
|
||||
"%s",
|
||||
|
@ -94,8 +103,10 @@ string HTTPReply(int nStatus, const string& strMsg, bool keepalive)
|
|||
rfc1123Time(),
|
||||
keepalive ? "keep-alive" : "close",
|
||||
strMsg.size(),
|
||||
contentType,
|
||||
FormatFullVersion(),
|
||||
strMsg);
|
||||
(headersOnly ? "" :
|
||||
(useInternalContent ? cStatus : strMsg.c_str())));
|
||||
}
|
||||
|
||||
bool ReadHTTPRequestLine(std::basic_istream<char>& stream, int &proto,
|
||||
|
|
|
@ -141,7 +141,9 @@ private:
|
|||
};
|
||||
|
||||
std::string HTTPPost(const std::string& strMsg, const std::map<std::string,std::string>& mapRequestHeaders);
|
||||
std::string HTTPReply(int nStatus, const std::string& strMsg, bool keepalive);
|
||||
std::string HTTPReply(int nStatus, const std::string& strMsg, bool keepalive,
|
||||
bool headerOnly = false,
|
||||
const char *contentType = "application/json");
|
||||
bool ReadHTTPRequestLine(std::basic_istream<char>& stream, int &proto,
|
||||
std::string& http_method, std::string& http_uri);
|
||||
int ReadHTTPStatus(std::basic_istream<char>& stream, int &proto);
|
||||
|
|
|
@ -393,16 +393,6 @@ bool ClientAllowed(const boost::asio::ip::address& address)
|
|||
return false;
|
||||
}
|
||||
|
||||
class AcceptedConnection
|
||||
{
|
||||
public:
|
||||
virtual ~AcceptedConnection() {}
|
||||
|
||||
virtual std::iostream& stream() = 0;
|
||||
virtual std::string peer_address_to_string() const = 0;
|
||||
virtual void close() = 0;
|
||||
};
|
||||
|
||||
template <typename Protocol>
|
||||
class AcceptedConnectionImpl : public AcceptedConnection
|
||||
{
|
||||
|
@ -819,6 +809,71 @@ static string JSONRPCExecBatch(const Array& vReq)
|
|||
return write_string(Value(ret), false) + "\n";
|
||||
}
|
||||
|
||||
static bool HTTPReq_JSONRPC(AcceptedConnection *conn,
|
||||
string& strRequest,
|
||||
map<string, string>& mapHeaders,
|
||||
bool fRun)
|
||||
{
|
||||
// Check authorization
|
||||
if (mapHeaders.count("authorization") == 0)
|
||||
{
|
||||
conn->stream() << HTTPReply(HTTP_UNAUTHORIZED, "", false) << std::flush;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!HTTPAuthorized(mapHeaders))
|
||||
{
|
||||
LogPrintf("ThreadRPCServer incorrect password attempt from %s\n", conn->peer_address_to_string());
|
||||
/* Deter brute-forcing short passwords.
|
||||
If this results in a DoS the user really
|
||||
shouldn't have their RPC port exposed. */
|
||||
if (mapArgs["-rpcpassword"].size() < 20)
|
||||
MilliSleep(250);
|
||||
|
||||
conn->stream() << HTTPReply(HTTP_UNAUTHORIZED, "", false) << std::flush;
|
||||
return false;
|
||||
}
|
||||
|
||||
JSONRequest jreq;
|
||||
try
|
||||
{
|
||||
// Parse request
|
||||
Value valRequest;
|
||||
if (!read_string(strRequest, valRequest))
|
||||
throw JSONRPCError(RPC_PARSE_ERROR, "Parse error");
|
||||
|
||||
string strReply;
|
||||
|
||||
// singleton request
|
||||
if (valRequest.type() == obj_type) {
|
||||
jreq.parse(valRequest);
|
||||
|
||||
Value result = tableRPC.execute(jreq.strMethod, jreq.params);
|
||||
|
||||
// Send reply
|
||||
strReply = JSONRPCReply(result, Value::null, jreq.id);
|
||||
|
||||
// array of requests
|
||||
} else if (valRequest.type() == array_type)
|
||||
strReply = JSONRPCExecBatch(valRequest.get_array());
|
||||
else
|
||||
throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error");
|
||||
|
||||
conn->stream() << HTTPReply(HTTP_OK, strReply, fRun) << std::flush;
|
||||
}
|
||||
catch (Object& objError)
|
||||
{
|
||||
ErrorReply(conn->stream(), objError, jreq.id);
|
||||
return false;
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
ErrorReply(conn->stream(), JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ServiceConnection(AcceptedConnection *conn)
|
||||
{
|
||||
bool fRun = true;
|
||||
|
@ -835,67 +890,17 @@ void ServiceConnection(AcceptedConnection *conn)
|
|||
// Read HTTP message headers and body
|
||||
ReadHTTPMessage(conn->stream(), mapHeaders, strRequest, nProto);
|
||||
|
||||
if (strURI != "/") {
|
||||
conn->stream() << HTTPReply(HTTP_NOT_FOUND, "", false) << std::flush;
|
||||
break;
|
||||
}
|
||||
|
||||
// Check authorization
|
||||
if (mapHeaders.count("authorization") == 0)
|
||||
{
|
||||
conn->stream() << HTTPReply(HTTP_UNAUTHORIZED, "", false) << std::flush;
|
||||
break;
|
||||
}
|
||||
if (!HTTPAuthorized(mapHeaders))
|
||||
{
|
||||
LogPrintf("ThreadRPCServer incorrect password attempt from %s\n", conn->peer_address_to_string());
|
||||
/* Deter brute-forcing short passwords.
|
||||
If this results in a DoS the user really
|
||||
shouldn't have their RPC port exposed. */
|
||||
if (mapArgs["-rpcpassword"].size() < 20)
|
||||
MilliSleep(250);
|
||||
|
||||
conn->stream() << HTTPReply(HTTP_UNAUTHORIZED, "", false) << std::flush;
|
||||
break;
|
||||
}
|
||||
// HTTP Keep-Alive is false; close connection immediately
|
||||
if (mapHeaders["connection"] == "close")
|
||||
fRun = false;
|
||||
|
||||
JSONRequest jreq;
|
||||
try
|
||||
{
|
||||
// Parse request
|
||||
Value valRequest;
|
||||
if (!read_string(strRequest, valRequest))
|
||||
throw JSONRPCError(RPC_PARSE_ERROR, "Parse error");
|
||||
|
||||
string strReply;
|
||||
|
||||
// singleton request
|
||||
if (valRequest.type() == obj_type) {
|
||||
jreq.parse(valRequest);
|
||||
|
||||
Value result = tableRPC.execute(jreq.strMethod, jreq.params);
|
||||
|
||||
// Send reply
|
||||
strReply = JSONRPCReply(result, Value::null, jreq.id);
|
||||
|
||||
// array of requests
|
||||
} else if (valRequest.type() == array_type)
|
||||
strReply = JSONRPCExecBatch(valRequest.get_array());
|
||||
else
|
||||
throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error");
|
||||
|
||||
conn->stream() << HTTPReply(HTTP_OK, strReply, fRun) << std::flush;
|
||||
if (strURI == "/") {
|
||||
if (!HTTPReq_JSONRPC(conn, strRequest, mapHeaders, fRun))
|
||||
break;
|
||||
}
|
||||
catch (Object& objError)
|
||||
{
|
||||
ErrorReply(conn->stream(), objError, jreq.id);
|
||||
break;
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
ErrorReply(conn->stream(), JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id);
|
||||
|
||||
else {
|
||||
conn->stream() << HTTPReply(HTTP_NOT_FOUND, "", false) << std::flush;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,16 @@
|
|||
class CBlockIndex;
|
||||
class CNetAddr;
|
||||
|
||||
class AcceptedConnection
|
||||
{
|
||||
public:
|
||||
virtual ~AcceptedConnection() {}
|
||||
|
||||
virtual std::iostream& stream() = 0;
|
||||
virtual std::string peer_address_to_string() const = 0;
|
||||
virtual void close() = 0;
|
||||
};
|
||||
|
||||
/* Start RPC threads */
|
||||
void StartRPCThreads();
|
||||
/* Alternative to StartRPCThreads for the GUI, when no server is
|
||||
|
|
Loading…
Add table
Reference in a new issue