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 =