19 #include <sys/types.h>
25 #include <event2/event.h>
26 #include <event2/http.h>
27 #include <event2/thread.h>
28 #include <event2/buffer.h>
29 #include <event2/util.h>
30 #include <event2/keyvalq_struct.h>
32 #ifdef EVENT__HAVE_NETINET_IN_H
33 #include <netinet/in.h>
34 #ifdef _XOPEN_SOURCE_EXTENDED
35 #include <arpa/inet.h>
40 static const size_t MAX_HEADERS_SIZE = 8192;
55 std::unique_ptr<HTTPRequest>
req;
65 template <
typename WorkItem>
71 std::condition_variable
cond;
72 std::deque<std::unique_ptr<WorkItem>>
queue;
84 std::lock_guard<std::mutex> lock(
wq.
cs);
89 std::lock_guard<std::mutex> lock(
wq.
cs);
110 std::unique_lock<std::mutex> lock(
cs);
114 queue.emplace_back(std::unique_ptr<WorkItem>(item));
123 std::unique_ptr<WorkItem> i;
125 std::unique_lock<std::mutex> lock(
cs);
130 i = std::move(
queue.front());
139 std::unique_lock<std::mutex> lock(
cs);
146 std::unique_lock<std::mutex> lock(
cs);
154 std::unique_lock<std::mutex> lock(
cs);
174 static struct event_base* eventBase = 0;
178 static std::vector<CSubNet> rpc_allow_subnets;
187 static bool ClientAllowed(
const CNetAddr& netaddr)
191 for(
const CSubNet& subnet : rpc_allow_subnets)
192 if (subnet.Match(netaddr))
198 static bool InitHTTPAllowList()
200 rpc_allow_subnets.clear();
205 rpc_allow_subnets.push_back(
CSubNet(localv4, 8));
206 rpc_allow_subnets.push_back(
CSubNet(localv6));
208 const std::vector<std::string>& vAllow =
mapMultiArgs.at(
"-rpcallowip");
209 for (std::string strAllow : vAllow) {
214 strprintf(
"Invalid -rpcallowip subnet specification: %s. Valid are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24).", strAllow),
218 rpc_allow_subnets.push_back(subnet);
221 std::string strAllowed;
222 for (
const CSubNet& subnet : rpc_allow_subnets)
223 strAllowed += subnet.
ToString() +
" ";
224 LogPrint(
"http",
"Allowing HTTP connections from: %s\n", strAllowed);
250 static void http_request_cb(
struct evhttp_request* req,
void* arg)
252 std::unique_ptr<HTTPRequest> hreq(
new HTTPRequest(req));
254 LogPrint(
"http",
"Received a %s request for %s from %s\n",
255 RequestMethodString(hreq->GetRequestMethod()), hreq->GetURI(), hreq->GetPeer().ToString());
258 if (!ClientAllowed(hreq->GetPeer())) {
265 hreq->WriteReply(HTTP_BADMETHOD);
270 std::string strURI = hreq->GetURI();
272 std::vector<HTTPPathHandler>::const_iterator i =
pathHandlers.begin();
273 std::vector<HTTPPathHandler>::const_iterator iend =
pathHandlers.end();
274 for (; i != iend; ++i) {
277 match = (strURI == i->prefix);
279 match = (strURI.substr(0, i->prefix.size()) == i->prefix);
281 path = strURI.substr(i->prefix.size());
288 std::unique_ptr<HTTPWorkItem> item(
new HTTPWorkItem(std::move(hreq), path, i->handler));
290 if (workQueue->Enqueue(item.get()))
293 LogPrintf(
"WARNING: request rejected because http work queue depth exceeded, it can be increased with the -rpcworkqueue= setting\n");
294 item->req->WriteReply(HTTP_INTERNAL,
"Work queue depth exceeded");
297 hreq->WriteReply(HTTP_NOTFOUND);
302 static void http_reject_request_cb(
struct evhttp_request* req,
void*)
304 LogPrint(
"http",
"Rejecting request while shutting down\n");
305 evhttp_send_error(req, HTTP_SERVUNAVAIL, NULL);
309 static bool ThreadHTTP(
struct event_base*
base,
struct evhttp* http)
312 LogPrint(
"http",
"Entering http event loop\n");
313 event_base_dispatch(
base);
315 LogPrint(
"http",
"Exited http event loop\n");
316 return event_base_got_break(
base) == 0;
320 static bool HTTPBindAddresses(
struct evhttp* http)
323 std::vector<std::pair<std::string, uint16_t> > endpoints;
327 endpoints.push_back(std::make_pair(
"::1", defaultPort));
328 endpoints.push_back(std::make_pair(
"127.0.0.1", defaultPort));
330 LogPrintf(
"WARNING: option -rpcbind was ignored because -rpcallowip was not specified, refusing to allow everyone to connect\n");
333 const std::vector<std::string>& vbind =
mapMultiArgs.at(
"-rpcbind");
334 for (std::vector<std::string>::const_iterator i = vbind.begin(); i != vbind.end(); ++i) {
335 int port = defaultPort;
338 endpoints.push_back(std::make_pair(host, port));
341 endpoints.push_back(std::make_pair(
"::", defaultPort));
342 endpoints.push_back(std::make_pair(
"0.0.0.0", defaultPort));
346 for (std::vector<std::pair<std::string, uint16_t> >::iterator i = endpoints.begin(); i != endpoints.end(); ++i) {
347 LogPrint(
"http",
"Binding RPC on address %s port %i\n", i->first, i->second);
348 evhttp_bound_socket *bind_handle = evhttp_bind_socket_with_handle(http, i->first.empty() ? NULL : i->first.c_str(), i->second);
352 LogPrintf(
"Binding RPC on address %s port %i failed.\n", i->first, i->second);
366 static void libevent_log_cb(
int severity,
const char *msg)
368 #ifndef EVENT_LOG_WARN
370 # define EVENT_LOG_WARN _EVENT_LOG_WARN
375 LogPrint(
"libevent",
"libevent: %s\n", msg);
380 struct evhttp* http = 0;
381 struct event_base*
base = 0;
383 if (!InitHTTPAllowList())
388 "SSL mode for RPC (-rpcssl) is no longer supported.",
394 event_set_log_callback(&libevent_log_cb);
395 #if LIBEVENT_VERSION_NUMBER >= 0x02010100
399 event_enable_debug_logging(EVENT_DBG_ALL);
401 event_enable_debug_logging(EVENT_DBG_NONE);
404 evthread_use_windows_threads();
406 evthread_use_pthreads();
409 base = event_base_new();
411 LogPrintf(
"Couldn't create an event_base: exiting\n");
416 http = evhttp_new(
base);
418 LogPrintf(
"couldn't create evhttp. Exiting.\n");
419 event_base_free(
base);
423 evhttp_set_timeout(http,
GetArg(
"-rpcservertimeout", DEFAULT_HTTP_SERVER_TIMEOUT));
424 evhttp_set_max_headers_size(http, MAX_HEADERS_SIZE);
425 evhttp_set_max_body_size(http, MAX_SIZE);
426 evhttp_set_gencb(http, http_request_cb, NULL);
428 if (!HTTPBindAddresses(http)) {
429 LogPrintf(
"Unable to bind any endpoint for RPC server\n");
431 event_base_free(
base);
435 LogPrint(
"http",
"Initialized HTTP server\n");
436 int workQueueDepth = std::max((
long)
GetArg(
"-rpcworkqueue", DEFAULT_HTTP_WORKQUEUE), 1L);
437 LogPrintf(
"HTTP: creating work queue of depth %d\n", workQueueDepth);
450 LogPrint(
"http",
"Starting HTTP server\n");
451 int rpcThreads = std::max((
long)
GetArg(
"-rpcthreads", DEFAULT_HTTP_THREADS), 1L);
452 LogPrintf(
"HTTP: starting %d worker threads\n", rpcThreads);
453 std::packaged_task<bool(event_base*, evhttp*)> task(ThreadHTTP);
457 for (
int i = 0; i < rpcThreads; i++) {
458 std::thread rpc_worker(HTTPWorkQueueRun, workQueue);
466 LogPrint(
"http",
"Interrupting HTTP server\n");
470 evhttp_del_accept_socket(
eventHTTP, socket);
473 evhttp_set_gencb(
eventHTTP, http_reject_request_cb, NULL);
476 workQueue->Interrupt();
481 LogPrint(
"http",
"Stopping HTTP server\n");
483 LogPrint(
"http",
"Waiting for HTTP worker threads to exit\n");
484 workQueue->WaitExit();
488 LogPrint(
"http",
"Waiting for HTTP event thread to exit\n");
495 if (
threadResult.valid() &&
threadResult.wait_for(std::chrono::milliseconds(2000)) == std::future_status::timeout) {
496 LogPrintf(
"HTTP event loop did not exit within allotted time, sending loopbreak\n");
497 event_base_loopbreak(eventBase);
506 event_base_free(eventBase);
509 LogPrint(
"http",
"Stopped HTTP server\n");
517 static void httpevent_callback_fn(evutil_socket_t,
short,
void* data)
522 if (self->deleteWhenTriggered)
527 deleteWhenTriggered(_deleteWhenTriggered),
handler(_handler)
529 ev = event_new(
base, -1, 0, httpevent_callback_fn,
this);
539 event_active(
ev, 0, 0);
551 LogPrintf(
"%s: Unhandled request\n", __func__);
552 WriteReply(HTTP_INTERNAL,
"Unhandled request");
559 const struct evkeyvalq* headers = evhttp_request_get_input_headers(
req);
561 const char* val = evhttp_find_header(headers, hdr.c_str());
563 return std::make_pair(
true, val);
565 return std::make_pair(
false,
"");
570 struct evbuffer* buf = evhttp_request_get_input_buffer(
req);
573 size_t size = evbuffer_get_length(buf);
580 const char* data = (
const char*)evbuffer_pullup(buf, size);
583 std::string rv(data, size);
584 evbuffer_drain(buf, size);
590 struct evkeyvalq* headers = evhttp_request_get_output_headers(
req);
592 evhttp_add_header(headers, hdr.c_str(), value.c_str());
604 struct evbuffer* evb = evhttp_request_get_output_buffer(
req);
606 evbuffer_add(evb, strReply.data(), strReply.size());
608 std::bind(evhttp_send_reply,
req, nStatus, (
const char*)NULL, (
struct evbuffer *)NULL));
616 evhttp_connection* con = evhttp_request_get_connection(
req);
620 const char* address =
"";
622 evhttp_connection_get_peer(con, (
char**)&address, &port);
630 return evhttp_request_get_uri(
req);
635 switch (evhttp_request_get_command(
req)) {
639 case EVHTTP_REQ_POST:
642 case EVHTTP_REQ_HEAD:
656 LogPrint(
"http",
"Registering HTTP handler for %s (exactmatch %d)\n",
prefix, exactMatch);
662 std::vector<HTTPPathHandler>::iterator i =
pathHandlers.begin();
663 std::vector<HTTPPathHandler>::iterator iend =
pathHandlers.end();
664 for (; i != iend; ++i)
665 if (i->prefix ==
prefix && i->exactMatch == exactMatch)
669 LogPrint(
"http",
"Unregistering HTTP handler for %s (exactmatch %d)\n",
prefix, exactMatch);
const CBaseChainParams & BaseParams()
Return the currently selected parameters.
boost::signals2::signal< bool(const std::string &message, const std::string &caption, unsigned int style), boost::signals2::last_value< bool > > ThreadSafeMessageBox
Show message box.
IP address (IPv6, or IPv4 using mapped IPv6 range (::FFFF:0:0/96))
A combination of a network address (CNetAddr) and a (TCP) port.
std::string ToString() const
std::function< void(void)> handler
HTTPEvent(struct event_base *base, bool deleteWhenTriggered, const std::function< void(void)> &handler)
Create a new event.
void trigger(struct timeval *tv)
Trigger the event.
void WriteReply(int nStatus, const std::string &strReply="")
Write HTTP reply.
std::string GetURI()
Get requested URI.
CService GetPeer()
Get CService (address:ip) for the origin of the http request.
std::pair< bool, std::string > GetHeader(const std::string &hdr)
Get the request header specified by hdr, or an empty string.
void WriteHeader(const std::string &hdr, const std::string &value)
Write output header.
struct evhttp_request * req
HTTPRequest(struct evhttp_request *req)
std::string ReadBody()
Read request body.
RequestMethod GetRequestMethod()
Get request method.
std::unique_ptr< HTTPRequest > req
HTTPWorkItem(std::unique_ptr< HTTPRequest > _req, const std::string &_path, const HTTPRequestHandler &_func)
RAII object to keep track of number of running worker threads.
ThreadCounter(WorkQueue &w)
Simple work queue for distributing work over multiple threads.
size_t Depth()
Return current depth of queue.
bool Enqueue(WorkItem *item)
Enqueue a work item.
void WaitExit()
Wait for worker threads to exit.
void Run()
Thread function.
std::mutex cs
Mutex protects entire object.
~WorkQueue()
Precondition: worker threads have all stopped (call WaitExit)
std::deque< std::unique_ptr< WorkItem > > queue
void Interrupt()
Interrupt and exit loops.
WorkQueue(size_t _maxDepth)
std::condition_variable cond
struct evhttp * eventHTTP
HTTP server.
void InterruptHTTPServer()
Interrupt HTTP server threads.
std::future< bool > threadResult
std::vector< evhttp_bound_socket * > boundSockets
Bound listening sockets.
void UnregisterHTTPHandler(const std::string &prefix, bool exactMatch)
Unregister handler for prefix.
void RegisterHTTPHandler(const std::string &prefix, bool exactMatch, const HTTPRequestHandler &handler)
Register handler for prefix.
struct event_base * EventBase()
Return evhttp event base.
bool InitHTTPServer()
Initialize HTTP server.
bool StartHTTPServer()
Start HTTP server.
void StopHTTPServer()
Stop HTTP server.
std::vector< HTTPPathHandler > pathHandlers
Handlers for (sub)paths.
std::function< bool(HTTPRequest *req, const std::string &)> HTTPRequestHandler
Handler for requests to a certain HTTP path.
void SplitHostPort(std::string in, int &portOut, std::string &hostOut)
bool LookupHost(const char *pszName, std::vector< CNetAddr > &vIP, unsigned int nMaxSolutions, bool fAllowLookup)
bool LookupSubNet(const char *pszName, CSubNet &ret)
CService LookupNumeric(const char *pszName, int portDefault)
bool(* handler)(HTTPRequest *req, const std::string &strReq)
HTTPPathHandler(std::string _prefix, bool _exactMatch, HTTPRequestHandler _handler)
HTTPRequestHandler handler
CClientUIInterface uiInterface
std::string GetArg(const std::string &strArg, const std::string &strDefault)
Return string argument or default value.
bool LogAcceptCategory(const char *category)
Return true if log accepts specified category.
bool GetBoolArg(const std::string &strArg, bool fDefault)
Return boolean argument or default value.
void RenameThread(const char *name)
const map< string, vector< string > > & mapMultiArgs
bool IsArgSet(const std::string &strArg)
Return true if the given argument has been manually set.
#define LogPrint(category,...)