From 96c5269511b0cecbea67c0981aaea1a8a3345ba3 Mon Sep 17 00:00:00 2001 From: David Joel Schwartz Date: Tue, 24 Apr 2012 01:10:02 -0400 Subject: [PATCH] RPC: Support HTTP/1.0 and HTTP/1.1, including the proper use of keep-alives --- src/bitcoinrpc.cpp | 50 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 38 insertions(+), 12 deletions(-) diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index b3a27101a..a324f0916 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -2357,7 +2357,7 @@ string rfc1123Time() return string(buffer); } -static string HTTPReply(int nStatus, const string& strMsg) +static string HTTPReply(int nStatus, const string& strMsg, bool keepalive) { if (nStatus == 401) return strprintf("HTTP/1.0 401 Authorization Required\r\n" @@ -2386,7 +2386,7 @@ static string HTTPReply(int nStatus, const string& strMsg) return strprintf( "HTTP/1.1 %d %s\r\n" "Date: %s\r\n" - "Connection: close\r\n" + "Connection: %s\r\n" "Content-Length: %d\r\n" "Content-Type: application/json\r\n" "Server: bitcoin-json-rpc/%s\r\n" @@ -2395,12 +2395,13 @@ static string HTTPReply(int nStatus, const string& strMsg) nStatus, cStatus, rfc1123Time().c_str(), + keepalive ? "keep-alive" : "close", strMsg.size(), FormatFullVersion().c_str(), strMsg.c_str()); } -int ReadHTTPStatus(std::basic_istream& stream) +int ReadHTTPStatus(std::basic_istream& stream, int &proto) { string str; getline(stream, str); @@ -2408,6 +2409,10 @@ int ReadHTTPStatus(std::basic_istream& stream) boost::split(vWords, str, boost::is_any_of(" ")); if (vWords.size() < 2) 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()); } @@ -2442,7 +2447,8 @@ int ReadHTTP(std::basic_istream& stream, map& mapHeadersRe strMessageRet = ""; // Read status - int nStatus = ReadHTTPStatus(stream); + int nProto; + int nStatus = ReadHTTPStatus(stream, nProto); // Read header int nLen = ReadHTTPHeader(stream, mapHeadersRet); @@ -2457,6 +2463,16 @@ int ReadHTTP(std::basic_istream& stream, map& mapHeadersRe 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; } @@ -2509,7 +2525,7 @@ void ErrorReply(std::ostream& stream, const Object& objError, const Value& id) if (code == -32600) nStatus = 400; else if (code == -32601) nStatus = 404; 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) @@ -2681,7 +2697,7 @@ void ThreadRPCServer2(void* parg) { // Accept connection AcceptedConnection *conn = - new AcceptedConnection(io_service, context, fUseSSL); + new AcceptedConnection(io_service, context, fUseSSL); vnThreadsRunning[THREAD_RPCLISTENER]--; 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. if (!fUseSSL) - conn->stream << HTTPReply(403, "") << std::flush; + conn->stream << HTTPReply(403, "", false) << std::flush; delete conn; } @@ -2718,7 +2734,15 @@ void ThreadRPCServer3(void* parg) vnThreadsRunning[THREAD_RPCHANDLER]++; AcceptedConnection *conn = (AcceptedConnection *) parg; - do { + bool fRun = true; + loop { + if (fShutdown || !fRun) + { + conn->stream.close(); + delete conn; + --vnThreadsRunning[THREAD_RPCHANDLER]; + return; + } map mapHeaders; string strRequest; @@ -2727,7 +2751,7 @@ void ThreadRPCServer3(void* parg) // Check authorization if (mapHeaders.count("authorization") == 0) { - conn->stream << HTTPReply(401, "") << std::flush; + conn->stream << HTTPReply(401, "", false) << std::flush; break; } if (!HTTPAuthorized(mapHeaders)) @@ -2739,9 +2763,11 @@ void ThreadRPCServer3(void* parg) if (mapArgs["-rpcpassword"].size() < 20) Sleep(250); - conn->stream << HTTPReply(401, "") << std::flush; + conn->stream << HTTPReply(401, "", false) << std::flush; break; } + if (mapHeaders["connection"] == "close") + fRun = false; Value id = Value::null; try @@ -2779,7 +2805,7 @@ void ThreadRPCServer3(void* parg) // Send reply 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) { @@ -2792,7 +2818,7 @@ void ThreadRPCServer3(void* parg) break; } } - while (0); + delete conn; vnThreadsRunning[THREAD_RPCHANDLER]--; }