Skip to content
Closed
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
5 changes: 4 additions & 1 deletion include/envoy/buffer/buffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -371,10 +371,13 @@ class WatermarkFactory {
* low watermark.
* @param above_high_watermark supplies a function to call if the buffer goes over a configured
* high watermark.
* @param above_overflow_watermark supplies a function to call if the buffer goes over a
* configured "overflow" watermark.
* @return a newly created InstancePtr.
*/
virtual InstancePtr create(std::function<void()> below_low_watermark,
std::function<void()> above_high_watermark) PURE;
std::function<void()> above_high_watermark,
std::function<void()> above_overflow_watermark) PURE;
};

using WatermarkFactoryPtr = std::unique_ptr<WatermarkFactory>;
Expand Down
2 changes: 1 addition & 1 deletion include/envoy/http/filter.h
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ class StreamDecoderFilterCallbacks : public virtual StreamFilterCallbacks {
virtual void encodeMetadata(MetadataMapPtr&& metadata_map) PURE;

/**
* Called when the buffer for a decoder filter or any buffers the filter sends data to go over
* Called when the buffer for a decoder filter, or any buffers the filter sends data to, go over
* their high watermark.
*
* In the case of a filter such as the router filter, which spills into multiple buffers (codec,
Expand Down
1 change: 1 addition & 0 deletions source/common/buffer/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ envoy_cc_library(
srcs = ["watermark_buffer.cc"],
hdrs = ["watermark_buffer.h"],
deps = [
"//include/envoy/runtime:runtime_interface",
"//source/common/buffer:buffer_lib",
"//source/common/common:assert_lib",
],
Expand Down
43 changes: 26 additions & 17 deletions source/common/buffer/watermark_buffer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,32 @@ namespace Buffer {

void WatermarkBuffer::add(const void* data, uint64_t size) {
OwnedImpl::add(data, size);
checkHighWatermark();
checkHighAndOverflowWatermarks();
}

void WatermarkBuffer::add(absl::string_view data) {
OwnedImpl::add(data);
checkHighWatermark();
checkHighAndOverflowWatermarks();
}

void WatermarkBuffer::add(const Instance& data) {
OwnedImpl::add(data);
checkHighWatermark();
checkHighAndOverflowWatermarks();
}

void WatermarkBuffer::prepend(absl::string_view data) {
OwnedImpl::prepend(data);
checkHighWatermark();
checkHighAndOverflowWatermarks();
}

void WatermarkBuffer::prepend(Instance& data) {
OwnedImpl::prepend(data);
checkHighWatermark();
checkHighAndOverflowWatermarks();
}

void WatermarkBuffer::commit(RawSlice* iovecs, uint64_t num_iovecs) {
OwnedImpl::commit(iovecs, num_iovecs);
checkHighWatermark();
checkHighAndOverflowWatermarks();
}

void WatermarkBuffer::drain(uint64_t size) {
Expand All @@ -42,23 +42,23 @@ void WatermarkBuffer::drain(uint64_t size) {

void WatermarkBuffer::move(Instance& rhs) {
OwnedImpl::move(rhs);
checkHighWatermark();
checkHighAndOverflowWatermarks();
}

void WatermarkBuffer::move(Instance& rhs, uint64_t length) {
OwnedImpl::move(rhs, length);
checkHighWatermark();
checkHighAndOverflowWatermarks();
}

Api::IoCallUint64Result WatermarkBuffer::read(Network::IoHandle& io_handle, uint64_t max_length) {
Api::IoCallUint64Result result = OwnedImpl::read(io_handle, max_length);
checkHighWatermark();
checkHighAndOverflowWatermarks();
return result;
}

uint64_t WatermarkBuffer::reserve(uint64_t length, RawSlice* iovecs, uint64_t num_iovecs) {
uint64_t bytes_reserved = OwnedImpl::reserve(length, iovecs, num_iovecs);
checkHighWatermark();
checkHighAndOverflowWatermarks();
return bytes_reserved;
}

Expand All @@ -72,12 +72,16 @@ void WatermarkBuffer::setWatermarks(uint32_t low_watermark, uint32_t high_waterm
ASSERT(low_watermark < high_watermark || (high_watermark == 0 && low_watermark == 0));
low_watermark_ = low_watermark;
high_watermark_ = high_watermark;
checkHighWatermark();
overflow_watermark_ = high_watermark * overflow_multiplier_;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I wonder if multiplier is the right approach. I think the worst case formula for figuring out how much data can end up in the downstream outgoing buffer is (for H/2 upstream):

min(max_concurrent_streams-downstream, max_concurrent_streams-upstream) * initial_stream_window_size-upstream.

So if the client makes a lot of simultaneous requests which land on distinct upstream servers all responses may end up in the outbound buffer before flow control can do anything about it. In this case low multiplier may cause spurious disconnects.
I wonder if the we should use a number instead of multiplier, or at the very least check if it makes sense given H2 settings for downstream and upstream?

// TODO(adip): What should be done if there's an overflow (overflow_watermark_ <
// overflow_watermark_)? should this be a release_assert?
ASSERT((high_watermark <= overflow_watermark_) || (overflow_multiplier_ == 0));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

|| overflow_watermark_ == 0

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'll add a comment here. This checks that there's no overflow due to multiplication:
overflow_watermark_ = high_watermark * overflow_multiplier_;

checkHighAndOverflowWatermarks();
checkLowWatermark();
}

void WatermarkBuffer::checkLowWatermark() {
if (!above_high_watermark_called_ ||
if (above_overflow_watermark_called_ || !above_high_watermark_called_ ||
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why do we check for above_overflow_watermark_called_ ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

At the moment, although an overflow occurs, the buffer can still be used. Once the buffer is read from, the low watermark callback will be called. I'm not sure that in cases where the connection is terminated, the readDisable should be called again.
If I'm missing a scenario please let me know.

That said, a different buffer design that should be considered is to disable all operations once an overflow has been detected.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Here's the problem: if the overflow callback is called, the buffer stops functioning properly since low watermark callbacks are no longer called when they should.

This PR feels high risk. I think we may be jumping the gun by going straight to closing the connection/resetting stream on overflow. It would be good to gather some data about what can cause the overflow cb to trigger. I know there's a runtime flag to control this behavior, but it defaulting on with a 2x overflow ratio could cause some problems.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Agreed that runtime control over the high watermark constant seems a useful capability.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@antoniovicente I'm unsure we have valid causes for the overflow to trigger at the moment. This feature to me is a safety valve to account for unknown bugs that may trigger buffer to grow way beyond high watermark. As such I would think outright socket closure is appropriate.
I agree though the multiplier is way too low and perhaps is not quite the right approach, see my comment above.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@yanavlasov triggering the 2x multipler overflow watermark is very easy. Specially when you factor in that the H2 connection flow control window is not coordinated with high watermarks. Going straight to closing connection is very risky.

I'm just arguing for keeping the high/low watermarks in working order even if the overflow one is hit, so we can add overflow implementations that just increment counters.

(high_watermark_ != 0 && OwnedImpl::length() > low_watermark_)) {
return;
}
Expand All @@ -86,14 +90,19 @@ void WatermarkBuffer::checkLowWatermark() {
below_low_watermark_();
}

void WatermarkBuffer::checkHighWatermark() {
if (above_high_watermark_called_ || high_watermark_ == 0 ||
OwnedImpl::length() <= high_watermark_) {
void WatermarkBuffer::checkHighAndOverflowWatermarks() {
if (!above_overflow_watermark_called_ && overflow_watermark_ != 0 &&
OwnedImpl::length() > overflow_watermark_) {
above_overflow_watermark_called_ = true;
above_overflow_watermark_();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This early return combined with above_overflow_watermark_called_ means that the second check can end up calling the high watermark cb. Should these checks happen in the opposite order: high watermark first, then overflow?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It really depends whether the normal flow control should be preserved or not, and if the buffer should be usable or not.
To the best of my understanding it should actually be that we first check if the overflow watermark cb was called, and if so, then early return.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is there any behavior we want to support other than connection shutdown and resource teardown? Presumably initiated within the space of a single epoll event call stack (we would do a hard connection shutdown, with no drain).

return;
}

above_high_watermark_called_ = true;
above_high_watermark_();
if (!above_high_watermark_called_ && high_watermark_ != 0 &&
OwnedImpl::length() > high_watermark_) {
above_high_watermark_called_ = true;
above_high_watermark_();
}
}

} // namespace Buffer
Expand Down
29 changes: 21 additions & 8 deletions source/common/buffer/watermark_buffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#include <functional>
#include <string>

#include "envoy/runtime/runtime.h"

#include "common/buffer/buffer_impl.h"

namespace Envoy {
Expand All @@ -16,8 +18,12 @@ namespace Buffer {
class WatermarkBuffer : public OwnedImpl {
public:
WatermarkBuffer(std::function<void()> below_low_watermark,
std::function<void()> above_high_watermark)
: below_low_watermark_(below_low_watermark), above_high_watermark_(above_high_watermark) {}
std::function<void()> above_high_watermark,
std::function<void()> above_overflow_watermark)
: below_low_watermark_(below_low_watermark), above_high_watermark_(above_high_watermark),
above_overflow_watermark_(above_overflow_watermark),
overflow_multiplier_(Runtime::LoaderSingleton::get().threadsafeSnapshot()->getInteger(
"buffer.overflow.high_watermark_multiplier", 2)) {}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is a factor of 2 safe based on the maximum expansion of a request that can happen during proxying due to encoding related issues?

Some cases that come to mind:

  1. H2 compressed headers expanding to H1 headers. I think this is limited by max header size bytes, which is not related to watermark parameters.
  2. H2 connection flow control windows vs buffer watermarks.
  3. H2 control frames which are not limited by flow control.
  4. H1 to H2 chunk body conversions. I think an 1-byte H1 chunk can be encoded in either 4 or 6 bytes based on which line termination the sender uses. The H2 data frame header is 9 bytes, so the H2 encoding for a 1 byte frame is 10 bytes (2.5x larger)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

  1. Asymmetric read/write buffer configs. If the read buffer size is 10x the write buffer, you are very likely to hit the overflow condition after a large read.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good points...
To add to the first bullet, should we verify that the max header size bytes is less-or-equal to overflow watermark value?
Regarding point 5, IIUC it is also the case where the rate of the producer is much faster than the rate of the consumer. In this case I think it's up to the configuration parameters, but if there's a way to warn that a configuration might be wrong, then this is an example where it should.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The max header limit excludes framing overhead. Also, you can end up writing headers and body in the same stack, you need space for both.
I think your changes effectively make it is unsafe to set buffer high water to anything below around 32kb or possibly, yet this is not enforced anywhere in the config plane. Note that there's widespread use of high watermarks of exactly 32kb.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think the goal of high watermark is to deal with highly unusual situations where normal watermarks have failed us. Examples are where a number of connection buffers are close to their watermark, but not over, and a large read occurs, adding a bunch of data before watermarking or overload manager can respond. Or when we forget to do watermark flow control push back due to a missing feedback path (e.g. for proxy generated direct responses).

Thus, I'm not super worried about the user having to explicitly size them, they should be sizing the normal watermarks (or knobs implying them) based on an understanding of traffic. Whenever there is compression involved, this might be tricky, e.g. zlib or HPACK. Headers are a special case, in general though compression could be 100x. Presumably the basic watermark limit is set to reflect this.

Setting some fixed constant as the fudge factor then makes sense to provide a zero tune capability here IMHO, it probably doesn't matter as long as it's some reasonable distance from the normal operational limit.


// Override all functions from Instance which can result in changing the size
// of the underlying buffer.
Expand All @@ -40,20 +46,25 @@ class WatermarkBuffer : public OwnedImpl {
uint32_t highWatermark() const { return high_watermark_; }

private:
void checkHighWatermark();
void checkHighAndOverflowWatermarks();
void checkLowWatermark();

std::function<void()> below_low_watermark_;
std::function<void()> above_high_watermark_;
std::function<void()> above_overflow_watermark_;

const uint32_t overflow_multiplier_{0};
// Used for enforcing buffer limits (off by default). If these are set to non-zero by a call to
// setWatermarks() the watermark callbacks will be called as described above.
uint32_t overflow_watermark_{0};
uint32_t high_watermark_{0};
uint32_t low_watermark_{0};
// Tracks the latest state of watermark callbacks.
// True between the time above_high_watermark_ has been called until above_high_watermark_ has
// been called.
// Set to true after above_high_watermark_ has been called, and reset to false after
// below_low_watermark_ has been called.
bool above_high_watermark_called_{false};
// Set to true after above_overflow_watermark_ has been called. Never reset, because we expect
// the associated connection and/or stream will be closed immediately in response to an overflow.
bool above_overflow_watermark_called_{false};
};

using WatermarkBufferPtr = std::unique_ptr<WatermarkBuffer>;
Expand All @@ -62,8 +73,10 @@ class WatermarkBufferFactory : public WatermarkFactory {
public:
// Buffer::WatermarkFactory
InstancePtr create(std::function<void()> below_low_watermark,
std::function<void()> above_high_watermark) override {
return InstancePtr{new WatermarkBuffer(below_low_watermark, above_high_watermark)};
std::function<void()> above_high_watermark,
std::function<void()> above_overflow_watermark) override {
return InstancePtr{
new WatermarkBuffer(below_low_watermark, above_high_watermark, above_overflow_watermark)};
}
};

Expand Down
86 changes: 47 additions & 39 deletions source/common/http/conn_manager_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2219,9 +2219,9 @@ void ConnectionManagerImpl::ActiveStreamFilterBase::clearRouteCache() {
}

Buffer::WatermarkBufferPtr ConnectionManagerImpl::ActiveStreamDecoderFilter::createBuffer() {
auto buffer =
std::make_unique<Buffer::WatermarkBuffer>([this]() -> void { this->requestDataDrained(); },
[this]() -> void { this->requestDataTooLarge(); });
auto buffer = std::make_unique<Buffer::WatermarkBuffer>(
[this]() -> void { this->requestDataDrained(); },
[this]() -> void { this->requestDataTooLarge(); }, [this]() -> void { this->resetStream(); });
buffer->setWatermarks(parent_.buffer_limit_);
return buffer;
}
Expand Down Expand Up @@ -2388,8 +2388,10 @@ ConnectionManagerImpl::ActiveStreamDecoderFilter::routeConfig() {
}

Buffer::WatermarkBufferPtr ConnectionManagerImpl::ActiveStreamEncoderFilter::createBuffer() {
auto buffer = new Buffer::WatermarkBuffer([this]() -> void { this->responseDataDrained(); },
[this]() -> void { this->responseDataTooLarge(); });
auto buffer = new Buffer::WatermarkBuffer(
[this]() -> void { this->responseDataDrained(); },
[this]() -> void { this->responseDataExceedsHighWatermark(); },
[this]() -> void { this->responseDataExceedsOverflowWatermark(); });
buffer->setWatermarks(parent_.buffer_limit_);
return Buffer::WatermarkBufferPtr{buffer};
}
Expand Down Expand Up @@ -2441,43 +2443,49 @@ void ConnectionManagerImpl::ActiveStreamEncoderFilter::

void ConnectionManagerImpl::ActiveStreamEncoderFilter::continueEncoding() { commonContinue(); }

void ConnectionManagerImpl::ActiveStreamEncoderFilter::responseDataTooLarge() {
void ConnectionManagerImpl::ActiveStreamEncoderFilter::responseDataExceedsHighWatermark() {
if (parent_.state_.encoder_filters_streaming_) {
// If we are streaming the response, there's still an opportunity to drain the response buffer.
onEncoderFilterAboveWriteBufferHighWatermark();
} else {
parent_.connection_manager_.stats_.named_.rs_too_large_.inc();

// If headers have not been sent to the user, send a 500.
if (!headers_continued_) {
// Make sure we won't end up with nested watermark calls from the body buffer.
parent_.state_.encoder_filters_streaming_ = true;
allowIteration();

parent_.stream_info_.setResponseCodeDetails(
StreamInfo::ResponseCodeDetails::get().RequestHeadersTooLarge);
// This does not call the standard sendLocalReply because if there is already response data
// we do not want to pass a second set of response headers through the filter chain.
// Instead, call the encodeHeadersInternal / encodeDataInternal helpers
// directly, which maximizes shared code with the normal response path.
Http::Utility::sendLocalReply(
Grpc::Common::hasGrpcContentType(*parent_.request_headers_),
[&](ResponseHeaderMapPtr&& response_headers, bool end_stream) -> void {
parent_.response_headers_ = std::move(response_headers);
parent_.encodeHeadersInternal(*parent_.response_headers_, end_stream);
},
[&](Buffer::Instance& data, bool end_stream) -> void {
parent_.encodeDataInternal(data, end_stream);
},
parent_.state_.destroyed_, Http::Code::InternalServerError,
CodeUtility::toString(Http::Code::InternalServerError), absl::nullopt,
parent_.state_.is_head_request_);
parent_.maybeEndEncode(parent_.state_.local_complete_);
} else {
ENVOY_STREAM_LOG(
debug, "Resetting stream. Response data too large and headers have already been sent",
*this);
resetStream();
}
// Otherwise, treat it as an overflow: send a 500 if possible and reset the stream.
responseDataExceedsOverflowWatermark();
}
}

void ConnectionManagerImpl::ActiveStreamEncoderFilter::responseDataExceedsOverflowWatermark() {
parent_.connection_manager_.stats_.named_.rs_too_large_.inc();

// If headers have not been sent to the user, send a 500.
if (!headers_continued_) {
// Make sure we won't end up with nested watermark calls from the body buffer.
parent_.state_.encoder_filters_streaming_ = true;
allowIteration();

parent_.stream_info_.setResponseCodeDetails(
StreamInfo::ResponseCodeDetails::get().RequestHeadersTooLarge);
// This does not call the standard sendLocalReply because if there is already response data
// we do not want to pass a second set of response headers through the filter chain.
// Instead, call the encodeHeadersInternal / encodeDataInternal helpers
// directly, which maximizes shared code with the normal response path.
Http::Utility::sendLocalReply(
Grpc::Common::hasGrpcContentType(*parent_.request_headers_),
[&](ResponseHeaderMapPtr&& response_headers, bool end_stream) -> void {
parent_.response_headers_ = std::move(response_headers);
parent_.encodeHeadersInternal(*parent_.response_headers_, end_stream);
},
[&](Buffer::Instance& data, bool end_stream) -> void {
parent_.encodeDataInternal(data, end_stream);
},
parent_.state_.destroyed_, Http::Code::InternalServerError,
CodeUtility::toString(Http::Code::InternalServerError), absl::nullopt,
parent_.state_.is_head_request_);
parent_.maybeEndEncode(parent_.state_.local_complete_);
} else {
ENVOY_STREAM_LOG(debug,
"Resetting stream. Response data too large and headers have already been sent",
*this);
resetStream();
}
}

Expand Down
3 changes: 2 additions & 1 deletion source/common/http/conn_manager_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,8 @@ class ConnectionManagerImpl : Logger::Loggable<Logger::Id::http>,
callback(*parent_.buffered_response_data_.get());
}

void responseDataTooLarge();
void responseDataExceedsHighWatermark();
void responseDataExceedsOverflowWatermark();
void responseDataDrained();

StreamEncoderFilterSharedPtr handle_;
Expand Down
8 changes: 7 additions & 1 deletion source/common/http/http1/codec_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,11 @@ void ConnectionImpl::copyToBuffer(const char* data, uint64_t length) {
output_buffer_.add(data, length);
}

void ConnectionImpl::onAboveOverflowWatermark() {
// TODO(mergeconflict): log and increment counter
connection_.close(Network::ConnectionCloseType::NoFlush);
}

void StreamEncoderImpl::resetStream(StreamResetReason reason) {
connection_.onResetStreamBase(reason);
}
Expand Down Expand Up @@ -412,7 +417,8 @@ ConnectionImpl::ConnectionImpl(Network::Connection& connection, Stats::Scope& st
"envoy.reloadable_features.connection_header_sanitization")),
enable_trailers_(enable_trailers),
output_buffer_([&]() -> void { this->onBelowLowWatermark(); },
[&]() -> void { this->onAboveHighWatermark(); }),
[&]() -> void { this->onAboveHighWatermark(); },
[&]() -> void { this->onAboveOverflowWatermark(); }),
max_headers_kb_(max_headers_kb), max_headers_count_(max_headers_count) {
output_buffer_.setWatermarks(connection.bufferLimit());
http_parser_init(&parser_, type);
Expand Down
5 changes: 5 additions & 0 deletions source/common/http/http1/codec_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,11 @@ class ConnectionImpl : public virtual Connection, protected Logger::Loggable<Log
*/
virtual void sendProtocolError(absl::string_view details) PURE;

/**
* Called when output_buffer_ or the underlying connection exceed the "overflow" watermark.
*/
void onAboveOverflowWatermark();

/**
* Called when output_buffer_ or the underlying connection go from below a low watermark to over
* a high watermark.
Expand Down
12 changes: 12 additions & 0 deletions source/common/http/http2/codec_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,12 @@ void ConnectionImpl::StreamImpl::readDisable(bool disable) {
}
}

void ConnectionImpl::StreamImpl::pendingRecvBufferOverflowWatermark() {
ENVOY_CONN_LOG(warn, "recv buffer overflow ", parent_.connection_);
// TODO(mergeconflict): increment a counter
resetStream(StreamResetReason::Overflow);
}

void ConnectionImpl::StreamImpl::pendingRecvBufferHighWatermark() {
ENVOY_CONN_LOG(debug, "recv buffer over limit ", parent_.connection_);
ASSERT(!pending_receive_buffer_high_watermark_called_);
Expand Down Expand Up @@ -266,6 +272,12 @@ void ConnectionImpl::ServerStreamImpl::decodeTrailers() {
std::move(absl::get<RequestTrailerMapPtr>(headers_or_trailers_)));
}

void ConnectionImpl::StreamImpl::pendingSendBufferOverflowWatermark() {
ENVOY_CONN_LOG(warn, "send buffer overflow ", parent_.connection_);
// TODO(mergeconflict): increment a counter
resetStream(StreamResetReason::Overflow);
}

void ConnectionImpl::StreamImpl::pendingSendBufferHighWatermark() {
ENVOY_CONN_LOG(debug, "send buffer over limit ", parent_.connection_);
ASSERT(!pending_send_buffer_high_watermark_called_);
Expand Down
Loading