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
6 changes: 6 additions & 0 deletions source/common/network/io_socket_error_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ IoSocketError* IoSocketError::getIoSocketInvalidAddressInstance() {
return instance;
}

IoSocketError* IoSocketError::getIoSocketEbadfInstance() {
static auto* instance =
new IoSocketError(SOCKET_ERROR_BADF, Api::IoError::IoErrorCode::NoSupport);
return instance;
}

IoSocketError* IoSocketError::getIoSocketEagainInstance() {
static auto* instance = new IoSocketError(SOCKET_ERROR_AGAIN, Api::IoError::IoErrorCode::Again);
return instance;
Expand Down
2 changes: 2 additions & 0 deletions source/common/network/io_socket_error_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ class IoSocketError : public Api::IoError {
// deleter deleteIoError() below to avoid deallocating memory for this error.
static IoSocketError* getIoSocketEagainInstance();

static IoSocketError* getIoSocketEbadfInstance();

// This error is introduced when Envoy create socket for unsupported address. It is either a bug,
// or this Envoy instance received config which is not yet supported. This should not be fatal
// error.
Expand Down
1 change: 1 addition & 0 deletions test/integration/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,7 @@ envoy_cc_test_library(
],
deps = [
":http_protocol_integration_lib",
":socket_interface_swap_lib",
"//source/common/http:header_map_lib",
"//source/extensions/filters/http/buffer:config",
"//test/common/http/http2:http2_frame",
Expand Down
22 changes: 22 additions & 0 deletions test/integration/protocol_integration_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include "test/common/upstream/utility.h"
#include "test/integration/autonomous_upstream.h"
#include "test/integration/http_integration.h"
#include "test/integration/socket_interface_swap.h"
#include "test/integration/test_host_predicate_config.h"
#include "test/integration/utility.h"
#include "test/mocks/upstream/retry_priority.h"
Expand Down Expand Up @@ -3525,4 +3526,25 @@ TEST_P(DownstreamProtocolIntegrationTest, ContentLengthLargerThanPayload) {
EXPECT_EQ(Http::StreamResetReason::RemoteReset, response->resetReason());
}

TEST_P(DownstreamProtocolIntegrationTest, HandleSocketFail) {
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.

Comment on what this is testing?

SocketInterfaceSwap socket_swap;
if (downstreamProtocol() == Http::CodecType::HTTP3) {
return;
}

initialize();
codec_client_ = makeHttpConnection(lookupPort("http"));
auto response = codec_client_->makeHeaderOnlyRequest(default_request_headers_);
waitForNextUpstreamRequest();

// Makes us have Envoy's writes to downstream return EBADF
Network::IoSocketError* ebadf = Network::IoSocketError::getIoSocketEbadfInstance();
socket_swap.writev_matcher_->setSourcePort(lookupPort("http"));
socket_swap.writev_matcher_->setWritevOverride(ebadf);
upstream_request_->encodeHeaders(Http::TestResponseHeaderMapImpl{{":status", "200"}}, false);

ASSERT_TRUE(codec_client_->waitForDisconnect());
socket_swap.writev_matcher_->setWritevOverride(nullptr);
}

} // namespace Envoy
8 changes: 4 additions & 4 deletions test/integration/socket_interface_swap.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ SocketInterfaceSwap::SocketInterfaceSwap() {
[writev_matcher = writev_matcher_](Envoy::Network::TestIoSocketHandle* io_handle,
const Buffer::RawSlice*,
uint64_t) -> absl::optional<Api::IoCallUint64Result> {
if (writev_matcher->shouldReturnEgain(io_handle)) {
Network::IoSocketError* error_override = writev_matcher->returnOverride(io_handle);
if (error_override) {
return Api::IoCallUint64Result(
0, Api::IoErrorPtr(Network::IoSocketError::getIoSocketEagainInstance(),
Network::IoSocketError::deleteIoError));
0, Api::IoErrorPtr(error_override, Network::IoSocketError::deleteIoError));
}
return absl::nullopt;
}));
Expand All @@ -23,7 +23,7 @@ void SocketInterfaceSwap::IoHandleMatcher::setResumeWrites() {
mutex_.Await(absl::Condition(
+[](Network::TestIoSocketHandle** matched_iohandle) { return *matched_iohandle != nullptr; },
&matched_iohandle_));
writev_returns_egain_ = false;
error_ = nullptr;
matched_iohandle_->activateInDispatcherThread(Event::FileReadyType::Write);
}

Expand Down
20 changes: 12 additions & 8 deletions test/integration/socket_interface_swap.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,16 @@ class SocketInterfaceSwap {
// Object of this class hold the state determining the IoHandle which
// should return EAGAIN from the `writev` call.
struct IoHandleMatcher {
bool shouldReturnEgain(Envoy::Network::TestIoSocketHandle* io_handle) {
Network::IoSocketError* returnOverride(Envoy::Network::TestIoSocketHandle* io_handle) {
absl::MutexLock lock(&mutex_);
if (writev_returns_egain_ && (io_handle->localAddress()->ip()->port() == src_port_ ||
io_handle->peerAddress()->ip()->port() == dst_port_)) {
if (error_ && (io_handle->localAddress()->ip()->port() == src_port_ ||
io_handle->peerAddress()->ip()->port() == dst_port_)) {
ASSERT(matched_iohandle_ == nullptr || matched_iohandle_ == io_handle,
"Matched multiple io_handles, expected at most one to match.");
matched_iohandle_ = io_handle;
return true;
return error_;
}
return false;
return nullptr;
}

// Source port to match. The port specified should be associated with a listener.
Expand All @@ -41,9 +41,14 @@ class SocketInterfaceSwap {
}

void setWritevReturnsEgain() {
setWritevOverride(Network::IoSocketError::getIoSocketEagainInstance());
}

// The caller is responsible for memory management.
void setWritevOverride(Network::IoSocketError* error) {
absl::WriterMutexLock lock(&mutex_);
ASSERT(src_port_ != 0 || dst_port_ != 0);
writev_returns_egain_ = true;
error_ = error;
}

void setResumeWrites();
Expand All @@ -52,7 +57,7 @@ class SocketInterfaceSwap {
mutable absl::Mutex mutex_;
uint32_t src_port_ ABSL_GUARDED_BY(mutex_) = 0;
uint32_t dst_port_ ABSL_GUARDED_BY(mutex_) = 0;
bool writev_returns_egain_ ABSL_GUARDED_BY(mutex_) = false;
Network::IoSocketError* error_ ABSL_GUARDED_BY(mutex_) = nullptr;
Network::TestIoSocketHandle* matched_iohandle_{};
};

Expand All @@ -63,7 +68,6 @@ class SocketInterfaceSwap {
Envoy::Network::SocketInterfaceSingleton::initialize(previous_socket_interface_);
}

protected:
Envoy::Network::SocketInterface* const previous_socket_interface_{
Envoy::Network::SocketInterfaceSingleton::getExisting()};
std::shared_ptr<IoHandleMatcher> writev_matcher_{std::make_shared<IoHandleMatcher>()};
Expand Down