Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
5 changes: 5 additions & 0 deletions source/common/http/codec_client.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ class CodecClient : Logger::Loggable<Logger::Id::client>,
*/
uint64_t id() { return connection_->id(); }

/**
* @return the underlying codec protocol.
*/
Protocol protocol() { return codec_->protocol(); }

/**
* @return the underlying connection error.
*/
Expand Down
25 changes: 14 additions & 11 deletions source/common/http/http1/conn_pool.cc
Original file line number Diff line number Diff line change
Expand Up @@ -203,8 +203,8 @@ void ConnPoolImpl::onResponseComplete(ActiveClient& client) {
if (!client.stream_wrapper_->encode_complete_) {
ENVOY_CONN_LOG(debug, "response before request complete", *client.codec_client_);
onDownstreamReset(client);
} else if (client.stream_wrapper_->saw_close_header_ || client.codec_client_->remoteClosed()) {
ENVOY_CONN_LOG(debug, "saw upstream connection: close", *client.codec_client_);
} else if (client.stream_wrapper_->close_connection_ || client.codec_client_->remoteClosed()) {
ENVOY_CONN_LOG(debug, "saw upstream close connection", *client.codec_client_);
onDownstreamReset(client);
} else if (client.remaining_requests_ > 0 && --client.remaining_requests_ == 0) {
ENVOY_CONN_LOG(debug, "maximum requests per connection", *client.codec_client_);
Expand Down Expand Up @@ -273,17 +273,20 @@ ConnPoolImpl::StreamWrapper::~StreamWrapper() {
void ConnPoolImpl::StreamWrapper::onEncodeComplete() { encode_complete_ = true; }

void ConnPoolImpl::StreamWrapper::decodeHeaders(HeaderMapPtr&& headers, bool end_stream) {
if (headers->Connection() &&
absl::EqualsIgnoreCase(headers->Connection()->value().getStringView(),
Headers::get().ConnectionValues.Close)) {
saw_close_header_ = true;
if ((headers->Connection() &&
(absl::EqualsIgnoreCase(headers->Connection()->value().getStringView(),
Headers::get().ConnectionValues.Close))) ||
(parent_.codec_client_->protocol() == Protocol::Http10 &&
(!headers->Connection() ||
!absl::EqualsIgnoreCase(headers->Connection()->value().getStringView(),
Headers::get().ConnectionValues.KeepAlive)))) {
parent_.parent_.host_->cluster().stats().upstream_cx_close_notify_.inc();
}
if (!saw_close_header_ && headers->ProxyConnection() &&
absl::EqualsIgnoreCase(headers->ProxyConnection()->value().getStringView(),
Headers::get().ConnectionValues.Close)) {
saw_close_header_ = true;
close_connection_ = true;
} else if (headers->ProxyConnection() &&
Comment thread
jplevyak marked this conversation as resolved.
Outdated
(absl::EqualsIgnoreCase(headers->ProxyConnection()->value().getStringView(),
Headers::get().ConnectionValues.Close))) {
parent_.parent_.host_->cluster().stats().upstream_cx_close_notify_.inc();
close_connection_ = true;
}

StreamDecoderWrapper::decodeHeaders(std::move(headers), end_stream);
Expand Down
2 changes: 1 addition & 1 deletion source/common/http/http1/conn_pool.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ class ConnPoolImpl : public ConnectionPool::Instance, public ConnPoolImplBase {

ActiveClient& parent_;
bool encode_complete_{};
bool saw_close_header_{};
bool close_connection_{};
bool decode_complete_{};
};

Expand Down
72 changes: 60 additions & 12 deletions test/common/http/http1/conn_pool_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,18 @@ struct ActiveTestRequest {
}
}

void completeKeepAliveResponse(bool with_body) {
// Test additional metric writes also.
Http::HeaderMapPtr response_headers(new TestHeaderMapImpl{
{"connection", "keep-alive"}, {":status", "200"}, {"x-envoy-upstream-canary", "true"}});

inner_decoder_->decodeHeaders(std::move(response_headers), !with_body);
if (with_body) {
Buffer::OwnedImpl data;
inner_decoder_->decodeData(data, true);
}
}

void expectNewStream() {
EXPECT_CALL(*parent_.conn_pool_.test_clients_[client_index_].codec_, newStream(_))
.WillOnce(DoAll(SaveArgAddress(&inner_decoder_), ReturnRef(request_encoder_)));
Expand Down Expand Up @@ -269,13 +281,13 @@ TEST_F(Http1ConnPoolImplTest, VerifyBufferLimits) {
* Tests a request that generates a new connection, completes, and then a second request that uses
* the same connection.
*/
TEST_F(Http1ConnPoolImplTest, MultipleRequestAndResponse) {
TEST_F(Http1ConnPoolImplTest, Http10MultipleRequestAndResponse) {
Comment thread
jplevyak marked this conversation as resolved.
Outdated
InSequence s;

// Request 1 should kick off a new connection.
ActiveTestRequest r1(*this, 0, ActiveTestRequest::Type::CreateConnection);
r1.startRequest();
r1.completeResponse(false);
r1.completeKeepAliveResponse(false);

// Request 2 should not.
ActiveTestRequest r2(*this, 0, ActiveTestRequest::Type::Immediate);
Expand Down Expand Up @@ -432,7 +444,7 @@ TEST_F(Http1ConnPoolImplTest, DisconnectWhileBound) {
/**
* Test that we correctly handle reaching max connections.
*/
TEST_F(Http1ConnPoolImplTest, MaxConnections) {
TEST_F(Http1ConnPoolImplTest, Http10MaxConnections) {
InSequence s;

EXPECT_EQ(0U, cluster_->circuit_breakers_stats_.cx_open_.value());
Expand Down Expand Up @@ -470,12 +482,13 @@ TEST_F(Http1ConnPoolImplTest, MaxConnections) {
EXPECT_CALL(callbacks2.pool_ready_, ready());

callbacks.outer_encoder_->encodeHeaders(TestHeaderMapImpl{}, true);
Http::HeaderMapPtr response_headers(new TestHeaderMapImpl{{":status", "200"}});
Http::HeaderMapPtr response_headers(
new TestHeaderMapImpl{{":status", "200"}, {"connection", "keep-alive"}});
inner_decoder->decodeHeaders(std::move(response_headers), true);

conn_pool_.expectAndRunUpstreamReady();
callbacks2.outer_encoder_->encodeHeaders(TestHeaderMapImpl{}, true);
response_headers.reset(new TestHeaderMapImpl{{":status", "200"}});
response_headers.reset(new TestHeaderMapImpl{{":status", "200"}, {"connection", "keep-alive"}});
inner_decoder->decodeHeaders(std::move(response_headers), true);

// Cause the connection to go away.
Expand Down Expand Up @@ -520,7 +533,8 @@ TEST_F(Http1ConnPoolImplTest, ConnectionCloseWithoutHeader) {
conn_pool_.expectEnableUpstreamReady();

callbacks.outer_encoder_->encodeHeaders(TestHeaderMapImpl{}, true);
Http::HeaderMapPtr response_headers(new TestHeaderMapImpl{{":status", "200"}});
Http::HeaderMapPtr response_headers(
new TestHeaderMapImpl{{":status", "200"}, {"connection", "keep-alive"}});
inner_decoder->decodeHeaders(std::move(response_headers), true);

// Cause the connection to go away.
Expand All @@ -537,7 +551,7 @@ TEST_F(Http1ConnPoolImplTest, ConnectionCloseWithoutHeader) {
conn_pool_.test_clients_[0].connection_->raiseEvent(Network::ConnectionEvent::Connected);

callbacks2.outer_encoder_->encodeHeaders(TestHeaderMapImpl{}, true);
response_headers.reset(new TestHeaderMapImpl{{":status", "200"}});
response_headers.reset(new TestHeaderMapImpl{{":status", "200"}, {"connection", "keep-alive"}});
Comment thread
jplevyak marked this conversation as resolved.
Outdated
inner_decoder->decodeHeaders(std::move(response_headers), true);

EXPECT_CALL(conn_pool_, onClientDestroy());
Expand Down Expand Up @@ -611,6 +625,39 @@ TEST_F(Http1ConnPoolImplTest, ProxyConnectionCloseHeader) {
EXPECT_EQ(0U, cluster_->stats_.upstream_cx_destroy_with_active_rq_.value());
}

/**
* Test when upstream is HTTP/1.0 and does not send 'connection: keep-alive'
*/
TEST_F(Http1ConnPoolImplTest, Http10NoConnectionKeepAlive) {
InSequence s;

// Request 1 should kick off a new connection.
NiceMock<Http::MockStreamDecoder> outer_decoder;
ConnPoolCallbacks callbacks;
conn_pool_.expectClientCreate();
Http::ConnectionPool::Cancellable* handle = conn_pool_.newStream(outer_decoder, callbacks);

EXPECT_NE(nullptr, handle);

NiceMock<Http::MockStreamEncoder> request_encoder;
Http::StreamDecoder* inner_decoder;
EXPECT_CALL(*conn_pool_.test_clients_[0].codec_, newStream(_))
.WillOnce(DoAll(SaveArgAddress(&inner_decoder), ReturnRef(request_encoder)));
EXPECT_CALL(callbacks.pool_ready_, ready());

conn_pool_.test_clients_[0].connection_->raiseEvent(Network::ConnectionEvent::Connected);
callbacks.outer_encoder_->encodeHeaders(TestHeaderMapImpl{}, true);

// Response without 'connection: keep-alive' which should cause the connection to go away.
EXPECT_CALL(conn_pool_, onClientDestroy());
Http::HeaderMapPtr response_headers(
new TestHeaderMapImpl{{":protocol", "HTTP/1.0"}, {":status", "200"}});
inner_decoder->decodeHeaders(std::move(response_headers), true);
dispatcher_.clearDeferredDeleteList();

EXPECT_EQ(0U, cluster_->stats_.upstream_cx_destroy_with_active_rq_.value());
}

/**
* Test when we reach max requests per connection.
*/
Expand Down Expand Up @@ -638,15 +685,16 @@ TEST_F(Http1ConnPoolImplTest, MaxRequestsPerConnection) {

// Response with 'connection: close' which should cause the connection to go away.
EXPECT_CALL(conn_pool_, onClientDestroy());
Http::HeaderMapPtr response_headers(new TestHeaderMapImpl{{":status", "200"}});
Http::HeaderMapPtr response_headers(
new TestHeaderMapImpl{{":status", "200"}, {"connection", "keep-alive"}});
inner_decoder->decodeHeaders(std::move(response_headers), true);
dispatcher_.clearDeferredDeleteList();

EXPECT_EQ(0U, cluster_->stats_.upstream_cx_destroy_with_active_rq_.value());
EXPECT_EQ(1U, cluster_->stats_.upstream_cx_max_requests_.value());
}

TEST_F(Http1ConnPoolImplTest, ConcurrentConnections) {
TEST_F(Http1ConnPoolImplTest, Http10ConcurrentConnections) {
cluster_->resetResourceManager(2, 1024, 1024, 1, 1);
InSequence s;

Expand All @@ -662,13 +710,13 @@ TEST_F(Http1ConnPoolImplTest, ConcurrentConnections) {
conn_pool_.expectEnableUpstreamReady();
r3.expectNewStream();

r1.completeResponse(false);
r1.completeKeepAliveResponse(false);
conn_pool_.expectAndRunUpstreamReady();
r3.startRequest();
EXPECT_EQ(3U, cluster_->stats_.upstream_rq_total_.value());

r2.completeResponse(false);
r3.completeResponse(false);
r2.completeKeepAliveResponse(false);
r3.completeKeepAliveResponse(false);

// Disconnect both clients.
EXPECT_CALL(conn_pool_, onClientDestroy()).Times(2);
Expand Down