Skip to content

Commit

Permalink
RPC cleanup: Improve HTTP server replies
Browse files Browse the repository at this point in the history
1) support varying content types
2) support only sending the header
3) properly deliver error message as content, if HTTP error
4) move AcceptedConnection class to header, for wider use
  • Loading branch information
Jeff Garzik committed Jun 27, 2014
1 parent 236ae86 commit c912e22
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 14 deletions.
17 changes: 14 additions & 3 deletions src/rpcprotocol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -73,19 +74,27 @@ 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";
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;
}

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",
Expand All @@ -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,
Expand Down
14 changes: 13 additions & 1 deletion src/rpcprotocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,16 @@ enum RPCErrorCode
RPC_WALLET_ALREADY_UNLOCKED = -17, // Wallet is already unlocked
};

class AcceptedConnection
{
public:
virtual ~AcceptedConnection() {}

virtual std::iostream& stream() = 0;
virtual std::string peer_address_to_string() const = 0;
virtual void close() = 0;
};

//
// IOStream device that speaks SSL but can also speak non-SSL
//
Expand Down Expand Up @@ -141,7 +151,9 @@ class SSLIOStreamDevice : public boost::iostreams::device<boost::iostreams::bidi
};

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);
Expand Down
10 changes: 0 additions & 10 deletions src/rpcserver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down

0 comments on commit c912e22

Please sign in to comment.