112e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org// Copyright 2014 The Chromium Authors. All rights reserved.
212e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org// Use of this source code is governed by a BSD-style license that can be
312e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org// found in the LICENSE file.
412e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org
512e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org#include <cmath>
612e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org#include <ctime>
712e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org#include <map>
812e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org#include <string>
912e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org#include <vector>
1012e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org
1112e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org#include "base/rand_util.h"
1212e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org#include "net/spdy/hpack_constants.h"
1312e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org#include "net/spdy/hpack_decoder.h"
1412e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org#include "net/spdy/hpack_encoder.h"
1512e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org#include "testing/gtest/include/gtest/gtest.h"
1612e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org
1712e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.orgnamespace net {
1812e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org
1912e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.orgusing std::map;
2012e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.orgusing std::string;
2112e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.orgusing std::vector;
2212e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org
2312e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.orgnamespace {
2412e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org
2512e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.orgclass HpackRoundTripTest : public ::testing::Test {
2612e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org protected:
2712e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org  HpackRoundTripTest()
2812e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org      : encoder_(ObtainHpackHuffmanTable()),
2912e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org        decoder_(ObtainHpackHuffmanTable()) {}
3012e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org
3112e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org  virtual void SetUp() {
3212e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    // Use a small table size to tickle eviction handling.
3312e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    encoder_.ApplyHeaderTableSizeSetting(256);
3412e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    decoder_.ApplyHeaderTableSizeSetting(256);
3512e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org  }
3612e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org
3712e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org  bool RoundTrip(const map<string, string>& header_set) {
3812e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    string encoded;
3912e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    encoder_.EncodeHeaderSet(header_set, &encoded);
4012e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org
4112e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    bool success = decoder_.HandleControlFrameHeadersData(
4212e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org        1, encoded.data(), encoded.size());
4312e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    success &= decoder_.HandleControlFrameHeadersComplete(1);
4412e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org
4512e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    EXPECT_EQ(header_set, decoder_.decoded_block());
4612e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    return success;
4712e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org  }
4812e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org
4912e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org  size_t SampleExponential(size_t mean, size_t sanity_bound) {
5012e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    return std::min<size_t>(-std::log(base::RandDouble()) * mean,
5112e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org                            sanity_bound);
5212e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org  }
5312e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org
5412e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org  HpackEncoder encoder_;
5512e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org  HpackDecoder decoder_;
5612e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org};
5712e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org
5812e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.orgTEST_F(HpackRoundTripTest, ResponseFixtures) {
5912e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org  {
6012e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    map<string, string> headers;
6112e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    headers[":status"] = "302";
6212e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    headers["cache-control"] = "private";
6312e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    headers["date"] = "Mon, 21 Oct 2013 20:13:21 GMT";
6412e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    headers["location"] = "https://www.example.com";
6512e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    EXPECT_TRUE(RoundTrip(headers));
6612e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org  }
6712e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org  {
6812e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    map<string, string> headers;
6912e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    headers[":status"] = "200";
70e3c177a423baa3c30225c4e422b6f6c76d38b951machenbach@chromium.org    headers["cache-control"] = "private";
7112e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    headers["date"] = "Mon, 21 Oct 2013 20:13:21 GMT";
7212e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    headers["location"] = "https://www.example.com";
7312e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    EXPECT_TRUE(RoundTrip(headers));
7412e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org  }
7512e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org  {
7612e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    map<string, string> headers;
7712e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    headers[":status"] = "200";
7812e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    headers["cache-control"] = "private";
7912e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    headers["content-encoding"] = "gzip";
8012e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    headers["date"] = "Mon, 21 Oct 2013 20:13:22 GMT";
8112e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    headers["location"] = "https://www.example.com";
8212e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    headers["set-cookie"] = "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU;"
8312e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org        " max-age=3600; version=1";
8412e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    headers["multivalue"] = string("foo\0bar", 7);
8512e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    EXPECT_TRUE(RoundTrip(headers));
8612e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org  }
8712e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org}
8812e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org
8912e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.orgTEST_F(HpackRoundTripTest, RequestFixtures) {
9012e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org  {
9112e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    map<string, string> headers;
9212e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    headers[":authority"] = "www.example.com";
9312e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    headers[":method"] = "GET";
9412e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    headers[":path"] = "/";
9512e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    headers[":scheme"] = "http";
9612e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    headers["cookie"] = "baz=bing; foo=bar";
9712e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    EXPECT_TRUE(RoundTrip(headers));
9812e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org  }
9912e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org  {
10012e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    map<string, string> headers;
10112e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    headers[":authority"] = "www.example.com";
10212e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    headers[":method"] = "GET";
10312e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    headers[":path"] = "/";
10412e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    headers[":scheme"] = "http";
10512e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    headers["cache-control"] = "no-cache";
10612e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    headers["cookie"] = "foo=bar; fizzle=fazzle";
10712e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    EXPECT_TRUE(RoundTrip(headers));
10812e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org  }
10912e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org  {
11012e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    map<string, string> headers;
11112e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    headers[":authority"] = "www.example.com";
11212e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    headers[":method"] = "GET";
11312e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    headers[":path"] = "/index.html";
11412e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    headers[":scheme"] = "https";
115e3c177a423baa3c30225c4e422b6f6c76d38b951machenbach@chromium.org    headers["custom-key"] = "custom-value";
11612e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    headers["cookie"] = "baz=bing; fizzle=fazzle; garbage";
11712e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    headers["multivalue"] = string("foo\0bar", 7);
11812e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    EXPECT_TRUE(RoundTrip(headers));
11912e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org  }
12012e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org}
12112e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org
12212e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.orgTEST_F(HpackRoundTripTest, RandomizedExamples) {
12312e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org  // Grow vectors of names & values, which are seeded with fixtures and then
12412e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org  // expanded with dynamically generated data. Samples are taken using the
12512e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org  // exponential distribution.
12612e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org  vector<string> names;
12712e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org  names.push_back(":authority");
12812e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org  names.push_back(":path");
12912e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org  names.push_back(":status");
13012e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org  // TODO(jgraettinger): Enable "cookie" as a name fixture. Crumbs may be
13112e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org  // reconstructed in any order, which breaks the simple validation used here.
13212e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org
13312e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org  vector<string> values;
13412e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org  values.push_back("/");
13512e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org  values.push_back("/index.html");
13612e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org  values.push_back("200");
13712e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org  values.push_back("404");
13812e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org  values.push_back("");
13912e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org  values.push_back("baz=bing; foo=bar; garbage");
14012e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org  values.push_back("baz=bing; fizzle=fazzle; garbage");
14112e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org
14212e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org  int seed = std::time(NULL);
14312e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org  LOG(INFO) << "Seeding with srand(" << seed << ")";
14412e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org  srand(seed);
14512e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org
14612e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org  for (size_t i = 0; i != 2000; ++i) {
14712e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    map<string, string> headers;
14812e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org
14912e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    size_t header_count = 1 + SampleExponential(7, 50);
15012e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    for (size_t j = 0; j != header_count; ++j) {
15112e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org      size_t name_index = SampleExponential(20, 200);
15212e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org      size_t value_index = SampleExponential(20, 200);
15312e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org
15412e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org      string name, value;
155e3c177a423baa3c30225c4e422b6f6c76d38b951machenbach@chromium.org      if (name_index >= names.size()) {
15612e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org        names.push_back(base::RandBytesAsString(1 + SampleExponential(5, 30)));
15712e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org        name = names.back();
15812e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org      } else {
15912e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org        name = names[name_index];
16012e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org      }
16112e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org      if (value_index >= values.size()) {
16212e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org        string newvalue =
16312e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org            base::RandBytesAsString(1 + SampleExponential(15, 75));
16412e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org        // Currently order is not preserved in the encoder.  In particular,
16512e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org        // when a value is decomposed at \0 delimiters, its parts might get
16612e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org        // encoded out of order if some but not all of them already exist in
16712e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org        // the header table.  For now, avoid \0 bytes in values.
16812e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org        std::replace(newvalue.begin(), newvalue.end(), '\x00', '\x01');
16912e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org        values.push_back(newvalue);
17012e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org        value = values.back();
17112e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org      } else {
17212e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org        value = values[value_index];
17312e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org      }
17412e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org      headers[name] = value;
17512e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    }
17612e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org    EXPECT_TRUE(RoundTrip(headers));
17712e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org  }
17812e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org}
17912e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org
18012e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org}  // namespace
18112e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org
18212e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org}  // namespace net
18312e05e8fde625d746b998a15049e8487c43a3b17machenbach@chromium.org