summaryrefslogtreecommitdiff
path: root/chromium/net/spdy/header_coalescer_test.cc
blob: 94ca09d40224b95c4a50bec928e06655884b5182 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
// Copyright 2016 The Chromium Authors
// 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/net_log.h"
#include "net/log/test_net_log.h"
#include "net/log/test_net_log_util.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::test {

class HeaderCoalescerTest : public ::testing::Test {
 public:
  HeaderCoalescerTest()
      : header_coalescer_(kMaxHeaderListSizeForTest, net_log_with_source_) {}

  void ExpectEntry(base::StringPiece expected_header_name,
                   base::StringPiece expected_header_value,
                   base::StringPiece expected_error_message) {
    auto entry_list = net_log_observer_.GetEntries();
    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_with_source_.source().id);
    std::string value;
    EXPECT_EQ(expected_header_name,
              GetStringValueFromParams(entry_list[0], "header_name"));
    EXPECT_EQ(expected_header_value,
              GetStringValueFromParams(entry_list[0], "header_value"));
    EXPECT_EQ(expected_error_message,
              GetStringValueFromParams(entry_list[0], "error"));
  }

 protected:
  NetLogWithSource net_log_with_source_{
      NetLogWithSource::Make(NetLog::Get(), NetLogSourceType::NONE)};
  RecordingNetLogObserver net_log_observer_;
  HeaderCoalescer header_coalescer_;
};

TEST_F(HeaderCoalescerTest, CorrectHeaders) {
  header_coalescer_.OnHeader(":foo", "bar");
  header_coalescer_.OnHeader("baz", "qux");
  EXPECT_FALSE(header_coalescer_.error_seen());

  spdy::Http2HeaderBlock 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.
  header_coalescer_.OnHeader("bar", "abcd");
  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::Http2HeaderBlock header_block = header_coalescer_.release_headers();
  EXPECT_THAT(header_block,
              ElementsAre(Pair("foo", absl::string_view("bar\0quux", 8)),
                          Pair("cookie", "baz; qux")));
}

TEST_F(HeaderCoalescerTest, HeaderNameNotValid) {
  absl::string_view header_name("\x1\x7F\x80\xFF");
  header_coalescer_.OnHeader(header_name, "foo");
  EXPECT_TRUE(header_coalescer_.error_seen());
  ExpectEntry("%ESCAPED:\xE2\x80\x8B \x1\x7F%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) {
  absl::string_view 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.
  absl::string_view header_name(
      "abcdefghijklmnopqrstuvwxyz0123456789!#$%&'*+-."
      "^_`|~");
  header_coalescer_.OnHeader(header_name, "foo");
  EXPECT_FALSE(header_coalescer_.error_seen());
  spdy::Http2HeaderBlock 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\nbaz", "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\rbaz", "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\x7F baz", "Invalid character 0x7F in header value.");
}

}  // namespace net::test