21 #include <sys/types.h> 26 #include <event2/thread.h> 27 #include <event2/buffer.h> 28 #include <event2/bufferevent.h> 29 #include <event2/util.h> 30 #include <event2/keyvalq_struct.h> 34 #ifdef EVENT__HAVE_NETINET_IN_H 35 #include <netinet/in.h> 36 #ifdef _XOPEN_SOURCE_EXTENDED 37 #include <arpa/inet.h> 42 static const size_t MAX_HEADERS_SIZE = 8192;
57 std::unique_ptr<HTTPRequest>
req;
67 template <
typename WorkItem>
73 std::condition_variable
cond;
74 std::deque<std::unique_ptr<WorkItem>>
queue;
95 queue.emplace_back(std::unique_ptr<WorkItem>(item));
103 std::unique_ptr<WorkItem> i;
110 i = std::move(
queue.front());
139 static struct event_base* eventBase =
nullptr;
144 static std::vector<CSubNet> rpc_allow_subnets;
153 static bool ClientAllowed(
const CNetAddr& netaddr)
157 for(
const CSubNet& subnet : rpc_allow_subnets)
158 if (subnet.Match(netaddr))
164 static bool InitHTTPAllowList()
166 rpc_allow_subnets.clear();
171 rpc_allow_subnets.push_back(
CSubNet(localv4, 8));
172 rpc_allow_subnets.push_back(
CSubNet(localv6));
173 for (
const std::string& strAllow :
gArgs.
GetArgs(
"-rpcallowip")) {
178 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),
182 rpc_allow_subnets.push_back(subnet);
184 std::string strAllowed;
185 for (
const CSubNet& subnet : rpc_allow_subnets)
186 strAllowed += subnet.
ToString() +
" ";
187 LogPrint(
BCLog::HTTP,
"Allowing HTTP connections from: %s\n", strAllowed);
213 static void http_request_cb(
struct evhttp_request* req,
void* arg)
216 if (event_get_version_number() >= 0x02010600 && event_get_version_number() < 0x02020001) {
217 evhttp_connection* conn = evhttp_request_get_connection(req);
219 bufferevent* bev = evhttp_connection_get_bufferevent(conn);
221 bufferevent_disable(bev, EV_READ);
225 std::unique_ptr<HTTPRequest> hreq(
new HTTPRequest(req));
227 LogPrint(
BCLog::HTTP,
"Received a %s request for %s from %s\n",
228 RequestMethodString(hreq->GetRequestMethod()), hreq->GetURI(), hreq->GetPeer().ToString());
231 if (!ClientAllowed(hreq->GetPeer())) {
238 hreq->WriteReply(HTTP_BADMETHOD);
243 std::string strURI = hreq->GetURI();
245 std::vector<HTTPPathHandler>::const_iterator i =
pathHandlers.begin();
246 std::vector<HTTPPathHandler>::const_iterator iend =
pathHandlers.end();
247 for (; i != iend; ++i) {
250 match = (strURI == i->prefix);
252 match = (strURI.substr(0, i->prefix.size()) == i->prefix);
254 path = strURI.substr(i->prefix.size());
261 std::unique_ptr<HTTPWorkItem> item(
new HTTPWorkItem(std::move(hreq), path, i->handler));
263 if (workQueue->Enqueue(item.get()))
266 LogPrintf(
"WARNING: request rejected because http work queue depth exceeded, it can be increased with the -rpcworkqueue= setting\n");
267 item->req->WriteReply(HTTP_INTERNAL,
"Work queue depth exceeded");
270 hreq->WriteReply(HTTP_NOTFOUND);
275 static void http_reject_request_cb(
struct evhttp_request* req,
void*)
277 LogPrint(
BCLog::HTTP,
"Rejecting request while shutting down\n");
278 evhttp_send_error(req, HTTP_SERVUNAVAIL,
nullptr);
282 static bool ThreadHTTP(
struct event_base* base)
285 LogPrint(
BCLog::HTTP,
"Entering http event loop\n");
286 event_base_dispatch(base);
289 return event_base_got_break(base) == 0;
293 static bool HTTPBindAddresses(
struct evhttp* http)
296 std::vector<std::pair<std::string, uint16_t> > endpoints;
300 endpoints.push_back(std::make_pair(
"::1", defaultPort));
301 endpoints.push_back(std::make_pair(
"127.0.0.1", defaultPort));
303 LogPrintf(
"WARNING: option -rpcbind was ignored because -rpcallowip was not specified, refusing to allow everyone to connect\n");
306 for (
const std::string& strRPCBind :
gArgs.
GetArgs(
"-rpcbind")) {
307 int port = defaultPort;
310 endpoints.push_back(std::make_pair(host, port));
313 endpoints.push_back(std::make_pair(
"::", defaultPort));
314 endpoints.push_back(std::make_pair(
"0.0.0.0", defaultPort));
318 for (std::vector<std::pair<std::string, uint16_t> >::iterator i = endpoints.begin(); i != endpoints.end(); ++i) {
319 LogPrint(
BCLog::HTTP,
"Binding RPC on address %s port %i\n", i->first, i->second);
320 evhttp_bound_socket *bind_handle = evhttp_bind_socket_with_handle(http, i->first.empty() ? nullptr : i->first.c_str(), i->second);
324 LogPrintf(
"Binding RPC on address %s port %i failed.\n", i->first, i->second);
338 static void libevent_log_cb(
int severity,
const char *msg)
340 #ifndef EVENT_LOG_WARN 342 # define EVENT_LOG_WARN _EVENT_LOG_WARN 345 LogPrintf(
"libevent: %s\n", msg);
352 if (!InitHTTPAllowList())
356 event_set_log_callback(&libevent_log_cb);
365 evthread_use_windows_threads();
367 evthread_use_pthreads();
374 struct evhttp* http = http_ctr.get();
376 LogPrintf(
"couldn't create evhttp. Exiting.\n");
380 evhttp_set_timeout(http,
gArgs.
GetArg(
"-rpcservertimeout", DEFAULT_HTTP_SERVER_TIMEOUT));
381 evhttp_set_max_headers_size(http, MAX_HEADERS_SIZE);
382 evhttp_set_max_body_size(http, MAX_SIZE);
383 evhttp_set_gencb(http, http_request_cb,
nullptr);
385 if (!HTTPBindAddresses(http)) {
386 LogPrintf(
"Unable to bind any endpoint for RPC server\n");
390 LogPrint(
BCLog::HTTP,
"Initialized HTTP server\n");
391 int workQueueDepth = std::max((
long)
gArgs.
GetArg(
"-rpcworkqueue", DEFAULT_HTTP_WORKQUEUE), 1L);
392 LogPrintf(
"HTTP: creating work queue of depth %d\n", workQueueDepth);
396 eventBase = base_ctr.release();
402 #if LIBEVENT_VERSION_NUMBER >= 0x02010100 404 event_enable_debug_logging(EVENT_DBG_ALL);
406 event_enable_debug_logging(EVENT_DBG_NONE);
417 static std::vector<std::thread> g_thread_http_workers;
422 int rpcThreads = std::max((
long)
gArgs.
GetArg(
"-rpcthreads", DEFAULT_HTTP_THREADS), 1L);
423 LogPrintf(
"HTTP: starting %d worker threads\n", rpcThreads);
424 std::packaged_task<bool(event_base*)> task(ThreadHTTP);
426 threadHTTP = std::thread(std::move(task), eventBase);
428 for (
int i = 0; i < rpcThreads; i++) {
429 g_thread_http_workers.emplace_back(HTTPWorkQueueRun, workQueue);
435 LogPrint(
BCLog::HTTP,
"Interrupting HTTP server\n");
439 evhttp_del_accept_socket(
eventHTTP, socket);
442 evhttp_set_gencb(
eventHTTP, http_reject_request_cb,
nullptr);
445 workQueue->Interrupt();
452 LogPrint(
BCLog::HTTP,
"Waiting for HTTP worker threads to exit\n");
453 for (
auto& thread: g_thread_http_workers) {
456 g_thread_http_workers.clear();
461 LogPrint(
BCLog::HTTP,
"Waiting for HTTP event thread to exit\n");
463 event_base_loopexit(eventBase,
nullptr);
470 if (
threadResult.valid() &&
threadResult.wait_for(std::chrono::milliseconds(2000)) == std::future_status::timeout) {
471 LogPrintf(
"HTTP event loop did not exit within allotted time, sending loopbreak\n");
472 event_base_loopbreak(eventBase);
481 event_base_free(eventBase);
492 static void httpevent_callback_fn(evutil_socket_t,
short,
void* data)
497 if (self->deleteWhenTriggered)
501 HTTPEvent::HTTPEvent(
struct event_base* base,
bool _deleteWhenTriggered,
const std::function<
void()>& _handler):
502 deleteWhenTriggered(_deleteWhenTriggered),
handler(_handler)
504 ev = event_new(base, -1, 0, httpevent_callback_fn,
this);
514 event_active(
ev, 0, 0);
526 LogPrintf(
"%s: Unhandled request\n", __func__);
527 WriteReply(HTTP_INTERNAL,
"Unhandled request");
534 const struct evkeyvalq* headers = evhttp_request_get_input_headers(
req);
536 const char* val = evhttp_find_header(headers, hdr.c_str());
538 return std::make_pair(
true, val);
540 return std::make_pair(
false,
"");
545 struct evbuffer* buf = evhttp_request_get_input_buffer(
req);
548 size_t size = evbuffer_get_length(buf);
555 const char* data = (
const char*)evbuffer_pullup(buf, size);
558 std::string rv(data, size);
559 evbuffer_drain(buf, size);
565 struct evkeyvalq* headers = evhttp_request_get_output_headers(
req);
567 evhttp_add_header(headers, hdr.c_str(), value.c_str());
579 struct evbuffer* evb = evhttp_request_get_output_buffer(
req);
581 evbuffer_add(evb, strReply.data(), strReply.size());
584 evhttp_send_reply(req_copy, nStatus,
nullptr,
nullptr);
587 if (event_get_version_number() >= 0x02010600 && event_get_version_number() < 0x02020001) {
588 evhttp_connection* conn = evhttp_request_get_connection(req_copy);
590 bufferevent* bev = evhttp_connection_get_bufferevent(conn);
592 bufferevent_enable(bev, EV_READ | EV_WRITE);
604 evhttp_connection* con = evhttp_request_get_connection(
req);
608 const char* address =
"";
610 evhttp_connection_get_peer(con, (
char**)&address, &port);
618 return evhttp_request_get_uri(
req);
623 switch (evhttp_request_get_command(
req)) {
627 case EVHTTP_REQ_POST:
630 case EVHTTP_REQ_HEAD:
644 LogPrint(
BCLog::HTTP,
"Registering HTTP handler for %s (exactmatch %d)\n",
prefix, exactMatch);
650 std::vector<HTTPPathHandler>::iterator i =
pathHandlers.begin();
651 std::vector<HTTPPathHandler>::iterator iend =
pathHandlers.end();
652 for (; i != iend; ++i)
653 if (i->prefix ==
prefix && i->exactMatch == exactMatch)
657 LogPrint(
BCLog::HTTP,
"Unregistering HTTP handler for %s (exactmatch %d)\n",
prefix, exactMatch);
664 if (!urlEncoded.empty()) {
665 char *decoded = evhttp_uridecode(urlEncoded.c_str(),
false,
nullptr);
667 res = std::string(decoded);
bool(* handler)(HTTPRequest *req, const std::string &strReq)
bool IsArgSet(const std::string &strArg) const
Return true if the given argument has been manually set.
raii_event_base obtain_event_base()
std::vector< evhttp_bound_socket * > boundSockets
Bound listening sockets.
HTTPWorkItem(std::unique_ptr< HTTPRequest > _req, const std::string &_path, const HTTPRequestHandler &_func)
HTTPRequest(struct evhttp_request *req)
raii_evhttp obtain_evhttp(struct event_base *base)
std::vector< HTTPPathHandler > pathHandlers
Handlers for (sub)paths.
CService LookupNumeric(const char *pszName, int portDefault)
std::string urlDecode(const std::string &urlEncoded)
const CBaseChainParams & BaseParams()
Return the currently selected parameters.
struct evhttp_request * req
std::deque< std::unique_ptr< WorkItem > > queue
void InterruptHTTPServer()
Interrupt HTTP server threads.
void RenameThread(const char *name)
bool Enqueue(WorkItem *item)
Enqueue a work item.
void RegisterHTTPHandler(const std::string &prefix, bool exactMatch, const HTTPRequestHandler &handler)
Register handler for prefix.
Mutex cs
Mutex protects entire object.
void Run()
Thread function.
HTTPPathHandler(std::string _prefix, bool _exactMatch, HTTPRequestHandler _handler)
bool InitHTTPServer()
Initialize HTTP server.
void StopHTTPServer()
Stop HTTP server.
void WriteReply(int nStatus, const std::string &strReply="")
Write HTTP reply.
HTTPEvent(struct event_base *base, bool deleteWhenTriggered, const std::function< void()> &handler)
Create a new event.
RequestMethod GetRequestMethod() const
Get request method.
A combination of a network address (CNetAddr) and a (TCP) port.
std::condition_variable cond
void UnregisterHTTPHandler(const std::string &prefix, bool exactMatch)
Unregister handler for prefix.
void DisableCategory(LogFlags flag)
#define WAIT_LOCK(cs, name)
std::future< bool > threadResult
bool WillLogCategory(LogFlags category) const
struct event_base * EventBase()
Return evhttp event base.
bool LookupSubNet(const char *pszName, CSubNet &ret)
IP address (IPv6, or IPv4 using mapped IPv6 range (::FFFF:0:0/96))
Simple work queue for distributing work over multiple threads.
std::string ToString() const
void SplitHostPort(std::string in, int &portOut, std::string &hostOut)
void WriteHeader(const std::string &hdr, const std::string &value)
Write output header.
void trigger(struct timeval *tv)
Trigger the event.
std::string GetArg(const std::string &strArg, const std::string &strDefault) const
Return string argument or default value.
std::pair< bool, std::string > GetHeader(const std::string &hdr) const
Get the request header specified by hdr, or an empty string.
std::function< void()> handler
std::function< bool(HTTPRequest *req, const std::string &)> HTTPRequestHandler
Handler for requests to a certain HTTP path.
CService GetPeer() const
Get CService (address:ip) for the origin of the http request.
void operator()() override
std::string ReadBody()
Read request body.
void StartHTTPServer()
Start HTTP server.
WorkQueue(size_t _maxDepth)
std::unique_ptr< HTTPRequest > req
CClientUIInterface uiInterface
bool LookupHost(const char *pszName, std::vector< CNetAddr > &vIP, unsigned int nMaxSolutions, bool fAllowLookup)
std::vector< std::string > GetArgs(const std::string &strArg) const
Return a vector of strings of the given argument.
HTTPRequestHandler handler
struct evhttp * eventHTTP
HTTP server.
BCLog::Logger *const g_logger
NOTE: the logger instances is leaked on exit.
std::string GetURI() const
Get requested URI.
bool UpdateHTTPServerLogging(bool enable)
Change logging level for libevent.
~WorkQueue()
Precondition: worker threads have all stopped (they have been joined).
void Interrupt()
Interrupt and exit loops.