Bitcoin ABC  0.22.13
P2P Digital Currency
httprpc.cpp
Go to the documentation of this file.
1 // Copyright (c) 2015-2016 The Bitcoin Core developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 
5 #include <httprpc.h>
6 
7 #include <chainparams.h>
8 #include <config.h>
9 #include <crypto/hmac_sha256.h>
10 #include <node/ui_interface.h>
11 #include <rpc/protocol.h>
12 #include <util/ref.h>
13 #include <util/strencodings.h>
14 #include <util/system.h>
15 #include <util/translation.h>
16 #include <walletinitinterface.h>
17 
18 #include <boost/algorithm/string.hpp> // boost::trim
19 
20 #include <algorithm>
21 #include <cstdio>
22 #include <iterator>
23 #include <map>
24 #include <memory>
25 #include <set>
26 #include <string>
27 
29 static const char *WWW_AUTH_HEADER_DATA = "Basic realm=\"jsonrpc\"";
30 
32 static const int64_t RPC_AUTH_BRUTE_FORCE_DELAY = 250;
33 
38 class HTTPRPCTimer : public RPCTimerBase {
39 public:
40  HTTPRPCTimer(struct event_base *eventBase, std::function<void()> &func,
41  int64_t millis)
42  : ev(eventBase, false, func) {
43  struct timeval tv;
44  tv.tv_sec = millis / 1000;
45  tv.tv_usec = (millis % 1000) * 1000;
46  ev.trigger(&tv);
47  }
48 
49 private:
51 };
52 
54 public:
55  explicit HTTPRPCTimerInterface(struct event_base *_base) : base(_base) {}
56 
57  const char *Name() override { return "HTTP"; }
58 
59  RPCTimerBase *NewTimer(std::function<void()> &func,
60  int64_t millis) override {
61  return new HTTPRPCTimer(base, func, millis);
62  }
63 
64 private:
65  struct event_base *base;
66 };
67 
68 /* Pre-base64-encoded authentication token */
69 static std::string strRPCUserColonPass;
70 /* Pre-base64-encoded authentication token */
71 static std::string strRPCCORSDomain;
72 /* Stored RPC timer interface (for unregistration) */
73 static std::unique_ptr<HTTPRPCTimerInterface> httpRPCTimerInterface;
74 /* RPC Auth Whitelist */
75 static std::map<std::string, std::set<std::string>> g_rpc_whitelist;
76 static bool g_rpc_whitelist_default = false;
77 
78 static void JSONErrorReply(HTTPRequest *req, const UniValue &objError,
79  const UniValue &id) {
80  // Send error reply from json-rpc error object.
81  int nStatus = HTTP_INTERNAL_SERVER_ERROR;
82  int code = find_value(objError, "code").get_int();
83 
84  if (code == RPC_INVALID_REQUEST) {
85  nStatus = HTTP_BAD_REQUEST;
86  } else if (code == RPC_METHOD_NOT_FOUND) {
87  nStatus = HTTP_NOT_FOUND;
88  }
89 
90  std::string strReply = JSONRPCReply(NullUniValue, objError, id);
91 
92  req->WriteHeader("Content-Type", "application/json");
93  req->WriteReply(nStatus, strReply);
94 }
95 
96 /*
97  * This function checks username and password against -rpcauth entries from
98  * config file.
99  */
100 static bool multiUserAuthorized(std::string strUserPass) {
101  if (strUserPass.find(':') == std::string::npos) {
102  return false;
103  }
104  std::string strUser = strUserPass.substr(0, strUserPass.find(':'));
105  std::string strPass = strUserPass.substr(strUserPass.find(':') + 1);
106 
107  for (const std::string &strRPCAuth : gArgs.GetArgs("-rpcauth")) {
108  // Search for multi-user login/pass "rpcauth" from config
109  std::vector<std::string> vFields;
110  boost::split(vFields, strRPCAuth, boost::is_any_of(":$"));
111  if (vFields.size() != 3) {
112  // Incorrect formatting in config file
113  continue;
114  }
115 
116  std::string strName = vFields[0];
117  if (!TimingResistantEqual(strName, strUser)) {
118  continue;
119  }
120 
121  std::string strSalt = vFields[1];
122  std::string strHash = vFields[2];
123 
124  static const unsigned int KEY_SIZE = 32;
125  uint8_t out[KEY_SIZE];
126 
127  CHMAC_SHA256(reinterpret_cast<const uint8_t *>(strSalt.data()),
128  strSalt.size())
129  .Write(reinterpret_cast<const uint8_t *>(strPass.data()),
130  strPass.size())
131  .Finalize(out);
132  std::vector<uint8_t> hexvec(out, out + KEY_SIZE);
133  std::string strHashFromPass = HexStr(hexvec);
134 
135  if (TimingResistantEqual(strHashFromPass, strHash)) {
136  return true;
137  }
138  }
139  return false;
140 }
141 
142 static bool RPCAuthorized(const std::string &strAuth,
143  std::string &strAuthUsernameOut) {
144  // Belt-and-suspenders measure if InitRPCAuthentication was not called.
145  if (strRPCUserColonPass.empty()) {
146  return false;
147  }
148 
149  if (strAuth.substr(0, 6) != "Basic ") {
150  return false;
151  }
152 
153  std::string strUserPass64 = strAuth.substr(6);
154  boost::trim(strUserPass64);
155  std::string strUserPass = DecodeBase64(strUserPass64);
156 
157  if (strUserPass.find(':') != std::string::npos) {
158  strAuthUsernameOut = strUserPass.substr(0, strUserPass.find(':'));
159  }
160 
161  // Check if authorized under single-user field
162  if (TimingResistantEqual(strUserPass, strRPCUserColonPass)) {
163  return true;
164  }
165  return multiUserAuthorized(strUserPass);
166 }
167 
168 static bool checkCORS(HTTPRequest *req) {
169  // https://www.w3.org/TR/cors/#resource-requests
170 
171  // 1. If the Origin header is not present terminate this set of steps.
172  // The request is outside the scope of this specification.
173  std::pair<bool, std::string> origin = req->GetHeader("origin");
174  if (!origin.first) {
175  return false;
176  }
177 
178  // 2. If the value of the Origin header is not a case-sensitive match for
179  // any of the values in list of origins do not set any additional headers
180  // and terminate this set of steps.
181  // Note: Always matching is acceptable since the list of origins can be
182  // unbounded.
183  if (origin.second != strRPCCORSDomain) {
184  return false;
185  }
186 
187  if (req->GetRequestMethod() == HTTPRequest::OPTIONS) {
188  // 6.2 Preflight Request
189  // In response to a preflight request the resource indicates which
190  // methods and headers (other than simple methods and simple
191  // headers) it is willing to handle and whether it supports
192  // credentials.
193  // Resources must use the following set of steps to determine which
194  // additional headers to use in the response:
195 
196  // 3. Let method be the value as result of parsing the
197  // Access-Control-Request-Method header.
198  // If there is no Access-Control-Request-Method header or if parsing
199  // failed, do not set any additional headers and terminate this set
200  // of steps. The request is outside the scope of this specification.
201  std::pair<bool, std::string> method =
202  req->GetHeader("access-control-request-method");
203  if (!method.first) {
204  return false;
205  }
206 
207  // 4. Let header field-names be the values as result of parsing
208  // the Access-Control-Request-Headers headers.
209  // If there are no Access-Control-Request-Headers headers let header
210  // field-names be the empty list.
211  // If parsing failed do not set any additional headers and terminate
212  // this set of steps. The request is outside the scope of this
213  // specification.
214  std::pair<bool, std::string> header_field_names =
215  req->GetHeader("access-control-request-headers");
216 
217  // 5. If method is not a case-sensitive match for any of the
218  // values in list of methods do not set any additional headers
219  // and terminate this set of steps.
220  // Note: Always matching is acceptable since the list of methods
221  // can be unbounded.
222  if (method.second != "POST") {
223  return false;
224  }
225 
226  // 6. If any of the header field-names is not a ASCII case-
227  // insensitive match for any of the values in list of headers do not
228  // set any additional headers and terminate this set of steps.
229  // Note: Always matching is acceptable since the list of headers can
230  // be unbounded.
231  const std::string &list_of_headers = "authorization,content-type";
232 
233  // 7. If the resource supports credentials add a single
234  // Access-Control-Allow-Origin header, with the value of the Origin
235  // header as value, and add a single
236  // Access-Control-Allow-Credentials header with the case-sensitive
237  // string "true" as value.
238  req->WriteHeader("Access-Control-Allow-Origin", origin.second);
239  req->WriteHeader("Access-Control-Allow-Credentials", "true");
240 
241  // 8. Optionally add a single Access-Control-Max-Age header with as
242  // value the amount of seconds the user agent is allowed to cache
243  // the result of the request.
244 
245  // 9. If method is a simple method this step may be skipped.
246  // Add one or more Access-Control-Allow-Methods headers consisting
247  // of (a subset of) the list of methods.
248  // If a method is a simple method it does not need to be listed, but
249  // this is not prohibited.
250  // Note: Since the list of methods can be unbounded, simply
251  // returning the method indicated by
252  // Access-Control-Request-Method (if supported) can be enough.
253  req->WriteHeader("Access-Control-Allow-Methods", method.second);
254 
255  // 10. If each of the header field-names is a simple header and none
256  // is Content-Type, this step may be skipped.
257  // Add one or more Access-Control-Allow-Headers headers consisting
258  // of (a subset of) the list of headers.
259  req->WriteHeader("Access-Control-Allow-Headers",
260  header_field_names.first ? header_field_names.second
261  : list_of_headers);
262  req->WriteReply(HTTP_OK);
263  return true;
264  }
265 
266  // 6.1 Simple Cross-Origin Request, Actual Request, and Redirects
267  // In response to a simple cross-origin request or actual request the
268  // resource indicates whether or not to share the response.
269  // If the resource has been relocated, it indicates whether to share its
270  // new URL.
271  // Resources must use the following set of steps to determine which
272  // additional headers to use in the response:
273 
274  // 3. If the resource supports credentials add a single
275  // Access-Control-Allow-Origin header, with the value of the Origin
276  // header as value, and add a single Access-Control-Allow-Credentials
277  // header with the case-sensitive string "true" as value.
278  req->WriteHeader("Access-Control-Allow-Origin", origin.second);
279  req->WriteHeader("Access-Control-Allow-Credentials", "true");
280 
281  // 4. If the list of exposed headers is not empty add one or more
282  // Access-Control-Expose-Headers headers, with as values the header
283  // field names given in the list of exposed headers.
284  req->WriteHeader("Access-Control-Expose-Headers", "WWW-Authenticate");
285 
286  return false;
287 }
288 
290  // First, check and/or set CORS headers
291  if (checkCORS(req)) {
292  return true;
293  }
294 
295  // JSONRPC handles only POST
296  if (req->GetRequestMethod() != HTTPRequest::POST) {
298  "JSONRPC server handles only POST requests");
299  return false;
300  }
301  // Check authorization
302  std::pair<bool, std::string> authHeader = req->GetHeader("authorization");
303  if (!authHeader.first) {
304  req->WriteHeader("WWW-Authenticate", WWW_AUTH_HEADER_DATA);
306  return false;
307  }
308 
309  JSONRPCRequest jreq(context);
310  jreq.peerAddr = req->GetPeer().ToString();
311  if (!RPCAuthorized(authHeader.second, jreq.authUser)) {
312  LogPrintf("ThreadRPCServer incorrect password attempt from %s\n",
313  jreq.peerAddr);
314 
321  std::chrono::milliseconds{RPC_AUTH_BRUTE_FORCE_DELAY});
322 
323  req->WriteHeader("WWW-Authenticate", WWW_AUTH_HEADER_DATA);
325  return false;
326  }
327 
328  try {
329  // Parse request
330  UniValue valRequest;
331  if (!valRequest.read(req->ReadBody())) {
332  throw JSONRPCError(RPC_PARSE_ERROR, "Parse error");
333  }
334 
335  // Set the URI
336  jreq.URI = req->GetURI();
337 
338  std::string strReply;
339  bool user_has_whitelist = g_rpc_whitelist.count(jreq.authUser);
340  if (!user_has_whitelist && g_rpc_whitelist_default) {
341  LogPrintf("RPC User %s not allowed to call any methods\n",
342  jreq.authUser);
344  return false;
345 
346  // singleton request
347  } else if (valRequest.isObject()) {
348  jreq.parse(valRequest);
349  if (user_has_whitelist &&
350  !g_rpc_whitelist[jreq.authUser].count(jreq.strMethod)) {
351  LogPrintf("RPC User %s not allowed to call method %s\n",
352  jreq.authUser, jreq.strMethod);
354  return false;
355  }
356  UniValue result = rpcServer.ExecuteCommand(config, jreq);
357 
358  // Send reply
359  strReply = JSONRPCReply(result, NullUniValue, jreq.id);
360 
361  // array of requests
362  } else if (valRequest.isArray()) {
363  if (user_has_whitelist) {
364  for (unsigned int reqIdx = 0; reqIdx < valRequest.size();
365  reqIdx++) {
366  if (!valRequest[reqIdx].isObject()) {
368  "Invalid Request object");
369  } else {
370  const UniValue &request = valRequest[reqIdx].get_obj();
371  // Parse method
372  std::string strMethod =
373  find_value(request, "method").get_str();
374  if (!g_rpc_whitelist[jreq.authUser].count(strMethod)) {
375  LogPrintf(
376  "RPC User %s not allowed to call method %s\n",
377  jreq.authUser, strMethod);
379  return false;
380  }
381  }
382  }
383  }
384  strReply = JSONRPCExecBatch(config, rpcServer, jreq,
385  valRequest.get_array());
386  } else {
387  throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error");
388  }
389 
390  req->WriteHeader("Content-Type", "application/json");
391  req->WriteReply(HTTP_OK, strReply);
392  } catch (const UniValue &objError) {
393  JSONErrorReply(req, objError, jreq.id);
394  return false;
395  } catch (const std::exception &e) {
396  JSONErrorReply(req, JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id);
397  return false;
398  }
399  return true;
400 }
401 
402 static bool InitRPCAuthentication() {
403  if (gArgs.GetArg("-rpcpassword", "") == "") {
404  LogPrintf("No rpcpassword set - using random cookie authentication.\n");
405  if (!GenerateAuthCookie(&strRPCUserColonPass)) {
406  // Same message as AbortNode.
407  uiInterface.ThreadSafeMessageBox(
408  _("Error: A fatal internal error occurred, see debug.log for "
409  "details"),
411  return false;
412  }
413  } else {
414  LogPrintf("Config options rpcuser and rpcpassword will soon be "
415  "deprecated. Locally-run instances may remove rpcuser to use "
416  "cookie-based auth, or may be replaced with rpcauth. Please "
417  "see share/rpcauth for rpcauth auth generation.\n");
418  strRPCUserColonPass = gArgs.GetArg("-rpcuser", "") + ":" +
419  gArgs.GetArg("-rpcpassword", "");
420  }
421 
422  strRPCCORSDomain = gArgs.GetArg("-rpccorsdomain", "");
423 
424  if (gArgs.GetArg("-rpcauth", "") != "") {
425  LogPrintf("Using rpcauth authentication.\n");
426  }
427 
428  g_rpc_whitelist_default = gArgs.GetBoolArg("-rpcwhitelistdefault",
429  gArgs.IsArgSet("-rpcwhitelist"));
430  for (const std::string &strRPCWhitelist : gArgs.GetArgs("-rpcwhitelist")) {
431  auto pos = strRPCWhitelist.find(':');
432  std::string strUser = strRPCWhitelist.substr(0, pos);
433  bool intersect = g_rpc_whitelist.count(strUser);
434  std::set<std::string> &whitelist = g_rpc_whitelist[strUser];
435  if (pos != std::string::npos) {
436  std::string strWhitelist = strRPCWhitelist.substr(pos + 1);
437  std::set<std::string> new_whitelist;
438  boost::split(new_whitelist, strWhitelist, boost::is_any_of(", "));
439  if (intersect) {
440  std::set<std::string> tmp_whitelist;
441  std::set_intersection(
442  new_whitelist.begin(), new_whitelist.end(),
443  whitelist.begin(), whitelist.end(),
444  std::inserter(tmp_whitelist, tmp_whitelist.end()));
445  new_whitelist = std::move(tmp_whitelist);
446  }
447  whitelist = std::move(new_whitelist);
448  }
449  }
450 
451  return true;
452 }
453 
454 bool StartHTTPRPC(HTTPRPCRequestProcessor &httpRPCRequestProcessor) {
455  LogPrint(BCLog::RPC, "Starting HTTP RPC server\n");
456  if (!InitRPCAuthentication()) {
457  return false;
458  }
459 
460  const std::function<bool(Config &, HTTPRequest *, const std::string &)>
461  &rpcFunction =
463  &httpRPCRequestProcessor, std::placeholders::_2);
464  RegisterHTTPHandler("/", true, rpcFunction);
466  RegisterHTTPHandler("/wallet/", false, rpcFunction);
467  }
468  struct event_base *eventBase = EventBase();
469  assert(eventBase);
470  httpRPCTimerInterface = std::make_unique<HTTPRPCTimerInterface>(eventBase);
471  RPCSetTimerInterface(httpRPCTimerInterface.get());
472  return true;
473 }
474 
476  LogPrint(BCLog::RPC, "Interrupting HTTP RPC server\n");
477 }
478 
479 void StopHTTPRPC() {
480  LogPrint(BCLog::RPC, "Stopping HTTP RPC server\n");
481  UnregisterHTTPHandler("/", true);
483  UnregisterHTTPHandler("/wallet/", false);
484  }
485  if (httpRPCTimerInterface) {
486  RPCUnsetTimerInterface(httpRPCTimerInterface.get());
487  httpRPCTimerInterface.reset();
488  }
489 }
bool isObject() const
Definition: univalue.h:96
bool IsArgSet(const std::string &strArg) const
Return true if the given argument has been manually set.
Definition: system.cpp:390
bool TimingResistantEqual(const T &a, const T &b)
Timing-attack-resistant comparison.
Definition: strencodings.h:160
RPC timer "driver".
Definition: server.h:98
#define LogPrint(category,...)
Definition: logging.h:192
HTTPRPCTimerInterface(struct event_base *_base)
Definition: httprpc.cpp:55
const char * Name() override
Implementation name.
Definition: httprpc.cpp:57
static const char * WWW_AUTH_HEADER_DATA
WWW-Authenticate to present with 401 Unauthorized response.
Definition: httprpc.cpp:29
bool read(const char *raw, size_t len)
static std::string strRPCUserColonPass
Definition: httprpc.cpp:69
HTTPEvent ev
Definition: httprpc.cpp:50
Event class.
Definition: httpserver.h:139
static bool InitRPCAuthentication()
Definition: httprpc.cpp:402
struct event_base * base
Definition: httprpc.cpp:65
static void JSONErrorReply(HTTPRequest *req, const UniValue &objError, const UniValue &id)
Definition: httprpc.cpp:78
A hasher class for HMAC-SHA-256.
Definition: hmac_sha256.h:14
std::vector< uint8_t > DecodeBase64(const char *p, bool *pf_invalid)
RPCTimerBase * NewTimer(std::function< void()> &func, int64_t millis) override
Factory function for timers.
Definition: httprpc.cpp:59
static void LogPrintf(const char *fmt, const Args &... args)
Definition: logging.h:174
const std::string & get_str() const
const UniValue & get_array() const
bool GetBoolArg(const std::string &strArg, bool fDefault) const
Return boolean argument or default value.
Definition: system.cpp:498
void InterruptHTTPRPC()
Interrupt HTTP RPC subsystem.
Definition: httprpc.cpp:475
bool ProcessHTTPRequest(HTTPRequest *request)
Definition: httprpc.cpp:289
std::string JSONRPCExecBatch(Config &config, RPCServer &rpcServer, const JSONRPCRequest &jreq, const UniValue &vReq)
Definition: server.cpp:415
void RPCUnsetTimerInterface(RPCTimerInterface *iface)
Unset factory function for timers.
Definition: server.cpp:538
const UniValue & find_value(const UniValue &obj, const std::string &name)
Definition: univalue.cpp:234
void RegisterHTTPHandler(const std::string &prefix, bool exactMatch, const HTTPRequestHandler &handler)
Register handler for prefix.
Definition: httpserver.cpp:676
std::string strMethod
Definition: request.h:36
static std::unique_ptr< HTTPRPCTimerInterface > httpRPCTimerInterface
Definition: httprpc.cpp:73
static bool RPCAuthorized(const std::string &strAuth, std::string &strAuthUsernameOut)
Definition: httprpc.cpp:142
static bool DelegateHTTPRequest(HTTPRPCRequestProcessor *requestProcessor, HTTPRequest *request)
Definition: httprpc.h:31
std::string peerAddr
Definition: request.h:41
static bool multiUserAuthorized(std::string strUserPass)
Definition: httprpc.cpp:100
UniValue JSONRPCError(int code, const std::string &message)
Definition: request.cpp:52
void WriteReply(int nStatus, const std::string &strReply="")
Write HTTP reply.
Definition: httpserver.cpp:611
Simple one-shot callback timer to be used by the RPC mechanism to e.g.
Definition: httprpc.cpp:38
RequestMethod GetRequestMethod() const
Get request method.
Definition: httpserver.cpp:659
bilingual_str _(const char *psz)
Translation function.
Definition: translation.h:55
UniValue id
Definition: request.h:35
void UnregisterHTTPHandler(const std::string &prefix, bool exactMatch)
Unregister handler for prefix.
Definition: httpserver.cpp:683
int get_int() const
struct event_base * EventBase()
Return evhttp event base.
Definition: httpserver.cpp:521
std::string JSONRPCReply(const UniValue &result, const UniValue &error, const UniValue &id)
Definition: request.cpp:46
void StopHTTPRPC()
Stop HTTP RPC subsystem.
Definition: httprpc.cpp:479
bool StartHTTPRPC(HTTPRPCRequestProcessor &httpRPCRequestProcessor)
Start HTTP RPC subsystem.
Definition: httprpc.cpp:454
const WalletInitInterface & g_wallet_init_interface
Definition: dummywallet.cpp:43
static bool checkCORS(HTTPRequest *req)
Definition: httprpc.cpp:168
void parse(const UniValue &valRequest)
Definition: request.cpp:157
static bool g_rpc_whitelist_default
Definition: httprpc.cpp:76
static std::string strRPCCORSDomain
Definition: httprpc.cpp:71
HTTPRPCTimer(struct event_base *eventBase, std::function< void()> &func, int64_t millis)
Definition: httprpc.cpp:40
void UninterruptibleSleep(const std::chrono::microseconds &n)
Definition: time.cpp:20
void WriteHeader(const std::string &hdr, const std::string &value)
Write output header.
Definition: httpserver.cpp:599
void trigger(struct timeval *tv)
Trigger the event.
Definition: httpserver.cpp:543
virtual bool HasWalletSupport() const =0
Is the wallet component enabled.
const UniValue & get_obj() const
std::string GetArg(const std::string &strArg, const std::string &strDefault) const
Return string argument or default value.
Definition: system.cpp:479
std::string URI
Definition: request.h:39
std::string authUser
Definition: request.h:40
void RPCSetTimerInterface(RPCTimerInterface *iface)
Set the factory function for timers.
Definition: server.cpp:534
std::pair< bool, std::string > GetHeader(const std::string &hdr) const
Get the request header specified by hdr, or an empty string.
Definition: httpserver.cpp:564
Opaque base class for timers returned by NewTimerFunc.
Definition: server.h:90
bool GenerateAuthCookie(std::string *cookie_out)
Generate a new RPC authentication cookie and write it to disk.
Definition: request.cpp:76
std::string HexStr(const T itbegin, const T itend)
Definition: strencodings.h:132
ArgsManager gArgs
Definition: system.cpp:76
const UniValue NullUniValue
Definition: univalue.cpp:13
CService GetPeer() const
Get CService (address:ip) for the origin of the http request.
Definition: httpserver.cpp:642
std::string ReadBody()
Read request body.
Definition: httpserver.cpp:575
Standard JSON-RPC 2.0 errors.
Definition: protocol.h:26
std::string ToString() const
Definition: netaddress.cpp:780
In-flight HTTP request.
Definition: httpserver.h:74
size_t size() const
Definition: univalue.h:80
CClientUIInterface uiInterface
static std::map< std::string, std::set< std::string > > g_rpc_whitelist
Definition: httprpc.cpp:75
std::vector< std::string > GetArgs(const std::string &strArg) const
Return a vector of strings of the given argument.
Definition: system.cpp:381
bool isArray() const
Definition: univalue.h:95
std::string GetURI() const
Get requested URI.
Definition: httpserver.cpp:655
static struct event_base * eventBase
HTTP module state.
Definition: httpserver.cpp:142
static const int64_t RPC_AUTH_BRUTE_FORCE_DELAY
RPC auth failure delay to make brute-forcing expensive.
Definition: httprpc.cpp:32