aboutsummaryrefslogtreecommitdiff
path: root/src/rpcprotocol.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/rpcprotocol.cpp')
-rw-r--r--src/rpcprotocol.cpp291
1 files changed, 291 insertions, 0 deletions
diff --git a/src/rpcprotocol.cpp b/src/rpcprotocol.cpp
new file mode 100644
index 000000000..c99d113bc
--- /dev/null
+++ b/src/rpcprotocol.cpp
@@ -0,0 +1,291 @@
+// Copyright (c) 2010 Satoshi Nakamoto
+// Copyright (c) 2009-2014 The Bitcoin developers
+// Distributed under the MIT/X11 software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include "rpcprotocol.h"
+
+#include "tinyformat.h"
+#include "util.h"
+#include "utilstrencodings.h"
+#include "utiltime.h"
+#include "version.h"
+
+#include <stdint.h>
+
+#include <boost/algorithm/string.hpp>
+#include <boost/asio.hpp>
+#include <boost/asio/ssl.hpp>
+#include <boost/bind.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/foreach.hpp>
+#include <boost/iostreams/concepts.hpp>
+#include <boost/iostreams/stream.hpp>
+#include <boost/shared_ptr.hpp>
+#include "json/json_spirit_writer_template.h"
+
+using namespace std;
+using namespace boost;
+using namespace boost::asio;
+using namespace json_spirit;
+
+// Number of bytes to allocate and read at most at once in post data
+const size_t POST_READ_SIZE = 256 * 1024;
+
+//
+// HTTP protocol
+//
+// This ain't Apache. We're just using HTTP header for the length field
+// and to be compatible with other JSON-RPC implementations.
+//
+
+string HTTPPost(const string& strMsg, const map<string,string>& mapRequestHeaders)
+{
+ ostringstream s;
+ s << "POST / HTTP/1.1\r\n"
+ << "User-Agent: bitcoin-json-rpc/" << FormatFullVersion() << "\r\n"
+ << "Host: 127.0.0.1\r\n"
+ << "Content-Type: application/json\r\n"
+ << "Content-Length: " << strMsg.size() << "\r\n"
+ << "Connection: close\r\n"
+ << "Accept: application/json\r\n";
+ BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapRequestHeaders)
+ s << item.first << ": " << item.second << "\r\n";
+ s << "\r\n" << strMsg;
+
+ return s.str();
+}
+
+static string rfc1123Time()
+{
+ return DateTimeStrFormat("%a, %d %b %Y %H:%M:%S +0000", GetTime());
+}
+
+static const char *httpStatusDescription(int nStatus)
+{
+ 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)
+ return strprintf("HTTP/1.0 401 Authorization Required\r\n"
+ "Date: %s\r\n"
+ "Server: bitcoin-json-rpc/%s\r\n"
+ "WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 296\r\n"
+ "\r\n"
+ "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\r\n"
+ "\"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd\">\r\n"
+ "<HTML>\r\n"
+ "<HEAD>\r\n"
+ "<TITLE>Error</TITLE>\r\n"
+ "<META HTTP-EQUIV='Content-Type' CONTENT='text/html; charset=ISO-8859-1'>\r\n"
+ "</HEAD>\r\n"
+ "<BODY><H1>401 Unauthorized.</H1></BODY>\r\n"
+ "</HTML>\r\n", rfc1123Time(), FormatFullVersion());
+
+ return HTTPReply(nStatus, httpStatusDescription(nStatus), keepalive,
+ headersOnly, "text/plain");
+}
+
+string HTTPReplyHeader(int nStatus, bool keepalive, size_t contentLength, const char *contentType)
+{
+ return strprintf(
+ "HTTP/1.1 %d %s\r\n"
+ "Date: %s\r\n"
+ "Connection: %s\r\n"
+ "Content-Length: %u\r\n"
+ "Content-Type: %s\r\n"
+ "Server: bitcoin-json-rpc/%s\r\n"
+ "\r\n",
+ nStatus,
+ httpStatusDescription(nStatus),
+ rfc1123Time(),
+ keepalive ? "keep-alive" : "close",
+ contentLength,
+ contentType,
+ FormatFullVersion());
+}
+
+string HTTPReply(int nStatus, const string& strMsg, bool keepalive,
+ bool headersOnly, const char *contentType)
+{
+ if (headersOnly)
+ {
+ return HTTPReplyHeader(nStatus, keepalive, 0, contentType);
+ } else {
+ return HTTPReplyHeader(nStatus, keepalive, strMsg.size(), contentType) + strMsg;
+ }
+}
+
+bool ReadHTTPRequestLine(std::basic_istream<char>& stream, int &proto,
+ string& http_method, string& http_uri)
+{
+ string str;
+ getline(stream, str);
+
+ // HTTP request line is space-delimited
+ vector<string> vWords;
+ boost::split(vWords, str, boost::is_any_of(" "));
+ if (vWords.size() < 2)
+ return false;
+
+ // HTTP methods permitted: GET, POST
+ http_method = vWords[0];
+ if (http_method != "GET" && http_method != "POST")
+ return false;
+
+ // HTTP URI must be an absolute path, relative to current host
+ http_uri = vWords[1];
+ if (http_uri.size() == 0 || http_uri[0] != '/')
+ return false;
+
+ // parse proto, if present
+ string strProto = "";
+ if (vWords.size() > 2)
+ strProto = vWords[2];
+
+ proto = 0;
+ const char *ver = strstr(strProto.c_str(), "HTTP/1.");
+ if (ver != NULL)
+ proto = atoi(ver+7);
+
+ return true;
+}
+
+int ReadHTTPStatus(std::basic_istream<char>& stream, int &proto)
+{
+ string str;
+ getline(stream, str);
+ vector<string> vWords;
+ boost::split(vWords, str, boost::is_any_of(" "));
+ if (vWords.size() < 2)
+ return HTTP_INTERNAL_SERVER_ERROR;
+ proto = 0;
+ const char *ver = strstr(str.c_str(), "HTTP/1.");
+ if (ver != NULL)
+ proto = atoi(ver+7);
+ return atoi(vWords[1].c_str());
+}
+
+int ReadHTTPHeaders(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet)
+{
+ int nLen = 0;
+ while (true)
+ {
+ string str;
+ std::getline(stream, str);
+ if (str.empty() || str == "\r")
+ break;
+ string::size_type nColon = str.find(":");
+ if (nColon != string::npos)
+ {
+ string strHeader = str.substr(0, nColon);
+ boost::trim(strHeader);
+ boost::to_lower(strHeader);
+ string strValue = str.substr(nColon+1);
+ boost::trim(strValue);
+ mapHeadersRet[strHeader] = strValue;
+ if (strHeader == "content-length")
+ nLen = atoi(strValue.c_str());
+ }
+ }
+ return nLen;
+}
+
+
+int ReadHTTPMessage(std::basic_istream<char>& stream, map<string,
+ string>& mapHeadersRet, string& strMessageRet,
+ int nProto, size_t max_size)
+{
+ mapHeadersRet.clear();
+ strMessageRet = "";
+
+ // Read header
+ int nLen = ReadHTTPHeaders(stream, mapHeadersRet);
+ if (nLen < 0 || (size_t)nLen > max_size)
+ return HTTP_INTERNAL_SERVER_ERROR;
+
+ // Read message
+ if (nLen > 0)
+ {
+ vector<char> vch;
+ size_t ptr = 0;
+ while (ptr < (size_t)nLen)
+ {
+ size_t bytes_to_read = std::min((size_t)nLen - ptr, POST_READ_SIZE);
+ vch.resize(ptr + bytes_to_read);
+ stream.read(&vch[ptr], bytes_to_read);
+ if (!stream) // Connection lost while reading
+ return HTTP_INTERNAL_SERVER_ERROR;
+ ptr += bytes_to_read;
+ }
+ 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 HTTP_OK;
+}
+
+//
+// JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility,
+// but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were
+// unspecified (HTTP errors and contents of 'error').
+//
+// 1.0 spec: http://json-rpc.org/wiki/specification
+// 1.2 spec: http://jsonrpc.org/historical/json-rpc-over-http.html
+// http://www.codeproject.com/KB/recipes/JSON_Spirit.aspx
+//
+
+string JSONRPCRequest(const string& strMethod, const Array& params, const Value& id)
+{
+ Object request;
+ request.push_back(Pair("method", strMethod));
+ request.push_back(Pair("params", params));
+ request.push_back(Pair("id", id));
+ return write_string(Value(request), false) + "\n";
+}
+
+Object JSONRPCReplyObj(const Value& result, const Value& error, const Value& id)
+{
+ Object reply;
+ if (error.type() != null_type)
+ reply.push_back(Pair("result", Value::null));
+ else
+ reply.push_back(Pair("result", result));
+ reply.push_back(Pair("error", error));
+ reply.push_back(Pair("id", id));
+ return reply;
+}
+
+string JSONRPCReply(const Value& result, const Value& error, const Value& id)
+{
+ Object reply = JSONRPCReplyObj(result, error, id);
+ return write_string(Value(reply), false) + "\n";
+}
+
+Object JSONRPCError(int code, const string& message)
+{
+ Object error;
+ error.push_back(Pair("code", code));
+ error.push_back(Pair("message", message));
+ return error;
+}