Adds a test comparing path character validation across implementations. PiperOrigin-RevId: 714147396
diff --git a/quiche/http2/adapter/adapter_impl_comparison_test.cc b/quiche/http2/adapter/adapter_impl_comparison_test.cc index 1713bd8..0c9d925 100644 --- a/quiche/http2/adapter/adapter_impl_comparison_test.cc +++ b/quiche/http2/adapter/adapter_impl_comparison_test.cc
@@ -3,6 +3,7 @@ #include <vector> #include "quiche/http2/adapter/http2_protocol.h" +#include "quiche/http2/adapter/mock_http2_visitor.h" #include "quiche/http2/adapter/nghttp2_adapter.h" #include "quiche/http2/adapter/oghttp2_adapter.h" #include "quiche/http2/adapter/recording_http2_visitor.h" @@ -15,6 +16,92 @@ namespace test { namespace { +using ::testing::Each; +using ::testing::InvokeWithoutArgs; + +enum class Impl { + kNgHttp2, + kOgHttp2, +}; + +class ComparisonTest : public ::quiche::test::QuicheTest { + public: + std::vector<Impl> implementations() { + return {Impl::kNgHttp2, Impl::kOgHttp2}; + } + + std::unique_ptr<Http2Adapter> CreateAdapter(Http2VisitorInterface& visitor, + Impl impl, Perspective p) { + switch (impl) { + case Impl::kNgHttp2: + if (p == Perspective::kClient) { + return NgHttp2Adapter::CreateClientAdapter(visitor); + } else { + return NgHttp2Adapter::CreateServerAdapter(visitor); + } + case Impl::kOgHttp2: + OgHttp2Adapter::Options options; + options.perspective = p; + return OgHttp2Adapter::Create(visitor, options); + } + } +}; + +// Verifies that the implementations consider the same set of characters valid +// in paths. +TEST_F(ComparisonTest, PathCharValidation) { + // Iterates over all character values. + for (int i = std::numeric_limits<char>::min(); + i < std::numeric_limits<char>::max(); ++i) { + const char c = static_cast<char>(i); + + // oghttp2 permits tab and space in path, unless the path validation option + // is enabled + if (c == ' ' || c == '\t') { + continue; + } + + // Constructs a path with the desired character. + const std::string path_value = + absl::StrCat("/aaa", absl::string_view(&c, 1), "bbb"); + + SCOPED_TRACE(absl::StrCat("Path: [", absl::CEscape(path_value), "]")); + + // Constructs a request with the desired :path pseudoheader. + const std::string frames = TestFrameSequence() + .ClientPreface() + .Headers(1, + {{":method", "GET"}, + {":scheme", "https"}, + {":authority", "example.com"}, + {":path", path_value}, + {"name", "value"}}, + /*fin=*/true) + .Serialize(); + // Accumulates frame validation results. + std::vector<bool> frame_valid_results; + bool frame_valid = true; + + testing::NiceMock<MockHttp2Visitor> visitor; + ON_CALL(visitor, OnInvalidFrame) + .WillByDefault(InvokeWithoutArgs([&frame_valid]() { + // Records that the frame was not valid. + frame_valid = false; + return true; + })); + + for (Impl impl : implementations()) { + frame_valid = true; + auto adapter = CreateAdapter(visitor, impl, Perspective::kServer); + const int64_t result = adapter->ProcessBytes(frames); + EXPECT_EQ(frames.size(), static_cast<size_t>(result)); + frame_valid_results.push_back(frame_valid); + } + // All implementations should agree on whether the frame was valid. + EXPECT_THAT(frame_valid_results, Each(frame_valid_results.back())); + } +} + TEST(AdapterImplComparisonTest, ClientHandlesFrames) { RecordingHttp2Visitor nghttp2_visitor; std::unique_ptr<NgHttp2Adapter> nghttp2_adapter =