fix RPC error replies

After pull #4288, RPC messages indicating errors have a Content-Length unrelated
to their actual contents, rendering bitcoin-cli and curl unable to decode the
reply.

This patch sets the Content-Length field based on the actual content returned.

Additionally, pull #4288 clobbered the error descriptions provided in
ErrorReply, which bitcoin-cli relies upon; this patch moves #4288 http-error
descriptions to an HTTPError method, allowing HTTPReply to pass content on
unchanged.
This commit is contained in:
kazcw 2014-06-28 18:14:36 -07:00
parent 7b8fc9d8ad
commit 16f33f163d
3 changed files with 29 additions and 23 deletions

View File

@ -54,8 +54,19 @@ static string rfc1123Time()
return DateTimeStrFormat("%a, %d %b %Y %H:%M:%S +0000", GetTime()); return DateTimeStrFormat("%a, %d %b %Y %H:%M:%S +0000", GetTime());
} }
string HTTPReply(int nStatus, const string& strMsg, bool keepalive, static const char *httpStatusDescription(int nStatus)
bool headersOnly, const char *contentType) {
switch (nStatus) {
case HTTP_OK: return "OK";
case HTTP_BAD_REQUEST: return "Bad Request";
case HTTP_FORBIDDEN: return "Forbidden";
case HTTP_NOT_FOUND: return "Not Found";
case HTTP_INTERNAL_SERVER_ERROR: return "Internal Server Error";
default: return "";
}
}
string HTTPError(int nStatus, bool keepalive, bool headersOnly)
{ {
if (nStatus == HTTP_UNAUTHORIZED) if (nStatus == HTTP_UNAUTHORIZED)
return strprintf("HTTP/1.0 401 Authorization Required\r\n" return strprintf("HTTP/1.0 401 Authorization Required\r\n"
@ -75,20 +86,13 @@ string HTTPReply(int nStatus, const string& strMsg, bool keepalive,
"<BODY><H1>401 Unauthorized.</H1></BODY>\r\n" "<BODY><H1>401 Unauthorized.</H1></BODY>\r\n"
"</HTML>\r\n", rfc1123Time(), FormatFullVersion()); "</HTML>\r\n", rfc1123Time(), FormatFullVersion());
const char *cStatus; return HTTPReply(nStatus, httpStatusDescription(nStatus), keepalive,
if (nStatus == HTTP_OK) cStatus = "OK"; headersOnly, "text/plain");
else if (nStatus == HTTP_BAD_REQUEST) cStatus = "Bad Request"; }
else if (nStatus == HTTP_FORBIDDEN) cStatus = "Forbidden";
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;
}
string HTTPReply(int nStatus, const string& strMsg, bool keepalive,
bool headersOnly, const char *contentType)
{
return strprintf( return strprintf(
"HTTP/1.1 %d %s\r\n" "HTTP/1.1 %d %s\r\n"
"Date: %s\r\n" "Date: %s\r\n"
@ -99,14 +103,14 @@ string HTTPReply(int nStatus, const string& strMsg, bool keepalive,
"\r\n" "\r\n"
"%s", "%s",
nStatus, nStatus,
cStatus, httpStatusDescription(nStatus),
rfc1123Time(), rfc1123Time(),
keepalive ? "keep-alive" : "close", keepalive ? "keep-alive" : "close",
strMsg.size(), (headersOnly ? 0 : strMsg.size()),
contentType, contentType,
FormatFullVersion(), FormatFullVersion(),
(headersOnly ? "" : (headersOnly ? "" : strMsg.c_str())
(useInternalContent ? cStatus : strMsg.c_str()))); );
} }
bool ReadHTTPRequestLine(std::basic_istream<char>& stream, int &proto, bool ReadHTTPRequestLine(std::basic_istream<char>& stream, int &proto,

View File

@ -141,6 +141,8 @@ private:
}; };
std::string HTTPPost(const std::string& strMsg, const std::map<std::string,std::string>& mapRequestHeaders); std::string HTTPPost(const std::string& strMsg, const std::map<std::string,std::string>& mapRequestHeaders);
std::string HTTPError(int nStatus, bool keepalive,
bool headerOnly = false);
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, bool headerOnly = false,
const char *contentType = "application/json"); const char *contentType = "application/json");

View File

@ -481,7 +481,7 @@ static void RPCAcceptHandler(boost::shared_ptr< basic_socket_acceptor<Protocol,
{ {
// Only send a 403 if we're not using SSL to prevent a DoS during the SSL handshake. // Only send a 403 if we're not using SSL to prevent a DoS during the SSL handshake.
if (!fUseSSL) if (!fUseSSL)
conn->stream() << HTTPReply(HTTP_FORBIDDEN, "", false) << std::flush; conn->stream() << HTTPError(HTTP_FORBIDDEN, false) << std::flush;
conn->close(); conn->close();
} }
else { else {
@ -807,7 +807,7 @@ static bool HTTPReq_JSONRPC(AcceptedConnection *conn,
// Check authorization // Check authorization
if (mapHeaders.count("authorization") == 0) if (mapHeaders.count("authorization") == 0)
{ {
conn->stream() << HTTPReply(HTTP_UNAUTHORIZED, "", false) << std::flush; conn->stream() << HTTPError(HTTP_UNAUTHORIZED, false) << std::flush;
return false; return false;
} }
@ -820,7 +820,7 @@ static bool HTTPReq_JSONRPC(AcceptedConnection *conn,
if (mapArgs["-rpcpassword"].size() < 20) if (mapArgs["-rpcpassword"].size() < 20)
MilliSleep(250); MilliSleep(250);
conn->stream() << HTTPReply(HTTP_UNAUTHORIZED, "", false) << std::flush; conn->stream() << HTTPError(HTTP_UNAUTHORIZED, false) << std::flush;
return false; return false;
} }
@ -888,7 +888,7 @@ void ServiceConnection(AcceptedConnection *conn)
if (!HTTPReq_JSONRPC(conn, strRequest, mapHeaders, fRun)) if (!HTTPReq_JSONRPC(conn, strRequest, mapHeaders, fRun))
break; break;
} else { } else {
conn->stream() << HTTPReply(HTTP_NOT_FOUND, "", false) << std::flush; conn->stream() << HTTPError(HTTP_NOT_FOUND, false) << std::flush;
break; break;
} }
} }