| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include <cmath> | 5 #include <cmath> |
| 6 #include <ctime> | 6 #include <ctime> |
| 7 #include <string> | |
| 8 #include <vector> | 7 #include <vector> |
| 9 | 8 |
| 10 #include "base/rand_util.h" | 9 #include "base/rand_util.h" |
| 11 #include "net/spdy/hpack/hpack_constants.h" | 10 #include "net/spdy/hpack/hpack_constants.h" |
| 12 #include "net/spdy/hpack/hpack_decoder.h" | 11 #include "net/spdy/hpack/hpack_decoder.h" |
| 13 #include "net/spdy/hpack/hpack_encoder.h" | 12 #include "net/spdy/hpack/hpack_encoder.h" |
| 13 #include "net/spdy/platform/api/spdy_string.h" |
| 14 #include "net/spdy/spdy_test_utils.h" | 14 #include "net/spdy/spdy_test_utils.h" |
| 15 #include "testing/gtest/include/gtest/gtest.h" | 15 #include "testing/gtest/include/gtest/gtest.h" |
| 16 | 16 |
| 17 namespace net { | 17 namespace net { |
| 18 namespace test { | 18 namespace test { |
| 19 | 19 |
| 20 using std::string; | |
| 21 | |
| 22 namespace { | 20 namespace { |
| 23 | 21 |
| 24 // Supports testing with the input split at every byte boundary. | 22 // Supports testing with the input split at every byte boundary. |
| 25 enum InputSizeParam { ALL_INPUT, ONE_BYTE, ZERO_THEN_ONE_BYTE }; | 23 enum InputSizeParam { ALL_INPUT, ONE_BYTE, ZERO_THEN_ONE_BYTE }; |
| 26 | 24 |
| 27 class HpackRoundTripTest : public ::testing::TestWithParam<InputSizeParam> { | 25 class HpackRoundTripTest : public ::testing::TestWithParam<InputSizeParam> { |
| 28 protected: | 26 protected: |
| 29 HpackRoundTripTest() : encoder_(ObtainHpackHuffmanTable()), decoder_() {} | 27 HpackRoundTripTest() : encoder_(ObtainHpackHuffmanTable()), decoder_() {} |
| 30 | 28 |
| 31 void SetUp() override { | 29 void SetUp() override { |
| 32 // Use a small table size to tickle eviction handling. | 30 // Use a small table size to tickle eviction handling. |
| 33 encoder_.ApplyHeaderTableSizeSetting(256); | 31 encoder_.ApplyHeaderTableSizeSetting(256); |
| 34 decoder_.ApplyHeaderTableSizeSetting(256); | 32 decoder_.ApplyHeaderTableSizeSetting(256); |
| 35 } | 33 } |
| 36 | 34 |
| 37 bool RoundTrip(const SpdyHeaderBlock& header_set) { | 35 bool RoundTrip(const SpdyHeaderBlock& header_set) { |
| 38 string encoded; | 36 SpdyString encoded; |
| 39 encoder_.EncodeHeaderSet(header_set, &encoded); | 37 encoder_.EncodeHeaderSet(header_set, &encoded); |
| 40 | 38 |
| 41 bool success = true; | 39 bool success = true; |
| 42 if (GetParam() == ALL_INPUT) { | 40 if (GetParam() == ALL_INPUT) { |
| 43 // Pass all the input to the decoder at once. | 41 // Pass all the input to the decoder at once. |
| 44 success = decoder_.HandleControlFrameHeadersData(encoded.data(), | 42 success = decoder_.HandleControlFrameHeadersData(encoded.data(), |
| 45 encoded.size()); | 43 encoded.size()); |
| 46 } else if (GetParam() == ONE_BYTE) { | 44 } else if (GetParam() == ONE_BYTE) { |
| 47 // Pass the input to the decoder one byte at a time. | 45 // Pass the input to the decoder one byte at a time. |
| 48 const char* data = encoded.data(); | 46 const char* data = encoded.data(); |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 103 { | 101 { |
| 104 SpdyHeaderBlock headers; | 102 SpdyHeaderBlock headers; |
| 105 headers[":status"] = "200"; | 103 headers[":status"] = "200"; |
| 106 headers["cache-control"] = "private"; | 104 headers["cache-control"] = "private"; |
| 107 headers["content-encoding"] = "gzip"; | 105 headers["content-encoding"] = "gzip"; |
| 108 headers["date"] = "Mon, 21 Oct 2013 20:13:22 GMT"; | 106 headers["date"] = "Mon, 21 Oct 2013 20:13:22 GMT"; |
| 109 headers["location"] = "https://www.example.com"; | 107 headers["location"] = "https://www.example.com"; |
| 110 headers["set-cookie"] = | 108 headers["set-cookie"] = |
| 111 "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU;" | 109 "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU;" |
| 112 " max-age=3600; version=1"; | 110 " max-age=3600; version=1"; |
| 113 headers["multivalue"] = string("foo\0bar", 7); | 111 headers["multivalue"] = SpdyString("foo\0bar", 7); |
| 114 EXPECT_TRUE(RoundTrip(headers)); | 112 EXPECT_TRUE(RoundTrip(headers)); |
| 115 } | 113 } |
| 116 } | 114 } |
| 117 | 115 |
| 118 TEST_P(HpackRoundTripTest, RequestFixtures) { | 116 TEST_P(HpackRoundTripTest, RequestFixtures) { |
| 119 { | 117 { |
| 120 SpdyHeaderBlock headers; | 118 SpdyHeaderBlock headers; |
| 121 headers[":authority"] = "www.example.com"; | 119 headers[":authority"] = "www.example.com"; |
| 122 headers[":method"] = "GET"; | 120 headers[":method"] = "GET"; |
| 123 headers[":path"] = "/"; | 121 headers[":path"] = "/"; |
| (...skipping 12 matching lines...) Expand all Loading... |
| 136 EXPECT_TRUE(RoundTrip(headers)); | 134 EXPECT_TRUE(RoundTrip(headers)); |
| 137 } | 135 } |
| 138 { | 136 { |
| 139 SpdyHeaderBlock headers; | 137 SpdyHeaderBlock headers; |
| 140 headers[":authority"] = "www.example.com"; | 138 headers[":authority"] = "www.example.com"; |
| 141 headers[":method"] = "GET"; | 139 headers[":method"] = "GET"; |
| 142 headers[":path"] = "/index.html"; | 140 headers[":path"] = "/index.html"; |
| 143 headers[":scheme"] = "https"; | 141 headers[":scheme"] = "https"; |
| 144 headers["custom-key"] = "custom-value"; | 142 headers["custom-key"] = "custom-value"; |
| 145 headers["cookie"] = "baz=bing; fizzle=fazzle; garbage"; | 143 headers["cookie"] = "baz=bing; fizzle=fazzle; garbage"; |
| 146 headers["multivalue"] = string("foo\0bar", 7); | 144 headers["multivalue"] = SpdyString("foo\0bar", 7); |
| 147 EXPECT_TRUE(RoundTrip(headers)); | 145 EXPECT_TRUE(RoundTrip(headers)); |
| 148 } | 146 } |
| 149 } | 147 } |
| 150 | 148 |
| 151 TEST_P(HpackRoundTripTest, RandomizedExamples) { | 149 TEST_P(HpackRoundTripTest, RandomizedExamples) { |
| 152 // Grow vectors of names & values, which are seeded with fixtures and then | 150 // Grow vectors of names & values, which are seeded with fixtures and then |
| 153 // expanded with dynamically generated data. Samples are taken using the | 151 // expanded with dynamically generated data. Samples are taken using the |
| 154 // exponential distribution. | 152 // exponential distribution. |
| 155 std::vector<string> pseudo_header_names, random_header_names; | 153 std::vector<SpdyString> pseudo_header_names, random_header_names; |
| 156 pseudo_header_names.push_back(":authority"); | 154 pseudo_header_names.push_back(":authority"); |
| 157 pseudo_header_names.push_back(":path"); | 155 pseudo_header_names.push_back(":path"); |
| 158 pseudo_header_names.push_back(":status"); | 156 pseudo_header_names.push_back(":status"); |
| 159 | 157 |
| 160 // TODO(jgraettinger): Enable "cookie" as a name fixture. Crumbs may be | 158 // TODO(jgraettinger): Enable "cookie" as a name fixture. Crumbs may be |
| 161 // reconstructed in any order, which breaks the simple validation used here. | 159 // reconstructed in any order, which breaks the simple validation used here. |
| 162 | 160 |
| 163 std::vector<string> values; | 161 std::vector<SpdyString> values; |
| 164 values.push_back("/"); | 162 values.push_back("/"); |
| 165 values.push_back("/index.html"); | 163 values.push_back("/index.html"); |
| 166 values.push_back("200"); | 164 values.push_back("200"); |
| 167 values.push_back("404"); | 165 values.push_back("404"); |
| 168 values.push_back(""); | 166 values.push_back(""); |
| 169 values.push_back("baz=bing; foo=bar; garbage"); | 167 values.push_back("baz=bing; foo=bar; garbage"); |
| 170 values.push_back("baz=bing; fizzle=fazzle; garbage"); | 168 values.push_back("baz=bing; fizzle=fazzle; garbage"); |
| 171 | 169 |
| 172 int seed = std::time(NULL); | 170 int seed = std::time(NULL); |
| 173 LOG(INFO) << "Seeding with srand(" << seed << ")"; | 171 LOG(INFO) << "Seeding with srand(" << seed << ")"; |
| 174 srand(seed); | 172 srand(seed); |
| 175 | 173 |
| 176 for (size_t i = 0; i != 2000; ++i) { | 174 for (size_t i = 0; i != 2000; ++i) { |
| 177 SpdyHeaderBlock headers; | 175 SpdyHeaderBlock headers; |
| 178 | 176 |
| 179 // Choose a random number of headers to add, and of these a random subset | 177 // Choose a random number of headers to add, and of these a random subset |
| 180 // will be HTTP/2 pseudo headers. | 178 // will be HTTP/2 pseudo headers. |
| 181 size_t header_count = 1 + SampleExponential(7, 50); | 179 size_t header_count = 1 + SampleExponential(7, 50); |
| 182 size_t pseudo_header_count = | 180 size_t pseudo_header_count = |
| 183 std::min(header_count, 1 + SampleExponential(7, 50)); | 181 std::min(header_count, 1 + SampleExponential(7, 50)); |
| 184 EXPECT_LE(pseudo_header_count, header_count); | 182 EXPECT_LE(pseudo_header_count, header_count); |
| 185 for (size_t j = 0; j != header_count; ++j) { | 183 for (size_t j = 0; j != header_count; ++j) { |
| 186 string name, value; | 184 SpdyString name, value; |
| 187 // Pseudo headers must be added before regular headers. | 185 // Pseudo headers must be added before regular headers. |
| 188 if (j < pseudo_header_count) { | 186 if (j < pseudo_header_count) { |
| 189 // Choose one of the defined pseudo headers at random. | 187 // Choose one of the defined pseudo headers at random. |
| 190 size_t name_index = base::RandGenerator(pseudo_header_names.size()); | 188 size_t name_index = base::RandGenerator(pseudo_header_names.size()); |
| 191 name = pseudo_header_names[name_index]; | 189 name = pseudo_header_names[name_index]; |
| 192 } else { | 190 } else { |
| 193 // Randomly reuse an existing header name, or generate a new one. | 191 // Randomly reuse an existing header name, or generate a new one. |
| 194 size_t name_index = SampleExponential(20, 200); | 192 size_t name_index = SampleExponential(20, 200); |
| 195 if (name_index >= random_header_names.size()) { | 193 if (name_index >= random_header_names.size()) { |
| 196 name = base::RandBytesAsString(1 + SampleExponential(5, 30)); | 194 name = base::RandBytesAsString(1 + SampleExponential(5, 30)); |
| 197 // A regular header cannot begin with the pseudo header prefix ":". | 195 // A regular header cannot begin with the pseudo header prefix ":". |
| 198 if (name[0] == ':') { | 196 if (name[0] == ':') { |
| 199 name[0] = 'x'; | 197 name[0] = 'x'; |
| 200 } | 198 } |
| 201 random_header_names.push_back(name); | 199 random_header_names.push_back(name); |
| 202 } else { | 200 } else { |
| 203 name = random_header_names[name_index]; | 201 name = random_header_names[name_index]; |
| 204 } | 202 } |
| 205 } | 203 } |
| 206 | 204 |
| 207 // Randomly reuse an existing value, or generate a new one. | 205 // Randomly reuse an existing value, or generate a new one. |
| 208 size_t value_index = SampleExponential(20, 200); | 206 size_t value_index = SampleExponential(20, 200); |
| 209 if (value_index >= values.size()) { | 207 if (value_index >= values.size()) { |
| 210 string newvalue = | 208 SpdyString newvalue = |
| 211 base::RandBytesAsString(1 + SampleExponential(15, 75)); | 209 base::RandBytesAsString(1 + SampleExponential(15, 75)); |
| 212 // Currently order is not preserved in the encoder. In particular, | 210 // Currently order is not preserved in the encoder. In particular, |
| 213 // when a value is decomposed at \0 delimiters, its parts might get | 211 // when a value is decomposed at \0 delimiters, its parts might get |
| 214 // encoded out of order if some but not all of them already exist in | 212 // encoded out of order if some but not all of them already exist in |
| 215 // the header table. For now, avoid \0 bytes in values. | 213 // the header table. For now, avoid \0 bytes in values. |
| 216 std::replace(newvalue.begin(), newvalue.end(), '\x00', '\x01'); | 214 std::replace(newvalue.begin(), newvalue.end(), '\x00', '\x01'); |
| 217 values.push_back(newvalue); | 215 values.push_back(newvalue); |
| 218 value = values.back(); | 216 value = values.back(); |
| 219 } else { | 217 } else { |
| 220 value = values[value_index]; | 218 value = values[value_index]; |
| 221 } | 219 } |
| 222 headers[name] = value; | 220 headers[name] = value; |
| 223 } | 221 } |
| 224 EXPECT_TRUE(RoundTrip(headers)); | 222 EXPECT_TRUE(RoundTrip(headers)); |
| 225 } | 223 } |
| 226 } | 224 } |
| 227 | 225 |
| 228 } // namespace | 226 } // namespace |
| 229 | 227 |
| 230 } // namespace test | 228 } // namespace test |
| 231 } // namespace net | 229 } // namespace net |
| OLD | NEW |