Merge pull request #7487 from Icinga/bugfix/http-api-shutdown-avoid-io

API: Avoid I/O on shutdown
This commit is contained in:
Michael Friedrich 2019-09-09 14:21:24 +02:00 committed by GitHub
commit 9e16502581
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 41 additions and 19 deletions

View File

@ -21,10 +21,12 @@
#include <limits> #include <limits>
#include <memory> #include <memory>
#include <stdexcept> #include <stdexcept>
#include <boost/asio/error.hpp>
#include <boost/asio/io_service.hpp> #include <boost/asio/io_service.hpp>
#include <boost/asio/spawn.hpp> #include <boost/asio/spawn.hpp>
#include <boost/beast/core.hpp> #include <boost/beast/core.hpp>
#include <boost/beast/http.hpp> #include <boost/beast/http.hpp>
#include <boost/system/error_code.hpp>
#include <boost/system/system_error.hpp> #include <boost/system/system_error.hpp>
#include <boost/thread/once.hpp> #include <boost/thread/once.hpp>
@ -88,13 +90,13 @@ void HttpServerConnection::Disconnect()
*/ */
boost::system::error_code ec; boost::system::error_code ec;
m_Stream->next_layer().async_shutdown(yc[ec]); m_CheckLivenessTimer.cancel();
m_Stream->lowest_layer().shutdown(m_Stream->lowest_layer().shutdown_both, ec);
m_Stream->lowest_layer().cancel(ec); m_Stream->lowest_layer().cancel(ec);
m_CheckLivenessTimer.cancel(); m_Stream->next_layer().async_shutdown(yc[ec]);
m_Stream->lowest_layer().shutdown(m_Stream->lowest_layer().shutdown_both, ec);
auto listener (ApiListener::GetInstance()); auto listener (ApiListener::GetInstance());
@ -141,11 +143,15 @@ bool EnsureValidHeaders(
boost::beast::flat_buffer& buf, boost::beast::flat_buffer& buf,
boost::beast::http::parser<true, boost::beast::http::string_body>& parser, boost::beast::http::parser<true, boost::beast::http::string_body>& parser,
boost::beast::http::response<boost::beast::http::string_body>& response, boost::beast::http::response<boost::beast::http::string_body>& response,
bool& shuttingDown,
boost::asio::yield_context& yc boost::asio::yield_context& yc
) )
{ {
namespace http = boost::beast::http; namespace http = boost::beast::http;
if (shuttingDown)
return false;
bool httpError = false; bool httpError = false;
String errorMsg; String errorMsg;
@ -154,16 +160,19 @@ bool EnsureValidHeaders(
http::async_read_header(stream, buf, parser, yc[ec]); http::async_read_header(stream, buf, parser, yc[ec]);
if (ec) { if (ec) {
if (ec == boost::asio::error::operation_aborted)
return false;
errorMsg = ec.message(); errorMsg = ec.message();
httpError = true; httpError = true;
} } else {
switch (parser.get().version()) {
switch (parser.get().version()) { case 10:
case 10: case 11:
case 11: break;
break; default:
default: errorMsg = "Unsupported HTTP version";
errorMsg = "Unsupported HTTP version"; }
} }
if (!errorMsg.IsEmpty() || httpError) { if (!errorMsg.IsEmpty() || httpError) {
@ -343,6 +352,7 @@ bool EnsureValidBody(
boost::beast::http::parser<true, boost::beast::http::string_body>& parser, boost::beast::http::parser<true, boost::beast::http::string_body>& parser,
ApiUser::Ptr& authenticatedUser, ApiUser::Ptr& authenticatedUser,
boost::beast::http::response<boost::beast::http::string_body>& response, boost::beast::http::response<boost::beast::http::string_body>& response,
bool& shuttingDown,
boost::asio::yield_context& yc boost::asio::yield_context& yc
) )
{ {
@ -385,11 +395,17 @@ bool EnsureValidBody(
parser.body_limit(maxSize); parser.body_limit(maxSize);
} }
if (shuttingDown)
return false;
boost::system::error_code ec; boost::system::error_code ec;
http::async_read(stream, buf, parser, yc[ec]); http::async_read(stream, buf, parser, yc[ec]);
if (ec) { if (ec) {
if (ec == boost::asio::error::operation_aborted)
return false;
/** /**
* Unfortunately there's no way to tell an HTTP protocol error * Unfortunately there's no way to tell an HTTP protocol error
* from an error on a lower layer: * from an error on a lower layer:
@ -443,6 +459,12 @@ bool ProcessRequest(
return false; return false;
} }
auto sysErr (dynamic_cast<const boost::system::system_error*>(&ex));
if (sysErr && sysErr->code() == boost::asio::error::operation_aborted) {
throw;
}
http::response<http::string_body> response; http::response<http::string_body> response;
HttpUtility::SendJsonError(response, nullptr, 500, "Unhandled exception" , DiagnosticInformation(ex)); HttpUtility::SendJsonError(response, nullptr, 500, "Unhandled exception" , DiagnosticInformation(ex));
@ -488,7 +510,7 @@ void HttpServerConnection::ProcessMessages(boost::asio::yield_context yc)
// Best practice is to always reset the buffer. // Best practice is to always reset the buffer.
buf = {}; buf = {};
if (!EnsureValidHeaders(*m_Stream, buf, parser, response, yc)) { if (!EnsureValidHeaders(*m_Stream, buf, parser, response, m_ShuttingDown, yc)) {
break; break;
} }
@ -535,7 +557,7 @@ void HttpServerConnection::ProcessMessages(boost::asio::yield_context yc)
// Best practice is to always reset the buffer. // Best practice is to always reset the buffer.
buf = {}; buf = {};
if (!EnsureValidBody(*m_Stream, buf, parser, authenticatedUser, response, yc)) { if (!EnsureValidBody(*m_Stream, buf, parser, authenticatedUser, response, m_ShuttingDown, yc)) {
break; break;
} }

View File

@ -224,14 +224,14 @@ void JsonRpcConnection::Disconnect()
*/ */
boost::system::error_code ec; boost::system::error_code ec;
m_Stream->next_layer().async_shutdown(yc[ec]); m_CheckLivenessTimer.cancel();
m_HeartbeatTimer.cancel();
m_Stream->lowest_layer().shutdown(m_Stream->lowest_layer().shutdown_both, ec);
m_Stream->lowest_layer().cancel(ec); m_Stream->lowest_layer().cancel(ec);
m_CheckLivenessTimer.cancel(); m_Stream->next_layer().async_shutdown(yc[ec]);
m_HeartbeatTimer.cancel();
m_Stream->lowest_layer().shutdown(m_Stream->lowest_layer().shutdown_both, ec);
} }
}); });
} }