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