Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

network: delayed conn close #4382

Merged
merged 21 commits into from
Sep 24, 2018
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
4d2426e
network: Support delayed close of downstream conns (#2929)
AndresGuedez Aug 31, 2018
94a835a
Add HTTP/2 integration tests.
AndresGuedez Sep 6, 2018
e2176aa
Kick CI
AndresGuedez Sep 10, 2018
3a67ecb
Adjust timeout values to prevent test flaking.
AndresGuedez Sep 10, 2018
30eb02e
Small cleanup and clarify comments.
AndresGuedez Sep 11, 2018
9ddf9d3
Add clarifying comments to unit tests.
AndresGuedez Sep 11, 2018
8e716eb
Disable bazel remote cache due to CI flaking.
AndresGuedez Sep 11, 2018
e71f798
Minor cleanup per style guidelines and best practices
AndresGuedez Sep 12, 2018
6130f28
Cleanup: comments and asserts/checks.
AndresGuedez Sep 13, 2018
890471e
Adjust integration test timeouts for flake resistance.
AndresGuedez Sep 13, 2018
aae3b52
Revert namespace comments erronously introduced.
AndresGuedez Sep 13, 2018
b8eb565
Kick CI
AndresGuedez Sep 13, 2018
6071d31
Change ASSERT to runtime check.
AndresGuedez Sep 13, 2018
36b6087
Improve delayed_close_timeout comment.
AndresGuedez Sep 17, 2018
d056d7a
Revert disablement of bazel remote caching.
AndresGuedez Sep 17, 2018
52c10d9
Merge remote-tracking branch 'upstream/master' into delayed-conn-close
AndresGuedez Sep 17, 2018
9d86332
Add clarifying comment and minor const correctness fix.
AndresGuedez Sep 21, 2018
435a518
Revert changes to http{1,2} codecs.
AndresGuedez Sep 23, 2018
f2c81ed
Set the delayed close timeout for a connection in the HCM.
AndresGuedez Sep 24, 2018
6704243
Merge remote-tracking branch 'upstream/master' into delayed-conn-close
AndresGuedez Sep 24, 2018
31bcaaa
Update release notes with delayed close timeout.
AndresGuedez Sep 24, 2018
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: 0 additions & 5 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ jobs:
resource_class: xlarge
working_directory: /source
environment:
BAZEL_REMOTE_CACHE: https://storage.googleapis.com/envoy-circleci-bazel-cache/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's plan to revert these changes before this gets merged. If we do disable this for all of Envoy, I'd like to do it in a separate PR and consult with maintainers. I think you can leave it in for now with this comment in place so senior maintainer gets a heads up.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's revert these; if we are going to disable the cache, it should be a separate PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

steps:
- run: rm -rf /home/circleci/project/.git # CircleCI git caching is likely broken
- checkout
Expand All @@ -24,7 +23,6 @@ jobs:
resource_class: xlarge
working_directory: /source
environment:
BAZEL_REMOTE_CACHE: https://storage.googleapis.com/envoy-circleci-bazel-cache/
steps:
- run: rm -rf /home/circleci/project/.git # CircleCI git caching is likely broken
- run: echo $CIRCLE_SHA1
Expand All @@ -38,7 +36,6 @@ jobs:
resource_class: xlarge
working_directory: /source
environment:
BAZEL_REMOTE_CACHE: https://storage.googleapis.com/envoy-circleci-bazel-cache/
steps:
- run: rm -rf /home/circleci/project/.git # CircleCI git caching is likely broken
- checkout
Expand Down Expand Up @@ -71,7 +68,6 @@ jobs:
ipv6_tests:
machine: true
environment:
BAZEL_REMOTE_CACHE: https://storage.googleapis.com/envoy-circleci-bazel-cache/
steps:
- run: rm -rf /home/circleci/project/.git # CircleCI git caching is likely broken
- checkout
Expand Down Expand Up @@ -145,7 +141,6 @@ jobs:
macos:
xcode: "9.3.0"
environment:
BAZEL_REMOTE_CACHE: https://storage.googleapis.com/envoy-circleci-bazel-cache/
steps:
- run: sudo ntpdate -vu time.apple.com
- run: rm -rf /home/circleci/project/.git # CircleCI git caching is likely broken
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import "gogoproto/gogo.proto";
// [#protodoc-title: HTTP connection manager]
// HTTP connection manager :ref:`configuration overview <config_http_conn_man>`.

// [#comment:next free field: 25]
// [#comment:next free field: 26]
message HttpConnectionManager {
enum CodecType {
option (gogoproto.goproto_enum_prefix) = false;
Expand Down Expand Up @@ -175,6 +175,19 @@ message HttpConnectionManager {
// option is not specified.
google.protobuf.Duration drain_timeout = 12 [(gogoproto.stdduration) = true];

// The delayed close timeout is for downstream connections managed by the HTTP connection manager.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to see some documentation on why one would change this setting. It's talked about in the linked issue, but from just reading this documentation, I wouldn't know what could go wrong if I change this setting to 0/disabled here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

// It is defined as a grace period after connection close processing has been locally initiated
// during which Envoy will flush the write buffers for the connection and await the peer to close
// the connection (i.e., a TCP FIN/RST is received by Envoy from the downstream connection).
//
// If the timeout triggers, Envoy will close the connection's socket.
//
// The default timeout is 1000 ms if this option is not specified.
//
// A value of 0 will completely disable delayed close processing, and the downstream connection's
// socket will be closed immediately after the write flush is completed.
google.protobuf.Duration delayed_close_timeout = 25 [(gogoproto.stdduration) = true];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a default value? What happens if delayed_close_timeout is not set?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a default timeout value of 1000 ms. I've amended the comment to note this.


// Configuration for :ref:`HTTP access logs <arch_overview_access_logs>`
// emitted by the connection manager.
repeated envoy.config.filter.accesslog.v2.AccessLog access_log = 13;
Expand Down
18 changes: 17 additions & 1 deletion include/envoy/network/connection.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,9 @@ class ConnectionCallbacks {
*/
enum class ConnectionCloseType {
FlushWrite, // Flush pending write data before raising ConnectionEvent::LocalClose
NoFlush // Do not flush any pending data and immediately raise ConnectionEvent::LocalClose
NoFlush, // Do not flush any pending data and immediately raise ConnectionEvent::LocalClose
FlushWriteAndDelay // Flush pending write data and delay raising a ConnectionEvent::LocalClose
// until the delayed_close_timeout expires
};

/**
Expand All @@ -86,6 +88,8 @@ class Connection : public Event::DeferredDeletable, public FilterManager {
Stats::Gauge& write_current_;
// Counter* as this is an optional counter. Bind errors will not be tracked if this is nullptr.
Stats::Counter* bind_errors_;
// Optional counter. Delayed close semantics are only used by HTTP connections.
Stats::Counter* delayed_close_timeouts_;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's remove the reference to HTTP, since this isn't necessarily HTTP-specific. I think the comment can just mirror the one for bind_errors_.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed the delayed close timeout is not inherently HTTP specific, but the intent of the comment is to point out that as currently implemented it only affects HTTP connections, since the H{1,2} codecs are the only callers of setDelayedCloseTimeout() on a connection to enable it.

I'm fine removing this however if you don't think the note/clarification is required.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My feeling is that it's just a comment we'll have to delete (or will be incorrect) when someone inevitably enables this for a different purpose.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. Cleaned up the comment.

};

virtual ~Connection() {}
Expand Down Expand Up @@ -233,6 +237,18 @@ class Connection : public Event::DeferredDeletable, public FilterManager {
* Get the socket options set on this connection.
*/
virtual const ConnectionSocket::OptionsSharedPtr& socketOptions() const PURE;

/**
* Set the timeout for delayed connection close()s.
* This is only used for downstream connections processing HTTP/1 and HTTP/2.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Likewise, remove this reference (or at least the "only" qualifier).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please document (probably on close) the relationship between this timeout value and the various close options. I had assumed that ConnectionCloseType::FlushWrite behavior was meant to be unchanged, but really it's going to wait up to the delayed close timeout, if set, to complete its flush before closing.

* @param timeout The timeout value in milliseconds
*/
virtual void setDelayedCloseTimeout(std::chrono::milliseconds timeout) PURE;

/**
* @return std::chrono::milliseconds The delayed close timeout value.
*/
virtual std::chrono::milliseconds delayedCloseTimeout() const PURE;
};

typedef std::unique_ptr<Connection> ConnectionPtr;
Expand Down
1 change: 1 addition & 0 deletions source/common/http/conn_manager_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ namespace Http {
GAUGE (downstream_cx_tx_bytes_buffered) \
COUNTER (downstream_cx_drain_close) \
COUNTER (downstream_cx_idle_timeout) \
COUNTER (downstream_cx_delayed_close_timeout) \
COUNTER (downstream_flow_control_paused_reading_total) \
COUNTER (downstream_flow_control_resumed_reading_total) \
COUNTER (downstream_rq_total) \
Expand Down
14 changes: 8 additions & 6 deletions source/common/http/conn_manager_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ void ConnectionManagerImpl::initializeReadFilterCallbacks(Network::ReadFilterCal
read_callbacks_->connection().setConnectionStats(
{stats_.named_.downstream_cx_rx_bytes_total_, stats_.named_.downstream_cx_rx_bytes_buffered_,
stats_.named_.downstream_cx_tx_bytes_total_, stats_.named_.downstream_cx_tx_bytes_buffered_,
nullptr});
nullptr, &stats_.named_.downstream_cx_delayed_close_timeout_});
}

ConnectionManagerImpl::~ConnectionManagerImpl() {
Expand Down Expand Up @@ -118,7 +118,7 @@ ConnectionManagerImpl::~ConnectionManagerImpl() {

void ConnectionManagerImpl::checkForDeferredClose() {
if (drain_state_ == DrainState::Closing && streams_.empty() && !codec_->wantsToWrite()) {
read_callbacks_->connection().close(Network::ConnectionCloseType::FlushWrite);
read_callbacks_->connection().close(Network::ConnectionCloseType::FlushWriteAndDelay);
}
}

Expand Down Expand Up @@ -233,12 +233,12 @@ Network::FilterStatus ConnectionManagerImpl::onData(Buffer::Instance& data, bool
ENVOY_CONN_LOG(debug, "dispatch error: {}", read_callbacks_->connection(), e.what());
stats_.named_.downstream_cx_protocol_error_.inc();

// In the protocol error case, we need to reset all streams now. Since we do a flush write,
// the connection might stick around long enough for a pending stream to come back and try
// to encode.
// In the protocol error case, we need to reset all streams now. Since we do a flush write and
// delayed close, the connection might stick around long enough for a pending stream to come
// back and try to encode.
resetAllStreams();

read_callbacks_->connection().close(Network::ConnectionCloseType::FlushWrite);
read_callbacks_->connection().close(Network::ConnectionCloseType::FlushWriteAndDelay);
return Network::FilterStatus::StopIteration;
}

Expand Down Expand Up @@ -317,6 +317,8 @@ void ConnectionManagerImpl::onIdleTimeout() {
ENVOY_CONN_LOG(debug, "idle timeout", read_callbacks_->connection());
stats_.named_.downstream_cx_idle_timeout_.inc();
if (!codec_) {
// No need to delay close after flushing since an idle timeout has already fired. Attempt to
// write out buffered data one last time and issue a local close if successful.
read_callbacks_->connection().close(Network::ConnectionCloseType::FlushWrite);
} else if (drain_state_ == DrainState::NotDraining) {
startDrainSequence();
Expand Down
7 changes: 5 additions & 2 deletions source/common/http/http1/codec_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -450,8 +450,11 @@ void ConnectionImpl::onResetStreamBase(StreamResetReason reason) {

ServerConnectionImpl::ServerConnectionImpl(Network::Connection& connection,
ServerConnectionCallbacks& callbacks,
Http1Settings settings)
: ConnectionImpl(connection, HTTP_REQUEST), callbacks_(callbacks), codec_settings_(settings) {}
Http1Settings settings,
std::chrono::milliseconds delayed_close_timeout)
: ConnectionImpl(connection, HTTP_REQUEST), callbacks_(callbacks), codec_settings_(settings) {
connection_.setDelayedCloseTimeout(delayed_close_timeout);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the reasoning for doing this set in the codec? It seems like something that is unrelated to the codec itself and should be done by higher layer code? Same question for the HTTP/2 codec below.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not related to the codec directly but this particular delayed_close_timeout is HTTP only since it is managed via the HCM configuration. This seemed to be the most appropriate place to set the timeout for HTTP connections just after they are constructed.

Since the timeout value is intended to be network filter specific, I wouldn't expect any layer higher than the corresponding filter to issue the call to setDelayedCloseTimeout().

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree it should be done in the filter. Why not do it in the HCM filter and avoid codec modifications?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed it's best to contain it within the HCM. I revisited the code and moved setting the timeout to the HCM config.

}

void ServerConnectionImpl::onEncodeComplete() {
ASSERT(active_request_);
Expand Down
5 changes: 3 additions & 2 deletions source/common/http/http1/codec_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -277,8 +277,9 @@ class ConnectionImpl : public virtual Connection, protected Logger::Loggable<Log
*/
class ServerConnectionImpl : public ServerConnection, public ConnectionImpl {
public:
ServerConnectionImpl(Network::Connection& connection, ServerConnectionCallbacks& callbacks,
Http1Settings settings);
ServerConnectionImpl(
Network::Connection& connection, ServerConnectionCallbacks& callbacks, Http1Settings settings,
std::chrono::milliseconds delayed_close_timeout = std::chrono::milliseconds(0));

virtual bool supports_http_10() override { return codec_settings_.accept_http_10_; }

Expand Down
2 changes: 1 addition & 1 deletion source/common/http/http1/conn_pool.cc
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ ConnPoolImpl::ActiveClient::ActiveClient(ConnPoolImpl& parent)
parent_.host_->cluster().stats().upstream_cx_rx_bytes_buffered_,
parent_.host_->cluster().stats().upstream_cx_tx_bytes_total_,
parent_.host_->cluster().stats().upstream_cx_tx_bytes_buffered_,
&parent_.host_->cluster().stats().bind_errors_});
&parent_.host_->cluster().stats().bind_errors_, nullptr});
}

ConnPoolImpl::ActiveClient::~ActiveClient() {
Expand Down
4 changes: 3 additions & 1 deletion source/common/http/http2/codec_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -812,8 +812,10 @@ int ClientConnectionImpl::onHeader(const nghttp2_frame* frame, HeaderString&& na

ServerConnectionImpl::ServerConnectionImpl(Network::Connection& connection,
Http::ServerConnectionCallbacks& callbacks,
Stats::Scope& scope, const Http2Settings& http2_settings)
Stats::Scope& scope, const Http2Settings& http2_settings,
std::chrono::milliseconds delayed_close_timeout)
: ConnectionImpl(connection, scope, http2_settings), callbacks_(callbacks) {
connection_.setDelayedCloseTimeout(delayed_close_timeout);
Http2Options http2_options(http2_settings);
nghttp2_session_server_new2(&session_, http2_callbacks_.callbacks(), base(),
http2_options.options());
Expand Down
6 changes: 4 additions & 2 deletions source/common/http/http2/codec_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -323,8 +323,10 @@ class ClientConnectionImpl : public ClientConnection, public ConnectionImpl {
*/
class ServerConnectionImpl : public ServerConnection, public ConnectionImpl {
public:
ServerConnectionImpl(Network::Connection& connection, ServerConnectionCallbacks& callbacks,
Stats::Scope& scope, const Http2Settings& http2_settings);
ServerConnectionImpl(
Network::Connection& connection, ServerConnectionCallbacks& callbacks, Stats::Scope& scope,
const Http2Settings& http2_settings,
std::chrono::milliseconds delayed_close_timeout = std::chrono::milliseconds(0));

private:
// ConnectionImpl
Expand Down
2 changes: 1 addition & 1 deletion source/common/http/http2/conn_pool.cc
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ ConnPoolImpl::ActiveClient::ActiveClient(ConnPoolImpl& parent)
parent_.host_->cluster().stats().upstream_cx_rx_bytes_buffered_,
parent_.host_->cluster().stats().upstream_cx_tx_bytes_total_,
parent_.host_->cluster().stats().upstream_cx_tx_bytes_buffered_,
&parent_.host_->cluster().stats().bind_errors_});
&parent_.host_->cluster().stats().bind_errors_, nullptr});
}

ConnPoolImpl::ActiveClient::~ActiveClient() {
Expand Down
42 changes: 37 additions & 5 deletions source/common/network/connection_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@ ConnectionImpl::ConnectionImpl(Event::Dispatcher& dispatcher, ConnectionSocketPt
ConnectionImpl::~ConnectionImpl() {
ASSERT(fd() == -1, "ConnectionImpl was unexpectedly torn down without being closed.");

if (delayed_close_timer_) {
// It's ok to disable even if the timer has already fired.
delayed_close_timer_->disableTimer();
delayed_close_timer_.reset();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: don't need to reset -- it's about to be destroyed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

}

// In general we assume that owning code has called close() previously to the destructor being
// run. This generally must be done so that callbacks run in the correct context (vs. deferred
// deletion). Hence the assert above. However, call close() here just to be completely sure that
Expand Down Expand Up @@ -106,10 +112,29 @@ void ConnectionImpl::close(ConnectionCloseType type) {

closeSocket(ConnectionEvent::LocalClose);
} else {
// TODO(mattklein123): We need a flush timer here. We might never get open socket window.
ASSERT(type == ConnectionCloseType::FlushWrite);
close_with_flush_ = true;
RELEASE_ASSERT(type == ConnectionCloseType::FlushWrite ||
type == ConnectionCloseType::FlushWriteAndDelay,
"");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the rationale behind making this a RELEASE_ASSERT? What's the practical implication if it was an ASSERT and happened in production?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm using the RELEASE_ASSERT to provide runtime protection against bugs introduced in this logic, since they could lead to issues such as resource leaks and/or memory corruption.

I just re-read the style guide and it does mention that ASSERT should be the common case for this type of check, so I can certainly change it if that's the convention.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The convention is generally to use ASSERT for this, if you feel strongly about RELEASE_ASSERT I'll defer to senior maintainer for the decision.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to ASSERT() per style guidelines.

delayed_close_ = true;
bool delayed_close_timeout_set = delayedCloseTimeout().count() > 0;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: const

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


// All close types that follow do not actually close() the socket immediately so that buffered
// data can be written. However, we do want to stop reading to apply TCP backpressure.
read_enabled_ = false;

// Force a closeSocket() after the write buffer is flushed if the close_type calls for it or if
// no delayed close timeout is set.
close_after_flush_ = !delayed_close_timeout_set || type == ConnectionCloseType::FlushWrite;

// Create and activate a timer which will immediately close the connection if triggered.
// A config value of 0 disables the timeout.
if (delayed_close_timeout_set) {
delayed_close_timer_ = dispatcher_.createTimer([this]() -> void { onDelayedCloseTimeout(); });
ENVOY_CONN_LOG(debug, "setting delayed close timer with timeout {} ms", *this,
delayedCloseTimeout().count());
delayed_close_timer_->enableTimer(delayedCloseTimeout());
}
zuercher marked this conversation as resolved.
Show resolved Hide resolved

file_event_->setEnabled(Event::FileReadyType::Write |
(enable_half_close_ ? 0 : Event::FileReadyType::Closed));
}
Expand All @@ -118,7 +143,7 @@ void ConnectionImpl::close(ConnectionCloseType type) {
Connection::State ConnectionImpl::state() const {
if (fd() == -1) {
return State::Closed;
} else if (close_with_flush_) {
} else if (delayed_close_) {
return State::Closing;
} else {
return State::Open;
Expand Down Expand Up @@ -488,7 +513,7 @@ void ConnectionImpl::onWriteReady() {
// write callback. This can happen if we manage to complete the SSL handshake in the write
// callback, raise a connected event, and close the connection.
closeSocket(ConnectionEvent::RemoteClose);
} else if ((close_with_flush_ && new_buffer_size == 0) || bothSidesHalfClosed()) {
} else if ((close_after_flush_ && new_buffer_size == 0) || bothSidesHalfClosed()) {
ENVOY_CONN_LOG(debug, "write flush complete", *this);
closeSocket(ConnectionEvent::LocalClose);
} else if (result.action_ == PostIoAction::KeepOpen && result.bytes_processed_ > 0) {
Expand Down Expand Up @@ -535,6 +560,13 @@ bool ConnectionImpl::bothSidesHalfClosed() {
return read_end_stream_ && write_end_stream_ && write_buffer_->length() == 0;
}

void ConnectionImpl::onDelayedCloseTimeout() {
ENVOY_CONN_LOG(debug, "triggered delayed close", *this);
ASSERT(connection_stats_->delayed_close_timeouts_);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer that you test if delayed_close_timeouts_ is null (similar to how bind_errors_ optionality is handled). Or else, ASSERT the pointer is non-null when the delayed close timeout is configured.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

connection_stats_->delayed_close_timeouts_->inc();
closeSocket(ConnectionEvent::LocalClose);
}

ClientConnectionImpl::ClientConnectionImpl(
Event::Dispatcher& dispatcher, const Address::InstanceConstSharedPtr& remote_address,
const Network::Address::InstanceConstSharedPtr& source_address,
Expand Down
13 changes: 12 additions & 1 deletion source/common/network/connection_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,11 @@ class ConnectionImpl : public virtual Connection,
// Obtain global next connection ID. This should only be used in tests.
static uint64_t nextGlobalIdForTest() { return next_global_id_; }

void setDelayedCloseTimeout(std::chrono::milliseconds timeout) override {
delayed_close_timeout_ = timeout;
}
std::chrono::milliseconds delayedCloseTimeout() const override { return delayed_close_timeout_; }

protected:
void closeSocket(ConnectionEvent close_type);

Expand All @@ -128,6 +133,7 @@ class ConnectionImpl : public virtual Connection,
// a generic pointer.
Buffer::InstancePtr write_buffer_;
uint32_t read_buffer_limit_ = 0;
std::chrono::milliseconds delayed_close_timeout_{0};

protected:
bool connecting_{false};
Expand All @@ -146,14 +152,19 @@ class ConnectionImpl : public virtual Connection,
// Returns true iff end of stream has been both written and read.
bool bothSidesHalfClosed();

// Callback issued when a delayed close timeout triggers.
void onDelayedCloseTimeout();

static std::atomic<uint64_t> next_global_id_;

Event::Dispatcher& dispatcher_;
const uint64_t id_;
Event::TimerPtr delayed_close_timer_;
std::list<ConnectionCallbacks*> callbacks_;
std::list<BytesSentCb> bytes_sent_callbacks_;
bool read_enabled_{true};
bool close_with_flush_{false};
bool close_after_flush_{false};
bool delayed_close_{false};
bool above_high_watermark_{false};
bool detect_early_close_{true};
bool enable_half_close_{false};
Expand Down
2 changes: 1 addition & 1 deletion source/common/tcp/conn_pool.cc
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ ConnPoolImpl::ActiveConn::ActiveConn(ConnPoolImpl& parent)
parent_.host_->cluster().stats().upstream_cx_rx_bytes_buffered_,
parent_.host_->cluster().stats().upstream_cx_tx_bytes_total_,
parent_.host_->cluster().stats().upstream_cx_tx_bytes_buffered_,
&parent_.host_->cluster().stats().bind_errors_});
&parent_.host_->cluster().stats().bind_errors_, nullptr});

// We just universally set no delay on connections. Theoretically we might at some point want
// to make this configurable.
Expand Down
2 changes: 1 addition & 1 deletion source/common/tcp_proxy/tcp_proxy.cc
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ void Filter::initialize(Network::ReadFilterCallbacks& callbacks, bool set_connec
{config_->stats().downstream_cx_rx_bytes_total_,
config_->stats().downstream_cx_rx_bytes_buffered_,
config_->stats().downstream_cx_tx_bytes_total_,
config_->stats().downstream_cx_tx_bytes_buffered_, nullptr});
config_->stats().downstream_cx_tx_bytes_buffered_, nullptr, nullptr});
}
}

Expand Down
Loading