| // Copyright 2016 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "net/spdy/header_coalescer.h" |
| |
| #include <string> |
| #include <vector> |
| |
| #include "net/log/test_net_log.h" |
| #include "net/spdy/spdy_test_util_common.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using ::testing::ElementsAre; |
| using ::testing::Pair; |
| |
| namespace net { |
| namespace test { |
| |
| class HeaderCoalescerTest : public ::testing::Test { |
| public: |
| HeaderCoalescerTest() |
| : header_coalescer_(kMaxHeaderListSizeForTest, net_log_.bound()) {} |
| |
| void ExpectEntry(base::StringPiece expected_header_name, |
| base::StringPiece expected_header_value, |
| base::StringPiece expected_error_message) { |
| TestNetLogEntry::List entry_list; |
| net_log_.GetEntries(&entry_list); |
| ASSERT_EQ(1u, entry_list.size()); |
| EXPECT_EQ(entry_list[0].type, |
| NetLogEventType::HTTP2_SESSION_RECV_INVALID_HEADER); |
| EXPECT_EQ(entry_list[0].source.id, net_log_.bound().source().id); |
| std::string value; |
| EXPECT_TRUE(entry_list[0].GetStringValue("header_name", &value)); |
| EXPECT_EQ(expected_header_name, value); |
| EXPECT_TRUE(entry_list[0].GetStringValue("header_value", &value)); |
| EXPECT_EQ(expected_header_value, value); |
| EXPECT_TRUE(entry_list[0].GetStringValue("error", &value)); |
| EXPECT_EQ(expected_error_message, value); |
| } |
| |
| protected: |
| BoundTestNetLog net_log_; |
| HeaderCoalescer header_coalescer_; |
| }; |
| |
| TEST_F(HeaderCoalescerTest, CorrectHeaders) { |
| header_coalescer_.OnHeader(":foo", "bar"); |
| header_coalescer_.OnHeader("baz", "qux"); |
| EXPECT_FALSE(header_coalescer_.error_seen()); |
| |
| spdy::SpdyHeaderBlock header_block = header_coalescer_.release_headers(); |
| EXPECT_THAT(header_block, |
| ElementsAre(Pair(":foo", "bar"), Pair("baz", "qux"))); |
| } |
| |
| TEST_F(HeaderCoalescerTest, EmptyHeaderKey) { |
| header_coalescer_.OnHeader("", "foo"); |
| EXPECT_TRUE(header_coalescer_.error_seen()); |
| ExpectEntry("", "foo", "Header name must not be empty."); |
| } |
| |
| TEST_F(HeaderCoalescerTest, HeaderBlockTooLarge) { |
| // key + value + overhead = 3 + kMaxHeaderListSizeForTest - 40 + 32 |
| // = kMaxHeaderListSizeForTest - 5 |
| std::string data(kMaxHeaderListSizeForTest - 40, 'a'); |
| header_coalescer_.OnHeader("foo", data); |
| EXPECT_FALSE(header_coalescer_.error_seen()); |
| |
| // Another 3 + 4 + 32 bytes: too large. |
| base::StringPiece header_value("abcd"); |
| header_coalescer_.OnHeader("bar", header_value); |
| EXPECT_TRUE(header_coalescer_.error_seen()); |
| ExpectEntry("bar", "abcd", "Header list too large."); |
| } |
| |
| TEST_F(HeaderCoalescerTest, PseudoHeadersMustNotFollowRegularHeaders) { |
| header_coalescer_.OnHeader("foo", "bar"); |
| EXPECT_FALSE(header_coalescer_.error_seen()); |
| header_coalescer_.OnHeader(":baz", "qux"); |
| EXPECT_TRUE(header_coalescer_.error_seen()); |
| ExpectEntry(":baz", "qux", "Pseudo header must not follow regular headers."); |
| } |
| |
| TEST_F(HeaderCoalescerTest, Append) { |
| header_coalescer_.OnHeader("foo", "bar"); |
| header_coalescer_.OnHeader("cookie", "baz"); |
| header_coalescer_.OnHeader("foo", "quux"); |
| header_coalescer_.OnHeader("cookie", "qux"); |
| EXPECT_FALSE(header_coalescer_.error_seen()); |
| |
| spdy::SpdyHeaderBlock header_block = header_coalescer_.release_headers(); |
| EXPECT_THAT(header_block, |
| ElementsAre(Pair("foo", base::StringPiece("bar\0quux", 8)), |
| Pair("cookie", "baz; qux"))); |
| } |
| |
| TEST_F(HeaderCoalescerTest, HeaderNameNotValid) { |
| base::StringPiece header_name("\x1\x7F\x80\xFF"); |
| header_coalescer_.OnHeader(header_name, "foo"); |
| EXPECT_TRUE(header_coalescer_.error_seen()); |
| ExpectEntry("%01%7F%80%FF", "foo", "Invalid character in header name."); |
| } |
| |
| // RFC 7540 Section 8.1.2.6. Uppercase in header name is invalid. |
| TEST_F(HeaderCoalescerTest, HeaderNameHasUppercase) { |
| base::StringPiece header_name("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); |
| header_coalescer_.OnHeader(header_name, "foo"); |
| EXPECT_TRUE(header_coalescer_.error_seen()); |
| ExpectEntry("ABCDEFGHIJKLMNOPQRSTUVWXYZ", "foo", |
| "Upper case characters in header name."); |
| } |
| |
| // RFC 7230 Section 3.2. Valid header name is defined as: |
| // field-name = token |
| // token = 1*tchar |
| // tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." / |
| // "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA |
| TEST_F(HeaderCoalescerTest, HeaderNameValid) { |
| // Due to RFC 7540 Section 8.1.2.6. Uppercase characters are not included. |
| base::StringPiece header_name( |
| "abcdefghijklmnopqrstuvwxyz0123456789!#$%&'*+-." |
| "^_`|~"); |
| header_coalescer_.OnHeader(header_name, "foo"); |
| EXPECT_FALSE(header_coalescer_.error_seen()); |
| spdy::SpdyHeaderBlock header_block = header_coalescer_.release_headers(); |
| EXPECT_THAT(header_block, ElementsAre(Pair(header_name, "foo"))); |
| } |
| |
| // According to RFC 7540 Section 10.3 and RFC 7230 Section 3.2, allowed |
| // characters in header values are '\t', ' ', 0x21 to 0x7E, and 0x80 to 0xFF. |
| TEST_F(HeaderCoalescerTest, HeaderValueValid) { |
| header_coalescer_.OnHeader("foo", " bar \x21 \x7e baz\tqux\x80\xff "); |
| EXPECT_FALSE(header_coalescer_.error_seen()); |
| } |
| |
| TEST_F(HeaderCoalescerTest, HeaderValueContainsLF) { |
| header_coalescer_.OnHeader("foo", "bar\nbaz"); |
| EXPECT_TRUE(header_coalescer_.error_seen()); |
| ExpectEntry("foo", "bar%0Abaz", "Invalid character 0x0A in header value."); |
| } |
| |
| TEST_F(HeaderCoalescerTest, HeaderValueContainsCR) { |
| header_coalescer_.OnHeader("foo", "bar\rbaz"); |
| EXPECT_TRUE(header_coalescer_.error_seen()); |
| ExpectEntry("foo", "bar%0Dbaz", "Invalid character 0x0D in header value."); |
| } |
| |
| TEST_F(HeaderCoalescerTest, HeaderValueContains0x7f) { |
| header_coalescer_.OnHeader("foo", "bar\x7f baz"); |
| EXPECT_TRUE(header_coalescer_.error_seen()); |
| ExpectEntry("foo", "bar%7F%20baz", "Invalid character 0x7F in header value."); |
| } |
| |
| } // namespace test |
| |
| } // namespace net |