You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

rpcprotocol.cpp 8.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. // Copyright (c) 2010 Satoshi Nakamoto
  2. // Copyright (c) 2009-2014 The Bitcoin developers
  3. // Distributed under the MIT/X11 software license, see the accompanying
  4. // file COPYING or http://www.opensource.org/licenses/mit-license.php.
  5. #include "rpcprotocol.h"
  6. #include "util.h"
  7. #include "version.h"
  8. #include <stdint.h>
  9. #include <boost/algorithm/string.hpp>
  10. #include <boost/asio.hpp>
  11. #include <boost/asio/ssl.hpp>
  12. #include <boost/bind.hpp>
  13. #include <boost/filesystem.hpp>
  14. #include <boost/foreach.hpp>
  15. #include <boost/iostreams/concepts.hpp>
  16. #include <boost/iostreams/stream.hpp>
  17. #include <boost/shared_ptr.hpp>
  18. #include "json/json_spirit_writer_template.h"
  19. using namespace std;
  20. using namespace boost;
  21. using namespace boost::asio;
  22. using namespace json_spirit;
  23. // Number of bytes to allocate and read at most at once in post data
  24. const size_t POST_READ_SIZE = 256 * 1024;
  25. //
  26. // HTTP protocol
  27. //
  28. // This ain't Apache. We're just using HTTP header for the length field
  29. // and to be compatible with other JSON-RPC implementations.
  30. //
  31. string HTTPPost(const string& strMsg, const map<string,string>& mapRequestHeaders)
  32. {
  33. ostringstream s;
  34. s << "POST / HTTP/1.1\r\n"
  35. << "User-Agent: bitcoin-json-rpc/" << FormatFullVersion() << "\r\n"
  36. << "Host: 127.0.0.1\r\n"
  37. << "Content-Type: application/json\r\n"
  38. << "Content-Length: " << strMsg.size() << "\r\n"
  39. << "Connection: close\r\n"
  40. << "Accept: application/json\r\n";
  41. BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapRequestHeaders)
  42. s << item.first << ": " << item.second << "\r\n";
  43. s << "\r\n" << strMsg;
  44. return s.str();
  45. }
  46. static string rfc1123Time()
  47. {
  48. return DateTimeStrFormat("%a, %d %b %Y %H:%M:%S +0000", GetTime());
  49. }
  50. static const char *httpStatusDescription(int nStatus)
  51. {
  52. switch (nStatus) {
  53. case HTTP_OK: return "OK";
  54. case HTTP_BAD_REQUEST: return "Bad Request";
  55. case HTTP_FORBIDDEN: return "Forbidden";
  56. case HTTP_NOT_FOUND: return "Not Found";
  57. case HTTP_INTERNAL_SERVER_ERROR: return "Internal Server Error";
  58. default: return "";
  59. }
  60. }
  61. string HTTPError(int nStatus, bool keepalive, bool headersOnly)
  62. {
  63. if (nStatus == HTTP_UNAUTHORIZED)
  64. return strprintf("HTTP/1.0 401 Authorization Required\r\n"
  65. "Date: %s\r\n"
  66. "Server: bitcoin-json-rpc/%s\r\n"
  67. "WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n"
  68. "Content-Type: text/html\r\n"
  69. "Content-Length: 296\r\n"
  70. "\r\n"
  71. "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\r\n"
  72. "\"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd\">\r\n"
  73. "<HTML>\r\n"
  74. "<HEAD>\r\n"
  75. "<TITLE>Error</TITLE>\r\n"
  76. "<META HTTP-EQUIV='Content-Type' CONTENT='text/html; charset=ISO-8859-1'>\r\n"
  77. "</HEAD>\r\n"
  78. "<BODY><H1>401 Unauthorized.</H1></BODY>\r\n"
  79. "</HTML>\r\n", rfc1123Time(), FormatFullVersion());
  80. return HTTPReply(nStatus, httpStatusDescription(nStatus), keepalive,
  81. headersOnly, "text/plain");
  82. }
  83. string HTTPReplyHeader(int nStatus, bool keepalive, size_t contentLength, const char *contentType)
  84. {
  85. return strprintf(
  86. "HTTP/1.1 %d %s\r\n"
  87. "Date: %s\r\n"
  88. "Connection: %s\r\n"
  89. "Content-Length: %u\r\n"
  90. "Content-Type: %s\r\n"
  91. "Server: bitcoin-json-rpc/%s\r\n"
  92. "\r\n",
  93. nStatus,
  94. httpStatusDescription(nStatus),
  95. rfc1123Time(),
  96. keepalive ? "keep-alive" : "close",
  97. contentLength,
  98. contentType,
  99. FormatFullVersion());
  100. }
  101. string HTTPReply(int nStatus, const string& strMsg, bool keepalive,
  102. bool headersOnly, const char *contentType)
  103. {
  104. if (headersOnly)
  105. {
  106. return HTTPReplyHeader(nStatus, keepalive, 0, contentType);
  107. } else {
  108. return HTTPReplyHeader(nStatus, keepalive, strMsg.size(), contentType) + strMsg;
  109. }
  110. }
  111. bool ReadHTTPRequestLine(std::basic_istream<char>& stream, int &proto,
  112. string& http_method, string& http_uri)
  113. {
  114. string str;
  115. getline(stream, str);
  116. // HTTP request line is space-delimited
  117. vector<string> vWords;
  118. boost::split(vWords, str, boost::is_any_of(" "));
  119. if (vWords.size() < 2)
  120. return false;
  121. // HTTP methods permitted: GET, POST
  122. http_method = vWords[0];
  123. if (http_method != "GET" && http_method != "POST")
  124. return false;
  125. // HTTP URI must be an absolute path, relative to current host
  126. http_uri = vWords[1];
  127. if (http_uri.size() == 0 || http_uri[0] != '/')
  128. return false;
  129. // parse proto, if present
  130. string strProto = "";
  131. if (vWords.size() > 2)
  132. strProto = vWords[2];
  133. proto = 0;
  134. const char *ver = strstr(strProto.c_str(), "HTTP/1.");
  135. if (ver != NULL)
  136. proto = atoi(ver+7);
  137. return true;
  138. }
  139. int ReadHTTPStatus(std::basic_istream<char>& stream, int &proto)
  140. {
  141. string str;
  142. getline(stream, str);
  143. vector<string> vWords;
  144. boost::split(vWords, str, boost::is_any_of(" "));
  145. if (vWords.size() < 2)
  146. return HTTP_INTERNAL_SERVER_ERROR;
  147. proto = 0;
  148. const char *ver = strstr(str.c_str(), "HTTP/1.");
  149. if (ver != NULL)
  150. proto = atoi(ver+7);
  151. return atoi(vWords[1].c_str());
  152. }
  153. int ReadHTTPHeaders(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet)
  154. {
  155. int nLen = 0;
  156. while (true)
  157. {
  158. string str;
  159. std::getline(stream, str);
  160. if (str.empty() || str == "\r")
  161. break;
  162. string::size_type nColon = str.find(":");
  163. if (nColon != string::npos)
  164. {
  165. string strHeader = str.substr(0, nColon);
  166. boost::trim(strHeader);
  167. boost::to_lower(strHeader);
  168. string strValue = str.substr(nColon+1);
  169. boost::trim(strValue);
  170. mapHeadersRet[strHeader] = strValue;
  171. if (strHeader == "content-length")
  172. nLen = atoi(strValue.c_str());
  173. }
  174. }
  175. return nLen;
  176. }
  177. int ReadHTTPMessage(std::basic_istream<char>& stream, map<string,
  178. string>& mapHeadersRet, string& strMessageRet,
  179. int nProto, size_t max_size)
  180. {
  181. mapHeadersRet.clear();
  182. strMessageRet = "";
  183. // Read header
  184. int nLen = ReadHTTPHeaders(stream, mapHeadersRet);
  185. if (nLen < 0 || (size_t)nLen > max_size)
  186. return HTTP_INTERNAL_SERVER_ERROR;
  187. // Read message
  188. if (nLen > 0)
  189. {
  190. vector<char> vch;
  191. size_t ptr = 0;
  192. while (ptr < (size_t)nLen)
  193. {
  194. size_t bytes_to_read = std::min((size_t)nLen - ptr, POST_READ_SIZE);
  195. vch.resize(ptr + bytes_to_read);
  196. stream.read(&vch[ptr], bytes_to_read);
  197. if (!stream) // Connection lost while reading
  198. return HTTP_INTERNAL_SERVER_ERROR;
  199. ptr += bytes_to_read;
  200. }
  201. strMessageRet = string(vch.begin(), vch.end());
  202. }
  203. string sConHdr = mapHeadersRet["connection"];
  204. if ((sConHdr != "close") && (sConHdr != "keep-alive"))
  205. {
  206. if (nProto >= 1)
  207. mapHeadersRet["connection"] = "keep-alive";
  208. else
  209. mapHeadersRet["connection"] = "close";
  210. }
  211. return HTTP_OK;
  212. }
  213. //
  214. // JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility,
  215. // but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were
  216. // unspecified (HTTP errors and contents of 'error').
  217. //
  218. // 1.0 spec: http://json-rpc.org/wiki/specification
  219. // 1.2 spec: http://jsonrpc.org/historical/json-rpc-over-http.html
  220. // http://www.codeproject.com/KB/recipes/JSON_Spirit.aspx
  221. //
  222. string JSONRPCRequest(const string& strMethod, const Array& params, const Value& id)
  223. {
  224. Object request;
  225. request.push_back(Pair("method", strMethod));
  226. request.push_back(Pair("params", params));
  227. request.push_back(Pair("id", id));
  228. return write_string(Value(request), false) + "\n";
  229. }
  230. Object JSONRPCReplyObj(const Value& result, const Value& error, const Value& id)
  231. {
  232. Object reply;
  233. if (error.type() != null_type)
  234. reply.push_back(Pair("result", Value::null));
  235. else
  236. reply.push_back(Pair("result", result));
  237. reply.push_back(Pair("error", error));
  238. reply.push_back(Pair("id", id));
  239. return reply;
  240. }
  241. string JSONRPCReply(const Value& result, const Value& error, const Value& id)
  242. {
  243. Object reply = JSONRPCReplyObj(result, error, id);
  244. return write_string(Value(reply), false) + "\n";
  245. }
  246. Object JSONRPCError(int code, const string& message)
  247. {
  248. Object error;
  249. error.push_back(Pair("code", code));
  250. error.push_back(Pair("message", message));
  251. return error;
  252. }