RPC: Support HTTP/1.0 and HTTP/1.1, including the proper use of keep-alives

This commit is contained in:
David Joel Schwartz 2012-04-24 01:10:02 -04:00 committed by Jeff Garzik
parent e9205293bd
commit 96c5269511

View file

@ -2357,7 +2357,7 @@ string rfc1123Time()
return string(buffer); return string(buffer);
} }
static string HTTPReply(int nStatus, const string& strMsg) static string HTTPReply(int nStatus, const string& strMsg, bool keepalive)
{ {
if (nStatus == 401) if (nStatus == 401)
return strprintf("HTTP/1.0 401 Authorization Required\r\n" return strprintf("HTTP/1.0 401 Authorization Required\r\n"
@ -2386,7 +2386,7 @@ static string HTTPReply(int nStatus, const string& strMsg)
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"
"Connection: close\r\n" "Connection: %s\r\n"
"Content-Length: %d\r\n" "Content-Length: %d\r\n"
"Content-Type: application/json\r\n" "Content-Type: application/json\r\n"
"Server: bitcoin-json-rpc/%s\r\n" "Server: bitcoin-json-rpc/%s\r\n"
@ -2395,12 +2395,13 @@ static string HTTPReply(int nStatus, const string& strMsg)
nStatus, nStatus,
cStatus, cStatus,
rfc1123Time().c_str(), rfc1123Time().c_str(),
keepalive ? "keep-alive" : "close",
strMsg.size(), strMsg.size(),
FormatFullVersion().c_str(), FormatFullVersion().c_str(),
strMsg.c_str()); strMsg.c_str());
} }
int ReadHTTPStatus(std::basic_istream<char>& stream) int ReadHTTPStatus(std::basic_istream<char>& stream, int &proto)
{ {
string str; string str;
getline(stream, str); getline(stream, str);
@ -2408,6 +2409,10 @@ int ReadHTTPStatus(std::basic_istream<char>& stream)
boost::split(vWords, str, boost::is_any_of(" ")); boost::split(vWords, str, boost::is_any_of(" "));
if (vWords.size() < 2) if (vWords.size() < 2)
return 500; return 500;
proto = 0;
const char *ver = strstr(str.c_str(), "HTTP/1.");
if (ver != NULL)
proto = atoi(ver+7);
return atoi(vWords[1].c_str()); return atoi(vWords[1].c_str());
} }
@ -2442,7 +2447,8 @@ int ReadHTTP(std::basic_istream<char>& stream, map<string, string>& mapHeadersRe
strMessageRet = ""; strMessageRet = "";
// Read status // Read status
int nStatus = ReadHTTPStatus(stream); int nProto;
int nStatus = ReadHTTPStatus(stream, nProto);
// Read header // Read header
int nLen = ReadHTTPHeader(stream, mapHeadersRet); int nLen = ReadHTTPHeader(stream, mapHeadersRet);
@ -2457,6 +2463,16 @@ int ReadHTTP(std::basic_istream<char>& stream, map<string, string>& mapHeadersRe
strMessageRet = string(vch.begin(), vch.end()); strMessageRet = string(vch.begin(), vch.end());
} }
string sConHdr = mapHeadersRet["connection"];
if ((sConHdr != "close") && (sConHdr != "keep-alive"))
{
if (nProto >= 1)
mapHeadersRet["connection"] = "keep-alive";
else
mapHeadersRet["connection"] = "close";
}
return nStatus; return nStatus;
} }
@ -2509,7 +2525,7 @@ void ErrorReply(std::ostream& stream, const Object& objError, const Value& id)
if (code == -32600) nStatus = 400; if (code == -32600) nStatus = 400;
else if (code == -32601) nStatus = 404; else if (code == -32601) nStatus = 404;
string strReply = JSONRPCReply(Value::null, objError, id); string strReply = JSONRPCReply(Value::null, objError, id);
stream << HTTPReply(nStatus, strReply) << std::flush; stream << HTTPReply(nStatus, strReply, false) << std::flush;
} }
bool ClientAllowed(const string& strAddress) bool ClientAllowed(const string& strAddress)
@ -2681,7 +2697,7 @@ void ThreadRPCServer2(void* parg)
{ {
// Accept connection // Accept connection
AcceptedConnection *conn = AcceptedConnection *conn =
new AcceptedConnection(io_service, context, fUseSSL); new AcceptedConnection(io_service, context, fUseSSL);
vnThreadsRunning[THREAD_RPCLISTENER]--; vnThreadsRunning[THREAD_RPCLISTENER]--;
acceptor.accept(conn->sslStream.lowest_layer(), conn->peer); acceptor.accept(conn->sslStream.lowest_layer(), conn->peer);
@ -2700,7 +2716,7 @@ void ThreadRPCServer2(void* parg)
{ {
// 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(403, "") << std::flush; conn->stream << HTTPReply(403, "", false) << std::flush;
delete conn; delete conn;
} }
@ -2718,7 +2734,15 @@ void ThreadRPCServer3(void* parg)
vnThreadsRunning[THREAD_RPCHANDLER]++; vnThreadsRunning[THREAD_RPCHANDLER]++;
AcceptedConnection *conn = (AcceptedConnection *) parg; AcceptedConnection *conn = (AcceptedConnection *) parg;
do { bool fRun = true;
loop {
if (fShutdown || !fRun)
{
conn->stream.close();
delete conn;
--vnThreadsRunning[THREAD_RPCHANDLER];
return;
}
map<string, string> mapHeaders; map<string, string> mapHeaders;
string strRequest; string strRequest;
@ -2727,7 +2751,7 @@ void ThreadRPCServer3(void* parg)
// Check authorization // Check authorization
if (mapHeaders.count("authorization") == 0) if (mapHeaders.count("authorization") == 0)
{ {
conn->stream << HTTPReply(401, "") << std::flush; conn->stream << HTTPReply(401, "", false) << std::flush;
break; break;
} }
if (!HTTPAuthorized(mapHeaders)) if (!HTTPAuthorized(mapHeaders))
@ -2739,9 +2763,11 @@ void ThreadRPCServer3(void* parg)
if (mapArgs["-rpcpassword"].size() < 20) if (mapArgs["-rpcpassword"].size() < 20)
Sleep(250); Sleep(250);
conn->stream << HTTPReply(401, "") << std::flush; conn->stream << HTTPReply(401, "", false) << std::flush;
break; break;
} }
if (mapHeaders["connection"] == "close")
fRun = false;
Value id = Value::null; Value id = Value::null;
try try
@ -2779,7 +2805,7 @@ void ThreadRPCServer3(void* parg)
// Send reply // Send reply
string strReply = JSONRPCReply(result, Value::null, id); string strReply = JSONRPCReply(result, Value::null, id);
conn->stream << HTTPReply(200, strReply) << std::flush; conn->stream << HTTPReply(200, strReply, fRun) << std::flush;
} }
catch (Object& objError) catch (Object& objError)
{ {
@ -2792,7 +2818,7 @@ void ThreadRPCServer3(void* parg)
break; break;
} }
} }
while (0);
delete conn; delete conn;
vnThreadsRunning[THREAD_RPCHANDLER]--; vnThreadsRunning[THREAD_RPCHANDLER]--;
} }