// Copyright 2021 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "net/spdy/alps_decoder.h" #include "base/test/metrics/histogram_tester.h" #include "base/test/scoped_feature_list.h" #include "net/base/features.h" #include "net/base/hex_utils.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" using ::spdy::AcceptChOriginValuePair; using ::testing::ElementsAre; using ::testing::IsEmpty; using ::testing::Pair; namespace net { namespace { TEST(AlpsDecoderTest, EmptyInput) { AlpsDecoder decoder; EXPECT_THAT(decoder.GetAcceptCh(), IsEmpty()); EXPECT_THAT(decoder.GetSettings(), IsEmpty()); EXPECT_EQ(0, decoder.settings_frame_count()); AlpsDecoder::Error error = decoder.Decode({}); EXPECT_EQ(AlpsDecoder::Error::kNoError, error); EXPECT_THAT(decoder.GetAcceptCh(), IsEmpty()); EXPECT_THAT(decoder.GetSettings(), IsEmpty()); EXPECT_EQ(0, decoder.settings_frame_count()); } TEST(AlpsDecoderTest, EmptyAcceptChFrame) { AlpsDecoder decoder; AlpsDecoder::Error error = decoder.Decode(HexDecode("000000" // length "89" // type ACCEPT_CH "00" // flags "00000000")); // stream ID EXPECT_EQ(AlpsDecoder::Error::kNoError, error); EXPECT_THAT(decoder.GetAcceptCh(), IsEmpty()); EXPECT_THAT(decoder.GetSettings(), IsEmpty()); EXPECT_EQ(0, decoder.settings_frame_count()); } TEST(AlpsDecoderTest, EmptySettingsFrame) { AlpsDecoder decoder; AlpsDecoder::Error error = decoder.Decode(HexDecode("000000" // length "04" // type SETTINGS "00" // flags "00000000")); // stream ID EXPECT_EQ(AlpsDecoder::Error::kNoError, error); EXPECT_THAT(decoder.GetAcceptCh(), IsEmpty()); EXPECT_THAT(decoder.GetSettings(), IsEmpty()); EXPECT_EQ(1, decoder.settings_frame_count()); } TEST(AlpsDecoderTest, ParseSettingsAndAcceptChFrames) { AlpsDecoder decoder; AlpsDecoder::Error error = decoder.Decode(HexDecode( // ACCEPT_CH frame "00003d" // length "89" // type ACCEPT_CH "00" // flags "00000000" // stream ID "0017" // origin length "68747470733a2f2f7777772e" // "6578616d706c652e636f6d" // origin "https://www.example.com" "0003" // value length "666f6f" // value "foo" "0018" // origin length "68747470733a2f2f6d61696c" // "2e6578616d706c652e636f6d" // origin "https://mail.example.com" "0003" // value length "626172" // value "bar" // SETTINGS frame "00000c" // length "04" // type "00" // flags "00000000" // stream ID "0dab" // identifier "01020304" // value "1234" // identifier "fedcba98")); // value EXPECT_EQ(AlpsDecoder::Error::kNoError, error); EXPECT_THAT( decoder.GetAcceptCh(), ElementsAre(AcceptChOriginValuePair{"https://www.example.com", "foo"}, AcceptChOriginValuePair{"https://mail.example.com", "bar"})); EXPECT_THAT(decoder.GetSettings(), ElementsAre(Pair(0x0dab, 0x01020304), Pair(0x1234, 0xfedcba98))); EXPECT_EQ(1, decoder.settings_frame_count()); } TEST(AlpsDecoderTest, ParseLargeAcceptChFrame) { std::string frame = HexDecode( // ACCEPT_CH frame "0001ab" // length: 427 total bytes "89" // type ACCEPT_CH "00" // flags "00000000" // stream ID "0017" // origin length "68747470733a2f2f7777772e" // "6578616d706c652e636f6d" // origin "https://www.example.com" "0190" // value length (400 in hex) ); // The Accept-CH tokens payload is a string of 400 'x' characters. const std::string accept_ch_tokens(400, 'x'); // Append the value bytes to the frame. frame += accept_ch_tokens; AlpsDecoder decoder; AlpsDecoder::Error error = decoder.Decode(frame); EXPECT_EQ(AlpsDecoder::Error::kNoError, error); EXPECT_THAT(decoder.GetAcceptCh(), ElementsAre(AcceptChOriginValuePair{"https://www.example.com", accept_ch_tokens})); } TEST(AlpsDecoderTest, DisableAlpsParsing) { base::test::ScopedFeatureList feature_list; feature_list.InitAndDisableFeature(features::kAlpsParsing); AlpsDecoder decoder; AlpsDecoder::Error error = decoder.Decode(HexDecode( // ACCEPT_CH frame "00003d" // length "89" // type ACCEPT_CH "00" // flags "00000000" // stream ID "0017" // origin length "68747470733a2f2f7777772e" // "6578616d706c652e636f6d" // origin "https://www.example.com" "0003" // value length "666f6f" // value "foo" "0018" // origin length "68747470733a2f2f6d61696c" // "2e6578616d706c652e636f6d" // origin "https://mail.example.com" "0003" // value length "626172" // value "bar" )); EXPECT_EQ(AlpsDecoder::Error::kNoError, error); EXPECT_THAT(decoder.GetAcceptCh(), IsEmpty()); } TEST(AlpsDecoderTest, DisableAlpsClientHintParsing) { base::test::ScopedFeatureList feature_list; feature_list.InitAndDisableFeature(features::kAlpsClientHintParsing); AlpsDecoder decoder; AlpsDecoder::Error error = decoder.Decode(HexDecode( // ACCEPT_CH frame "00003d" // length "89" // type ACCEPT_CH "00" // flags "00000000" // stream ID "0017" // origin length "68747470733a2f2f7777772e" // "6578616d706c652e636f6d" // origin "https://www.example.com" "0003" // value length "666f6f" // value "foo" "0018" // origin length "68747470733a2f2f6d61696c" // "2e6578616d706c652e636f6d" // origin "https://mail.example.com" "0003" // value length "626172" // value "bar" )); EXPECT_EQ(AlpsDecoder::Error::kNoError, error); EXPECT_THAT(decoder.GetAcceptCh(), IsEmpty()); } TEST(AlpsDecoderTest, IncompleteFrame) { AlpsDecoder decoder; AlpsDecoder::Error error = decoder.Decode(HexDecode("00000c" // length "04" // type "00" // flags "00000000" // stream ID "0dab" // identifier "01")); // first byte of value EXPECT_EQ(AlpsDecoder::Error::kNotOnFrameBoundary, error); } TEST(AlpsDecoderTest, TwoSettingsFrames) { AlpsDecoder decoder; AlpsDecoder::Error error = decoder.Decode(HexDecode("000006" // length "04" // type SETTINGS "00" // flags "00000000" // stream ID "0dab" // identifier "01020304" // value "000006" // length "04" // type SETTINGS "00" // flags "00000000" // stream ID "1234" // identifier "fedcba98")); // value EXPECT_EQ(AlpsDecoder::Error::kNoError, error); EXPECT_EQ(2, decoder.settings_frame_count()); EXPECT_THAT(decoder.GetSettings(), ElementsAre(Pair(0x0dab, 0x01020304), Pair(0x1234, 0xfedcba98))); } TEST(AlpsDecoderTest, AcceptChOnInvalidStream) { AlpsDecoder decoder; AlpsDecoder::Error error = decoder.Decode( HexDecode("00001e" // length "89" // type ACCEPT_CH "00" // flags "00000001" // invalid stream ID: should be zero "0017" // origin length "68747470733a2f2f7777772e" // "6578616d706c652e636f6d" // origin "https://www.example.com" "0003" // value length "666f6f")); // value "foo" EXPECT_EQ(AlpsDecoder::Error::kAcceptChInvalidStream, error); } // According to // https://davidben.github.io/http-client-hint-reliability/ \ // draft-davidben-http-client-hint-reliability.html#name-http-2-accept_ch-frame // "If a user agent receives an ACCEPT_CH frame whose stream [...] flags // field is non-zero, it MUST respond with a connection error [...]." TEST(AlpsDecoderTest, AcceptChWithInvalidFlags) { AlpsDecoder decoder; AlpsDecoder::Error error = decoder.Decode( HexDecode("00001e" // length "89" // type ACCEPT_CH "02" // invalid flags: should be zero "00000000" // stream ID "0017" // origin length "68747470733a2f2f7777772e" // "6578616d706c652e636f6d" // origin "https://www.example.com" "0003" // value length "666f6f")); // value "foo" EXPECT_EQ(AlpsDecoder::Error::kAcceptChWithFlags, error); } TEST(AlpsDecoderTest, SettingsOnInvalidStream) { AlpsDecoder decoder; AlpsDecoder::Error error = decoder.Decode(HexDecode("000006" // length "04" // type SETTINGS "00" // flags "00000001" // invalid stream ID: should be zero "1234" // identifier "fedcba98")); // value EXPECT_EQ(AlpsDecoder::Error::kFramingError, error); } TEST(AlpsDecoderTest, SettingsAck) { AlpsDecoder decoder; AlpsDecoder::Error error = decoder.Decode(HexDecode("000000" // length "04" // type SETTINGS "01" // ACK flag "00000000")); // stream ID EXPECT_EQ(AlpsDecoder::Error::kSettingsWithAck, error); } // According to https://httpwg.org/specs/rfc7540.html#FrameHeader: // "Flags that have no defined semantics for a particular frame type MUST be // ignored [...]" TEST(AlpsDecoderTest, SettingsWithInvalidFlags) { AlpsDecoder decoder; AlpsDecoder::Error error = decoder.Decode(HexDecode("000006" // length "04" // type SETTINGS "02" // invalid flag "00000000" // stream ID "1234" // identifier "fedcba98")); // value EXPECT_EQ(AlpsDecoder::Error::kNoError, error); } TEST(AlpsDecoderTest, ForbiddenFrame) { AlpsDecoder decoder; AlpsDecoder::Error error = decoder.Decode(HexDecode("000003" // length "00" // frame type DATA "01" // flags END_STREAM "00000001" // stream ID "666f6f")); // payload "foo" EXPECT_EQ(AlpsDecoder::Error::kForbiddenFrame, error); } TEST(AlpsDecoderTest, UnknownFrame) { AlpsDecoder decoder; AlpsDecoder::Error error = decoder.Decode(HexDecode("000003" // length "2a" // unknown frame type "ff" // flags "00000008" // stream ID "666f6f")); // payload "foo" EXPECT_EQ(AlpsDecoder::Error::kNoError, error); EXPECT_THAT(decoder.GetAcceptCh(), IsEmpty()); EXPECT_THAT(decoder.GetSettings(), IsEmpty()); EXPECT_EQ(0, decoder.settings_frame_count()); } class AlpsDecoderTestWithFeature : public ::testing::TestWithParam { public: bool ShouldKillSessionOnAcceptChMalformed() { return GetParam(); } private: void SetUp() override { feature_list_.InitWithFeatureState( features::kShouldKillSessionOnAcceptChMalformed, ShouldKillSessionOnAcceptChMalformed()); } base::test::ScopedFeatureList feature_list_; }; INSTANTIATE_TEST_SUITE_P(All, AlpsDecoderTestWithFeature, testing::Bool()); TEST_P(AlpsDecoderTestWithFeature, MalformedAcceptChFrame) { // Correct, complete payload. std::string payload = HexDecode( "0017" // origin length "68747470733a2f2f7777772e" "6578616d706c652e636f6d" // origin "https://www.example.com" "0003" // value length "666f6f"); // value "foo" for (uint8_t payload_length = 1; payload_length < payload.length(); payload_length++) { base::HistogramTester histogram_tester; // First two bytes of length. std::string frame = HexDecode("0000"); // Last byte of length. frame.push_back(static_cast(payload_length)); frame.append( HexDecode("89" // type ACCEPT_CH "00" // flags "00000000")); // stream ID // Incomplete, malformed payload. frame.append(payload.data(), payload_length); AlpsDecoder decoder; AlpsDecoder::Error error = decoder.Decode(frame); if (ShouldKillSessionOnAcceptChMalformed()) { EXPECT_EQ(AlpsDecoder::Error::kAcceptChMalformed, error); histogram_tester.ExpectUniqueSample( "Net.SpdySession.AlpsDecoderStatus.Bypassed", static_cast(AlpsDecoder::Error::kNoError), 1); } else { EXPECT_EQ(AlpsDecoder::Error::kNoError, error); histogram_tester.ExpectUniqueSample( "Net.SpdySession.AlpsDecoderStatus.Bypassed", static_cast(AlpsDecoder::Error::kAcceptChMalformed), 1); } } } } // namespace } // namespace net