1 // Copyright (c) 2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2014 The Bitcoin Core developers
3 // Distributed under the MIT software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
6 #include "rpcprotocol.h"
8 #include "clientversion.h"
10 #include "tinyformat.h"
12 #include "utilstrencodings.h"
19 #include <boost/algorithm/string.hpp>
20 #include <boost/asio.hpp>
21 #include <boost/asio/ssl.hpp>
22 #include <boost/bind.hpp>
23 #include <boost/filesystem.hpp>
24 #include <boost/foreach.hpp>
25 #include <boost/iostreams/concepts.hpp>
26 #include <boost/iostreams/stream.hpp>
27 #include <boost/shared_ptr.hpp>
29 #include "univalue/univalue.h"
33 //! Number of bytes to allocate and read at most at once in post data
34 const size_t POST_READ_SIZE = 256 * 1024;
39 * This ain't Apache. We're just using HTTP header for the length field
40 * and to be compatible with other JSON-RPC implementations.
43 string HTTPPost(const string& strMsg, const map<string,string>& mapRequestHeaders)
46 s << "POST / HTTP/1.1\r\n"
47 << "User-Agent: zcash-json-rpc/" << FormatFullVersion() << "\r\n"
48 << "Host: 127.0.0.1\r\n"
49 << "Content-Type: application/json\r\n"
50 << "Content-Length: " << strMsg.size() << "\r\n"
51 << "Connection: close\r\n"
52 << "Accept: application/json\r\n";
53 BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapRequestHeaders)
54 s << item.first << ": " << item.second << "\r\n";
55 s << "\r\n" << strMsg;
60 static string rfc1123Time()
62 return DateTimeStrFormat("%a, %d %b %Y %H:%M:%S +0000", GetTime());
65 static const char *httpStatusDescription(int nStatus)
68 case HTTP_OK: return "OK";
69 case HTTP_BAD_REQUEST: return "Bad Request";
70 case HTTP_FORBIDDEN: return "Forbidden";
71 case HTTP_NOT_FOUND: return "Not Found";
72 case HTTP_INTERNAL_SERVER_ERROR: return "Internal Server Error";
77 string HTTPError(int nStatus, bool keepalive, bool headersOnly)
79 if (nStatus == HTTP_UNAUTHORIZED)
80 return strprintf("HTTP/1.0 401 Authorization Required\r\n"
82 "Server: zcash-json-rpc/%s\r\n"
83 "WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n"
84 "Content-Type: text/html\r\n"
85 "Content-Length: 296\r\n"
87 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\r\n"
88 "\"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd\">\r\n"
91 "<TITLE>Error</TITLE>\r\n"
92 "<META HTTP-EQUIV='Content-Type' CONTENT='text/html; charset=ISO-8859-1'>\r\n"
94 "<BODY><H1>401 Unauthorized.</H1></BODY>\r\n"
95 "</HTML>\r\n", rfc1123Time(), FormatFullVersion());
97 return HTTPReply(nStatus, httpStatusDescription(nStatus), keepalive,
98 headersOnly, "text/plain");
101 string HTTPReplyHeader(int nStatus, bool keepalive, size_t contentLength, const char *contentType)
107 "Content-Length: %u\r\n"
108 "Content-Type: %s\r\n"
109 "Server: zcash-json-rpc/%s\r\n"
112 httpStatusDescription(nStatus),
114 keepalive ? "keep-alive" : "close",
117 FormatFullVersion());
120 string HTTPReply(int nStatus, const string& strMsg, bool keepalive,
121 bool headersOnly, const char *contentType)
125 return HTTPReplyHeader(nStatus, keepalive, 0, contentType);
127 return HTTPReplyHeader(nStatus, keepalive, strMsg.size(), contentType) + strMsg;
131 bool ReadHTTPRequestLine(std::basic_istream<char>& stream, int &proto,
132 string& http_method, string& http_uri)
135 getline(stream, str);
137 // HTTP request line is space-delimited
138 vector<string> vWords;
139 boost::split(vWords, str, boost::is_any_of(" "));
140 if (vWords.size() < 2)
143 // HTTP methods permitted: GET, POST
144 http_method = vWords[0];
145 if (http_method != "GET" && http_method != "POST")
148 // HTTP URI must be an absolute path, relative to current host
149 http_uri = vWords[1];
150 if (http_uri.size() == 0 || http_uri[0] != '/')
153 // parse proto, if present
154 string strProto = "";
155 if (vWords.size() > 2)
156 strProto = vWords[2];
159 const char *ver = strstr(strProto.c_str(), "HTTP/1.");
166 int ReadHTTPStatus(std::basic_istream<char>& stream, int &proto)
169 getline(stream, str);
170 vector<string> vWords;
171 boost::split(vWords, str, boost::is_any_of(" "));
172 if (vWords.size() < 2)
173 return HTTP_INTERNAL_SERVER_ERROR;
175 const char *ver = strstr(str.c_str(), "HTTP/1.");
178 return atoi(vWords[1].c_str());
181 int ReadHTTPHeaders(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet)
187 std::getline(stream, str);
188 if (str.empty() || str == "\r")
190 string::size_type nColon = str.find(":");
191 if (nColon != string::npos)
193 string strHeader = str.substr(0, nColon);
194 boost::trim(strHeader);
195 boost::to_lower(strHeader);
196 string strValue = str.substr(nColon+1);
197 boost::trim(strValue);
198 mapHeadersRet[strHeader] = strValue;
199 if (strHeader == "content-length")
200 nLen = atoi(strValue.c_str());
207 int ReadHTTPMessage(std::basic_istream<char>& stream, map<string,
208 string>& mapHeadersRet, string& strMessageRet,
209 int nProto, size_t max_size)
211 mapHeadersRet.clear();
215 int nLen = ReadHTTPHeaders(stream, mapHeadersRet);
216 if (nLen < 0 || (size_t)nLen > max_size)
217 return HTTP_INTERNAL_SERVER_ERROR;
224 while (ptr < (size_t)nLen)
226 size_t bytes_to_read = std::min((size_t)nLen - ptr, POST_READ_SIZE);
227 vch.resize(ptr + bytes_to_read);
228 stream.read(&vch[ptr], bytes_to_read);
229 if (!stream) // Connection lost while reading
230 return HTTP_INTERNAL_SERVER_ERROR;
231 ptr += bytes_to_read;
233 strMessageRet = string(vch.begin(), vch.end());
236 string sConHdr = mapHeadersRet["connection"];
238 if ((sConHdr != "close") && (sConHdr != "keep-alive"))
241 mapHeadersRet["connection"] = "keep-alive";
243 mapHeadersRet["connection"] = "close";
250 * JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility,
251 * but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were
252 * unspecified (HTTP errors and contents of 'error').
254 * 1.0 spec: http://json-rpc.org/wiki/specification
255 * 1.2 spec: http://jsonrpc.org/historical/json-rpc-over-http.html
256 * http://www.codeproject.com/KB/recipes/JSON_Spirit.aspx
259 string JSONRPCRequest(const string& strMethod, const UniValue& params, const UniValue& id)
261 UniValue request(UniValue::VOBJ);
262 request.push_back(Pair("method", strMethod));
263 request.push_back(Pair("params", params));
264 request.push_back(Pair("id", id));
265 return request.write() + "\n";
268 UniValue JSONRPCReplyObj(const UniValue& result, const UniValue& error, const UniValue& id)
270 UniValue reply(UniValue::VOBJ);
272 reply.push_back(Pair("result", NullUniValue));
274 reply.push_back(Pair("result", result));
275 reply.push_back(Pair("error", error));
276 reply.push_back(Pair("id", id));
280 string JSONRPCReply(const UniValue& result, const UniValue& error, const UniValue& id)
282 UniValue reply = JSONRPCReplyObj(result, error, id);
283 return reply.write() + "\n";
286 UniValue JSONRPCError(int code, const string& message)
288 UniValue error(UniValue::VOBJ);
289 error.push_back(Pair("code", code));
290 error.push_back(Pair("message", message));
294 /** Username used when cookie authentication is in use (arbitrary, only for
295 * recognizability in debugging/logging purposes)
297 static const std::string COOKIEAUTH_USER = "__cookie__";
298 /** Default name for auth cookie file */
299 static const std::string COOKIEAUTH_FILE = ".cookie";
301 boost::filesystem::path GetAuthCookieFile()
303 boost::filesystem::path path(GetArg("-rpccookiefile", COOKIEAUTH_FILE));
304 if (!path.is_complete()) path = GetDataDir() / path;
308 bool GenerateAuthCookie(std::string *cookie_out)
310 unsigned char rand_pwd[32];
311 GetRandBytes(rand_pwd, 32);
312 std::string cookie = COOKIEAUTH_USER + ":" + EncodeBase64(&rand_pwd[0],32);
314 /** the umask determines what permissions are used to create this file -
315 * these are set to 077 in init.cpp unless overridden with -sysperms.
318 boost::filesystem::path filepath = GetAuthCookieFile();
319 file.open(filepath.string().c_str());
320 if (!file.is_open()) {
321 LogPrintf("Unable to open cookie authentication file %s for writing\n", filepath.string());
326 LogPrintf("Generated RPC authentication cookie %s\n", filepath.string());
329 *cookie_out = cookie;
333 bool GetAuthCookie(std::string *cookie_out)
337 boost::filesystem::path filepath = GetAuthCookieFile();
338 file.open(filepath.string().c_str());
341 std::getline(file, cookie);
345 *cookie_out = cookie;
349 void DeleteAuthCookie()
352 boost::filesystem::remove(GetAuthCookieFile());
353 } catch (const boost::filesystem::filesystem_error& e) {
354 LogPrintf("%s: Unable to remove random auth cookie file: %s\n", __func__, e.what());