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