OLD | NEW |
| (Empty) |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include <cmath> | |
6 #include <ctime> | |
7 #include <map> | |
8 #include <string> | |
9 #include <vector> | |
10 | |
11 #include "base/rand_util.h" | |
12 #include "net/spdy/hpack_constants.h" | |
13 #include "net/spdy/hpack_decoder.h" | |
14 #include "net/spdy/hpack_encoder.h" | |
15 #include "testing/gtest/include/gtest/gtest.h" | |
16 | |
17 namespace net { | |
18 | |
19 using std::map; | |
20 using std::string; | |
21 using std::vector; | |
22 | |
23 namespace { | |
24 | |
25 class HpackRoundTripTest : public ::testing::Test { | |
26 protected: | |
27 HpackRoundTripTest() | |
28 : encoder_(ObtainHpackHuffmanTable()), | |
29 decoder_(ObtainHpackHuffmanTable()) {} | |
30 | |
31 void SetUp() override { | |
32 // Use a small table size to tickle eviction handling. | |
33 encoder_.ApplyHeaderTableSizeSetting(256); | |
34 decoder_.ApplyHeaderTableSizeSetting(256); | |
35 } | |
36 | |
37 bool RoundTrip(const map<string, string>& header_set) { | |
38 string encoded; | |
39 encoder_.EncodeHeaderSet(header_set, &encoded); | |
40 | |
41 bool success = decoder_.HandleControlFrameHeadersData( | |
42 1, encoded.data(), encoded.size()); | |
43 success &= decoder_.HandleControlFrameHeadersComplete(1); | |
44 | |
45 EXPECT_EQ(header_set, decoder_.decoded_block()); | |
46 return success; | |
47 } | |
48 | |
49 size_t SampleExponential(size_t mean, size_t sanity_bound) { | |
50 return std::min<size_t>(-std::log(base::RandDouble()) * mean, | |
51 sanity_bound); | |
52 } | |
53 | |
54 HpackEncoder encoder_; | |
55 HpackDecoder decoder_; | |
56 }; | |
57 | |
58 TEST_F(HpackRoundTripTest, ResponseFixtures) { | |
59 { | |
60 map<string, string> headers; | |
61 headers[":status"] = "302"; | |
62 headers["cache-control"] = "private"; | |
63 headers["date"] = "Mon, 21 Oct 2013 20:13:21 GMT"; | |
64 headers["location"] = "https://www.example.com"; | |
65 EXPECT_TRUE(RoundTrip(headers)); | |
66 } | |
67 { | |
68 map<string, string> headers; | |
69 headers[":status"] = "200"; | |
70 headers["cache-control"] = "private"; | |
71 headers["date"] = "Mon, 21 Oct 2013 20:13:21 GMT"; | |
72 headers["location"] = "https://www.example.com"; | |
73 EXPECT_TRUE(RoundTrip(headers)); | |
74 } | |
75 { | |
76 map<string, string> headers; | |
77 headers[":status"] = "200"; | |
78 headers["cache-control"] = "private"; | |
79 headers["content-encoding"] = "gzip"; | |
80 headers["date"] = "Mon, 21 Oct 2013 20:13:22 GMT"; | |
81 headers["location"] = "https://www.example.com"; | |
82 headers["set-cookie"] = "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU;" | |
83 " max-age=3600; version=1"; | |
84 headers["multivalue"] = string("foo\0bar", 7); | |
85 EXPECT_TRUE(RoundTrip(headers)); | |
86 } | |
87 } | |
88 | |
89 TEST_F(HpackRoundTripTest, RequestFixtures) { | |
90 { | |
91 map<string, string> headers; | |
92 headers[":authority"] = "www.example.com"; | |
93 headers[":method"] = "GET"; | |
94 headers[":path"] = "/"; | |
95 headers[":scheme"] = "http"; | |
96 headers["cookie"] = "baz=bing; foo=bar"; | |
97 EXPECT_TRUE(RoundTrip(headers)); | |
98 } | |
99 { | |
100 map<string, string> headers; | |
101 headers[":authority"] = "www.example.com"; | |
102 headers[":method"] = "GET"; | |
103 headers[":path"] = "/"; | |
104 headers[":scheme"] = "http"; | |
105 headers["cache-control"] = "no-cache"; | |
106 headers["cookie"] = "foo=bar; spam=eggs"; | |
107 EXPECT_TRUE(RoundTrip(headers)); | |
108 } | |
109 { | |
110 map<string, string> headers; | |
111 headers[":authority"] = "www.example.com"; | |
112 headers[":method"] = "GET"; | |
113 headers[":path"] = "/index.html"; | |
114 headers[":scheme"] = "https"; | |
115 headers["custom-key"] = "custom-value"; | |
116 headers["cookie"] = "baz=bing; fizzle=fazzle; garbage"; | |
117 headers["multivalue"] = string("foo\0bar", 7); | |
118 EXPECT_TRUE(RoundTrip(headers)); | |
119 } | |
120 } | |
121 | |
122 TEST_F(HpackRoundTripTest, RandomizedExamples) { | |
123 // Grow vectors of names & values, which are seeded with fixtures and then | |
124 // expanded with dynamically generated data. Samples are taken using the | |
125 // exponential distribution. | |
126 vector<string> names; | |
127 names.push_back(":authority"); | |
128 names.push_back(":path"); | |
129 names.push_back(":status"); | |
130 // TODO(jgraettinger): Enable "cookie" as a name fixture. Crumbs may be | |
131 // reconstructed in any order, which breaks the simple validation used here. | |
132 | |
133 vector<string> values; | |
134 values.push_back("/"); | |
135 values.push_back("/index.html"); | |
136 values.push_back("200"); | |
137 values.push_back("404"); | |
138 values.push_back(""); | |
139 values.push_back("baz=bing; foo=bar; garbage"); | |
140 values.push_back("baz=bing; fizzle=fazzle; garbage"); | |
141 | |
142 int seed = std::time(NULL); | |
143 LOG(INFO) << "Seeding with srand(" << seed << ")"; | |
144 srand(seed); | |
145 | |
146 for (size_t i = 0; i != 2000; ++i) { | |
147 map<string, string> headers; | |
148 | |
149 size_t header_count = 1 + SampleExponential(7, 50); | |
150 for (size_t j = 0; j != header_count; ++j) { | |
151 size_t name_index = SampleExponential(20, 200); | |
152 size_t value_index = SampleExponential(20, 200); | |
153 | |
154 string name, value; | |
155 if (name_index >= names.size()) { | |
156 names.push_back(base::RandBytesAsString(1 + SampleExponential(5, 30))); | |
157 name = names.back(); | |
158 } else { | |
159 name = names[name_index]; | |
160 } | |
161 if (value_index >= values.size()) { | |
162 string newvalue = | |
163 base::RandBytesAsString(1 + SampleExponential(15, 75)); | |
164 // Currently order is not preserved in the encoder. In particular, | |
165 // when a value is decomposed at \0 delimiters, its parts might get | |
166 // encoded out of order if some but not all of them already exist in | |
167 // the header table. For now, avoid \0 bytes in values. | |
168 std::replace(newvalue.begin(), newvalue.end(), '\x00', '\x01'); | |
169 values.push_back(newvalue); | |
170 value = values.back(); | |
171 } else { | |
172 value = values[value_index]; | |
173 } | |
174 headers[name] = value; | |
175 } | |
176 EXPECT_TRUE(RoundTrip(headers)); | |
177 } | |
178 } | |
179 | |
180 } // namespace | |
181 | |
182 } // namespace net | |
OLD | NEW |