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