Anoncoin  0.9.4
P2P Digital Currency
rpcprotocol.cpp
Go to the documentation of this file.
1 // Copyright (c) 2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2014 The Bitcoin developers
3 // Copyright (c) 2013-2014 The Anoncoin Core developers
4 // Distributed under the MIT/X11 software license, see the accompanying
5 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
6 
7 #include "rpcprotocol.h"
8 
9 #include "clientversion.h"
10 #include "tinyformat.h"
11 #include "util.h"
12 
13 #include <stdint.h>
14 
15 #include <boost/algorithm/string.hpp>
16 #include <boost/asio.hpp>
17 #include <boost/asio/ssl.hpp>
18 #include <boost/bind.hpp>
19 #include <boost/filesystem.hpp>
20 #include <boost/foreach.hpp>
21 #include <boost/iostreams/concepts.hpp>
22 #include <boost/iostreams/stream.hpp>
23 #include <boost/shared_ptr.hpp>
24 #include "json/json_spirit_writer_template.h"
25 
26 using namespace std;
27 using namespace boost;
28 using namespace boost::asio;
29 using namespace json_spirit;
30 
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 
38 string HTTPPost(const string& strMsg, const map<string,string>& mapRequestHeaders)
39 {
40  ostringstream s;
41  s << "POST / HTTP/1.1\r\n"
42  << "User-Agent: anoncoin-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 
55 static string rfc1123Time()
56 {
57  return DateTimeStrFormat("%a, %d %b %Y %H:%M:%S +0000", GetTime());
58 }
59 
60 string HTTPReply(int nStatus, const string& strMsg, bool keepalive)
61 {
62  if (nStatus == HTTP_UNAUTHORIZED)
63  return strprintf("HTTP/1.0 401 Authorization Required\r\n"
64  "Date: %s\r\n"
65  "Server: anoncoin-json-rpc/%s\r\n"
66  "WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n"
67  "Content-Type: text/html\r\n"
68  "Content-Length: 296\r\n"
69  "\r\n"
70  "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\r\n"
71  "\"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd\">\r\n"
72  "<HTML>\r\n"
73  "<HEAD>\r\n"
74  "<TITLE>Error</TITLE>\r\n"
75  "<META HTTP-EQUIV='Content-Type' CONTENT='text/html; charset=ISO-8859-1'>\r\n"
76  "</HEAD>\r\n"
77  "<BODY><H1>401 Unauthorized.</H1></BODY>\r\n"
78  "</HTML>\r\n", rfc1123Time(), FormatFullVersion());
79  const char *cStatus;
80  if (nStatus == HTTP_OK) cStatus = "OK";
81  else if (nStatus == HTTP_BAD_REQUEST) cStatus = "Bad Request";
82  else if (nStatus == HTTP_FORBIDDEN) cStatus = "Forbidden";
83  else if (nStatus == HTTP_NOT_FOUND) cStatus = "Not Found";
84  else if (nStatus == HTTP_INTERNAL_SERVER_ERROR) cStatus = "Internal Server Error";
85  else cStatus = "";
86  return strprintf(
87  "HTTP/1.1 %d %s\r\n"
88  "Date: %s\r\n"
89  "Connection: %s\r\n"
90  "Content-Length: %u\r\n"
91  "Content-Type: application/json\r\n"
92  "Server: anoncoin-json-rpc/%s\r\n"
93  "\r\n"
94  "%s",
95  nStatus,
96  cStatus,
97  rfc1123Time(),
98  keepalive ? "keep-alive" : "close",
99  strMsg.size(),
101  strMsg);
102 }
103 
104 int static GetPostIndex(const vector<string>& vWords)
105 {
106  if (vWords[0] == "GET" || vWords[0] == "POST")
107  return 0;
108  if (vWords[1] == "GET" || vWords[1] == "POST")
109  return 1;
110  return -1;
111 }
112 
113 int static GetURIIndex(const vector<string>& vWords)
114 {
115  if (vWords[0].size() > 0 && vWords[0][0] == '/')
116  return 0;
117  if (vWords[1].size() > 0 && vWords[1][0] == '/')
118  return 1;
119  return -1;
120 }
121 
122 bool ReadHTTPRequestLine(std::basic_istream<char>& stream, int &proto,
123  string& http_method, string& http_uri)
124 {
125  string str;
126  getline(stream, str);
127 
128  // HTTP request line is space-delimited
129  vector<string> vWords;
130  boost::split(vWords, str, boost::is_any_of(" "));
131  if (vWords.size() < 2)
132  return false;
133 
134  // HTTP methods permitted: GET, POST
135  int postIndex = GetPostIndex(vWords);
136  if(postIndex < 0)
137  {
138  printf("ReadHTTPRequestLine GetPostIndex FAIL\n");
139  return false;
140  }
141  http_method = vWords[postIndex];
142 
143  int URIIndex = GetURIIndex(vWords);
144  if(URIIndex < 0)
145  {
146  printf("ReadHTTPRequestLine GetURIIndex FAIL\n");
147  return false;
148  }
149  // HTTP URI must be an absolute path, relative to current host
150  http_uri = vWords[URIIndex];
151 
152  // parse proto, if present
153  string strProto = "";
154  if (vWords.size() > 2)
155  strProto = vWords[2];
156 
157  proto = 0;
158  const char *ver = strstr(strProto.c_str(), "HTTP/1.");
159  if (ver != NULL)
160  proto = atoi(ver+7);
161 
162  return true;
163 }
164 
165 int ReadHTTPStatus(std::basic_istream<char>& stream, int &proto)
166 {
167  string str;
168  getline(stream, str);
169  vector<string> vWords;
170  boost::split(vWords, str, boost::is_any_of(" "));
171  if (vWords.size() < 2)
173  proto = 0;
174  const char *ver = strstr(str.c_str(), "HTTP/1.");
175  if (ver != NULL)
176  proto = atoi(ver+7);
177  return atoi(vWords[1].c_str());
178 }
179 
180 int ReadHTTPHeaders(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet)
181 {
182  int nLen = 0;
183  while (true)
184  {
185  string str;
186  std::getline(stream, str);
187  if (str.empty() || str == "\r")
188  break;
189  string::size_type nColon = str.find(":");
190  if (nColon != string::npos)
191  {
192  string strHeader = str.substr(0, nColon);
193  boost::trim(strHeader);
194  boost::to_lower(strHeader);
195  string strValue = str.substr(nColon+1);
196  boost::trim(strValue);
197  mapHeadersRet[strHeader] = strValue;
198  if (strHeader == "content-length")
199  nLen = atoi(strValue.c_str());
200  }
201  }
202  return nLen;
203 }
204 
205 
206 int ReadHTTPMessage(std::basic_istream<char>& stream, map<string,
207  string>& mapHeadersRet, string& strMessageRet,
208  int nProto)
209 {
210  mapHeadersRet.clear();
211  strMessageRet = "";
212 
213  // Read header
214  int nLen = ReadHTTPHeaders(stream, mapHeadersRet);
215  if (nLen < 0 || nLen > (int)MAX_SIZE)
217 
218  // Read message
219  if (nLen > 0)
220  {
221  vector<char> vch(nLen);
222  stream.read(&vch[0], nLen);
223  strMessageRet = string(vch.begin(), vch.end());
224  }
225 
226  string sConHdr = mapHeadersRet["connection"];
227 
228  if ((sConHdr != "close") && (sConHdr != "keep-alive"))
229  {
230  if (nProto >= 1)
231  mapHeadersRet["connection"] = "keep-alive";
232  else
233  mapHeadersRet["connection"] = "close";
234  }
235 
236  return HTTP_OK;
237 }
238 
239 //
240 // JSON-RPC protocol. Anoncoin speaks version 1.0 for maximum compatibility,
241 // but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were
242 // unspecified (HTTP errors and contents of 'error').
243 //
244 // 1.0 spec: http://json-rpc.org/wiki/specification
245 // 1.2 spec: http://jsonrpc.org/historical/json-rpc-over-http.html
246 // http://www.codeproject.com/KB/recipes/JSON_Spirit.aspx
247 //
248 
249 string JSONRPCRequest(const string& strMethod, const Array& params, const Value& id)
250 {
251  Object request;
252  request.push_back(Pair("method", strMethod));
253  request.push_back(Pair("params", params));
254  request.push_back(Pair("id", id));
255  return write_string(Value(request), false) + "\n";
256 }
257 
258 Object JSONRPCReplyObj(const Value& result, const Value& error, const Value& id)
259 {
260  Object reply;
261  if (error.type() != null_type)
262  reply.push_back(Pair("result", Value::null));
263  else
264  reply.push_back(Pair("result", result));
265  reply.push_back(Pair("error", error));
266  reply.push_back(Pair("id", id));
267  return reply;
268 }
269 
270 string JSONRPCReply(const Value& result, const Value& error, const Value& id)
271 {
272  Object reply = JSONRPCReplyObj(result, error, id);
273  return write_string(Value(reply), false) + "\n";
274 }
275 
276 Object JSONRPCError(int code, const string& message)
277 {
278  Object error;
279  error.push_back(Pair("code", code));
280  error.push_back(Pair("message", message));
281  return error;
282 }
int ReadHTTPStatus(std::basic_istream< char > &stream, int &proto)
string JSONRPCReply(const Value &result, const Value &error, const Value &id)
Definition: init.h:14
#define PAIRTYPE(t1, t2)
Definition: util.h:49
bool ReadHTTPRequestLine(std::basic_istream< char > &stream, int &proto, string &http_method, string &http_uri)
#define strprintf
Definition: tinyformat.h:1011
Object JSONRPCError(int code, const string &message)
std::string DateTimeStrFormat(const char *pszFormat, int64_t nTime)
Definition: util.cpp:1411
int ReadHTTPMessage(std::basic_istream< char > &stream, map< string, string > &mapHeadersRet, string &strMessageRet, int nProto)
string HTTPReply(int nStatus, const string &strMsg, bool keepalive)
Definition: rpcprotocol.cpp:60
int64_t GetTime()
Definition: util.cpp:1220
std::string FormatFullVersion()
Object JSONRPCReplyObj(const Value &result, const Value &error, const Value &id)
string HTTPPost(const string &strMsg, const map< string, string > &mapRequestHeaders)
Definition: rpcprotocol.cpp:38
int ReadHTTPHeaders(std::basic_istream< char > &stream, map< string, string > &mapHeadersRet)
string JSONRPCRequest(const string &strMethod, const Array &params, const Value &id)
int atoi(const std::string &str)
Definition: util.h:235