blob: e07c6224ff7edd84276691b059858d26a9467f7f [file] [log] [blame]
// Copyright (c) 2015 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 "net/spdy/spdy_alt_svc_wire_format.h"
#include "base/logging.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/platform_test.h"
using ::testing::_;
using base::StringPiece;
namespace net {
namespace test {
// Expose all private methods of class SpdyAltSvcWireFormat.
class SpdyAltSvcWireFormatPeer {
public:
static void SkipWhiteSpace(StringPiece::const_iterator* c,
StringPiece::const_iterator end) {
SpdyAltSvcWireFormat::SkipWhiteSpace(c, end);
}
static bool PercentDecode(StringPiece::const_iterator c,
StringPiece::const_iterator end,
std::string* output) {
return SpdyAltSvcWireFormat::PercentDecode(c, end, output);
}
static bool ParseAltAuthority(StringPiece::const_iterator c,
StringPiece::const_iterator end,
std::string* host,
uint16_t* port) {
return SpdyAltSvcWireFormat::ParseAltAuthority(c, end, host, port);
}
static bool ParsePositiveInteger16(StringPiece::const_iterator c,
StringPiece::const_iterator end,
uint16_t* max_age) {
return SpdyAltSvcWireFormat::ParsePositiveInteger16(c, end, max_age);
}
static bool ParsePositiveInteger32(StringPiece::const_iterator c,
StringPiece::const_iterator end,
uint32_t* max_age) {
return SpdyAltSvcWireFormat::ParsePositiveInteger32(c, end, max_age);
}
};
} // namespace test
namespace {
// Generate header field values, possibly with multiply defined parameters and
// random case, and corresponding AlternativeService entries.
void FuzzHeaderFieldValue(
int i,
std::string* header_field_value,
SpdyAltSvcWireFormat::AlternativeService* expected_altsvc) {
if (!header_field_value->empty()) {
header_field_value->push_back(',');
}
expected_altsvc->protocol_id = "a=b%c";
header_field_value->append("a%3Db%25c=\"");
expected_altsvc->host = "";
if (i & 1 << 0) {
expected_altsvc->host = "foo\"bar\\baz";
header_field_value->append("foo\\\"bar\\\\baz");
}
expected_altsvc->port = 42;
header_field_value->append(":42\"");
if (i & 1 << 1) {
header_field_value->append(" ");
}
if (i & 3 << 2) {
expected_altsvc->max_age = 1111;
header_field_value->append(";");
if (i & 1 << 2) {
header_field_value->append(" ");
}
header_field_value->append("mA=1111");
if (i & 2 << 2) {
header_field_value->append(" ");
}
}
if (i & 1 << 4) {
header_field_value->append("; J=s");
}
if (i & 1 << 5) {
expected_altsvc->version.push_back(24);
header_field_value->append("; v=\"24\"");
}
if (i & 1 << 6) {
expected_altsvc->max_age = 999999999;
header_field_value->append("; Ma=999999999");
}
if (i & 1 << 7) {
header_field_value->append(";");
}
if (i & 1 << 8) {
header_field_value->append(" ");
}
if (i & 1 << 9) {
header_field_value->append(",");
}
if (i & 1 << 10) {
header_field_value->append(" ");
}
}
// Generate AlternativeService entries and corresponding header field values in
// canonical form, that is, what SerializeHeaderFieldValue() should output.
void FuzzAlternativeService(int i,
SpdyAltSvcWireFormat::AlternativeService* altsvc,
std::string* expected_header_field_value) {
if (!expected_header_field_value->empty()) {
expected_header_field_value->push_back(',');
}
altsvc->protocol_id = "a=b%c";
altsvc->port = 42;
expected_header_field_value->append("a%3Db%25c=\"");
if (i & 1 << 0) {
altsvc->host = "foo\"bar\\baz";
expected_header_field_value->append("foo\\\"bar\\\\baz");
}
expected_header_field_value->append(":42\"");
if (i & 1 << 1) {
altsvc->max_age = 1111;
expected_header_field_value->append("; ma=1111");
}
if (i & 1 << 2) {
altsvc->version.push_back(24);
altsvc->version.push_back(25);
expected_header_field_value->append("; v=\"24,25\"");
}
}
class SpdyAltSvcWireFormatTest : public ::testing::Test {};
// Tests of public API.
TEST(SpdyAltSvcWireFormatTest, DefaultValues) {
SpdyAltSvcWireFormat::AlternativeService altsvc;
EXPECT_EQ("", altsvc.protocol_id);
EXPECT_EQ("", altsvc.host);
EXPECT_EQ(0u, altsvc.port);
EXPECT_EQ(86400u, altsvc.max_age);
EXPECT_TRUE(altsvc.version.empty());
}
TEST(SpdyAltSvcWireFormatTest, ParseInvalidEmptyHeaderFieldValue) {
SpdyAltSvcWireFormat::AlternativeServiceVector altsvc_vector;
ASSERT_FALSE(SpdyAltSvcWireFormat::ParseHeaderFieldValue("", &altsvc_vector));
}
TEST(SpdyAltSvcWireFormatTest, ParseHeaderFieldValueClear) {
SpdyAltSvcWireFormat::AlternativeServiceVector altsvc_vector;
ASSERT_TRUE(
SpdyAltSvcWireFormat::ParseHeaderFieldValue("clear", &altsvc_vector));
EXPECT_EQ(0u, altsvc_vector.size());
}
// Fuzz test of ParseHeaderFieldValue() with optional whitespaces, ignored
// parameters, duplicate parameters, trailing space, trailing alternate service
// separator, etc. Single alternative service at a time.
TEST(SpdyAltSvcWireFormatTest, ParseHeaderFieldValue) {
for (int i = 0; i < 1 << 11; ++i) {
std::string header_field_value;
SpdyAltSvcWireFormat::AlternativeService expected_altsvc;
FuzzHeaderFieldValue(i, &header_field_value, &expected_altsvc);
SpdyAltSvcWireFormat::AlternativeServiceVector altsvc_vector;
ASSERT_TRUE(SpdyAltSvcWireFormat::ParseHeaderFieldValue(header_field_value,
&altsvc_vector));
ASSERT_EQ(1u, altsvc_vector.size());
EXPECT_EQ(expected_altsvc.protocol_id, altsvc_vector[0].protocol_id);
EXPECT_EQ(expected_altsvc.host, altsvc_vector[0].host);
EXPECT_EQ(expected_altsvc.port, altsvc_vector[0].port);
EXPECT_EQ(expected_altsvc.max_age, altsvc_vector[0].max_age);
EXPECT_EQ(expected_altsvc.version, altsvc_vector[0].version);
// Roundtrip test starting with |altsvc_vector|.
std::string reserialized_header_field_value =
SpdyAltSvcWireFormat::SerializeHeaderFieldValue(altsvc_vector);
SpdyAltSvcWireFormat::AlternativeServiceVector roundtrip_altsvc_vector;
ASSERT_TRUE(SpdyAltSvcWireFormat::ParseHeaderFieldValue(
reserialized_header_field_value, &roundtrip_altsvc_vector));
ASSERT_EQ(1u, roundtrip_altsvc_vector.size());
EXPECT_EQ(expected_altsvc.protocol_id,
roundtrip_altsvc_vector[0].protocol_id);
EXPECT_EQ(expected_altsvc.host, roundtrip_altsvc_vector[0].host);
EXPECT_EQ(expected_altsvc.port, roundtrip_altsvc_vector[0].port);
EXPECT_EQ(expected_altsvc.max_age, roundtrip_altsvc_vector[0].max_age);
EXPECT_EQ(expected_altsvc.version, roundtrip_altsvc_vector[0].version);
}
}
// Fuzz test of ParseHeaderFieldValue() with optional whitespaces, ignored
// parameters, duplicate parameters, trailing space, trailing alternate service
// separator, etc. Possibly multiple alternative service at a time.
TEST(SpdyAltSvcWireFormatTest, ParseHeaderFieldValueMultiple) {
for (int i = 0; i < 1 << 11;) {
std::string header_field_value;
SpdyAltSvcWireFormat::AlternativeServiceVector expected_altsvc_vector;
// This will generate almost two hundred header field values with two,
// three, four, five, six, and seven alternative services each, and
// thousands with a single one.
do {
SpdyAltSvcWireFormat::AlternativeService expected_altsvc;
FuzzHeaderFieldValue(i, &header_field_value, &expected_altsvc);
expected_altsvc_vector.push_back(expected_altsvc);
++i;
} while ((i < 1 << 13) && (i % 6 < i % 7));
SpdyAltSvcWireFormat::AlternativeServiceVector altsvc_vector;
ASSERT_TRUE(SpdyAltSvcWireFormat::ParseHeaderFieldValue(header_field_value,
&altsvc_vector));
ASSERT_EQ(expected_altsvc_vector.size(), altsvc_vector.size());
for (unsigned int j = 0; j < altsvc_vector.size(); ++j) {
EXPECT_EQ(expected_altsvc_vector[j].protocol_id,
altsvc_vector[j].protocol_id);
EXPECT_EQ(expected_altsvc_vector[j].host, altsvc_vector[j].host);
EXPECT_EQ(expected_altsvc_vector[j].port, altsvc_vector[j].port);
EXPECT_EQ(expected_altsvc_vector[j].max_age, altsvc_vector[j].max_age);
EXPECT_EQ(expected_altsvc_vector[j].version, altsvc_vector[j].version);
}
// Roundtrip test starting with |altsvc_vector|.
std::string reserialized_header_field_value =
SpdyAltSvcWireFormat::SerializeHeaderFieldValue(altsvc_vector);
SpdyAltSvcWireFormat::AlternativeServiceVector roundtrip_altsvc_vector;
ASSERT_TRUE(SpdyAltSvcWireFormat::ParseHeaderFieldValue(
reserialized_header_field_value, &roundtrip_altsvc_vector));
ASSERT_EQ(expected_altsvc_vector.size(), roundtrip_altsvc_vector.size());
for (unsigned int j = 0; j < roundtrip_altsvc_vector.size(); ++j) {
EXPECT_EQ(expected_altsvc_vector[j].protocol_id,
roundtrip_altsvc_vector[j].protocol_id);
EXPECT_EQ(expected_altsvc_vector[j].host,
roundtrip_altsvc_vector[j].host);
EXPECT_EQ(expected_altsvc_vector[j].port,
roundtrip_altsvc_vector[j].port);
EXPECT_EQ(expected_altsvc_vector[j].max_age,
roundtrip_altsvc_vector[j].max_age);
EXPECT_EQ(expected_altsvc_vector[j].version,
roundtrip_altsvc_vector[j].version);
}
}
}
TEST(SpdyAltSvcWireFormatTest, SerializeEmptyHeaderFieldValue) {
SpdyAltSvcWireFormat::AlternativeServiceVector altsvc_vector;
EXPECT_EQ("clear",
SpdyAltSvcWireFormat::SerializeHeaderFieldValue(altsvc_vector));
}
// Test ParseHeaderFieldValue() and SerializeHeaderFieldValue() on the same pair
// of |expected_header_field_value| and |altsvc|, with and without hostname and
// each
// parameter. Single alternative service at a time.
TEST(SpdyAltSvcWireFormatTest, RoundTrip) {
for (int i = 0; i < 1 << 3; ++i) {
SpdyAltSvcWireFormat::AlternativeService altsvc;
std::string expected_header_field_value;
FuzzAlternativeService(i, &altsvc, &expected_header_field_value);
// Test ParseHeaderFieldValue().
SpdyAltSvcWireFormat::AlternativeServiceVector parsed_altsvc_vector;
ASSERT_TRUE(SpdyAltSvcWireFormat::ParseHeaderFieldValue(
expected_header_field_value, &parsed_altsvc_vector));
ASSERT_EQ(1u, parsed_altsvc_vector.size());
EXPECT_EQ(altsvc.protocol_id, parsed_altsvc_vector[0].protocol_id);
EXPECT_EQ(altsvc.host, parsed_altsvc_vector[0].host);
EXPECT_EQ(altsvc.port, parsed_altsvc_vector[0].port);
EXPECT_EQ(altsvc.max_age, parsed_altsvc_vector[0].max_age);
EXPECT_EQ(altsvc.version, parsed_altsvc_vector[0].version);
// Test SerializeHeaderFieldValue().
SpdyAltSvcWireFormat::AlternativeServiceVector altsvc_vector;
altsvc_vector.push_back(altsvc);
EXPECT_EQ(expected_header_field_value,
SpdyAltSvcWireFormat::SerializeHeaderFieldValue(altsvc_vector));
}
}
// Test ParseHeaderFieldValue() and SerializeHeaderFieldValue() on the same pair
// of |expected_header_field_value| and |altsvc|, with and without hostname and
// each
// parameter. Multiple alternative services at a time.
TEST(SpdyAltSvcWireFormatTest, RoundTripMultiple) {
SpdyAltSvcWireFormat::AlternativeServiceVector altsvc_vector;
std::string expected_header_field_value;
for (int i = 0; i < 1 << 3; ++i) {
SpdyAltSvcWireFormat::AlternativeService altsvc;
FuzzAlternativeService(i, &altsvc, &expected_header_field_value);
altsvc_vector.push_back(altsvc);
}
// Test ParseHeaderFieldValue().
SpdyAltSvcWireFormat::AlternativeServiceVector parsed_altsvc_vector;
ASSERT_TRUE(SpdyAltSvcWireFormat::ParseHeaderFieldValue(
expected_header_field_value, &parsed_altsvc_vector));
ASSERT_EQ(altsvc_vector.size(), parsed_altsvc_vector.size());
SpdyAltSvcWireFormat::AlternativeServiceVector::iterator expected_it =
altsvc_vector.begin();
SpdyAltSvcWireFormat::AlternativeServiceVector::iterator parsed_it =
parsed_altsvc_vector.begin();
for (; expected_it != altsvc_vector.end(); ++expected_it, ++parsed_it) {
EXPECT_EQ(expected_it->protocol_id, parsed_it->protocol_id);
EXPECT_EQ(expected_it->host, parsed_it->host);
EXPECT_EQ(expected_it->port, parsed_it->port);
EXPECT_EQ(expected_it->max_age, parsed_it->max_age);
EXPECT_EQ(expected_it->version, parsed_it->version);
}
// Test SerializeHeaderFieldValue().
EXPECT_EQ(expected_header_field_value,
SpdyAltSvcWireFormat::SerializeHeaderFieldValue(altsvc_vector));
}
// ParseHeaderFieldValue() should return false on malformed field values:
// invalid percent encoding, unmatched quotation mark, empty port, non-numeric
// characters in numeric fields.
TEST(SpdyAltSvcWireFormatTest, ParseHeaderFieldValueInvalid) {
SpdyAltSvcWireFormat::AlternativeServiceVector altsvc_vector;
const char* invalid_field_value_array[] = {"a%",
"a%x",
"a%b",
"a%9z",
"a=",
"a=\"",
"a=\"b\"",
"a=\":\"",
"a=\"c:\"",
"a=\"c:foo\"",
"a=\"c:42foo\"",
"a=\"b:42\"bar",
"a=\"b:42\" ; m",
"a=\"b:42\" ; min-age",
"a=\"b:42\" ; ma",
"a=\"b:42\" ; ma=",
"a=\"b:42\" ; v=\"..\"",
"a=\"b:42\" ; ma=ma",
"a=\"b:42\" ; ma=123bar",
"a=\"b:42\" ; v=24",
"a=\"b:42\" ; v=24,25",
"a=\"b:42\" ; v=\"-3\"",
"a=\"b:42\" ; v=\"1.2\"",
"a=\"b:42\" ; v=\"24,\""};
for (const char* invalid_field_value : invalid_field_value_array) {
EXPECT_FALSE(SpdyAltSvcWireFormat::ParseHeaderFieldValue(
invalid_field_value, &altsvc_vector))
<< invalid_field_value;
}
}
// ParseHeaderFieldValue() should return false on a field values truncated
// before closing quotation mark, without trying to access memory beyond the end
// of the input.
TEST(SpdyAltSvcWireFormatTest, ParseTruncatedHeaderFieldValue) {
SpdyAltSvcWireFormat::AlternativeServiceVector altsvc_vector;
const char* field_value_array[] = {"a=\":137\"", "a=\"foo:137\"",
"a%25=\"foo\\\"bar\\\\baz:137\""};
for (const std::string& field_value : field_value_array) {
for (size_t len = 1; len < field_value.size(); ++len) {
EXPECT_FALSE(SpdyAltSvcWireFormat::ParseHeaderFieldValue(
field_value.substr(0, len), &altsvc_vector))
<< len;
}
}
}
// Tests of private methods.
// Test SkipWhiteSpace().
TEST(SpdyAltSvcWireFormatTest, SkipWhiteSpace) {
StringPiece input("a \tb ");
StringPiece::const_iterator c = input.begin();
test::SpdyAltSvcWireFormatPeer::SkipWhiteSpace(&c, input.end());
ASSERT_EQ(input.begin(), c);
++c;
test::SpdyAltSvcWireFormatPeer::SkipWhiteSpace(&c, input.end());
ASSERT_EQ(input.begin() + 3, c);
++c;
test::SpdyAltSvcWireFormatPeer::SkipWhiteSpace(&c, input.end());
ASSERT_EQ(input.end(), c);
}
// Test PercentDecode() on valid input.
TEST(SpdyAltSvcWireFormatTest, PercentDecodeValid) {
StringPiece input("");
std::string output;
ASSERT_TRUE(test::SpdyAltSvcWireFormatPeer::PercentDecode(
input.begin(), input.end(), &output));
EXPECT_EQ("", output);
input = StringPiece("foo");
output.clear();
ASSERT_TRUE(test::SpdyAltSvcWireFormatPeer::PercentDecode(
input.begin(), input.end(), &output));
EXPECT_EQ("foo", output);
input = StringPiece("%2ca%5Cb");
output.clear();
ASSERT_TRUE(test::SpdyAltSvcWireFormatPeer::PercentDecode(
input.begin(), input.end(), &output));
EXPECT_EQ(",a\\b", output);
}
// Test PercentDecode() on invalid input.
TEST(SpdyAltSvcWireFormatTest, PercentDecodeInvalid) {
const char* invalid_input_array[] = {"a%", "a%x", "a%b", "%J22", "%9z"};
for (const char* invalid_input : invalid_input_array) {
StringPiece input(invalid_input);
std::string output;
EXPECT_FALSE(test::SpdyAltSvcWireFormatPeer::PercentDecode(
input.begin(), input.end(), &output))
<< input;
}
}
// Test ParseAltAuthority() on valid input.
TEST(SpdyAltSvcWireFormatTest, ParseAltAuthorityValid) {
StringPiece input(":42");
std::string host;
uint16_t port;
ASSERT_TRUE(test::SpdyAltSvcWireFormatPeer::ParseAltAuthority(
input.begin(), input.end(), &host, &port));
EXPECT_TRUE(host.empty());
EXPECT_EQ(42, port);
input = StringPiece("foo:137");
ASSERT_TRUE(test::SpdyAltSvcWireFormatPeer::ParseAltAuthority(
input.begin(), input.end(), &host, &port));
EXPECT_EQ("foo", host);
EXPECT_EQ(137, port);
}
// Test ParseAltAuthority() on invalid input: empty string, no port, zero port,
// non-digit characters following port.
TEST(SpdyAltSvcWireFormatTest, ParseAltAuthorityInvalid) {
const char* invalid_input_array[] = {"", ":", "foo:", ":bar",
":0", "foo:0", ":12bar", "foo:23bar",
" ", ":12 ", "foo:12 "};
for (const char* invalid_input : invalid_input_array) {
StringPiece input(invalid_input);
std::string host;
uint16_t port;
EXPECT_FALSE(test::SpdyAltSvcWireFormatPeer::ParseAltAuthority(
input.begin(), input.end(), &host, &port))
<< input;
}
}
// Test ParseInteger() on valid input.
TEST(SpdyAltSvcWireFormatTest, ParseIntegerValid) {
StringPiece input("3");
uint16_t value;
ASSERT_TRUE(test::SpdyAltSvcWireFormatPeer::ParsePositiveInteger16(
input.begin(), input.end(), &value));
EXPECT_EQ(3, value);
input = StringPiece("1337");
ASSERT_TRUE(test::SpdyAltSvcWireFormatPeer::ParsePositiveInteger16(
input.begin(), input.end(), &value));
EXPECT_EQ(1337, value);
}
// Test ParseIntegerValid() on invalid input: empty, zero, non-numeric, trailing
// non-numeric characters.
TEST(SpdyAltSvcWireFormatTest, ParseIntegerInvalid) {
const char* invalid_input_array[] = {"", " ", "a", "0", "00", "1 ", "12b"};
for (const char* invalid_input : invalid_input_array) {
StringPiece input(invalid_input);
uint16_t value;
EXPECT_FALSE(test::SpdyAltSvcWireFormatPeer::ParsePositiveInteger16(
input.begin(), input.end(), &value))
<< input;
}
}
// Test ParseIntegerValid() around overflow limit.
TEST(SpdyAltSvcWireFormatTest, ParseIntegerOverflow) {
// Largest possible uint16_t value.
StringPiece input("65535");
uint16_t value16;
ASSERT_TRUE(test::SpdyAltSvcWireFormatPeer::ParsePositiveInteger16(
input.begin(), input.end(), &value16));
EXPECT_EQ(65535, value16);
// Overflow uint16_t, ParsePositiveInteger16() should return false.
input = StringPiece("65536");
ASSERT_FALSE(test::SpdyAltSvcWireFormatPeer::ParsePositiveInteger16(
input.begin(), input.end(), &value16));
// However, even if overflow is not checked for, 65536 overflows to 0, which
// returns false anyway. Check for a larger number which overflows to 1.
input = StringPiece("65537");
ASSERT_FALSE(test::SpdyAltSvcWireFormatPeer::ParsePositiveInteger16(
input.begin(), input.end(), &value16));
// Largest possible uint32_t value.
input = StringPiece("4294967295");
uint32_t value32;
ASSERT_TRUE(test::SpdyAltSvcWireFormatPeer::ParsePositiveInteger32(
input.begin(), input.end(), &value32));
EXPECT_EQ(4294967295, value32);
// Overflow uint32_t, ParsePositiveInteger32() should return false.
input = StringPiece("4294967296");
ASSERT_FALSE(test::SpdyAltSvcWireFormatPeer::ParsePositiveInteger32(
input.begin(), input.end(), &value32));
// However, even if overflow is not checked for, 4294967296 overflows to 0,
// which returns false anyway. Check for a larger number which overflows to
// 1.
input = StringPiece("4294967297");
ASSERT_FALSE(test::SpdyAltSvcWireFormatPeer::ParsePositiveInteger32(
input.begin(), input.end(), &value32));
}
} // namespace
} // namespace net