text: // 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 "quiche/quic/core/http/spdy_utils.h"
  
  #include <memory>
  #include <string>
  
  #include "absl/base/macros.h"
  #include "absl/strings/string_view.h"
  #include "quiche/quic/core/quic_versions.h"
  #include "quiche/quic/platform/api/quic_test.h"
  
  using spdy::SpdyHeaderBlock;
  using testing::Pair;
  using testing::UnorderedElementsAre;
  
  namespace quic {
  namespace test {
  namespace {
  
  const bool kExpectFinalByteOffset = true;
  const bool kDoNotExpectFinalByteOffset = false;
  
  static std::unique_ptr<QuicHeaderList> FromList(
      const QuicHeaderList::ListType& src) {
    std::unique_ptr<QuicHeaderList> headers(new QuicHeaderList);
    headers->OnHeaderBlockStart();
    for (const auto& p : src) {
      headers->OnHeader(p.first, p.second);
    }
    headers->OnHeaderBlockEnd(0, 0);
    return headers;
  }
  
  static void ValidateDatagramFlowId(
      const std::string& header_value,
      absl::optional<QuicDatagramStreamId> expected_flow_id) {
    SpdyHeaderBlock headers;
    headers["datagram-flow-id"] = header_value;
    ASSERT_EQ(SpdyUtils::ParseDatagramFlowIdHeader(headers), expected_flow_id);
  }
  
  }  // anonymous namespace
  
  using CopyAndValidateHeaders = QuicTest;
  
  TEST_F(CopyAndValidateHeaders, NormalUsage) {
    auto headers = FromList({// All cookie crumbs are joined.
                             {"cookie", " part 1"},
                             {"cookie", "part 2 "},
                             {"cookie", "part3"},
  
                             // Already-delimited headers are passed through.
                             {"passed-through", std::string("foo\0baz", 7)},
  
                             // Other headers are joined on \0.
                             {"joined", "value 1"},
                             {"joined", "value 2"},
  
                             // Empty headers remain empty.
                             {"empty", ""},
  
                             // Joined empty headers work as expected.
                             {"empty-joined", ""},
                             {"empty-joined", "foo"},
                             {"empty-joined", ""},
                             {"empty-joined", ""},
  
                             // Non-continguous cookie crumb.
                             {"cookie", " fin!"}});
  
    int64_t content_length = -1;
    SpdyHeaderBlock block;
    ASSERT_TRUE(
        SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block));
    EXPECT_THAT(block,
                UnorderedElementsAre(
                    Pair("cookie", " part 1; part 2 ; part3;  fin!"),
                    Pair("passed-through", absl::string_view("foo\0baz", 7)),
                    Pair("joined", absl::string_view("value 1\0value 2", 15)),
                    Pair("empty", ""),
                    Pair("empty-joined", absl::string_view("\0foo\0\0", 6))));
    EXPECT_EQ(-1, content_length);
  }
  
  TEST_F(CopyAndValidateHeaders, EmptyName) {
    auto headers = FromList({{"foo", "foovalue"}, {"", "barvalue"}, {"baz", ""}});
    int64_t content_length = -1;
    SpdyHeaderBlock block;
    ASSERT_FALSE(
        SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block));
  }
  
  TEST_F(CopyAndValidateHeaders, UpperCaseName) {
    auto headers =
        FromList({{"foo", "foovalue"}, {"bar", "barvalue"}, {"bAz", ""}});
    int64_t content_length = -1;
    SpdyHeaderBlock block;
    ASSERT_FALSE(
        SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block));
  }
  
  TEST_F(CopyAndValidateHeaders, MultipleContentLengths) {
    auto headers = FromList({{"content-length", "9"},
                             {"foo", "foovalue"},
                             {"content-length", "9"},
                             {"bar", "barvalue"},
                             {"baz", ""}});
    int64_t content_length = -1;
    SpdyHeaderBlock block;
    ASSERT_TRUE(
        SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block));
    EXPECT_THAT(block, UnorderedElementsAre(
                           Pair("foo", "foovalue"), Pair("bar", "barvalue"),
                           Pair("content-length", absl::string_view("9\09", 3)),
                           Pair("baz", "")));
    EXPECT_EQ(9, content_length);
  }
  
  TEST_F(CopyAndValidateHeaders, InconsistentContentLengths) {
    auto headers = FromList({{"content-length", "9"},
                             {"foo", "foovalue"},
                             {"content-length", "8"},
                             {"bar", "barvalue"},
                             {"baz", ""}});
    int64_t content_length = -1;
    SpdyHeaderBlock block;
    ASSERT_FALSE(
        SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block));
  }
  
  TEST_F(CopyAndValidateHeaders, LargeContentLength) {
    auto headers = FromList({{"content-length", "9000000000"},
                             {"foo", "foovalue"},
                             {"bar", "barvalue"},
                             {"baz", ""}});
    int64_t content_length = -1;
    SpdyHeaderBlock block;
    ASSERT_TRUE(
        SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block));
    EXPECT_THAT(block,
                UnorderedElementsAre(
                    Pair("foo", "foovalue"), Pair("bar", "barvalue"),
                    Pair("content-length", absl::string_view("9000000000")),
                    Pair("baz", "")));
    EXPECT_EQ(9000000000, content_length);
  }
  
  TEST_F(CopyAndValidateHeaders, NonDigitContentLength) {
    // Section 3.3.2 of RFC 7230 defines content-length as being only digits.
    // Number parsers might accept symbols like a leading plus; test that this
    // fails to parse.
    auto headers = FromList({{"content-length", "+123"},
                             {"foo", "foovalue"},
                             {"bar", "barvalue"},
                             {"baz", ""}});
    int64_t content_length = -1;
    SpdyHeaderBlock block;
    EXPECT_FALSE(
        SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block));
  }
  
  TEST_F(CopyAndValidateHeaders, MultipleValues) {
    auto headers = FromList({{"foo", "foovalue"},
                             {"bar", "barvalue"},
                             {"baz", ""},
                             {"foo", "boo"},
                             {"baz", "buzz"}});
    int64_t content_length = -1;
    SpdyHeaderBlock block;
    ASSERT_TRUE(
        SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block));
    EXPECT_THAT(block, UnorderedElementsAre(
                           Pair("foo", absl::string_view("foovalue\0boo", 12)),
                           Pair("bar", "barvalue"),
                           Pair("baz", absl::string_view("\0buzz", 5))));
    EXPECT_EQ(-1, content_length);
  }
  
  TEST_F(CopyAndValidateHeaders, MoreThanTwoValues) {
    auto headers = FromList({{"set-cookie", "value1"},
                             {"set-cookie", "value2"},
                             {"set-cookie", "value3"}});
    int64_t content_length = -1;
    SpdyHeaderBlock block;
    ASSERT_TRUE(
        SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block));
    EXPECT_THAT(block, UnorderedElementsAre(Pair(
                           "set-cookie",
                           absl::string_view("value1\0value2\0value3", 20))));
    EXPECT_EQ(-1, content_length);
  }
  
  TEST_F(CopyAndValidateHeaders, Cookie) {
    auto headers = FromList({{"foo", "foovalue"},
                             {"bar", "barvalue"},
                             {"cookie", "value1"},
                             {"baz", ""}});
    int64_t content_length = -1;
    SpdyHeaderBlock block;
    ASSERT_TRUE(
        SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block));
    EXPECT_THAT(block, UnorderedElementsAre(
                           Pair("foo", "foovalue"), Pair("bar", "barvalue"),
                           Pair("cookie", "value1"), Pair("baz", "")));
    EXPECT_EQ(-1, content_length);
  }
  
  TEST_F(CopyAndValidateHeaders, MultipleCookies) {
    auto headers = FromList({{"foo", "foovalue"},
                             {"bar", "barvalue"},
                             {"cookie", "value1"},
                             {"baz", ""},
                             {"cookie", "value2"}});
    int64_t content_length = -1;
    SpdyHeaderBlock block;
    ASSERT_TRUE(
        SpdyUtils::CopyAndValidateHeaders(*headers, &content_length, &block));
    EXPECT_THAT(block, UnorderedElementsAre(
                           Pair("foo", "foovalue"), Pair("bar", "barvalue"),
                           Pair("cookie", "value1; value2"), Pair("baz", "")));
    EXPECT_EQ(-1, content_length);
  }
  
  using CopyAndValidateTrailers = QuicTest;
  
  TEST_F(CopyAndValidateTrailers, SimplestValidList) {
    // Verify that the simplest trailers are valid: just a final byte offset that
    // gets parsed successfully.
    auto trailers = FromList({{kFinalOffsetHeaderKey, "1234"}});
    size_t final_byte_offset = 0;
    SpdyHeaderBlock block;
    EXPECT_TRUE(SpdyUtils::CopyAndValidateTrailers(
        *trailers, kExpectFinalByteOffset, &final_byte_offset, &block));
    EXPECT_EQ(1234u, final_byte_offset);
  }
  
  TEST_F(CopyAndValidateTrailers, EmptyTrailerListWithFinalByteOffsetExpected) {
    // An empty trailer list will fail as expected key kFinalOffsetHeaderKey is
    // not present.
    QuicHeaderList trailers;
    size_t final_byte_offset = 0;
    SpdyHeaderBlock block;
    EXPECT_FALSE(SpdyUtils::CopyAndValidateTrailers(
        trailers, kExpectFinalByteOffset, &final_byte_offset, &block));
  }
  
  TEST_F(CopyAndValidateTrailers,
         EmptyTrailerListWithFinalByteOffsetNotExpected) {
    // An empty trailer list will pass successfully if kFinalOffsetHeaderKey is
    // not expected.
    QuicHeaderList trailers;
    size_t final_byte_offset = 0;
    SpdyHeaderBlock block;
    EXPECT_TRUE(SpdyUtils::CopyAndValidateTrailers(
        trailers, kDoNotExpectFinalByteOffset, &final_byte_offset, &block));
    EXPECT_TRUE(block.empty());
  }
  
  TEST_F(CopyAndValidateTrailers, FinalByteOffsetExpectedButNotPresent) {
    // Validation fails if expected kFinalOffsetHeaderKey is not present, even if
    // the rest of the header block is valid.
    auto trailers = FromList({{"key", "value"}});
    size_t final_byte_offset = 0;
    SpdyHeaderBlock block;
    EXPECT_FALSE(SpdyUtils::CopyAndValidateTrailers(
        *trailers, kExpectFinalByteOffset, &final_byte_offset, &block));
  }
  
  TEST_F(CopyAndValidateTrailers, FinalByteOffsetNotExpectedButPresent) {
    // Validation fails if kFinalOffsetHeaderKey is present but should not be,
    // even if the rest of the header block is valid.
    auto trailers = FromList({{"key", "value"}, {kFinalOffsetHeaderKey, "1234"}});
    size_t final_byte_offset = 0;
    SpdyHeaderBlock block;
    EXPECT_FALSE(SpdyUtils::CopyAndValidateTrailers(
        *trailers, kDoNotExpectFinalByteOffset, &final_byte_offset, &block));
  }
  
  TEST_F(CopyAndValidateTrailers, FinalByteOffsetNotExpectedAndNotPresent) {
    // Validation succeeds if kFinalOffsetHeaderKey is not expected and not
    // present.
    auto trailers = FromList({{"key", "value"}});
    size_t final_byte_offset = 0;
    SpdyHeaderBlock block;
    EXPECT_TRUE(SpdyUtils::CopyAndValidateTrailers(
        *trailers, kDoNotExpectFinalByteOffset, &final_byte_offset, &block));
    EXPECT_THAT(block, UnorderedElementsAre(Pair("key", "value")));
  }
  
  TEST_F(CopyAndValidateTrailers, EmptyName) {
    // Trailer validation will fail with an empty header key, in an otherwise
    // valid block of trailers.
    auto trailers = FromList({{"", "value"}, {kFinalOffsetHeaderKey, "1234"}});
    size_t final_byte_offset = 0;
    SpdyHeaderBlock block;
    EXPECT_FALSE(SpdyUtils::CopyAndValidateTrailers(
        *trailers, kExpectFinalByteOffset, &final_byte_offset, &block));
  }
  
  TEST_F(CopyAndValidateTrailers, PseudoHeaderInTrailers) {
    // Pseudo headers are illegal in trailers.
    auto trailers =
        FromList({{":pseudo_key", "value"}, {kFinalOffsetHeaderKey, "1234"}});
    size_t final_byte_offset = 0;
    SpdyHeaderBlock block;
    EXPECT_FALSE(SpdyUtils::CopyAndValidateTrailers(
        *trailers, kExpectFinalByteOffset, &final_byte_offset, &block));
  }
  
  TEST_F(CopyAndValidateTrailers, DuplicateTrailers) {
    // Duplicate trailers are allowed, and their values are concatenated into a
    // single string delimted with '\0'. Some of the duplicate headers
    // deliberately have an empty value.
    auto trailers = FromList({{"key", "value0"},
                              {"key", "value1"},
                              {"key", ""},
                              {"key", ""},
                              {"key", "value2"},
                              {"key", ""},
                              {kFinalOffsetHeaderKey, "1234"},
                              {"other_key", "value"},
                              {"key", "non_contiguous_duplicate"}});
    size_t final_byte_offset = 0;
    SpdyHeaderBlock block;
    EXPECT_TRUE(SpdyUtils::CopyAndValidateTrailers(
        *trailers, kExpectFinalByteOffset, &final_byte_offset, &block));
    EXPECT_THAT(
        block,
        UnorderedElementsAre(
            Pair("key",
                 absl::string_view(
                     "value0\0value1\0\0\0value2\0\0non_contiguous_duplicate",
                     48)),
            Pair("other_key", "value")));
  }
  
  TEST_F(CopyAndValidateTrailers, DuplicateCookies) {
    // Duplicate cookie headers in trailers should be concatenated into a single
    //  "; " delimted string.
    auto headers = FromList({{"cookie", " part 1"},
                             {"cookie", "part 2 "},
                             {"cookie", "part3"},
                             {"key", "value"},
                             {kFinalOffsetHeaderKey, "1234"},
                             {"cookie", " non_contiguous_cookie!"}});
  
    size_t final_byte_offset = 0;
    SpdyHeaderBlock block;
    EXPECT_TRUE(SpdyUtils::CopyAndValidateTrailers(
        *headers, kExpectFinalByteOffset, &final_byte_offset, &block));
    EXPECT_THAT(
        block,
        UnorderedElementsAre(
            Pair("cookie", " part 1; part 2 ; part3;  non_contiguous_cookie!"),
            Pair("key", "value")));
  }
  
  using PopulateHeaderBlockFromUrl = QuicTest;
  
  TEST_F(PopulateHeaderBlockFromUrl, NormalUsage) {
    std::string url = "https://www.google.com/index.html";
    SpdyHeaderBlock headers;
    EXPECT_TRUE(SpdyUtils::PopulateHeaderBlockFromUrl(url, &headers));
    EXPECT_EQ("https", headers[":scheme"].as_string());
    EXPECT_EQ("www.google.com", headers[":authority"].as_string());
    EXPECT_EQ("/index.html", headers[":path"].as_string());
  }
  
  TEST_F(PopulateHeaderBlockFromUrl, UrlWithNoPath) {
    std::string url = "https://www.google.com";
    SpdyHeaderBlock headers;
    EXPECT_TRUE(SpdyUtils::PopulateHeaderBlockFromUrl(url, &headers));
    EXPECT_EQ("https", headers[":scheme"].as_string());
    EXPECT_EQ("www.google.com", headers[":authority"].as_string());
    EXPECT_EQ("/", headers[":path"].as_string());
  }
  
  TEST_F(PopulateHeaderBlockFromUrl, Failure) {
    SpdyHeaderBlock headers;
    EXPECT_FALSE(SpdyUtils::PopulateHeaderBlockFromUrl("/", &headers));
    EXPECT_FALSE(SpdyUtils::PopulateHeaderBlockFromUrl("/index.html", &headers));
    EXPECT_FALSE(
        SpdyUtils::PopulateHeaderBlockFromUrl("www.google.com/", &headers));
  }
  
  using DatagramFlowIdTest = QuicTest;
  
  TEST_F(DatagramFlowIdTest, DatagramFlowId) {
    // Test missing header.
    SpdyHeaderBlock headers;
    EXPECT_EQ(SpdyUtils::ParseDatagramFlowIdHeader(headers), absl::nullopt);
    // Add header and verify it parses.
    QuicDatagramStreamId flow_id = 123;
    SpdyUtils::AddDatagramFlowIdHeader(&headers, flow_id);
    EXPECT_EQ(SpdyUtils::ParseDatagramFlowIdHeader(headers), flow_id);
    // Test empty header.
    ValidateDatagramFlowId("", absl::nullopt);
    // Test invalid header.
    ValidateDatagramFlowId("M4SQU3", absl::nullopt);
    // Test simple header.
    ValidateDatagramFlowId("42", 42);
    // Test with parameter.
    ValidateDatagramFlowId("42; abc=def", 42);
    // Test list.
    ValidateDatagramFlowId("42, 44; ecn-ect0, 46; ecn-ect1, 48; ecn-ce", 42);
    // Test reordered list.
    ValidateDatagramFlowId("44; ecn-ect0, 42, 48; ecn-ce, 46; ecn-ect1", 42);
  }
  
  using ExtractQuicVersionFromAltSvcEntry = QuicTest;
  
  TEST_F(ExtractQuicVersionFromAltSvcEntry, SupportedVersion) {
    ParsedQuicVersionVector supported_versions = AllSupportedVersions();
    spdy::SpdyAltSvcWireFormat::AlternativeService entry;
    for (const ParsedQuicVersion& version : supported_versions) {
      entry.protocol_id = AlpnForVersion(version);
      ParsedQuicVersion expected_version = version;
      // Versions with share an ALPN with v1 are currently unable to be
      // advertised with Alt-Svc.
      if (entry.protocol_id == AlpnForVersion(ParsedQuicVersion::RFCv1()) &&
          version != ParsedQuicVersion::RFCv1()) {
        expected_version = ParsedQuicVersion::RFCv1();
      }
      EXPECT_EQ(expected_version, SpdyUtils::ExtractQuicVersionFromAltSvcEntry(
                                      entry, supported_versions))
          << "version: " << version;
    }
  }
  
  TEST_F(ExtractQuicVersionFromAltSvcEntry, UnsupportedVersion) {
    spdy::SpdyAltSvcWireFormat::AlternativeService entry;
    entry.protocol_id = "quic";
    EXPECT_EQ(ParsedQuicVersion::Unsupported(),
              SpdyUtils::ExtractQuicVersionFromAltSvcEntry(
                  entry, AllSupportedVersions()));
  }
  
  }  // namespace test
  }  // namespace quic
  