Skip to content

Commit dc84fe5

Browse files
authored
backport to rel1.32: http_inspector dynamic buffering #37002 (#39065)
1 parent d4b45a2 commit dc84fe5

File tree

4 files changed

+121
-12
lines changed

4 files changed

+121
-12
lines changed

source/extensions/filters/listener/http_inspector/config.cc

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
#include "envoy/extensions/filters/listener/http_inspector/v3/http_inspector.pb.h"
21
#include "envoy/extensions/filters/listener/http_inspector/v3/http_inspector.pb.validate.h"
32
#include "envoy/registry/registry.h"
43
#include "envoy/server/filter_config.h"

source/extensions/filters/listener/http_inspector/http_inspector.cc

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ Config::Config(Stats::Scope& scope)
2323

2424
const absl::string_view Filter::HTTP2_CONNECTION_PREFACE = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
2525

26-
Filter::Filter(const ConfigSharedPtr config) : config_(config) {
26+
Filter::Filter(const ConfigSharedPtr config)
27+
: config_(config), requested_read_bytes_(Config::DEFAULT_INITIAL_BUFFER_SIZE) {
2728
http_parser_init(&parser_, HTTP_REQUEST);
2829
}
2930

@@ -44,6 +45,24 @@ Network::FilterStatus Filter::onData(Network::ListenerFilterBuffer& buffer) {
4445
done(true);
4546
return Network::FilterStatus::Continue;
4647
case ParseState::Continue:
48+
ENVOY_LOG(trace, "http inspector: need more bytes");
49+
50+
// If we have requested the maximum amount of data, then close the connection
51+
// the request line is too large to determine the http version.
52+
if (static_cast<size_t>(parser_.nread) >= Config::MAX_INSPECT_SIZE) {
53+
ENVOY_LOG(warn, "http inspector: reached max buffer without determining HTTP version, "
54+
"dropping connection");
55+
config_->stats().read_error_.inc();
56+
cb_->socket().ioHandle().close();
57+
return Network::FilterStatus::StopIteration;
58+
}
59+
60+
// Otherwise, double the buffer size and try again
61+
if (static_cast<size_t>(parser_.nread) >= requested_read_bytes_) {
62+
requested_read_bytes_ =
63+
std::min<uint32_t>(2 * requested_read_bytes_, Config::MAX_INSPECT_SIZE);
64+
}
65+
4766
return Network::FilterStatus::StopIteration;
4867
}
4968
PANIC_DUE_TO_CORRUPT_ENUM

source/extensions/filters/listener/http_inspector/http_inspector.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ class Config {
5252

5353
const HttpInspectorStats& stats() const { return stats_; }
5454

55-
static constexpr uint32_t MAX_INSPECT_SIZE = 8192;
55+
static constexpr uint32_t DEFAULT_INITIAL_BUFFER_SIZE = 8 * 1024;
56+
static constexpr uint32_t MAX_INSPECT_SIZE = 64 * 1024;
5657

5758
private:
5859
HttpInspectorStats stats_;
@@ -71,7 +72,7 @@ class Filter : public Network::ListenerFilter, Logger::Loggable<Logger::Id::filt
7172
Network::FilterStatus onAccept(Network::ListenerFilterCallbacks& cb) override;
7273
Network::FilterStatus onData(Network::ListenerFilterBuffer& buffer) override;
7374

74-
size_t maxReadBytes() const override { return Config::MAX_INSPECT_SIZE; }
75+
size_t maxReadBytes() const override { return requested_read_bytes_; }
7576

7677
private:
7778
static const absl::string_view HTTP2_CONNECTION_PREFACE;
@@ -87,6 +88,7 @@ class Filter : public Network::ListenerFilter, Logger::Loggable<Logger::Id::filt
8788
absl::string_view protocol_;
8889
http_parser parser_;
8990
static http_parser_settings settings_;
91+
size_t requested_read_bytes_ = 0;
9092
};
9193

9294
} // namespace HttpInspector

test/extensions/filters/listener/http_inspector/http_inspector_test.cc

Lines changed: 97 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include "source/common/common/hex.h"
2+
#include "source/common/common/logger.h"
23
#include "source/common/http/utility.h"
34
#include "source/common/network/io_socket_handle_impl.h"
45
#include "source/common/network/listener_filter_buffer_impl.h"
@@ -33,8 +34,6 @@ class HttpInspectorTest : public testing::Test {
3334
: cfg_(std::make_shared<Config>(*store_.rootScope())),
3435
io_handle_(Network::SocketInterfaceImpl::makePlatformSpecificSocket(42, false,
3536
absl::nullopt, {})) {}
36-
~HttpInspectorTest() override { io_handle_->close(); }
37-
3837
void init() {
3938
filter_ = std::make_unique<Filter>(cfg_);
4039

@@ -50,7 +49,7 @@ class HttpInspectorTest : public testing::Test {
5049
DoAll(SaveArg<1>(&file_event_callback_), ReturnNew<NiceMock<Event::MockFileEvent>>()));
5150
buffer_ = std::make_unique<Network::ListenerFilterBufferImpl>(
5251
*io_handle_, dispatcher_, [](bool) {}, [](Network::ListenerFilterBuffer&) {},
53-
filter_->maxReadBytes() == 0, filter_->maxReadBytes());
52+
Config::DEFAULT_INITIAL_BUFFER_SIZE == 0, Config::DEFAULT_INITIAL_BUFFER_SIZE);
5453
}
5554

5655
void testHttpInspectMultipleReadsNotFound(absl::string_view header, bool http2 = false) {
@@ -516,7 +515,7 @@ TEST_F(HttpInspectorTest, Http1WithLargeRequestLine) {
516515
// multiple recv calls.
517516
init();
518517
absl::string_view method = "GET", http = "/index HTTP/1.0\r";
519-
std::string spaces(Config::MAX_INSPECT_SIZE - method.size() - http.size(), ' ');
518+
std::string spaces(Config::DEFAULT_INITIAL_BUFFER_SIZE - method.size() - http.size(), ' ');
520519
const std::string data = absl::StrCat(method, spaces, http);
521520
{
522521
InSequence s;
@@ -529,7 +528,7 @@ TEST_F(HttpInspectorTest, Http1WithLargeRequestLine) {
529528
}));
530529
#endif
531530

532-
uint64_t num_loops = Config::MAX_INSPECT_SIZE;
531+
uint64_t num_loops = Config::DEFAULT_INITIAL_BUFFER_SIZE;
533532
#if defined(__has_feature) && \
534533
((__has_feature(thread_sanitizer)) || (__has_feature(address_sanitizer)))
535534
num_loops = 2;
@@ -556,7 +555,7 @@ TEST_F(HttpInspectorTest, Http1WithLargeRequestLine) {
556555
size_t len = (*ctr);
557556
if (num_loops == 2) {
558557
ASSERT(*ctr != 3);
559-
len = size_t(Config::MAX_INSPECT_SIZE / (3 - (*ctr)));
558+
len = size_t(Config::DEFAULT_INITIAL_BUFFER_SIZE / (3 - (*ctr)));
560559
}
561560
ASSERT(length >= len);
562561
memcpy(buffer, data.data(), len);
@@ -584,8 +583,8 @@ TEST_F(HttpInspectorTest, Http1WithLargeRequestLine) {
584583
TEST_F(HttpInspectorTest, Http1WithLargeHeader) {
585584
init();
586585
absl::string_view request = "GET /index HTTP/1.0\rfield: ";
587-
// 0 20
588-
std::string value(Config::MAX_INSPECT_SIZE - request.size(), 'a');
586+
// 0 20
587+
std::string value(Config::DEFAULT_INITIAL_BUFFER_SIZE - request.size(), 'a');
589588
const std::string data = absl::StrCat(request, value);
590589

591590
{
@@ -635,6 +634,96 @@ TEST_F(HttpInspectorTest, Http1WithLargeHeader) {
635634
EXPECT_EQ(1, cfg_->stats().http10_found_.value());
636635
}
637636

637+
TEST_F(HttpInspectorTest, HttpExceedMaxBufferSize) {
638+
absl::string_view method = "GET", http = "/index HTTP/1.0\r\n";
639+
std::string spaces(Config::MAX_INSPECT_SIZE, ' ');
640+
const std::string data = absl::StrCat(method, spaces, http);
641+
642+
cfg_ = std::make_shared<Config>(*store_.rootScope());
643+
644+
init();
645+
buffer_->resetCapacity(Config::MAX_INSPECT_SIZE);
646+
647+
EXPECT_CALL(os_sys_calls_, recv(42, _, _, MSG_PEEK))
648+
.WillOnce(
649+
Invoke([&data](os_fd_t, void* buffer, size_t length, int) -> Api::SysCallSizeResult {
650+
const size_t amount_to_copy = std::min(length, data.size());
651+
memcpy(buffer, data.data(), amount_to_copy);
652+
return Api::SysCallSizeResult{ssize_t(amount_to_copy), 0};
653+
}));
654+
655+
EXPECT_TRUE(file_event_callback_(Event::FileReadyType::Read).ok());
656+
657+
auto accepted = filter_->onAccept(cb_);
658+
EXPECT_EQ(accepted, Network::FilterStatus::StopIteration);
659+
auto status = filter_->onData(*buffer_);
660+
EXPECT_EQ(Network::FilterStatus::StopIteration, status);
661+
EXPECT_EQ(1, cfg_->stats().read_error_.value());
662+
EXPECT_FALSE(io_handle_->isOpen());
663+
}
664+
665+
TEST_F(HttpInspectorTest, HttpExceedInitialBufferSize) {
666+
uint32_t buffer_size = Config::DEFAULT_INITIAL_BUFFER_SIZE;
667+
668+
absl::string_view method = "GET", http = "/index HTTP/1.0\r\n";
669+
670+
// Want to test doubling a couple times.
671+
std::string spaces(buffer_size * 3, ' ');
672+
673+
const std::string data = absl::StrCat(method, spaces, http);
674+
675+
init();
676+
677+
EXPECT_CALL(os_sys_calls_, recv(42, _, _, MSG_PEEK))
678+
.WillOnce(
679+
Invoke([&data](os_fd_t, void* buffer, size_t length, int) -> Api::SysCallSizeResult {
680+
const size_t amount_to_copy = std::min(length, data.size());
681+
memcpy(buffer, data.data(), amount_to_copy);
682+
return Api::SysCallSizeResult{ssize_t(amount_to_copy), 0};
683+
}));
684+
EXPECT_TRUE(file_event_callback_(Event::FileReadyType::Read).ok());
685+
686+
auto accepted = filter_->onAccept(cb_);
687+
EXPECT_EQ(accepted, Network::FilterStatus::StopIteration);
688+
auto status = filter_->onData(*buffer_);
689+
EXPECT_EQ(status, Network::FilterStatus::StopIteration);
690+
EXPECT_EQ(filter_->maxReadBytes(), 2 * buffer_size);
691+
buffer_size *= 2;
692+
buffer_->resetCapacity(buffer_size);
693+
694+
EXPECT_CALL(os_sys_calls_, recv(42, _, _, MSG_PEEK))
695+
.WillOnce(
696+
Invoke([&data](os_fd_t, void* buffer, size_t length, int) -> Api::SysCallSizeResult {
697+
const size_t amount_to_copy = std::min(length, data.size());
698+
memcpy(buffer, data.data(), amount_to_copy);
699+
return Api::SysCallSizeResult{ssize_t(amount_to_copy), 0};
700+
}));
701+
EXPECT_TRUE(file_event_callback_(Event::FileReadyType::Read).ok());
702+
703+
status = filter_->onData(*buffer_);
704+
EXPECT_EQ(status, Network::FilterStatus::StopIteration);
705+
EXPECT_EQ(filter_->maxReadBytes(), 2 * buffer_size);
706+
buffer_size *= 2;
707+
buffer_->resetCapacity(buffer_size);
708+
709+
EXPECT_CALL(os_sys_calls_, recv(42, _, _, MSG_PEEK))
710+
.WillOnce(
711+
Invoke([&data](os_fd_t, void* buffer, size_t length, int) -> Api::SysCallSizeResult {
712+
const size_t amount_to_copy = std::min(length, data.size());
713+
memcpy(buffer, data.data(), amount_to_copy);
714+
return Api::SysCallSizeResult{ssize_t(amount_to_copy), 0};
715+
}));
716+
EXPECT_TRUE(file_event_callback_(Event::FileReadyType::Read).ok());
717+
718+
const std::vector<absl::string_view> alpn_protos{Http::Utility::AlpnNames::get().Http10};
719+
EXPECT_CALL(socket_, setRequestedApplicationProtocols(alpn_protos));
720+
721+
status = filter_->onData(*buffer_);
722+
EXPECT_EQ(status, Network::FilterStatus::Continue);
723+
724+
EXPECT_EQ(1, cfg_->stats().http10_found_.value());
725+
}
726+
638727
} // namespace
639728
} // namespace HttpInspector
640729
} // namespace ListenerFilters

0 commit comments

Comments
 (0)