Line data Source code
1 : // Copyright (c) 2015-2021 The Bitcoin Core developers
2 : // Copyright (c) 2018-2021 The PIVX Core developers
3 : // Distributed under the MIT software license, see the accompanying
4 : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 :
6 : #include "httpserver.h"
7 :
8 : #include "chainparamsbase.h"
9 : #include "compat.h"
10 : #include "util/system.h"
11 : #include "netbase.h"
12 : #include "rpc/protocol.h" // For HTTP status codes
13 : #include "sync.h"
14 : #include "shutdown.h"
15 : #include "guiinterface.h"
16 :
17 : #include <stdio.h>
18 : #include <stdlib.h>
19 : #include <string.h>
20 : #include <deque>
21 :
22 : #include <sys/types.h>
23 : #include <sys/stat.h>
24 : #include <signal.h>
25 :
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>
31 :
32 : #include "support/events.h"
33 :
34 : #ifdef EVENT__HAVE_NETINET_IN_H
35 : #include <netinet/in.h>
36 : #ifdef _XOPEN_SOURCE_EXTENDED
37 : #include <arpa/inet.h>
38 : #endif
39 : #endif
40 :
41 : /** Maximum size of http request (request line + headers) */
42 : static const size_t MAX_HEADERS_SIZE = 8192;
43 :
44 : /** HTTP request work item */
45 : class HTTPWorkItem : public HTTPClosure
46 : {
47 : public:
48 298193 : HTTPWorkItem(HTTPRequest* req, const std::string &path, const HTTPRequestHandler& func):
49 596386 : req(req), path(path), func(func)
50 : {
51 298193 : }
52 298193 : void operator()()
53 : {
54 298193 : func(req.get(), path);
55 298193 : }
56 :
57 : std::unique_ptr<HTTPRequest> req;
58 :
59 : private:
60 : std::string path;
61 : HTTPRequestHandler func;
62 : };
63 :
64 : /** Simple work queue for distributing work over multiple threads.
65 : * Work items are simply callable objects.
66 : */
67 : template <typename WorkItem>
68 : class WorkQueue
69 : {
70 : private:
71 : /** Mutex protects entire object */
72 : std::mutex cs;
73 : std::condition_variable cond;
74 : /* XXX in C++11 we can use std::unique_ptr here and avoid manual cleanup */
75 : std::deque<WorkItem*> queue;
76 : bool running;
77 : size_t maxDepth;
78 :
79 : public:
80 375 : explicit WorkQueue(size_t _maxDepth) : running(true),
81 375 : maxDepth(_maxDepth)
82 : {
83 375 : }
84 : /** Precondition: worker threads have all stopped (they have been joined).
85 : */
86 375 : ~WorkQueue()
87 : {
88 375 : while (!queue.empty()) {
89 0 : delete queue.front();
90 375 : queue.pop_front();
91 : }
92 375 : }
93 : /** Enqueue a work item */
94 298193 : bool Enqueue(WorkItem* item)
95 : {
96 596386 : std::unique_lock<std::mutex> lock(cs);
97 298193 : if (!running || queue.size() >= maxDepth) {
98 : return false;
99 : }
100 298193 : queue.push_back(item);
101 298193 : cond.notify_one();
102 298193 : return true;
103 : }
104 : /** Thread function */
105 1500 : void Run()
106 : {
107 : while (true) {
108 299693 : WorkItem* i = nullptr;
109 : {
110 299693 : std::unique_lock<std::mutex> lock(cs);
111 599369 : while (running && queue.empty())
112 299676 : cond.wait(lock);
113 299693 : if (!running && queue.empty())
114 : break;
115 298193 : i = queue.front();
116 596386 : queue.pop_front();
117 : }
118 298193 : (*i)();
119 298193 : delete i;
120 : }
121 1500 : }
122 : /** Interrupt and exit loops */
123 375 : void Interrupt()
124 : {
125 375 : std::unique_lock<std::mutex> lock(cs);
126 375 : running = false;
127 375 : cond.notify_all();
128 375 : }
129 : };
130 :
131 13731 : struct HTTPPathHandler
132 : {
133 : HTTPPathHandler() {}
134 3718 : HTTPPathHandler(std::string prefix, bool exactMatch, HTTPRequestHandler handler):
135 3718 : prefix(prefix), exactMatch(exactMatch), handler(handler)
136 : {
137 3718 : }
138 : std::string prefix{};
139 : bool exactMatch{false};
140 : HTTPRequestHandler handler{};
141 : };
142 :
143 : /** HTTP module state */
144 :
145 : //! libevent event loop
146 : static struct event_base* eventBase = 0;
147 : //! HTTP server
148 : struct evhttp* eventHTTP = 0;
149 : //! List of subnets to allow RPC connections from
150 : static std::vector<CSubNet> rpc_allow_subnets;
151 : //! Work queue for handling longer requests off the event loop thread
152 : static WorkQueue<HTTPClosure>* workQueue = 0;
153 : //! Handlers for (sub)paths
154 : std::vector<HTTPPathHandler> pathHandlers;
155 : std::vector<evhttp_bound_socket *> boundSockets;
156 :
157 : /** Check if a network address is allowed to access the HTTP server */
158 298195 : static bool ClientAllowed(const CNetAddr& netaddr)
159 : {
160 298195 : if (!netaddr.IsValid())
161 : return false;
162 298211 : for (const CSubNet& subnet : rpc_allow_subnets)
163 298210 : if (subnet.Match(netaddr))
164 298194 : return true;
165 1 : return false;
166 : }
167 :
168 : /** Initialize ACL list for HTTP server */
169 375 : static bool InitHTTPAllowList()
170 : {
171 375 : rpc_allow_subnets.clear();
172 750 : CNetAddr localv4;
173 750 : CNetAddr localv6;
174 375 : LookupHost("127.0.0.1", localv4, false);
175 375 : LookupHost("::1", localv6, false);
176 375 : rpc_allow_subnets.emplace_back(localv4, 8); // always allow IPv4 local subnet
177 375 : rpc_allow_subnets.emplace_back(localv6); // always allow IPv6 localhost
178 384 : for (const std::string& strAllow : gArgs.GetArgs("-rpcallowip")) {
179 18 : CSubNet subnet;
180 9 : LookupSubNet(strAllow.c_str(), subnet);
181 9 : if (!subnet.IsValid()) {
182 0 : uiInterface.ThreadSafeMessageBox(
183 0 : 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),
184 : "", CClientUIInterface::MSG_ERROR);
185 0 : return false;
186 : }
187 9 : rpc_allow_subnets.push_back(subnet);
188 : }
189 750 : std::string strAllowed;
190 1134 : for (const CSubNet& subnet : rpc_allow_subnets)
191 1518 : strAllowed += subnet.ToString() + " ";
192 375 : LogPrint(BCLog::HTTP, "Allowing HTTP connections from: %s\n", strAllowed);
193 375 : return true;
194 : }
195 :
196 : /** HTTP request method as string - use for logging only */
197 296990 : static std::string RequestMethodString(HTTPRequest::RequestMethod m)
198 : {
199 296990 : switch (m) {
200 22 : case HTTPRequest::GET:
201 22 : return "GET";
202 296968 : break;
203 296968 : case HTTPRequest::POST:
204 296968 : return "POST";
205 0 : break;
206 0 : case HTTPRequest::HEAD:
207 0 : return "HEAD";
208 0 : break;
209 0 : case HTTPRequest::PUT:
210 0 : return "PUT";
211 0 : break;
212 0 : default:
213 0 : return "unknown";
214 : }
215 : }
216 :
217 : /** HTTP request callback */
218 298195 : static void http_request_cb(struct evhttp_request* req, void* arg)
219 : {
220 : // Disable reading to work around a libevent bug, fixed in 2.2.0.
221 298195 : if (event_get_version_number() >= 0x02010600 && event_get_version_number() < 0x02020001) {
222 298195 : evhttp_connection* conn = evhttp_request_get_connection(req);
223 298195 : if (conn) {
224 298195 : bufferevent* bev = evhttp_connection_get_bufferevent(conn);
225 298195 : if (bev) {
226 298195 : bufferevent_disable(bev, EV_READ);
227 : }
228 : }
229 : }
230 298195 : std::unique_ptr<HTTPRequest> hreq(new HTTPRequest(req));
231 :
232 595221 : LogPrint(BCLog::HTTP, "Received a %s request for %s from %s\n",
233 : RequestMethodString(hreq->GetRequestMethod()), hreq->GetURI(), hreq->GetPeer().ToString());
234 :
235 : // Early address-based allow check
236 298195 : if (!ClientAllowed(hreq->GetPeer())) {
237 1 : hreq->WriteReply(HTTP_FORBIDDEN);
238 1 : return;
239 : }
240 :
241 : // Early reject unknown HTTP methods
242 298194 : if (hreq->GetRequestMethod() == HTTPRequest::UNKNOWN) {
243 0 : hreq->WriteReply(HTTP_BADMETHOD);
244 0 : return;
245 : }
246 :
247 : // Find registered handler for prefix
248 596388 : std::string strURI = hreq->GetURI();
249 596388 : std::string path;
250 298194 : std::vector<HTTPPathHandler>::const_iterator i = pathHandlers.begin();
251 298194 : std::vector<HTTPPathHandler>::const_iterator iend = pathHandlers.end();
252 298409 : for (; i != iend; ++i) {
253 298408 : bool match = false;
254 298408 : if (i->exactMatch)
255 298194 : match = (strURI == i->prefix);
256 : else
257 273 : match = (strURI.substr(0, i->prefix.size()) == i->prefix);
258 298408 : if (match) {
259 298193 : path = strURI.substr(i->prefix.size());
260 298193 : break;
261 : }
262 : }
263 :
264 : // Dispatch to worker thread
265 298194 : if (i != iend) {
266 596386 : std::unique_ptr<HTTPWorkItem> item(new HTTPWorkItem(hreq.release(), path, i->handler));
267 298193 : assert(workQueue);
268 298193 : if (workQueue->Enqueue(item.get()))
269 298193 : item.release(); /* if true, queue took ownership */
270 : else
271 0 : item->req->WriteReply(HTTP_INTERNAL, "Work queue depth exceeded");
272 : } else {
273 2 : hreq->WriteReply(HTTP_NOTFOUND);
274 : }
275 : }
276 :
277 : /** Callback to reject HTTP requests after shutdown. */
278 0 : static void http_reject_request_cb(struct evhttp_request* req, void*)
279 : {
280 0 : LogPrint(BCLog::HTTP, "Rejecting request while shutting down\n");
281 0 : evhttp_send_error(req, HTTP_SERVUNAVAIL, nullptr);
282 0 : }
283 : /** Event dispatcher thread */
284 375 : static bool ThreadHTTP(struct event_base* base, struct evhttp* http)
285 : {
286 375 : util::ThreadRename("bitcoin-http");
287 375 : LogPrint(BCLog::HTTP, "Entering http event loop\n");
288 375 : event_base_dispatch(base);
289 : // Event loop will be interrupted by InterruptHTTPServer()
290 375 : LogPrint(BCLog::HTTP, "Exited http event loop\n");
291 375 : return event_base_got_break(base) == 0;
292 : }
293 :
294 : /** Bind HTTP server to specified addresses */
295 375 : static bool HTTPBindAddresses(struct evhttp* http)
296 : {
297 375 : int defaultPort = gArgs.GetArg("-rpcport", BaseParams().RPCPort());
298 750 : std::vector<std::pair<std::string, uint16_t> > endpoints;
299 :
300 : // Determine what addresses to bind to
301 385 : if (!(gArgs.IsArgSet("-rpcallowip") && gArgs.IsArgSet("-rpcbind"))) { // Default to loopback if not allowing external IPs
302 367 : endpoints.emplace_back("::1", defaultPort);
303 367 : endpoints.emplace_back("127.0.0.1", defaultPort);
304 367 : if (gArgs.IsArgSet("-rpcallowip")) {
305 1 : LogPrintf("WARNING: option -rpcallowip was specified without -rpcbind; this doesn't usually make sense\n");
306 : }
307 367 : if (gArgs.IsArgSet("-rpcbind")) {
308 0 : LogPrintf("WARNING: option -rpcbind was ignored because -rpcallowip was not specified, refusing to allow everyone to connect\n");
309 : }
310 8 : } else if (gArgs.IsArgSet("-rpcbind")) { // Specific bind address
311 20 : for (const std::string& strRPCBind : gArgs.GetArgs("-rpcbind")) {
312 12 : int port = defaultPort;
313 24 : std::string host;
314 24 : SplitHostPort(strRPCBind, port, host);
315 12 : endpoints.emplace_back(host, port);
316 : }
317 : }
318 :
319 : // Bind addresses
320 1121 : for (std::vector<std::pair<std::string, uint16_t> >::iterator i = endpoints.begin(); i != endpoints.end(); ++i) {
321 746 : LogPrint(BCLog::HTTP, "Binding RPC on address %s port %i\n", i->first, i->second);
322 746 : evhttp_bound_socket *bind_handle = evhttp_bind_socket_with_handle(http, i->first.empty() ? nullptr : i->first.c_str(), i->second);
323 746 : if (bind_handle) {
324 1492 : CNetAddr addr;
325 746 : if (i->first.empty() || (LookupHost(i->first, addr, false) && addr.IsBindAny())) {
326 0 : LogPrintf("WARNING: the RPC server is not safe to expose to untrusted networks such as the public internet\n");
327 : }
328 746 : boundSockets.push_back(bind_handle);
329 : } else {
330 0 : LogPrintf("Binding RPC on address %s port %i failed.\n", i->first, i->second);
331 : }
332 : }
333 375 : return !boundSockets.empty();
334 : }
335 :
336 : /** Simple wrapper to set thread name and run work queue */
337 1500 : static void HTTPWorkQueueRun(WorkQueue<HTTPClosure>* queue)
338 : {
339 1500 : util::ThreadRename("bitcoin-httpworker");
340 1500 : queue->Run();
341 1500 : }
342 :
343 : /** libevent event log callback */
344 0 : static void libevent_log_cb(int severity, const char *msg)
345 : {
346 : #ifndef EVENT_LOG_WARN
347 : // EVENT_LOG_WARN was added in 2.0.19; but before then _EVENT_LOG_WARN existed.
348 : # define EVENT_LOG_WARN _EVENT_LOG_WARN
349 : #endif
350 0 : if (severity >= EVENT_LOG_WARN) // Log warn messages and higher without debug category
351 0 : LogPrintf("libevent: %s\n", msg);
352 : else
353 0 : LogPrint(BCLog::LIBEVENT, "libevent: %s\n", msg);
354 0 : }
355 :
356 375 : bool InitHTTPServer()
357 : {
358 375 : if (!InitHTTPAllowList())
359 : return false;
360 :
361 375 : if (gArgs.GetBoolArg("-rpcssl", false)) {
362 0 : uiInterface.ThreadSafeMessageBox(
363 : "SSL mode for RPC (-rpcssl) is no longer supported.",
364 : "", CClientUIInterface::MSG_ERROR);
365 0 : return false;
366 : }
367 :
368 : // Redirect libevent's logging to our own log
369 375 : event_set_log_callback(&libevent_log_cb);
370 : // Update libevent's log handling. Returns false if our version of
371 : // libevent doesn't support debug logging, in which case we should
372 : // clear the BCLog::LIBEVENT flag.
373 375 : if (!UpdateHTTPServerLogging(g_logger->WillLogCategory(BCLog::LIBEVENT))) {
374 0 : g_logger->DisableCategory(BCLog::LIBEVENT);
375 : }
376 :
377 : #ifdef WIN32
378 : evthread_use_windows_threads();
379 : #else
380 375 : evthread_use_pthreads();
381 : #endif
382 :
383 750 : raii_event_base base_ctr = obtain_event_base();
384 :
385 : /* Create a new evhttp object to handle requests. */
386 750 : raii_evhttp http_ctr = obtain_evhttp(base_ctr.get());
387 375 : struct evhttp* http = http_ctr.get();
388 375 : if (!http) {
389 0 : LogPrintf("couldn't create evhttp. Exiting.\n");
390 : return false;
391 : }
392 :
393 375 : evhttp_set_timeout(http, gArgs.GetArg("-rpcservertimeout", DEFAULT_HTTP_SERVER_TIMEOUT));
394 375 : evhttp_set_max_headers_size(http, MAX_HEADERS_SIZE);
395 375 : evhttp_set_max_body_size(http, MAX_SIZE);
396 375 : evhttp_set_gencb(http, http_request_cb, nullptr);
397 :
398 375 : if (!HTTPBindAddresses(http)) {
399 0 : LogPrintf("Unable to bind any endpoint for RPC server\n");
400 : return false;
401 : }
402 :
403 375 : LogPrint(BCLog::HTTP, "Initialized HTTP server\n");
404 375 : int workQueueDepth = std::max((long)gArgs.GetArg("-rpcworkqueue", DEFAULT_HTTP_WORKQUEUE), 1L);
405 375 : LogPrintf("HTTP: creating work queue of depth %d\n", workQueueDepth);
406 :
407 375 : workQueue = new WorkQueue<HTTPClosure>(workQueueDepth);
408 : // transfer ownership to eventBase/HTTP via .release()
409 375 : eventBase = base_ctr.release();
410 375 : eventHTTP = http_ctr.release();
411 375 : return true;
412 : }
413 :
414 375 : bool UpdateHTTPServerLogging(bool enable) {
415 : #if LIBEVENT_VERSION_NUMBER >= 0x02010100
416 375 : if (enable) {
417 0 : event_enable_debug_logging(EVENT_DBG_ALL);
418 : } else {
419 375 : event_enable_debug_logging(EVENT_DBG_NONE);
420 : }
421 375 : return true;
422 : #else
423 : // Can't update libevent logging if version < 02010100
424 : return false;
425 : #endif
426 : }
427 :
428 : std::thread threadHTTP;
429 : static std::vector<std::thread> g_thread_http_workers;
430 :
431 375 : bool StartHTTPServer()
432 : {
433 375 : LogPrint(BCLog::HTTP, "Starting HTTP server\n");
434 375 : int rpcThreads = std::max((long)gArgs.GetArg("-rpcthreads", DEFAULT_HTTP_THREADS), 1L);
435 375 : LogPrintf("HTTP: starting %d worker threads\n", rpcThreads);
436 375 : threadHTTP = std::thread(ThreadHTTP, eventBase, eventHTTP);
437 :
438 1875 : for (int i = 0; i < rpcThreads; i++) {
439 1500 : g_thread_http_workers.emplace_back(HTTPWorkQueueRun, workQueue);
440 : }
441 375 : return true;
442 : }
443 :
444 378 : void InterruptHTTPServer()
445 : {
446 378 : LogPrint(BCLog::HTTP, "Interrupting HTTP server\n");
447 378 : if (eventHTTP) {
448 : // Reject requests on current connections
449 375 : evhttp_set_gencb(eventHTTP, http_reject_request_cb, nullptr);
450 : }
451 378 : if (workQueue)
452 375 : workQueue->Interrupt();
453 378 : }
454 :
455 378 : void StopHTTPServer()
456 : {
457 378 : LogPrint(BCLog::HTTP, "Stopping HTTP server\n");
458 :
459 378 : if (workQueue) {
460 375 : LogPrint(BCLog::HTTP, "Waiting for HTTP worker threads to exit\n");
461 1875 : for (auto& thread : g_thread_http_workers) {
462 : // Guard threadHTTP
463 1500 : if (thread.joinable()) {
464 1500 : thread.join();
465 : }
466 : }
467 375 : g_thread_http_workers.clear();
468 : }
469 :
470 378 : MilliSleep(500); // Avoid race condition while the last HTTP-thread is exiting
471 :
472 : // Unlisten sockets, these are what make the event loop running, which means
473 : // that after this and all connections are closed the event loop will quit.
474 1124 : for (evhttp_bound_socket *socket : boundSockets) {
475 746 : evhttp_del_accept_socket(eventHTTP, socket);
476 : }
477 378 : boundSockets.clear();
478 378 : if (eventBase) {
479 375 : LogPrint(BCLog::HTTP, "Waiting for HTTP event thread to exit\n");
480 : // Guard threadHTTP
481 375 : if (threadHTTP.joinable()) {
482 375 : threadHTTP.join();
483 : }
484 : }
485 :
486 378 : if (eventHTTP) {
487 375 : evhttp_free(eventHTTP);
488 375 : eventHTTP = nullptr;
489 : }
490 378 : if (eventBase) {
491 375 : event_base_free(eventBase);
492 375 : eventBase = nullptr;
493 : }
494 378 : if (workQueue) {
495 375 : delete workQueue;
496 375 : workQueue = nullptr;
497 : }
498 378 : LogPrint(BCLog::HTTP, "Stopped HTTP server\n");
499 378 : }
500 :
501 750 : struct event_base* EventBase()
502 : {
503 750 : return eventBase;
504 : }
505 :
506 298197 : static void httpevent_callback_fn(evutil_socket_t, short, void* data)
507 : {
508 : // Static handler: simply call inner handler
509 298197 : HTTPEvent *self = static_cast<HTTPEvent*>(data);
510 298197 : self->handler();
511 298197 : if (self->deleteWhenTriggered)
512 298195 : delete self;
513 298197 : }
514 :
515 298209 : HTTPEvent::HTTPEvent(struct event_base* base, bool deleteWhenTriggered, const std::function<void(void)>& handler):
516 298209 : deleteWhenTriggered(deleteWhenTriggered), handler(handler)
517 : {
518 298209 : ev = event_new(base, -1, 0, httpevent_callback_fn, this);
519 298209 : assert(ev);
520 298209 : }
521 298209 : HTTPEvent::~HTTPEvent()
522 : {
523 298209 : event_free(ev);
524 298209 : }
525 298209 : void HTTPEvent::trigger(struct timeval* tv)
526 : {
527 298209 : if (tv == nullptr)
528 298195 : event_active(ev, 0, 0); // immediately trigger event in main thread
529 : else
530 14 : evtimer_add(ev, tv); // trigger after timeval passed
531 298209 : }
532 298195 : HTTPRequest::HTTPRequest(struct evhttp_request* req) : req(req),
533 298195 : replySent(false)
534 : {
535 298195 : }
536 596390 : HTTPRequest::~HTTPRequest()
537 : {
538 298195 : if (!replySent) {
539 : // Keep track of whether reply was sent to avoid request leaks
540 0 : LogPrintf("%s: Unhandled request\n", __func__);
541 0 : WriteReply(HTTP_INTERNAL, "Unhandled request");
542 : }
543 : // evhttpd cleans up the request, as long as a reply was sent.
544 298195 : }
545 :
546 298166 : std::pair<bool, std::string> HTTPRequest::GetHeader(const std::string& hdr)
547 : {
548 298166 : const struct evkeyvalq* headers = evhttp_request_get_input_headers(req);
549 298166 : assert(headers);
550 298166 : const char* val = evhttp_find_header(headers, hdr.c_str());
551 298166 : if (val)
552 298166 : return std::make_pair(true, val);
553 : else
554 0 : return std::make_pair(false, "");
555 : }
556 :
557 298162 : std::string HTTPRequest::ReadBody()
558 : {
559 298162 : struct evbuffer* buf = evhttp_request_get_input_buffer(req);
560 298162 : if (!buf)
561 0 : return "";
562 298162 : size_t size = evbuffer_get_length(buf);
563 : /** Trivial implementation: if this is ever a performance bottleneck,
564 : * internal copying can be avoided in multi-segment buffers by using
565 : * evbuffer_peek and an awkward loop. Though in that case, it'd be even
566 : * better to not copy into an intermediate string but use a stream
567 : * abstraction to consume the evbuffer on the fly in the parsing algorithm.
568 : */
569 298162 : const char* data = (const char*)evbuffer_pullup(buf, size);
570 298162 : if (!data) // returns nullptr in case of empty buffer
571 8 : return "";
572 596316 : std::string rv(data, size);
573 298154 : evbuffer_drain(buf, size);
574 596308 : return rv;
575 : }
576 :
577 298533 : void HTTPRequest::WriteHeader(const std::string& hdr, const std::string& value)
578 : {
579 298533 : struct evkeyvalq* headers = evhttp_request_get_output_headers(req);
580 298533 : assert(headers);
581 298533 : evhttp_add_header(headers, hdr.c_str(), value.c_str());
582 298533 : }
583 :
584 : /** Closure sent to main thread to request a reply to be sent to
585 : * a HTTP request.
586 : * Replies must be sent in the main loop in the main http thread,
587 : * this cannot be done from worker threads.
588 : */
589 298195 : void HTTPRequest::WriteReply(int nStatus, const std::string& strReply)
590 : {
591 298195 : assert(!replySent && req);
592 298195 : if (ShutdownRequested()) {
593 710 : WriteHeader("Connection", "close");
594 : }
595 : // Send event to main http thread to send reply message
596 298195 : struct evbuffer* evb = evhttp_request_get_output_buffer(req);
597 298195 : assert(evb);
598 298195 : evbuffer_add(evb, strReply.data(), strReply.size());
599 298195 : auto req_copy = req;
600 894585 : HTTPEvent* ev = new HTTPEvent(eventBase, true, [req_copy, nStatus]{
601 298195 : evhttp_send_reply(req_copy, nStatus, nullptr, nullptr);
602 : // Re-enable reading from the socket. This is the second part of the libevent
603 : // workaround above.
604 298195 : if (event_get_version_number() >= 0x02010600 && event_get_version_number() < 0x02020001) {
605 298195 : evhttp_connection* conn = evhttp_request_get_connection(req_copy);
606 298195 : if (conn) {
607 298195 : bufferevent* bev = evhttp_connection_get_bufferevent(conn);
608 298195 : if (bev) {
609 298195 : bufferevent_enable(bev, EV_READ | EV_WRITE);
610 : }
611 : }
612 : }
613 298195 : });
614 298195 : ev->trigger(nullptr);
615 298195 : replySent = true;
616 298195 : req = 0; // transferred back to main thread
617 298195 : }
618 :
619 595200 : CService HTTPRequest::GetPeer()
620 : {
621 595200 : evhttp_connection* con = evhttp_request_get_connection(req);
622 595200 : CService peer;
623 595200 : if (con) {
624 : // evhttp retains ownership over returned address string
625 595200 : const char* address = "";
626 595200 : uint16_t port = 0;
627 595200 : evhttp_connection_get_peer(con, (char**)&address, &port);
628 595200 : peer = LookupNumeric(address, port);
629 : }
630 595200 : return peer;
631 : }
632 :
633 893335 : std::string HTTPRequest::GetURI()
634 : {
635 893335 : return evhttp_request_get_uri(req);
636 : }
637 :
638 893350 : HTTPRequest::RequestMethod HTTPRequest::GetRequestMethod()
639 : {
640 893350 : switch (evhttp_request_get_command(req)) {
641 : case EVHTTP_REQ_GET:
642 : return GET;
643 893306 : break;
644 893306 : case EVHTTP_REQ_POST:
645 893306 : return POST;
646 0 : break;
647 0 : case EVHTTP_REQ_HEAD:
648 0 : return HEAD;
649 0 : break;
650 0 : case EVHTTP_REQ_PUT:
651 0 : return PUT;
652 0 : break;
653 0 : default:
654 0 : return UNKNOWN;
655 : break;
656 : }
657 : }
658 :
659 3718 : void RegisterHTTPHandler(const std::string &prefix, bool exactMatch, const HTTPRequestHandler &handler)
660 : {
661 3718 : LogPrint(BCLog::HTTP, "Registering HTTP handler for %s (exactmatch %d)\n", prefix, exactMatch);
662 3718 : pathHandlers.emplace_back(prefix, exactMatch, handler);
663 3718 : }
664 :
665 3402 : void UnregisterHTTPHandler(const std::string &prefix, bool exactMatch)
666 : {
667 3402 : std::vector<HTTPPathHandler>::iterator i = pathHandlers.begin();
668 3402 : std::vector<HTTPPathHandler>::iterator iend = pathHandlers.end();
669 6402 : for (; i != iend; ++i)
670 6343 : if (i->prefix == prefix && i->exactMatch == exactMatch)
671 : break;
672 3402 : if (i != iend)
673 : {
674 3343 : LogPrint(BCLog::HTTP, "Unregistering HTTP handler for %s (exactmatch %d)\n", prefix, exactMatch);
675 3343 : pathHandlers.erase(i);
676 : }
677 3402 : }
678 :
679 28 : std::string urlDecode(const std::string &urlEncoded) {
680 28 : std::string res;
681 28 : if (!urlEncoded.empty()) {
682 27 : char *decoded = evhttp_uridecode(urlEncoded.c_str(), false, nullptr);
683 27 : if (decoded) {
684 27 : res = std::string(decoded);
685 27 : free(decoded);
686 : }
687 : }
688 28 : return res;
689 : }
|