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