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