Clear headers on error during handler execution (#5122)

This commit is contained in:
Amaury Chamayou 2023-03-20 19:18:12 +00:00 коммит произвёл GitHub
Родитель 158bf42106
Коммит 159f9b1dbc
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
4 изменённых файлов: 22 добавлений и 5 удалений

Просмотреть файл

@ -137,6 +137,7 @@ namespace ccf
{ {
set_response_header(kv.first, kv.second); set_response_header(kv.first, kv.second);
} }
virtual void clear_response_headers() = 0;
virtual void set_response_trailer( virtual void set_response_trailer(
const std::string_view& name, const std::string_view& value) = 0; const std::string_view& name, const std::string_view& value) = 0;

Просмотреть файл

@ -250,6 +250,11 @@ namespace http
response_headers[std::string(name)] = value; response_headers[std::string(name)] = value;
} }
virtual void clear_response_headers() override
{
response_headers.clear();
}
virtual void set_response_trailer( virtual void set_response_trailer(
const std::string_view& name, const std::string_view& value) override const std::string_view& name, const std::string_view& value) override
{ {

Просмотреть файл

@ -517,6 +517,7 @@ namespace ccf
ctx->get_request_verb().c_str(), ctx->get_request_verb().c_str(),
ctx->get_request_path()); ctx->get_request_path());
ctx->clear_response_headers();
ctx->set_error( ctx->set_error(
HTTP_STATUS_INTERNAL_SERVER_ERROR, HTTP_STATUS_INTERNAL_SERVER_ERROR,
ccf::errors::InternalError, ccf::errors::InternalError,
@ -566,6 +567,7 @@ namespace ccf
catch (const std::exception& e) catch (const std::exception& e)
{ {
// run default handler to set transaction id in header // run default handler to set transaction id in header
ctx->clear_response_headers();
ccf::endpoints::default_locally_committed_func( ccf::endpoints::default_locally_committed_func(
args, tx_id.value()); args, tx_id.value());
ctx->set_error( ctx->set_error(
@ -578,6 +580,7 @@ namespace ccf
catch (...) catch (...)
{ {
// run default handler to set transaction id in header // run default handler to set transaction id in header
ctx->clear_response_headers();
ccf::endpoints::default_locally_committed_func( ccf::endpoints::default_locally_committed_func(
args, tx_id.value()); args, tx_id.value());
ctx->set_error( ctx->set_error(
@ -605,6 +608,7 @@ namespace ccf
case kv::CommitResult::FAIL_NO_REPLICATE: case kv::CommitResult::FAIL_NO_REPLICATE:
{ {
ctx->clear_response_headers();
ctx->set_error( ctx->set_error(
HTTP_STATUS_SERVICE_UNAVAILABLE, HTTP_STATUS_SERVICE_UNAVAILABLE,
ccf::errors::TransactionReplicationFailed, ccf::errors::TransactionReplicationFailed,
@ -624,12 +628,14 @@ namespace ccf
} }
catch (RpcException& e) catch (RpcException& e)
{ {
ctx->clear_response_headers();
ctx->set_error(std::move(e.error)); ctx->set_error(std::move(e.error));
update_metrics(ctx); update_metrics(ctx);
return; return;
} }
catch (const JsonParseError& e) catch (const JsonParseError& e)
{ {
ctx->clear_response_headers();
ctx->set_error( ctx->set_error(
HTTP_STATUS_BAD_REQUEST, ccf::errors::InvalidInput, e.describe()); HTTP_STATUS_BAD_REQUEST, ccf::errors::InvalidInput, e.describe());
update_metrics(ctx); update_metrics(ctx);
@ -637,6 +643,7 @@ namespace ccf
} }
catch (const nlohmann::json::exception& e) catch (const nlohmann::json::exception& e)
{ {
ctx->clear_response_headers();
ctx->set_error( ctx->set_error(
HTTP_STATUS_BAD_REQUEST, ccf::errors::InvalidInput, e.what()); HTTP_STATUS_BAD_REQUEST, ccf::errors::InvalidInput, e.what());
update_metrics(ctx); update_metrics(ctx);
@ -653,6 +660,7 @@ namespace ccf
} }
catch (const std::exception& e) catch (const std::exception& e)
{ {
ctx->clear_response_headers();
ctx->set_error( ctx->set_error(
HTTP_STATUS_INTERNAL_SERVER_ERROR, HTTP_STATUS_INTERNAL_SERVER_ERROR,
ccf::errors::InternalError, ccf::errors::InternalError,
@ -662,6 +670,7 @@ namespace ccf
} }
} // end of while loop } // end of while loop
ctx->clear_response_headers();
ctx->set_error( ctx->set_error(
HTTP_STATUS_SERVICE_UNAVAILABLE, HTTP_STATUS_SERVICE_UNAVAILABLE,
ccf::errors::TransactionCommitAttemptsExceedLimit, ccf::errors::TransactionCommitAttemptsExceedLimit,
@ -669,6 +678,7 @@ namespace ccf
"Transaction continued to conflict after {} attempts. Retry " "Transaction continued to conflict after {} attempts. Retry "
"later.", "later.",
max_attempts)); max_attempts));
update_metrics(ctx);
static constexpr size_t retry_after_seconds = 3; static constexpr size_t retry_after_seconds = 3;
ctx->set_response_header(http::headers::RETRY_AFTER, retry_after_seconds); ctx->set_response_header(http::headers::RETRY_AFTER, retry_after_seconds);

Просмотреть файл

@ -1491,6 +1491,7 @@ class TestConflictFrontend : public BaseTestFrontend
{ {
public: public:
using Values = kv::Map<size_t, size_t>; using Values = kv::Map<size_t, size_t>;
size_t execution_count = 0;
TestConflictFrontend(kv::Store& tables) : BaseTestFrontend(tables) TestConflictFrontend(kv::Store& tables) : BaseTestFrontend(tables)
{ {
@ -1502,8 +1503,6 @@ public:
.value()); // This header only exists in the context of .value()); // This header only exists in the context of
// this test // this test
static size_t execution_count = 0;
auto conflict_map = ctx.tx.template rw<Values>("test_values_conflict"); auto conflict_map = ctx.tx.template rw<Values>("test_values_conflict");
conflict_map->get(0); // Record a read dependency conflict_map->get(0); // Record a read dependency
@ -1547,6 +1546,7 @@ TEST_CASE("Retry on conflict")
INFO("Does not reach execution limit"); INFO("Does not reach execution limit");
{ {
frontend.execution_count = 0;
size_t retry_count = ccf_max_attempts - 1; size_t retry_count = ccf_max_attempts - 1;
req.set_header("test-retry-count", fmt::format("{}", retry_count)); req.set_header("test-retry-count", fmt::format("{}", retry_count));
auto serialized_call = req.build_request(); auto serialized_call = req.build_request();
@ -1564,6 +1564,7 @@ TEST_CASE("Retry on conflict")
INFO("Reaches execution limit"); INFO("Reaches execution limit");
{ {
frontend.execution_count = 0;
size_t retry_count = ccf_max_attempts + 1; size_t retry_count = ccf_max_attempts + 1;
req.set_header("test-retry-count", fmt::format("{}", retry_count)); req.set_header("test-retry-count", fmt::format("{}", retry_count));
auto serialized_call = req.build_request(); auto serialized_call = req.build_request();
@ -1573,10 +1574,10 @@ TEST_CASE("Retry on conflict")
auto response = parse_response(rpc_ctx->serialise_response()); auto response = parse_response(rpc_ctx->serialise_response());
CHECK(response.status == HTTP_STATUS_SERVICE_UNAVAILABLE); CHECK(response.status == HTTP_STATUS_SERVICE_UNAVAILABLE);
CHECK(response.headers["test-has-conflicted"] == "true"); // Response headers are cleared on error
CHECK( CHECK(
response.headers["test-execution-count"] == response.headers.find("test-has-conflicted") == response.headers.end());
fmt::format("{}", ccf_max_attempts)); CHECK(frontend.execution_count == ccf_max_attempts);
} }
} }