aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWladimir J. van der Laan <laanwj@gmail.com>2014-06-27 09:37:01 +0200
committerWladimir J. van der Laan <laanwj@gmail.com>2014-06-27 10:03:19 +0200
commitd77f761be954da397ea32335b4ccda36dc33767d (patch)
treeaa4a6e97cb6590e4e6b11e716aa11f3fc4cc95f1
parent19168c3377a052b7c2c5418b9f35d3aa2a63fa64 (diff)
parented5769f536f663a8deb8e8b7a68681cebaa52bdd (diff)
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)
-rw-r--r--src/rpcprotocol.cpp17
-rw-r--r--src/rpcprotocol.h4
-rw-r--r--src/rpcserver.cpp139
-rw-r--r--src/rpcserver.h10
4 files changed, 99 insertions, 71 deletions
diff --git a/src/rpcprotocol.cpp b/src/rpcprotocol.cpp
index 2718f81783..2cb4a35c4b 100644
--- a/src/rpcprotocol.cpp
+++ b/src/rpcprotocol.cpp
@@ -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,
diff --git a/src/rpcprotocol.h b/src/rpcprotocol.h
index 11bdd171d9..f1317e9c28 100644
--- a/src/rpcprotocol.h
+++ b/src/rpcprotocol.h
@@ -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);
diff --git a/src/rpcserver.cpp b/src/rpcserver.cpp
index 6552de8c49..49bc05e5d6 100644
--- a/src/rpcserver.cpp
+++ b/src/rpcserver.cpp
@@ -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;
- }
- catch (Object& objError)
- {
- ErrorReply(conn->stream(), objError, jreq.id);
- break;
+ if (strURI == "/") {
+ if (!HTTPReq_JSONRPC(conn, strRequest, mapHeaders, fRun))
+ 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;
}
}
diff --git a/src/rpcserver.h b/src/rpcserver.h
index 1966a65b50..fcd293663f 100644
--- a/src/rpcserver.h
+++ b/src/rpcserver.h
@@ -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