Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion api/envoy/api/v2/core/health_check.proto
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,9 @@ message HealthCheck {
string service_name = 5;

// Specifies a list of HTTP headers that should be added to each request that is sent to the
// health checked cluster.
// health checked cluster. For more information, including details on header value syntax, see
// the documentation on :ref:`custom request headers
// <config_http_conn_man_headers_custom_request_headers>`.
repeated core.HeaderValueOption request_headers_to_add = 6;

// If set, health checks will be made using http/2.
Expand Down
2 changes: 2 additions & 0 deletions docs/root/intro/version_history.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ Version history
* health check: added support for :ref:`custom health check <envoy_api_field_core.HealthCheck.custom_health_check>`.
* health check: added support for :ref:`specifying jitter as a percentage <envoy_api_field_core.HealthCheck.interval_jitter_percent>`.
* health_check: added support for :ref:`health check event logging <arch_overview_health_check_logging>`.
* health_check: added support for specifying :ref:`custom request headers <config_http_conn_man_headers_custom_request_headers>`
to HTTP health checker requests.
* http: added support for a per-stream idle timeout. This applies at both :ref:`connection manager
<envoy_api_field_config.filter.network.http_connection_manager.v2.HttpConnectionManager.stream_idle_timeout>`
and :ref:`per-route granularity <envoy_api_field_route.RouteAction.idle_timeout>`. The timeout
Expand Down
22 changes: 14 additions & 8 deletions source/common/upstream/health_checker_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "common/config/well_known_names.h"
#include "common/grpc/common.h"
#include "common/http/header_map_impl.h"
#include "common/network/address_impl.h"
#include "common/router/router.h"
#include "common/upstream/host_utility.h"

Expand Down Expand Up @@ -98,7 +99,13 @@ HttpHealthCheckerImpl::HttpHealthCheckerImpl(const Cluster& cluster,

HttpHealthCheckerImpl::HttpActiveHealthCheckSession::HttpActiveHealthCheckSession(
HttpHealthCheckerImpl& parent, const HostSharedPtr& host)
: ActiveHealthCheckSession(parent, host), parent_(parent) {}
: ActiveHealthCheckSession(parent, host), parent_(parent),
hostname_(parent_.host_value_.empty() ? parent_.cluster_.info()->name()
: parent_.host_value_),
protocol_(parent_.codec_client_type_ == Http::CodecClient::Type::HTTP1
? Http::Protocol::Http11
: Http::Protocol::Http2),
local_address_(std::make_shared<Network::Address::Ipv4Instance>("127.0.0.1")) {}

HttpHealthCheckerImpl::HttpActiveHealthCheckSession::~HttpActiveHealthCheckSession() {
if (client_) {
Expand Down Expand Up @@ -127,9 +134,6 @@ void HttpHealthCheckerImpl::HttpActiveHealthCheckSession::onEvent(Network::Conne
}
}

const RequestInfo::RequestInfoImpl
HttpHealthCheckerImpl::HttpActiveHealthCheckSession::REQUEST_INFO;

void HttpHealthCheckerImpl::HttpActiveHealthCheckSession::onInterval() {
if (!client_) {
Upstream::Host::CreateConnectionData conn =
Expand All @@ -144,13 +148,15 @@ void HttpHealthCheckerImpl::HttpActiveHealthCheckSession::onInterval() {

Http::HeaderMapImpl request_headers{
{Http::Headers::get().Method, "GET"},
{Http::Headers::get().Host,
parent_.host_value_.empty() ? parent_.cluster_.info()->name() : parent_.host_value_},
{Http::Headers::get().Host, hostname_},
{Http::Headers::get().Path, parent_.path_},
{Http::Headers::get().UserAgent, Http::Headers::get().UserAgentValues.EnvoyHealthChecker}};
Router::FilterUtility::setUpstreamScheme(request_headers, *parent_.cluster_.info());

parent_.request_headers_parser_->evaluateHeaders(request_headers, REQUEST_INFO);
RequestInfo::RequestInfoImpl request_info(protocol_);
request_info.setDownstreamLocalAddress(local_address_);
request_info.setDownstreamRemoteAddress(local_address_);
request_info.onUpstreamHostSelected(host_);
parent_.request_headers_parser_->evaluateHeaders(request_headers, request_info);
request_encoder_->encodeHeaders(request_headers, true);
request_encoder_ = nullptr;
}
Expand Down
5 changes: 3 additions & 2 deletions source/common/upstream/health_checker_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,13 +90,14 @@ class HttpHealthCheckerImpl : public HealthCheckerImplBase {
HttpActiveHealthCheckSession& parent_;
};

static const RequestInfo::RequestInfoImpl REQUEST_INFO;

ConnectionCallbackImpl connection_callback_impl_{*this};
HttpHealthCheckerImpl& parent_;
Http::CodecClientPtr client_;
Http::StreamEncoder* request_encoder_{};
Http::HeaderMapPtr response_headers_;
const std::string& hostname_;
const Http::Protocol protocol_;
Network::Address::InstanceConstSharedPtr local_address_;
bool expect_reset_{};
};

Expand Down
90 changes: 71 additions & 19 deletions test/common/upstream/health_checker_impl_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,24 @@ class HttpHealthCheckerImplTest : public testing::Test {
key: user-agent
value: CoolEnvoy/HC
append: false
- header:
key: x-protocol
value: "%PROTOCOL%"
- header:
key: x-upstream-metadata
value: "%UPSTREAM_METADATA([\"namespace\", \"key\"])%"
- header:
key: x-downstream-remote-address-without-port
value: "%DOWNSTREAM_REMOTE_ADDRESS_WITHOUT_PORT%"
- header:
key: x-downstream-local-address
value: "%DOWNSTREAM_LOCAL_ADDRESS%"
- header:
key: x-downstream-local-address-without-port
value: "%DOWNSTREAM_LOCAL_ADDRESS_WITHOUT_PORT%"
- header:
key: x-start-time
value: "%START_TIME(%s.%9f)%"
)EOF";

health_checker_.reset(new TestHttpHealthCheckerImpl(*cluster_, parseHealthCheckFromV2Yaml(yaml),
Expand Down Expand Up @@ -697,36 +715,68 @@ TEST_F(HttpHealthCheckerImplTest, SuccessServiceCheckWithCustomHostValue) {
}

TEST_F(HttpHealthCheckerImplTest, SuccessServiceCheckWithAdditionalHeaders) {
const Http::LowerCaseString headerOk("x-envoy-ok");
const Http::LowerCaseString headerCool("x-envoy-cool");
const Http::LowerCaseString headerAwesome("x-envoy-awesome");

const std::string valueOk = "ok";
const std::string valueCool = "cool";
const std::string valueAwesome = "awesome";

const std::string valueUserAgent = "CoolEnvoy/HC";
const Http::LowerCaseString header_ok("x-envoy-ok");
const Http::LowerCaseString header_cool("x-envoy-cool");
const Http::LowerCaseString header_awesome("x-envoy-awesome");
const Http::LowerCaseString upstream_metadata("x-upstream-metadata");
const Http::LowerCaseString protocol("x-protocol");
const Http::LowerCaseString downstream_remote_address_without_port(
"x-downstream-remote-address-without-port");
const Http::LowerCaseString downstream_local_address("x-downstream-local-address");
const Http::LowerCaseString downstream_local_address_without_port(
"x-downstream-local-address-without-port");
const Http::LowerCaseString start_time("x-start-time");

const std::string value_ok = "ok";
const std::string value_cool = "cool";
const std::string value_awesome = "awesome";

const std::string value_user_agent = "CoolEnvoy/HC";
const std::string value_upstream_metadata = "value";
const std::string value_protocol = "HTTP/1.1";
const std::string value_downstream_remote_address_without_port = "127.0.0.1";
const std::string value_downstream_local_address = "127.0.0.1:0";
const std::string value_downstream_local_address_without_port = "127.0.0.1";

setupServiceValidationWithAdditionalHeaders();
// requires non-empty `service_name` in config.
EXPECT_CALL(runtime_.snapshot_, featureEnabled("health_check.verify_cluster", 100))
.WillOnce(Return(true));

EXPECT_CALL(*this, onHostStatus(_, HealthTransition::Unchanged)).Times(1);

auto metadata = TestUtility::parseYaml<envoy::api::v2::core::Metadata>(
R"EOF(
filter_metadata:
namespace:
key: value
)EOF");

std::string current_start_time;
cluster_->prioritySet().getMockHostSet(0)->hosts_ = {
makeTestHost(cluster_->info_, "tcp://127.0.0.1:80")};
makeTestHost(cluster_->info_, "tcp://127.0.0.1:80", metadata)};
cluster_->info_->stats().upstream_cx_total_.inc();
expectSessionCreate();
expectStreamCreate(0);
EXPECT_CALL(*test_sessions_[0]->timeout_timer_, enableTimer(_));
EXPECT_CALL(test_sessions_[0]->request_encoder_, encodeHeaders(_, true))
.WillOnce(Invoke([&](const Http::HeaderMap& headers, bool) {
EXPECT_EQ(headers.get(headerOk)->value().c_str(), valueOk);
EXPECT_EQ(headers.get(headerCool)->value().c_str(), valueCool);
EXPECT_EQ(headers.get(headerAwesome)->value().c_str(), valueAwesome);

EXPECT_EQ(headers.UserAgent()->value().c_str(), valueUserAgent);
.WillRepeatedly(Invoke([&](const Http::HeaderMap& headers, bool) {
EXPECT_EQ(headers.get(header_ok)->value().c_str(), value_ok);
EXPECT_EQ(headers.get(header_cool)->value().c_str(), value_cool);
EXPECT_EQ(headers.get(header_awesome)->value().c_str(), value_awesome);

EXPECT_EQ(headers.UserAgent()->value().c_str(), value_user_agent);
EXPECT_EQ(headers.get(upstream_metadata)->value().c_str(), value_upstream_metadata);

EXPECT_EQ(headers.get(protocol)->value().c_str(), value_protocol);
EXPECT_EQ(headers.get(downstream_remote_address_without_port)->value().c_str(),
value_downstream_remote_address_without_port);
EXPECT_EQ(headers.get(downstream_local_address)->value().c_str(),
value_downstream_local_address);
EXPECT_EQ(headers.get(downstream_local_address_without_port)->value().c_str(),
value_downstream_local_address_without_port);

EXPECT_NE(headers.get(start_time)->value().c_str(), current_start_time);
current_start_time = headers.get(start_time)->value().c_str();
}));
health_checker_->start();

Expand All @@ -738,6 +788,10 @@ TEST_F(HttpHealthCheckerImplTest, SuccessServiceCheckWithAdditionalHeaders) {
absl::optional<std::string> health_checked_cluster("locations-production-iad");
respond(0, "200", false, true, false, health_checked_cluster);
EXPECT_TRUE(cluster_->prioritySet().getMockHostSet(0)->hosts_[0]->healthy());

EXPECT_CALL(*test_sessions_[0]->timeout_timer_, enableTimer(_));
expectStreamCreate(0);
test_sessions_[0]->interval_timer_->callback_();
}

TEST_F(HttpHealthCheckerImplTest, ServiceDoesNotMatchFail) {
Expand Down Expand Up @@ -775,8 +829,6 @@ TEST_F(HttpHealthCheckerImplTest, ServiceNotPresentInResponseFail) {

EXPECT_CALL(*this, onHostStatus(_, HealthTransition::Changed)).Times(1);
EXPECT_CALL(*event_logger_, logEjectUnhealthy(_, _, _));
;
;

cluster_->prioritySet().getMockHostSet(0)->hosts_ = {
makeTestHost(cluster_->info_, "tcp://127.0.0.1:80")};
Expand Down