From 40b556d3742a1f65d67e2d4c760d0b13fe8be5b7 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 23 Jan 2015 07:53:17 +0100 Subject: evhttpd implementation - *Replace usage of boost::asio with [libevent2](http://libevent.org/)*. boost::asio is not part of C++11, so unlike other boost there is no forwards-compatibility reason to stick with it. Together with #4738 (convert json_spirit to UniValue), this rids Bitcoin Core of the worst offenders with regard to compile-time slowness. - *Replace spit-and-duct-tape http server with evhttp*. Front-end http handling is handled by libevent, a work queue (with configurable depth and parallelism) is used to handle application requests. - *Wrap HTTP request in C++ class*; this makes the application code mostly HTTP-server-neutral - *Refactor RPC to move all http-specific code to a separate file*. Theoreticaly this can allow building without HTTP server but with another RPC backend, e.g. Qt's debug console (currently not implemented) or future RPC mechanisms people may want to use. - *HTTP dispatch mechanism*; services (e.g., RPC, REST) register which URL paths they want to handle. By using a proven, high-performance asynchronous networking library (also used by Tor) and HTTP server, problems such as #5674, #5655, #344 should be avoided. What works? bitcoind, bitcoin-cli, bitcoin-qt. Unit tests and RPC/REST tests pass. The aim for now is everything but SSL support. Configuration options: - `-rpcthreads`: repurposed as "number of work handler threads". Still defaults to 4. - `-rpcworkqueue`: maximum depth of work queue. When this is reached, new requests will return a 500 Internal Error. - `-rpctimeout`: inactivity time, in seconds, after which to disconnect a client. - `-debug=http`: low-level http activity logging --- src/httpserver.cpp | 586 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 586 insertions(+) create mode 100644 src/httpserver.cpp (limited to 'src/httpserver.cpp') diff --git a/src/httpserver.cpp b/src/httpserver.cpp new file mode 100644 index 000000000..89366b2e4 --- /dev/null +++ b/src/httpserver.cpp @@ -0,0 +1,586 @@ +// Copyright (c) 2015 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "httpserver.h" + +#include "chainparamsbase.h" +#include "compat.h" +#include "util.h" +#include "netbase.h" +#include "rpcprotocol.h" // For HTTP status codes +#include "sync.h" +#include "ui_interface.h" + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifdef EVENT__HAVE_NETINET_IN_H +#include +#ifdef _XOPEN_SOURCE_EXTENDED +#include +#endif +#endif + +#include // for to_lower() +#include +#include + +/** HTTP request work item */ +class HTTPWorkItem : public HTTPClosure +{ +public: + HTTPWorkItem(HTTPRequest* req, const std::string &path, const HTTPRequestHandler& func): + req(req), path(path), func(func) + { + } + void operator()() + { + func(req.get(), path); + } + + boost::scoped_ptr req; + +private: + std::string path; + HTTPRequestHandler func; +}; + +/** Simple work queue for distributing work over multiple threads. + * Work items are simply callable objects. + */ +template +class WorkQueue +{ +private: + /** Mutex protects entire object */ + CWaitableCriticalSection cs; + CConditionVariable cond; + /* XXX in C++11 we can use std::unique_ptr here and avoid manual cleanup */ + std::deque queue; + bool running; + size_t maxDepth; + +public: + WorkQueue(size_t maxDepth) : running(true), + maxDepth(maxDepth) + { + } + /* Precondition: worker threads have all stopped */ + ~WorkQueue() + { + while (!queue.empty()) { + delete queue.front(); + queue.pop_front(); + } + } + /** Enqueue a work item */ + bool Enqueue(WorkItem* item) + { + boost::unique_lock lock(cs); + if (queue.size() >= maxDepth) { + return false; + } + queue.push_back(item); + cond.notify_one(); + return true; + } + /** Thread function */ + void Run() + { + while (running) { + WorkItem* i = 0; + { + boost::unique_lock lock(cs); + while (running && queue.empty()) + cond.wait(lock); + if (!running) + break; + i = queue.front(); + queue.pop_front(); + } + (*i)(); + delete i; + } + } + /** Interrupt and exit loops */ + void Interrupt() + { + boost::unique_lock lock(cs); + running = false; + cond.notify_all(); + } + + /** Return current depth of queue */ + size_t Depth() + { + boost::unique_lock lock(cs); + return queue.size(); + } +}; + +struct HTTPPathHandler +{ + HTTPPathHandler() {} + HTTPPathHandler(std::string prefix, bool exactMatch, HTTPRequestHandler handler): + prefix(prefix), exactMatch(exactMatch), handler(handler) + { + } + std::string prefix; + bool exactMatch; + HTTPRequestHandler handler; +}; + +/** HTTP module state */ + +//! libevent event loop +static struct event_base* eventBase = 0; +//! HTTP server +struct evhttp* eventHTTP = 0; +//! List of subnets to allow RPC connections from +static std::vector rpc_allow_subnets; +//! Work queue for handling longer requests off the event loop thread +static WorkQueue* workQueue = 0; +//! Handlers for (sub)paths +std::vector pathHandlers; + +/** Check if a network address is allowed to access the HTTP server */ +static bool ClientAllowed(const CNetAddr& netaddr) +{ + if (!netaddr.IsValid()) + return false; + BOOST_FOREACH (const CSubNet& subnet, rpc_allow_subnets) + if (subnet.Match(netaddr)) + return true; + return false; +} + +/** Initialize ACL list for HTTP server */ +static bool InitHTTPAllowList() +{ + rpc_allow_subnets.clear(); + rpc_allow_subnets.push_back(CSubNet("127.0.0.0/8")); // always allow IPv4 local subnet + rpc_allow_subnets.push_back(CSubNet("::1")); // always allow IPv6 localhost + if (mapMultiArgs.count("-rpcallowip")) { + const std::vector& vAllow = mapMultiArgs["-rpcallowip"]; + BOOST_FOREACH (std::string strAllow, vAllow) { + CSubNet subnet(strAllow); + if (!subnet.IsValid()) { + uiInterface.ThreadSafeMessageBox( + 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), + "", CClientUIInterface::MSG_ERROR); + return false; + } + rpc_allow_subnets.push_back(subnet); + } + } + std::string strAllowed; + BOOST_FOREACH (const CSubNet& subnet, rpc_allow_subnets) + strAllowed += subnet.ToString() + " "; + LogPrint("http", "Allowing HTTP connections from: %s\n", strAllowed); + return true; +} + +/** HTTP request method as string - use for logging only */ +static std::string RequestMethodString(HTTPRequest::RequestMethod m) +{ + switch (m) { + case HTTPRequest::GET: + return "GET"; + break; + case HTTPRequest::POST: + return "POST"; + break; + case HTTPRequest::HEAD: + return "HEAD"; + break; + case HTTPRequest::PUT: + return "PUT"; + break; + default: + return "unknown"; + } +} + +/** HTTP request callback */ +static void http_request_cb(struct evhttp_request* req, void* arg) +{ + std::auto_ptr hreq(new HTTPRequest(req)); + + LogPrint("http", "Received a %s request for %s from %s\n", + RequestMethodString(hreq->GetRequestMethod()), hreq->GetURI(), hreq->GetPeer().ToString()); + + // Early address-based allow check + if (!ClientAllowed(hreq->GetPeer())) { + hreq->WriteReply(HTTP_FORBIDDEN); + return; + } + + // Early reject unknown HTTP methods + if (hreq->GetRequestMethod() == HTTPRequest::UNKNOWN) { + hreq->WriteReply(HTTP_BADMETHOD); + return; + } + + // Find registered handler for prefix + std::string strURI = hreq->GetURI(); + std::string path; + std::vector::const_iterator i = pathHandlers.begin(); + std::vector::const_iterator iend = pathHandlers.end(); + for (; i != iend; ++i) { + bool match = false; + if (i->exactMatch) + match = (strURI == i->prefix); + else + match = (strURI.substr(0, i->prefix.size()) == i->prefix); + if (match) { + path = strURI.substr(i->prefix.size()); + break; + } + } + + // Dispatch to worker thread + if (i != iend) { + std::auto_ptr item(new HTTPWorkItem(hreq.release(), path, i->handler)); + assert(workQueue); + if (workQueue->Enqueue(item.get())) + item.release(); /* if true, queue took ownership */ + else + item->req->WriteReply(HTTP_INTERNAL, "Work queue depth exceeded"); + } else { + hreq->WriteReply(HTTP_NOTFOUND); + } +} + +/** Event dispatcher thread */ +static void ThreadHTTP(struct event_base* base, struct evhttp* http) +{ + RenameThread("bitcoin-http"); + LogPrint("http", "Entering http event loop\n"); + event_base_dispatch(base); + // Event loop will be interrupted by InterruptHTTPServer() + LogPrint("http", "Exited http event loop\n"); +} + +/** Bind HTTP server to specified addresses */ +static bool HTTPBindAddresses(struct evhttp* http) +{ + int defaultPort = GetArg("-rpcport", BaseParams().RPCPort()); + int nBound = 0; + std::vector > endpoints; + + // Determine what addresses to bind to + if (!mapArgs.count("-rpcallowip")) { // Default to loopback if not allowing external IPs + endpoints.push_back(std::make_pair("::1", defaultPort)); + endpoints.push_back(std::make_pair("127.0.0.1", defaultPort)); + if (mapArgs.count("-rpcbind")) { + LogPrintf("WARNING: option -rpcbind was ignored because -rpcallowip was not specified, refusing to allow everyone to connect\n"); + } + } else if (mapArgs.count("-rpcbind")) { // Specific bind address + const std::vector& vbind = mapMultiArgs["-rpcbind"]; + for (std::vector::const_iterator i = vbind.begin(); i != vbind.end(); ++i) { + int port = defaultPort; + std::string host; + SplitHostPort(*i, port, host); + endpoints.push_back(std::make_pair(host, port)); + } + } else { // No specific bind address specified, bind to any + endpoints.push_back(std::make_pair("::", defaultPort)); + endpoints.push_back(std::make_pair("0.0.0.0", defaultPort)); + } + + // Bind addresses + for (std::vector >::iterator i = endpoints.begin(); i != endpoints.end(); ++i) { + LogPrint("http", "Binding RPC on address %s port %i\n", i->first, i->second); + if (evhttp_bind_socket(http, i->first.empty() ? NULL : i->first.c_str(), i->second) == 0) { + nBound += 1; + } else { + LogPrintf("Binding RPC on address %s port %i failed.\n", i->first, i->second); + } + } + return nBound > 0; +} + +/** Simple wrapper to set thread name and run work queue */ +static void HTTPWorkQueueRun(WorkQueue* queue) +{ + RenameThread("bitcoin-httpworker"); + queue->Run(); +} + +bool StartHTTPServer(boost::thread_group& threadGroup) +{ + struct evhttp* http = 0; + struct event_base* base = 0; + + if (!InitHTTPAllowList()) + return false; + + if (GetBoolArg("-rpcssl", false)) { + uiInterface.ThreadSafeMessageBox( + "SSL mode for RPC (-rpcssl) is no longer supported.", + "", CClientUIInterface::MSG_ERROR); + return false; + } + +#ifdef WIN32 + evthread_use_windows_threads(); +#else + evthread_use_pthreads(); +#endif + + base = event_base_new(); // XXX RAII + if (!base) { + LogPrintf("Couldn't create an event_base: exiting\n"); + return false; + } + + /* Create a new evhttp object to handle requests. */ + http = evhttp_new(base); // XXX RAII + if (!http) { + LogPrintf("couldn't create evhttp. Exiting.\n"); + event_base_free(base); + return false; + } + + evhttp_set_timeout(http, GetArg("-rpctimeout", 30)); + evhttp_set_max_body_size(http, MAX_SIZE); + evhttp_set_gencb(http, http_request_cb, NULL); + + if (!HTTPBindAddresses(http)) { + LogPrintf("Unable to bind any endpoint for RPC server\n"); + evhttp_free(http); + event_base_free(base); + return false; + } + + LogPrint("http", "Starting HTTP server\n"); + int workQueueDepth = std::max((long)GetArg("-rpcworkqueue", 16), 1L); + int rpcThreads = std::max((long)GetArg("-rpcthreads", 4), 1L); + LogPrintf("HTTP: creating work queue of depth %d and %d worker threads\n", workQueueDepth, rpcThreads); + workQueue = new WorkQueue(workQueueDepth); + + threadGroup.create_thread(boost::bind(&ThreadHTTP, base, http)); + + for (int i = 0; i < rpcThreads; i++) + threadGroup.create_thread(boost::bind(&HTTPWorkQueueRun, workQueue)); + + eventBase = base; + eventHTTP = http; + return true; +} + +void InterruptHTTPServer() +{ + LogPrint("http", "Interrupting HTTP server\n"); + if (eventBase) + event_base_loopbreak(eventBase); + if (workQueue) + workQueue->Interrupt(); +} + +void StopHTTPServer() +{ + LogPrint("http", "Stopping HTTP server\n"); + delete workQueue; + if (eventHTTP) { + evhttp_free(eventHTTP); + eventHTTP = 0; + } + if (eventBase) { + event_base_free(eventBase); + eventBase = 0; + } +} + +struct event_base* EventBase() +{ + return eventBase; +} + +static void httpevent_callback_fn(evutil_socket_t, short, void* data) +{ + // Static handler simply passes through execution flow to _handle method + ((HTTPEvent*)data)->_handle(); +} + +void HTTPEvent::_handle() +{ + (*handler)(); + if (deleteWhenTriggered) + delete this; +} + +HTTPEvent::HTTPEvent(struct event_base* base, bool deleteWhenTriggered, HTTPClosure* handler) : deleteWhenTriggered(deleteWhenTriggered), handler(handler) +{ + ev = event_new(base, -1, 0, httpevent_callback_fn, this); + assert(ev); +} +HTTPEvent::~HTTPEvent() +{ + event_free(ev); +} +void HTTPEvent::trigger(struct timeval* tv) +{ + if (tv == NULL) + event_active(ev, 0, 0); // immediately trigger event in main thread + else + evtimer_add(ev, tv); // trigger after timeval passed +} +HTTPRequest::HTTPRequest(struct evhttp_request* req) : req(req), + replySent(false) +{ +} +HTTPRequest::~HTTPRequest() +{ + if (!replySent) { + // Keep track of whether reply was sent to avoid request leaks + LogPrintf("%s: Unhandled request\n", __func__); + WriteReply(HTTP_INTERNAL, "Unhandled request"); + } + // evhttpd cleans up the request, as long as a reply was sent. +} + +std::pair HTTPRequest::GetHeader(const std::string& hdr) +{ + const struct evkeyvalq* headers = evhttp_request_get_input_headers(req); + assert(headers); + const char* val = evhttp_find_header(headers, hdr.c_str()); + if (val) + return std::make_pair(true, val); + else + return std::make_pair(false, ""); +} + +std::string HTTPRequest::ReadBody() +{ + struct evbuffer* buf = evhttp_request_get_input_buffer(req); + if (!buf) + return ""; + size_t size = evbuffer_get_length(buf); + /** Trivial implementation: if this is ever a performance bottleneck, + * internal copying can be avoided in multi-segment buffers by using + * evbuffer_peek and an awkward loop. Though in that case, it'd be even + * better to not copy into an intermediate string but use a stream + * abstraction to consume the evbuffer on the fly in the parsing algorithm. + */ + const char* data = (const char*)evbuffer_pullup(buf, size); + if (!data) // returns NULL in case of empty buffer + return ""; + std::string rv(data, size); + evbuffer_drain(buf, size); + return rv; +} + +void HTTPRequest::WriteHeader(const std::string& hdr, const std::string& value) +{ + struct evkeyvalq* headers = evhttp_request_get_output_headers(req); + assert(headers); + evhttp_add_header(headers, hdr.c_str(), value.c_str()); +} + +/** Closure sent to main thread to request a reply to be sent to + * a HTTP request. + * Replies must be sent in the main loop in the main http thread, + * this cannot be done from worker threads. + */ +struct HTTPSendReplyHandler : HTTPClosure { +public: + HTTPSendReplyHandler(struct evhttp_request* req, int nStatus) : req(req), nStatus(nStatus) + { + } + void operator()() + { + evhttp_send_reply(req, nStatus, NULL, NULL); + } +private: + struct evhttp_request* req; + int nStatus; +}; + +void HTTPRequest::WriteReply(int nStatus, const std::string& strReply) +{ + assert(!replySent && req); + // Send event to main http thread to send reply message + struct evbuffer* evb = evhttp_request_get_output_buffer(req); + assert(evb); + evbuffer_add(evb, strReply.data(), strReply.size()); + HTTPEvent* ev = new HTTPEvent(eventBase, true, + new HTTPSendReplyHandler(req, nStatus)); + ev->trigger(0); + replySent = true; + req = 0; // transferred back to main thread +} + +CService HTTPRequest::GetPeer() +{ + evhttp_connection* con = evhttp_request_get_connection(req); + CService peer; + if (con) { + // evhttp retains ownership over returned address string + const char* address = ""; + uint16_t port = 0; + evhttp_connection_get_peer(con, (char**)&address, &port); + peer = CService(address, port); + } + return peer; +} + +std::string HTTPRequest::GetURI() +{ + return evhttp_request_get_uri(req); +} + +HTTPRequest::RequestMethod HTTPRequest::GetRequestMethod() +{ + switch (evhttp_request_get_command(req)) { + case EVHTTP_REQ_GET: + return GET; + break; + case EVHTTP_REQ_POST: + return POST; + break; + case EVHTTP_REQ_HEAD: + return HEAD; + break; + case EVHTTP_REQ_PUT: + return PUT; + break; + default: + return UNKNOWN; + break; + } +} + +void RegisterHTTPHandler(const std::string &prefix, bool exactMatch, const HTTPRequestHandler &handler) +{ + LogPrint("http", "Registering HTTP handler for %s (exactmath %d)\n", prefix, exactMatch); + pathHandlers.push_back(HTTPPathHandler(prefix, exactMatch, handler)); +} + +void UnregisterHTTPHandler(const std::string &prefix, bool exactMatch) +{ + std::vector::iterator i = pathHandlers.begin(); + std::vector::iterator iend = pathHandlers.end(); + for (; i != iend; ++i) + if (i->prefix == prefix && i->exactMatch == exactMatch) + break; + if (i != iend) + { + LogPrint("http", "Unregistering HTTP handler for %s (exactmath %d)\n", prefix, exactMatch); + pathHandlers.erase(i); + } +} + -- cgit v1.2.3 From be33f3f50b7358bbad9e16bf730fac2ab3c4886b Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 28 Aug 2015 16:46:20 +0200 Subject: Implement RPCTimerHandler for Qt RPC console Implement RPCTimerHandler for Qt RPC console, so that `walletpassphrase` works with GUI and `-server=0`. Also simplify HTTPEvent-related code by using boost::function directly. --- src/httpserver.cpp | 33 ++++++++------------------------- 1 file changed, 8 insertions(+), 25 deletions(-) (limited to 'src/httpserver.cpp') diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 89366b2e4..13f870567 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -412,18 +412,15 @@ struct event_base* EventBase() static void httpevent_callback_fn(evutil_socket_t, short, void* data) { - // Static handler simply passes through execution flow to _handle method - ((HTTPEvent*)data)->_handle(); + // Static handler: simply call inner handler + HTTPEvent *self = ((HTTPEvent*)data); + self->handler(); + if (self->deleteWhenTriggered) + delete self; } -void HTTPEvent::_handle() -{ - (*handler)(); - if (deleteWhenTriggered) - delete this; -} - -HTTPEvent::HTTPEvent(struct event_base* base, bool deleteWhenTriggered, HTTPClosure* handler) : deleteWhenTriggered(deleteWhenTriggered), handler(handler) +HTTPEvent::HTTPEvent(struct event_base* base, bool deleteWhenTriggered, const boost::function& handler): + deleteWhenTriggered(deleteWhenTriggered), handler(handler) { ev = event_new(base, -1, 0, httpevent_callback_fn, this); assert(ev); @@ -496,20 +493,6 @@ void HTTPRequest::WriteHeader(const std::string& hdr, const std::string& value) * Replies must be sent in the main loop in the main http thread, * this cannot be done from worker threads. */ -struct HTTPSendReplyHandler : HTTPClosure { -public: - HTTPSendReplyHandler(struct evhttp_request* req, int nStatus) : req(req), nStatus(nStatus) - { - } - void operator()() - { - evhttp_send_reply(req, nStatus, NULL, NULL); - } -private: - struct evhttp_request* req; - int nStatus; -}; - void HTTPRequest::WriteReply(int nStatus, const std::string& strReply) { assert(!replySent && req); @@ -518,7 +501,7 @@ void HTTPRequest::WriteReply(int nStatus, const std::string& strReply) assert(evb); evbuffer_add(evb, strReply.data(), strReply.size()); HTTPEvent* ev = new HTTPEvent(eventBase, true, - new HTTPSendReplyHandler(req, nStatus)); + boost::bind(evhttp_send_reply, req, nStatus, (const char*)NULL, (struct evbuffer *)NULL)); ev->trigger(0); replySent = true; req = 0; // transferred back to main thread -- cgit v1.2.3 From 6d2bc221463ffe3ed3a99e8c682b090983b2e7b5 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 28 Aug 2015 17:14:51 +0200 Subject: Document options for new HTTP/RPC server in --help --- src/httpserver.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/httpserver.cpp') diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 13f870567..813764f22 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -355,7 +355,7 @@ bool StartHTTPServer(boost::thread_group& threadGroup) return false; } - evhttp_set_timeout(http, GetArg("-rpctimeout", 30)); + evhttp_set_timeout(http, GetArg("-rpctimeout", DEFAULT_HTTP_TIMEOUT)); evhttp_set_max_body_size(http, MAX_SIZE); evhttp_set_gencb(http, http_request_cb, NULL); @@ -367,8 +367,8 @@ bool StartHTTPServer(boost::thread_group& threadGroup) } LogPrint("http", "Starting HTTP server\n"); - int workQueueDepth = std::max((long)GetArg("-rpcworkqueue", 16), 1L); - int rpcThreads = std::max((long)GetArg("-rpcthreads", 4), 1L); + int workQueueDepth = std::max((long)GetArg("-rpcworkqueue", DEFAULT_HTTP_WORKQUEUE), 1L); + int rpcThreads = std::max((long)GetArg("-rpcthreads", DEFAULT_HTTP_THREADS), 1L); LogPrintf("HTTP: creating work queue of depth %d and %d worker threads\n", workQueueDepth, rpcThreads); workQueue = new WorkQueue(workQueueDepth); -- cgit v1.2.3 From 3a174cd400c6c239539d4c0c10b557c3e0615212 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 28 Aug 2015 16:55:16 +0200 Subject: Fix race condition between starting HTTP server thread and setting EventBase() Split StartHTTPServer into InitHTTPServer and StartHTTPServer to give clients a window to register their handlers without race conditions. Thanks @ajweiss for figuring this out. --- src/httpserver.cpp | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) (limited to 'src/httpserver.cpp') diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 813764f22..7e599b1d7 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -320,7 +320,7 @@ static void HTTPWorkQueueRun(WorkQueue* queue) queue->Run(); } -bool StartHTTPServer(boost::thread_group& threadGroup) +bool InitHTTPServer() { struct evhttp* http = 0; struct event_base* base = 0; @@ -366,19 +366,25 @@ bool StartHTTPServer(boost::thread_group& threadGroup) return false; } - LogPrint("http", "Starting HTTP server\n"); + LogPrint("http", "Initialized HTTP server\n"); int workQueueDepth = std::max((long)GetArg("-rpcworkqueue", DEFAULT_HTTP_WORKQUEUE), 1L); - int rpcThreads = std::max((long)GetArg("-rpcthreads", DEFAULT_HTTP_THREADS), 1L); - LogPrintf("HTTP: creating work queue of depth %d and %d worker threads\n", workQueueDepth, rpcThreads); + LogPrintf("HTTP: creating work queue of depth %d\n", workQueueDepth); + workQueue = new WorkQueue(workQueueDepth); + eventBase = base; + eventHTTP = http; + return true; +} - threadGroup.create_thread(boost::bind(&ThreadHTTP, base, http)); +bool StartHTTPServer(boost::thread_group& threadGroup) +{ + LogPrint("http", "Starting HTTP server\n"); + int rpcThreads = std::max((long)GetArg("-rpcthreads", DEFAULT_HTTP_THREADS), 1L); + LogPrintf("HTTP: starting %d worker threads\n", rpcThreads); + threadGroup.create_thread(boost::bind(&ThreadHTTP, eventBase, eventHTTP)); for (int i = 0; i < rpcThreads; i++) threadGroup.create_thread(boost::bind(&HTTPWorkQueueRun, workQueue)); - - eventBase = base; - eventHTTP = http; return true; } -- cgit v1.2.3 From eb3002bb7138db3aa4490b00be88779818b6da0d Mon Sep 17 00:00:00 2001 From: paveljanik Date: Fri, 4 Sep 2015 19:22:48 +0200 Subject: [TRIVIAL] Fix typo: exactmath -> exactmatch ... but not yet in trivial tree --- src/httpserver.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/httpserver.cpp') diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 7e599b1d7..baca00757 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -555,7 +555,7 @@ HTTPRequest::RequestMethod HTTPRequest::GetRequestMethod() void RegisterHTTPHandler(const std::string &prefix, bool exactMatch, const HTTPRequestHandler &handler) { - LogPrint("http", "Registering HTTP handler for %s (exactmath %d)\n", prefix, exactMatch); + LogPrint("http", "Registering HTTP handler for %s (exactmatch %d)\n", prefix, exactMatch); pathHandlers.push_back(HTTPPathHandler(prefix, exactMatch, handler)); } @@ -568,7 +568,7 @@ void UnregisterHTTPHandler(const std::string &prefix, bool exactMatch) break; if (i != iend) { - LogPrint("http", "Unregistering HTTP handler for %s (exactmath %d)\n", prefix, exactMatch); + LogPrint("http", "Unregistering HTTP handler for %s (exactmatch %d)\n", prefix, exactMatch); pathHandlers.erase(i); } } -- cgit v1.2.3 From 8b2d6edaa9fbfb6344ca51edd0b3655b451cbcac Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 18 Sep 2015 14:58:08 +0200 Subject: http: Disable libevent debug logging, if not explicitly enabled Add a option "-debug=libevent" to enable libevent debugging for troubleshooting. Libevent logging is redirected to our own log. --- src/httpserver.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'src/httpserver.cpp') diff --git a/src/httpserver.cpp b/src/httpserver.cpp index baca00757..3fc880214 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -320,6 +320,15 @@ static void HTTPWorkQueueRun(WorkQueue* queue) queue->Run(); } +/** libevent event log callback */ +static void libevent_log_cb(int severity, const char *msg) +{ + if (severity >= EVENT_LOG_WARN) // Log warn messages and higher without debug category + LogPrintf("libevent: %s\n", msg); + else + LogPrint("libevent", "libevent: %s\n", msg); +} + bool InitHTTPServer() { struct evhttp* http = 0; @@ -335,6 +344,16 @@ bool InitHTTPServer() return false; } + // Redirect libevent's logging to our own log + event_set_log_callback(&libevent_log_cb); +#if LIBEVENT_VERSION_NUMBER >= 0x02010100 + // If -debug=libevent, set full libevent debugging. + // Otherwise, disable all libevent debugging. + if (LogAcceptCategory("libevent")) + event_enable_debug_logging(EVENT_DBG_ALL); + else + event_enable_debug_logging(EVENT_DBG_NONE); +#endif #ifdef WIN32 evthread_use_windows_threads(); #else -- cgit v1.2.3 From 2190ea6c4e7f56f29fc18539284801a5f504ee48 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 18 Sep 2015 15:45:38 +0200 Subject: rpc: Split option -rpctimeout into -rpcservertimeout and -rpcclienttimeout The two timeouts for the server and client, are essentially different: - In the case of the server it should be a lower value to avoid clients clogging up connection slots - In the case of the client it should be a high value to accomedate slow responses from the server, for example for slow queries or when the lock is contended Split the options into `-rpcservertimeout` and `-rpcclienttimeout` with respective defaults of 30 and 900. --- src/httpserver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/httpserver.cpp') diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 3fc880214..600e57b7c 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -374,7 +374,7 @@ bool InitHTTPServer() return false; } - evhttp_set_timeout(http, GetArg("-rpctimeout", DEFAULT_HTTP_TIMEOUT)); + evhttp_set_timeout(http, GetArg("-rpcservertimeout", DEFAULT_HTTP_SERVER_TIMEOUT)); evhttp_set_max_body_size(http, MAX_SIZE); evhttp_set_gencb(http, http_request_cb, NULL); -- cgit v1.2.3 From 5e0c22135600fe36811da3b78216efc61ba765fb Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 24 Sep 2015 17:29:22 +0200 Subject: Make HTTP server shutdown more graceful Shutting down the HTTP server currently breaks off all current requests. This can create a race condition with RPC `stop` command, where the calling process never receives confirmation. This change removes the listening sockets on shutdown so that no new requests can come in, but no longer breaks off requests in progress. Meant to fix #6717. --- src/httpserver.cpp | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) (limited to 'src/httpserver.cpp') diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 600e57b7c..e967d3a4a 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -155,6 +155,8 @@ static std::vector rpc_allow_subnets; static WorkQueue* workQueue = 0; //! Handlers for (sub)paths std::vector pathHandlers; +//! Bound listening sockets +std::vector boundSockets; /** Check if a network address is allowed to access the HTTP server */ static bool ClientAllowed(const CNetAddr& netaddr) @@ -264,6 +266,13 @@ static void http_request_cb(struct evhttp_request* req, void* arg) } } +/** Callback to reject HTTP requests after shutdown. */ +static void http_reject_request_cb(struct evhttp_request* req, void*) +{ + LogPrint("http", "Rejecting request while shutting down\n"); + evhttp_send_error(req, HTTP_SERVUNAVAIL, NULL); +} + /** Event dispatcher thread */ static void ThreadHTTP(struct event_base* base, struct evhttp* http) { @@ -278,7 +287,6 @@ static void ThreadHTTP(struct event_base* base, struct evhttp* http) static bool HTTPBindAddresses(struct evhttp* http) { int defaultPort = GetArg("-rpcport", BaseParams().RPCPort()); - int nBound = 0; std::vector > endpoints; // Determine what addresses to bind to @@ -304,13 +312,14 @@ static bool HTTPBindAddresses(struct evhttp* http) // Bind addresses for (std::vector >::iterator i = endpoints.begin(); i != endpoints.end(); ++i) { LogPrint("http", "Binding RPC on address %s port %i\n", i->first, i->second); - if (evhttp_bind_socket(http, i->first.empty() ? NULL : i->first.c_str(), i->second) == 0) { - nBound += 1; + evhttp_bound_socket *bind_handle = evhttp_bind_socket_with_handle(http, i->first.empty() ? NULL : i->first.c_str(), i->second); + if (bind_handle) { + boundSockets.push_back(bind_handle); } else { LogPrintf("Binding RPC on address %s port %i failed.\n", i->first, i->second); } } - return nBound > 0; + return !boundSockets.empty(); } /** Simple wrapper to set thread name and run work queue */ @@ -410,8 +419,14 @@ bool StartHTTPServer(boost::thread_group& threadGroup) void InterruptHTTPServer() { LogPrint("http", "Interrupting HTTP server\n"); - if (eventBase) - event_base_loopbreak(eventBase); + if (eventHTTP) { + // Unlisten sockets + BOOST_FOREACH (evhttp_bound_socket *socket, boundSockets) { + evhttp_del_accept_socket(eventHTTP, socket); + } + // Reject requests on current connections + evhttp_set_gencb(eventHTTP, http_reject_request_cb, NULL); + } if (workQueue) workQueue->Interrupt(); } -- cgit v1.2.3 From de9de2de361ab1355b976f17371d73e36fe3bf56 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 25 Sep 2015 13:49:08 +0200 Subject: http: Wait for worker threads to exit Add a WaitExit() call to http's WorkQueue to make it delete the work queue only when all worker threads stopped. This fixes a problem that was reproducable by pressing Ctrl-C during AppInit2: ``` /usr/include/boost/thread/pthread/condition_variable_fwd.hpp:81: boost::condition_variable::~condition_variable(): Assertion `!ret' failed. /usr/include/boost/thread/pthread/mutex.hpp:108: boost::mutex::~mutex(): Assertion `!posix::pthread_mutex_destroy(&m)' failed. ``` I was assuming that `threadGroup->join_all();` would always have been called when entering the Shutdown(). However this is not the case in bitcoind's AppInit2-non-zero-exit case "was left out intentionally here". --- src/httpserver.cpp | 40 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) (limited to 'src/httpserver.cpp') diff --git a/src/httpserver.cpp b/src/httpserver.cpp index e967d3a4a..9f079aedf 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -72,13 +72,35 @@ private: std::deque queue; bool running; size_t maxDepth; + int numThreads; + + /** RAII object to keep track of number of running worker threads */ + class ThreadCounter + { + public: + WorkQueue &wq; + ThreadCounter(WorkQueue &w): wq(w) + { + boost::lock_guard lock(wq.cs); + wq.numThreads += 1; + } + ~ThreadCounter() + { + boost::lock_guard lock(wq.cs); + wq.numThreads -= 1; + wq.cond.notify_all(); + } + }; public: WorkQueue(size_t maxDepth) : running(true), - maxDepth(maxDepth) + maxDepth(maxDepth), + numThreads(0) { } - /* Precondition: worker threads have all stopped */ + /*( Precondition: worker threads have all stopped + * (call WaitExit) + */ ~WorkQueue() { while (!queue.empty()) { @@ -100,6 +122,7 @@ public: /** Thread function */ void Run() { + ThreadCounter count(*this); while (running) { WorkItem* i = 0; { @@ -122,6 +145,13 @@ public: running = false; cond.notify_all(); } + /** Wait for worker threads to exit */ + void WaitExit() + { + boost::unique_lock lock(cs); + while (numThreads > 0) + cond.wait(lock); + } /** Return current depth of queue */ size_t Depth() @@ -434,7 +464,11 @@ void InterruptHTTPServer() void StopHTTPServer() { LogPrint("http", "Stopping HTTP server\n"); - delete workQueue; + if (workQueue) { + LogPrint("http", "Waiting for HTTP worker threads to exit\n"); + workQueue->WaitExit(); + delete workQueue; + } if (eventHTTP) { evhttp_free(eventHTTP); eventHTTP = 0; -- cgit v1.2.3 From ec908d5f7aa9ad7e3487018e06a24cb6449cc58b Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 25 Sep 2015 15:35:37 +0200 Subject: http: Force-exit event loop after predefined time This makes sure that the event loop eventually terminates, even if an event (like an open timeout, or a hanging connection) happens to be holding it up. --- src/httpserver.cpp | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src/httpserver.cpp') diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 9f079aedf..0a7f903e9 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -457,6 +457,13 @@ void InterruptHTTPServer() // Reject requests on current connections evhttp_set_gencb(eventHTTP, http_reject_request_cb, NULL); } + if (eventBase) { + // Force-exit event loop after predefined time + struct timeval tv; + tv.tv_sec = 10; + tv.tv_usec = 0; + event_base_loopexit(eventBase, &tv); + } if (workQueue) workQueue->Interrupt(); } -- cgit v1.2.3 From 41db8c4733b34d56834162c4d054823c240ffc92 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Tue, 20 Oct 2015 11:35:10 +0200 Subject: http: Restrict maximum size of request line + headers Prevent memory exhaustion by sending lots of data. Also add a test to `httpbasics.py`. Closes #6425 --- src/httpserver.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/httpserver.cpp') diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 0a7f903e9..8698abb90 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -38,6 +38,9 @@ #include #include +/** Maximum size of http request (request line + headers) */ +static const size_t MAX_HEADERS_SIZE = 8192; + /** HTTP request work item */ class HTTPWorkItem : public HTTPClosure { @@ -414,6 +417,7 @@ bool InitHTTPServer() } evhttp_set_timeout(http, GetArg("-rpcservertimeout", DEFAULT_HTTP_SERVER_TIMEOUT)); + evhttp_set_max_headers_size(http, MAX_HEADERS_SIZE); evhttp_set_max_body_size(http, MAX_SIZE); evhttp_set_gencb(http, http_request_cb, NULL); -- cgit v1.2.3 From a264c32e3321ae909ca59cb8ce8bf5d812dbc4e1 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Wed, 11 Nov 2015 17:34:10 +0100 Subject: http: speed up shutdown This continues/fixes #6719. `event_base_loopbreak` was not doing what I expected it to, at least in libevent 2.0.21. What I expected was that it sets a timeout, given that no other pending events it would exit in N seconds. However, what it does was delay the event loop exit with 10 seconds, even if nothing is pending. Solve it in a different way: give the event loop thread time to exit out of itself, and if it doesn't, send loopbreak. This speeds up the RPC tests a lot, each exit incurred a 10 second overhead, with this change there should be no shutdown overhead in the common case and up to two seconds if the event loop is blocking. As a bonus this breaks dependency on boost::thread_group, as the HTTP server minds its own offspring. --- src/httpserver.cpp | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) (limited to 'src/httpserver.cpp') diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 8698abb90..424ef015c 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -438,15 +438,17 @@ bool InitHTTPServer() return true; } -bool StartHTTPServer(boost::thread_group& threadGroup) +boost::thread threadHTTP; + +bool StartHTTPServer() { LogPrint("http", "Starting HTTP server\n"); int rpcThreads = std::max((long)GetArg("-rpcthreads", DEFAULT_HTTP_THREADS), 1L); LogPrintf("HTTP: starting %d worker threads\n", rpcThreads); - threadGroup.create_thread(boost::bind(&ThreadHTTP, eventBase, eventHTTP)); + threadHTTP = boost::thread(boost::bind(&ThreadHTTP, eventBase, eventHTTP)); for (int i = 0; i < rpcThreads; i++) - threadGroup.create_thread(boost::bind(&HTTPWorkQueueRun, workQueue)); + boost::thread(boost::bind(&HTTPWorkQueueRun, workQueue)); return true; } @@ -461,13 +463,6 @@ void InterruptHTTPServer() // Reject requests on current connections evhttp_set_gencb(eventHTTP, http_reject_request_cb, NULL); } - if (eventBase) { - // Force-exit event loop after predefined time - struct timeval tv; - tv.tv_sec = 10; - tv.tv_usec = 0; - event_base_loopexit(eventBase, &tv); - } if (workQueue) workQueue->Interrupt(); } @@ -480,6 +475,20 @@ void StopHTTPServer() workQueue->WaitExit(); delete workQueue; } + if (eventBase) { + LogPrint("http", "Waiting for HTTP event thread to exit\n"); + // Give event loop a few seconds to exit (to send back last RPC responses), then break it + // Before this was solved with event_base_loopexit, but that didn't work as expected in + // at least libevent 2.0.21 and always introduced a delay. In libevent + // master that appears to be solved, so in the future that solution + // could be used again (if desirable). + // (see discussion in https://github.com/bitcoin/bitcoin/pull/6990) + if (!threadHTTP.try_join_for(boost::chrono::milliseconds(2000))) { + LogPrintf("HTTP event loop did not exit within allotted time, sending loopbreak\n"); + event_base_loopbreak(eventBase); + threadHTTP.join(); + } + } if (eventHTTP) { evhttp_free(eventHTTP); eventHTTP = 0; @@ -488,6 +497,7 @@ void StopHTTPServer() event_base_free(eventBase); eventBase = 0; } + LogPrint("http", "Stopped HTTP server\n"); } struct event_base* EventBase() -- cgit v1.2.3 From aee22bf2886acd3151992c42f7c0ca5f2aae3fdf Mon Sep 17 00:00:00 2001 From: Gregory Maxwell Date: Sat, 14 Nov 2015 13:54:21 +0000 Subject: Avoid a compile error on hosts with libevent too old for EVENT_LOG_WARN. This uses _EVENT_LOG_WARN instead, which appears to be defined in the old versions of libevent that I have on some systems. --- src/httpserver.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/httpserver.cpp') diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 424ef015c..52f5675e8 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -365,6 +365,10 @@ static void HTTPWorkQueueRun(WorkQueue* queue) /** libevent event log callback */ static void libevent_log_cb(int severity, const char *msg) { +#ifndef EVENT_LOG_WARN +// EVENT_LOG_WARN was added in 2.0.19; but before then _EVENT_LOG_WARN existed. +# define EVENT_LOG_WARN _EVENT_LOG_WARN +#endif if (severity >= EVENT_LOG_WARN) // Log warn messages and higher without debug category LogPrintf("libevent: %s\n", msg); else -- cgit v1.2.3 From 3522f49f5ae74fe3ef310fad6fa7e09d65c9c1b8 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Fri, 20 Nov 2015 10:14:21 +0100 Subject: http: add Boost 1.49 compatibility `try_join_for` was introduced in Boost 1.50: http://www.boost.org/doc/libs/1_50_0/doc/html/thread/thread_management.html#thread.thread_management.thread.try_join_for 1.49 has `timed_join`, one can accomplish the same with: http://www.boost.org/doc/libs/1_49_0/doc/html/thread/thread_management.html#thread.thread_management.thread.timed_join However, `timed_join` was deprecated in 1.50. So a conditional is necessary. This solution was tested in #7031. --- src/httpserver.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/httpserver.cpp') diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 52f5675e8..91518d7c5 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -487,7 +487,11 @@ void StopHTTPServer() // master that appears to be solved, so in the future that solution // could be used again (if desirable). // (see discussion in https://github.com/bitcoin/bitcoin/pull/6990) +#if BOOST_VERSION >= 105000 if (!threadHTTP.try_join_for(boost::chrono::milliseconds(2000))) { +#else + if (!threadHTTP.timed_join(boost::posix_time::milliseconds(2000))) { +#endif LogPrintf("HTTP event loop did not exit within allotted time, sending loopbreak\n"); event_base_loopbreak(eventBase); threadHTTP.join(); -- cgit v1.2.3 From a0eaff8a1d18ebba33cdea4cd1efaddeb55519e7 Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Fri, 15 Jan 2016 11:55:17 +1100 Subject: move rpc* to rpc/ --- src/httpserver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/httpserver.cpp') diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 91518d7c5..ce1accb04 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -8,7 +8,7 @@ #include "compat.h" #include "util.h" #include "netbase.h" -#include "rpcprotocol.h" // For HTTP status codes +#include "rpc/protocol.h" // For HTTP status codes #include "sync.h" #include "ui_interface.h" -- cgit v1.2.3 From 07e4edb056249e017b0e5a4783e4452ce892b52d Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 28 Apr 2016 13:40:20 +0200 Subject: =?UTF-8?q?auto=5Fptr=20=E2=86=92=20unique=5Fptr?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change the few occurrences of the deprecated `auto_ptr` to c++11 `unique_ptr`. Silences the deprecation warnings. Also add a missing `std::` for consistency. --- src/httpserver.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/httpserver.cpp') diff --git a/src/httpserver.cpp b/src/httpserver.cpp index ce1accb04..a98eff7c1 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -252,7 +252,7 @@ static std::string RequestMethodString(HTTPRequest::RequestMethod m) /** HTTP request callback */ static void http_request_cb(struct evhttp_request* req, void* arg) { - std::auto_ptr hreq(new HTTPRequest(req)); + std::unique_ptr hreq(new HTTPRequest(req)); LogPrint("http", "Received a %s request for %s from %s\n", RequestMethodString(hreq->GetRequestMethod()), hreq->GetURI(), hreq->GetPeer().ToString()); @@ -288,7 +288,7 @@ static void http_request_cb(struct evhttp_request* req, void* arg) // Dispatch to worker thread if (i != iend) { - std::auto_ptr item(new HTTPWorkItem(hreq.release(), path, i->handler)); + std::unique_ptr item(new HTTPWorkItem(hreq.release(), path, i->handler)); assert(workQueue); if (workQueue->Enqueue(item.get())) item.release(); /* if true, queue took ownership */ -- cgit v1.2.3 From 091d6e04998d2c88dd7f42ad2d90929f428764c2 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Thu, 28 Apr 2016 19:03:05 +0200 Subject: http: Do a pending c++11 simplification Use std::unique_ptr for handling work items. This makes the code more RAII and, as mentioned in the comment, is what I planned when I wrote the code in the first place. --- src/httpserver.cpp | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) (limited to 'src/httpserver.cpp') diff --git a/src/httpserver.cpp b/src/httpserver.cpp index a98eff7c1..8297b6f75 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -71,8 +71,7 @@ private: /** Mutex protects entire object */ CWaitableCriticalSection cs; CConditionVariable cond; - /* XXX in C++11 we can use std::unique_ptr here and avoid manual cleanup */ - std::deque queue; + std::deque> queue; bool running; size_t maxDepth; int numThreads; @@ -101,15 +100,11 @@ public: numThreads(0) { } - /*( Precondition: worker threads have all stopped + /** Precondition: worker threads have all stopped * (call WaitExit) */ ~WorkQueue() { - while (!queue.empty()) { - delete queue.front(); - queue.pop_front(); - } } /** Enqueue a work item */ bool Enqueue(WorkItem* item) @@ -118,7 +113,7 @@ public: if (queue.size() >= maxDepth) { return false; } - queue.push_back(item); + queue.emplace_back(std::unique_ptr(item)); cond.notify_one(); return true; } @@ -127,18 +122,17 @@ public: { ThreadCounter count(*this); while (running) { - WorkItem* i = 0; + std::unique_ptr i; { boost::unique_lock lock(cs); while (running && queue.empty()) cond.wait(lock); if (!running) break; - i = queue.front(); + i = std::move(queue.front()); queue.pop_front(); } (*i)(); - delete i; } } /** Interrupt and exit loops */ -- cgit v1.2.3 From f97b410fdd57743f9a26ea49086534000eb9626a Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Wed, 4 May 2016 15:37:26 +0200 Subject: http: Add log message when work queue is full More useful error reporting. --- src/httpserver.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/httpserver.cpp') diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 8297b6f75..67a9e1e92 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -286,8 +286,10 @@ static void http_request_cb(struct evhttp_request* req, void* arg) assert(workQueue); if (workQueue->Enqueue(item.get())) item.release(); /* if true, queue took ownership */ - else + else { + LogPrintf("WARNING: request rejected because http work queue depth exceeded, it can be increased with the -rpcworkqueue= setting\n"); item->req->WriteReply(HTTP_INTERNAL, "Work queue depth exceeded"); + } } else { hreq->WriteReply(HTTP_NOTFOUND); } -- cgit v1.2.3 From 37b21372a0a3e5d876a87f03c8cbef2e02d8dc92 Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Wed, 4 May 2016 15:55:23 +0200 Subject: http: Change boost::scoped_ptr to std::unique_ptr in HTTPRequest No need for boost here. --- src/httpserver.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/httpserver.cpp') diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 67a9e1e92..c193d2af1 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -36,7 +36,6 @@ #include // for to_lower() #include -#include /** Maximum size of http request (request line + headers) */ static const size_t MAX_HEADERS_SIZE = 8192; @@ -54,7 +53,7 @@ public: func(req.get(), path); } - boost::scoped_ptr req; + std::unique_ptr req; private: std::string path; -- cgit v1.2.3 From f0188f9178a22fd493ed228c008d4cc25ac2952d Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Wed, 4 May 2016 16:05:17 +0200 Subject: http: use std::move to move HTTPRequest into HTTPWorkItem Thanks to Cory Fields for the idea. --- src/httpserver.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/httpserver.cpp') diff --git a/src/httpserver.cpp b/src/httpserver.cpp index c193d2af1..812940eaf 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -44,8 +44,8 @@ static const size_t MAX_HEADERS_SIZE = 8192; class HTTPWorkItem : public HTTPClosure { public: - HTTPWorkItem(HTTPRequest* req, const std::string &path, const HTTPRequestHandler& func): - req(req), path(path), func(func) + HTTPWorkItem(std::unique_ptr req, const std::string &path, const HTTPRequestHandler& func): + req(std::move(req)), path(path), func(func) { } void operator()() @@ -281,7 +281,7 @@ static void http_request_cb(struct evhttp_request* req, void* arg) // Dispatch to worker thread if (i != iend) { - std::unique_ptr item(new HTTPWorkItem(hreq.release(), path, i->handler)); + std::unique_ptr item(new HTTPWorkItem(std::move(hreq), path, i->handler)); assert(workQueue); if (workQueue->Enqueue(item.get())) item.release(); /* if true, queue took ownership */ -- cgit v1.2.3 From 755aa05174e06effd758eeb78c5af9fb465e9611 Mon Sep 17 00:00:00 2001 From: Cory Fields Date: Thu, 28 Jul 2016 17:52:51 -0400 Subject: httpserver: use a future rather than relying on boost's try_join_for --- src/httpserver.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'src/httpserver.cpp') diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 812940eaf..7150f96ed 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -302,13 +303,14 @@ static void http_reject_request_cb(struct evhttp_request* req, void*) } /** Event dispatcher thread */ -static void ThreadHTTP(struct event_base* base, struct evhttp* http) +static bool ThreadHTTP(struct event_base* base, struct evhttp* http) { RenameThread("bitcoin-http"); LogPrint("http", "Entering http event loop\n"); event_base_dispatch(base); // Event loop will be interrupted by InterruptHTTPServer() LogPrint("http", "Exited http event loop\n"); + return event_base_got_break(base) == 0; } /** Bind HTTP server to specified addresses */ @@ -438,13 +440,16 @@ bool InitHTTPServer() } boost::thread threadHTTP; +std::future threadResult; bool StartHTTPServer() { LogPrint("http", "Starting HTTP server\n"); int rpcThreads = std::max((long)GetArg("-rpcthreads", DEFAULT_HTTP_THREADS), 1L); LogPrintf("HTTP: starting %d worker threads\n", rpcThreads); - threadHTTP = boost::thread(boost::bind(&ThreadHTTP, eventBase, eventHTTP)); + std::packaged_task task(ThreadHTTP); + threadResult = task.get_future(); + threadHTTP = boost::thread(std::bind(std::move(task), eventBase, eventHTTP)); for (int i = 0; i < rpcThreads; i++) boost::thread(boost::bind(&HTTPWorkQueueRun, workQueue)); @@ -482,15 +487,11 @@ void StopHTTPServer() // master that appears to be solved, so in the future that solution // could be used again (if desirable). // (see discussion in https://github.com/bitcoin/bitcoin/pull/6990) -#if BOOST_VERSION >= 105000 - if (!threadHTTP.try_join_for(boost::chrono::milliseconds(2000))) { -#else - if (!threadHTTP.timed_join(boost::posix_time::milliseconds(2000))) { -#endif + if (threadResult.valid() && threadResult.wait_for(std::chrono::milliseconds(2000)) == std::future_status::timeout) { LogPrintf("HTTP event loop did not exit within allotted time, sending loopbreak\n"); event_base_loopbreak(eventBase); - threadHTTP.join(); } + threadHTTP.join(); } if (eventHTTP) { evhttp_free(eventHTTP); -- cgit v1.2.3 From d3773ca9aeb0d2f12dc0c5a0726778050c8cb455 Mon Sep 17 00:00:00 2001 From: Cory Fields Date: Thu, 28 Jul 2016 18:21:00 -0400 Subject: httpserver: explicitly detach worker threads When using std::thread in place of boost::thread, letting the threads destruct results in a std::terminate. According to the docs, the same thing should be be happening in later boost versions: http://www.boost.org/doc/libs/1_55_0/doc/html/thread/thread_management.html#thread.thread_management.thread.destructor I'm unsure why this hasn't blown up already, but explicitly detaching can't hurt. --- src/httpserver.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/httpserver.cpp') diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 7150f96ed..8d0d3c158 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -451,8 +451,10 @@ bool StartHTTPServer() threadResult = task.get_future(); threadHTTP = boost::thread(std::bind(std::move(task), eventBase, eventHTTP)); - for (int i = 0; i < rpcThreads; i++) - boost::thread(boost::bind(&HTTPWorkQueueRun, workQueue)); + for (int i = 0; i < rpcThreads; i++) { + boost::thread rpc_worker(HTTPWorkQueueRun, workQueue); + rpc_worker.detach(); + } return true; } -- cgit v1.2.3 From 7e87033447149e54d9b5ab2f90ad3a7ed094d784 Mon Sep 17 00:00:00 2001 From: Cory Fields Date: Thu, 28 Jul 2016 18:31:25 -0400 Subject: httpserver: replace boost threads with std along with mutex/condvar/bind/etc. httpserver handles its own interruption, so there's no reason not to use std threading. While we're at it, may as well kill the BOOST_FOREACH's as well. --- src/httpserver.cpp | 39 ++++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 21 deletions(-) (limited to 'src/httpserver.cpp') diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 8d0d3c158..be7a6a1dd 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -35,9 +35,6 @@ #endif #endif -#include // for to_lower() -#include - /** Maximum size of http request (request line + headers) */ static const size_t MAX_HEADERS_SIZE = 8192; @@ -69,8 +66,8 @@ class WorkQueue { private: /** Mutex protects entire object */ - CWaitableCriticalSection cs; - CConditionVariable cond; + std::mutex cs; + std::condition_variable cond; std::deque> queue; bool running; size_t maxDepth; @@ -83,12 +80,12 @@ private: WorkQueue &wq; ThreadCounter(WorkQueue &w): wq(w) { - boost::lock_guard lock(wq.cs); + std::lock_guard lock(wq.cs); wq.numThreads += 1; } ~ThreadCounter() { - boost::lock_guard lock(wq.cs); + std::lock_guard lock(wq.cs); wq.numThreads -= 1; wq.cond.notify_all(); } @@ -109,7 +106,7 @@ public: /** Enqueue a work item */ bool Enqueue(WorkItem* item) { - boost::unique_lock lock(cs); + std::unique_lock lock(cs); if (queue.size() >= maxDepth) { return false; } @@ -124,7 +121,7 @@ public: while (running) { std::unique_ptr i; { - boost::unique_lock lock(cs); + std::unique_lock lock(cs); while (running && queue.empty()) cond.wait(lock); if (!running) @@ -138,14 +135,14 @@ public: /** Interrupt and exit loops */ void Interrupt() { - boost::unique_lock lock(cs); + std::unique_lock lock(cs); running = false; cond.notify_all(); } /** Wait for worker threads to exit */ void WaitExit() { - boost::unique_lock lock(cs); + std::unique_lock lock(cs); while (numThreads > 0) cond.wait(lock); } @@ -153,7 +150,7 @@ public: /** Return current depth of queue */ size_t Depth() { - boost::unique_lock lock(cs); + std::unique_lock lock(cs); return queue.size(); } }; @@ -190,7 +187,7 @@ static bool ClientAllowed(const CNetAddr& netaddr) { if (!netaddr.IsValid()) return false; - BOOST_FOREACH (const CSubNet& subnet, rpc_allow_subnets) + for(const CSubNet& subnet : rpc_allow_subnets) if (subnet.Match(netaddr)) return true; return false; @@ -204,7 +201,7 @@ static bool InitHTTPAllowList() rpc_allow_subnets.push_back(CSubNet("::1")); // always allow IPv6 localhost if (mapMultiArgs.count("-rpcallowip")) { const std::vector& vAllow = mapMultiArgs["-rpcallowip"]; - BOOST_FOREACH (std::string strAllow, vAllow) { + for (std::string strAllow : vAllow) { CSubNet subnet(strAllow); if (!subnet.IsValid()) { uiInterface.ThreadSafeMessageBox( @@ -216,7 +213,7 @@ static bool InitHTTPAllowList() } } std::string strAllowed; - BOOST_FOREACH (const CSubNet& subnet, rpc_allow_subnets) + for (const CSubNet& subnet : rpc_allow_subnets) strAllowed += subnet.ToString() + " "; LogPrint("http", "Allowing HTTP connections from: %s\n", strAllowed); return true; @@ -439,7 +436,7 @@ bool InitHTTPServer() return true; } -boost::thread threadHTTP; +std::thread threadHTTP; std::future threadResult; bool StartHTTPServer() @@ -449,10 +446,10 @@ bool StartHTTPServer() LogPrintf("HTTP: starting %d worker threads\n", rpcThreads); std::packaged_task task(ThreadHTTP); threadResult = task.get_future(); - threadHTTP = boost::thread(std::bind(std::move(task), eventBase, eventHTTP)); + threadHTTP = std::thread(std::move(task), eventBase, eventHTTP); for (int i = 0; i < rpcThreads; i++) { - boost::thread rpc_worker(HTTPWorkQueueRun, workQueue); + std::thread rpc_worker(HTTPWorkQueueRun, workQueue); rpc_worker.detach(); } return true; @@ -463,7 +460,7 @@ void InterruptHTTPServer() LogPrint("http", "Interrupting HTTP server\n"); if (eventHTTP) { // Unlisten sockets - BOOST_FOREACH (evhttp_bound_socket *socket, boundSockets) { + for (evhttp_bound_socket *socket : boundSockets) { evhttp_del_accept_socket(eventHTTP, socket); } // Reject requests on current connections @@ -520,7 +517,7 @@ static void httpevent_callback_fn(evutil_socket_t, short, void* data) delete self; } -HTTPEvent::HTTPEvent(struct event_base* base, bool deleteWhenTriggered, const boost::function& handler): +HTTPEvent::HTTPEvent(struct event_base* base, bool deleteWhenTriggered, const std::function& handler): deleteWhenTriggered(deleteWhenTriggered), handler(handler) { ev = event_new(base, -1, 0, httpevent_callback_fn, this); @@ -602,7 +599,7 @@ void HTTPRequest::WriteReply(int nStatus, const std::string& strReply) assert(evb); evbuffer_add(evb, strReply.data(), strReply.size()); HTTPEvent* ev = new HTTPEvent(eventBase, true, - boost::bind(evhttp_send_reply, req, nStatus, (const char*)NULL, (struct evbuffer *)NULL)); + std::bind(evhttp_send_reply, req, nStatus, (const char*)NULL, (struct evbuffer *)NULL)); ev->trigger(0); replySent = true; req = 0; // transferred back to main thread -- cgit v1.2.3 From f96c7c4d91f3c09d26658bc9c15aa561689fa2d4 Mon Sep 17 00:00:00 2001 From: Cory Fields Date: Tue, 31 May 2016 13:51:11 -0400 Subject: net: Split resolving out of CService --- src/httpserver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/httpserver.cpp') diff --git a/src/httpserver.cpp b/src/httpserver.cpp index be7a6a1dd..57aa134b3 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -614,7 +614,7 @@ CService HTTPRequest::GetPeer() const char* address = ""; uint16_t port = 0; evhttp_connection_get_peer(con, (char**)&address, &port); - peer = CService(address, port); + LookupNumeric(address, peer, port); } return peer; } -- cgit v1.2.3 From b6c3ff3daed432e71d1c5bfe2357c4bb9fdc862a Mon Sep 17 00:00:00 2001 From: Cory Fields Date: Tue, 31 May 2016 15:50:24 -0400 Subject: net: Split resolving out of CSubNet --- src/httpserver.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'src/httpserver.cpp') diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 57aa134b3..2c5bc2c79 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -197,12 +197,17 @@ static bool ClientAllowed(const CNetAddr& netaddr) static bool InitHTTPAllowList() { rpc_allow_subnets.clear(); - rpc_allow_subnets.push_back(CSubNet("127.0.0.0/8")); // always allow IPv4 local subnet - rpc_allow_subnets.push_back(CSubNet("::1")); // always allow IPv6 localhost + CNetAddr localv4; + CNetAddr localv6; + LookupHost("127.0.0.1", localv4, false); + LookupHost("::1", localv6, false); + rpc_allow_subnets.push_back(CSubNet(localv4, 8)); // always allow IPv4 local subnet + rpc_allow_subnets.push_back(CSubNet(localv6)); // always allow IPv6 localhost if (mapMultiArgs.count("-rpcallowip")) { const std::vector& vAllow = mapMultiArgs["-rpcallowip"]; for (std::string strAllow : vAllow) { - CSubNet subnet(strAllow); + CSubNet subnet; + LookupSubNet(strAllow.c_str(), subnet); if (!subnet.IsValid()) { uiInterface.ThreadSafeMessageBox( 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), -- cgit v1.2.3 From ff8d279a78fef40224665353624771cd970c6095 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Jan=C3=ADk?= Date: Fri, 27 May 2016 08:00:56 +0200 Subject: Do not shadow member variables --- src/httpserver.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'src/httpserver.cpp') diff --git a/src/httpserver.cpp b/src/httpserver.cpp index be7a6a1dd..64ccb263e 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -42,8 +42,8 @@ static const size_t MAX_HEADERS_SIZE = 8192; class HTTPWorkItem : public HTTPClosure { public: - HTTPWorkItem(std::unique_ptr req, const std::string &path, const HTTPRequestHandler& func): - req(std::move(req)), path(path), func(func) + HTTPWorkItem(std::unique_ptr _req, const std::string &_path, const HTTPRequestHandler& _func): + req(std::move(_req)), path(_path), func(_func) { } void operator()() @@ -92,8 +92,8 @@ private: }; public: - WorkQueue(size_t maxDepth) : running(true), - maxDepth(maxDepth), + WorkQueue(size_t _maxDepth) : running(true), + maxDepth(_maxDepth), numThreads(0) { } @@ -158,8 +158,8 @@ public: struct HTTPPathHandler { HTTPPathHandler() {} - HTTPPathHandler(std::string prefix, bool exactMatch, HTTPRequestHandler handler): - prefix(prefix), exactMatch(exactMatch), handler(handler) + HTTPPathHandler(std::string _prefix, bool _exactMatch, HTTPRequestHandler _handler): + prefix(_prefix), exactMatch(_exactMatch), handler(_handler) { } std::string prefix; @@ -517,8 +517,8 @@ static void httpevent_callback_fn(evutil_socket_t, short, void* data) delete self; } -HTTPEvent::HTTPEvent(struct event_base* base, bool deleteWhenTriggered, const std::function& handler): - deleteWhenTriggered(deleteWhenTriggered), handler(handler) +HTTPEvent::HTTPEvent(struct event_base* base, bool _deleteWhenTriggered, const std::function& _handler): + deleteWhenTriggered(_deleteWhenTriggered), handler(_handler) { ev = event_new(base, -1, 0, httpevent_callback_fn, this); assert(ev); @@ -534,7 +534,7 @@ void HTTPEvent::trigger(struct timeval* tv) else evtimer_add(ev, tv); // trigger after timeval passed } -HTTPRequest::HTTPRequest(struct evhttp_request* req) : req(req), +HTTPRequest::HTTPRequest(struct evhttp_request* _req) : req(_req), replySent(false) { } -- cgit v1.2.3 From 8945384bca00f74ba85c98a52925c254c49025a5 Mon Sep 17 00:00:00 2001 From: Cory Fields Date: Thu, 4 Aug 2016 16:37:49 -0400 Subject: net: Have LookupNumeric return a CService directly Also fix up a few small issues: - Lookup with "badip:port" now sets the port to 0 - Don't allow assert to have side-effects --- src/httpserver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/httpserver.cpp') diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 2c5bc2c79..2bb4be564 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -619,7 +619,7 @@ CService HTTPRequest::GetPeer() const char* address = ""; uint16_t port = 0; evhttp_connection_get_peer(con, (char**)&address, &port); - LookupNumeric(address, peer, port); + peer = LookupNumeric(address, port); } return peer; } -- cgit v1.2.3 From 9e9d644f5178da8b5743ea3ddc3b779a2c8d1cd4 Mon Sep 17 00:00:00 2001 From: Cory Fields Date: Fri, 12 Aug 2016 14:21:50 -0400 Subject: net: fixup nits --- src/httpserver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/httpserver.cpp') diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 2bb4be564..f921305fc 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -201,7 +201,7 @@ static bool InitHTTPAllowList() CNetAddr localv6; LookupHost("127.0.0.1", localv4, false); LookupHost("::1", localv6, false); - rpc_allow_subnets.push_back(CSubNet(localv4, 8)); // always allow IPv4 local subnet + rpc_allow_subnets.push_back(CSubNet(localv4, 8)); // always allow IPv4 local subnet rpc_allow_subnets.push_back(CSubNet(localv6)); // always allow IPv6 localhost if (mapMultiArgs.count("-rpcallowip")) { const std::vector& vAllow = mapMultiArgs["-rpcallowip"]; -- cgit v1.2.3 From 2b5f085ad11b4b354f48d77e66698fa386c8abbd Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Tue, 29 Nov 2016 16:50:49 -0800 Subject: Fix non-const mapMultiArgs[] access after init. Swap mapMultiArgs for a const-reference to a _mapMultiArgs which is only accessed in util.cpp --- src/httpserver.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/httpserver.cpp') diff --git a/src/httpserver.cpp b/src/httpserver.cpp index b296b2850..2d25168a1 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -204,7 +204,7 @@ static bool InitHTTPAllowList() rpc_allow_subnets.push_back(CSubNet(localv4, 8)); // always allow IPv4 local subnet rpc_allow_subnets.push_back(CSubNet(localv6)); // always allow IPv6 localhost if (mapMultiArgs.count("-rpcallowip")) { - const std::vector& vAllow = mapMultiArgs["-rpcallowip"]; + const std::vector& vAllow = mapMultiArgs.at("-rpcallowip"); for (std::string strAllow : vAllow) { CSubNet subnet; LookupSubNet(strAllow.c_str(), subnet); @@ -328,8 +328,8 @@ static bool HTTPBindAddresses(struct evhttp* http) if (mapArgs.count("-rpcbind")) { LogPrintf("WARNING: option -rpcbind was ignored because -rpcallowip was not specified, refusing to allow everyone to connect\n"); } - } else if (mapArgs.count("-rpcbind")) { // Specific bind address - const std::vector& vbind = mapMultiArgs["-rpcbind"]; + } else if (mapMultiArgs.count("-rpcbind")) { // Specific bind address + const std::vector& vbind = mapMultiArgs.at("-rpcbind"); for (std::vector::const_iterator i = vbind.begin(); i != vbind.end(); ++i) { int port = defaultPort; std::string host; -- cgit v1.2.3 From 0cf86a6678413aa03e765a7133f048df4001ff4c Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Tue, 29 Nov 2016 17:51:30 -0800 Subject: Introduce (and use) an IsArgSet accessor method --- src/httpserver.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/httpserver.cpp') diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 2d25168a1..257390074 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -322,10 +322,10 @@ static bool HTTPBindAddresses(struct evhttp* http) std::vector > endpoints; // Determine what addresses to bind to - if (!mapArgs.count("-rpcallowip")) { // Default to loopback if not allowing external IPs + if (!IsArgSet("-rpcallowip")) { // Default to loopback if not allowing external IPs endpoints.push_back(std::make_pair("::1", defaultPort)); endpoints.push_back(std::make_pair("127.0.0.1", defaultPort)); - if (mapArgs.count("-rpcbind")) { + if (IsArgSet("-rpcbind")) { LogPrintf("WARNING: option -rpcbind was ignored because -rpcallowip was not specified, refusing to allow everyone to connect\n"); } } else if (mapMultiArgs.count("-rpcbind")) { // Specific bind address -- cgit v1.2.3 From 27765b6403cece54320374b37afb01a0cfe571c3 Mon Sep 17 00:00:00 2001 From: isle2983 Date: Sat, 31 Dec 2016 11:01:21 -0700 Subject: Increment MIT Licence copyright header year on files modified in 2016 Edited via: $ contrib/devtools/copyright_header.py update . --- src/httpserver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/httpserver.cpp') diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 257390074..1692b43f2 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2015 The Bitcoin Core developers +// Copyright (c) 2015-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -- cgit v1.2.3 From 7b2d96b634f9fd283480caf3bece56138d0587e3 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Fri, 3 Feb 2017 13:48:48 -0500 Subject: Access WorkQueue::running only within the cs lock. This removes a "race" between Interrupt() and Run(), though it should not effect any of our supported platforms. --- src/httpserver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/httpserver.cpp') diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 1692b43f2..e1763c6ad 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -118,7 +118,7 @@ public: void Run() { ThreadCounter count(*this); - while (running) { + while (true) { std::unique_ptr i; { std::unique_lock lock(cs); -- cgit v1.2.3 From 148a2aca05fe98031bcf857fa186d792c507090c Mon Sep 17 00:00:00 2001 From: Ross Nicoll Date: Mon, 27 Jul 2015 16:35:30 +0100 Subject: Introduce basic Dogecoin branding --- src/httpserver.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/httpserver.cpp') diff --git a/src/httpserver.cpp b/src/httpserver.cpp index e1763c6ad..dba952730 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -307,7 +307,7 @@ static void http_reject_request_cb(struct evhttp_request* req, void*) /** Event dispatcher thread */ static bool ThreadHTTP(struct event_base* base, struct evhttp* http) { - RenameThread("bitcoin-http"); + RenameThread("dogecoin-http"); LogPrint("http", "Entering http event loop\n"); event_base_dispatch(base); // Event loop will be interrupted by InterruptHTTPServer() @@ -357,7 +357,7 @@ static bool HTTPBindAddresses(struct evhttp* http) /** Simple wrapper to set thread name and run work queue */ static void HTTPWorkQueueRun(WorkQueue* queue) { - RenameThread("bitcoin-httpworker"); + RenameThread("dogecoin-httpworker"); queue->Run(); } -- cgit v1.2.3