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