// Copyright (c) 2012 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/buffered_spdy_framer.h" #include #include #include "base/logging.h" #include "net/log/net_log_with_source.h" #include "net/spdy/spdy_test_util_common.h" #include "testing/platform_test.h" namespace net { namespace { class TestBufferedSpdyVisitor : public BufferedSpdyFramerVisitorInterface { public: TestBufferedSpdyVisitor() : buffered_spdy_framer_(kMaxHeaderListSizeForTest, NetLogWithSource()), error_count_(0), setting_count_(0), headers_frame_count_(0), push_promise_frame_count_(0), goaway_count_(0), altsvc_count_(0), header_stream_id_(static_cast(-1)), promised_stream_id_(static_cast(-1)) {} void OnError( http2::Http2DecoderAdapter::SpdyFramerError spdy_framer_error) override { VLOG(1) << "spdy::SpdyFramer Error: " << spdy_framer_error; error_count_++; } void OnStreamError(spdy::SpdyStreamId stream_id, const std::string& description) override { VLOG(1) << "spdy::SpdyFramer Error on stream: " << stream_id << " " << description; error_count_++; } void OnHeaders(spdy::SpdyStreamId stream_id, bool has_priority, int weight, spdy::SpdyStreamId parent_stream_id, bool exclusive, bool fin, spdy::SpdyHeaderBlock headers, base::TimeTicks recv_first_byte_time) override { header_stream_id_ = stream_id; headers_frame_count_++; headers_ = std::move(headers); } void OnDataFrameHeader(spdy::SpdyStreamId stream_id, size_t length, bool fin) override { ADD_FAILURE() << "Unexpected OnDataFrameHeader call."; } void OnStreamFrameData(spdy::SpdyStreamId stream_id, const char* data, size_t len) override { LOG(FATAL) << "Unexpected OnStreamFrameData call."; } void OnStreamEnd(spdy::SpdyStreamId stream_id) override { LOG(FATAL) << "Unexpected OnStreamEnd call."; } void OnStreamPadding(spdy::SpdyStreamId stream_id, size_t len) override { LOG(FATAL) << "Unexpected OnStreamPadding call."; } void OnSettings() override {} void OnSettingsAck() override {} void OnSettingsEnd() override {} void OnSetting(spdy::SpdySettingsId id, uint32_t value) override { setting_count_++; } void OnPing(spdy::SpdyPingId unique_id, bool is_ack) override {} void OnRstStream(spdy::SpdyStreamId stream_id, spdy::SpdyErrorCode error_code) override {} void OnGoAway(spdy::SpdyStreamId last_accepted_stream_id, spdy::SpdyErrorCode error_code, base::StringPiece debug_data) override { goaway_count_++; goaway_last_accepted_stream_id_ = last_accepted_stream_id; goaway_error_code_ = error_code; goaway_debug_data_.assign(debug_data.data(), debug_data.size()); } void OnDataFrameHeader(const spdy::SpdySerializedFrame* frame) { LOG(FATAL) << "Unexpected OnDataFrameHeader call."; } void OnRstStream(const spdy::SpdySerializedFrame& frame) {} void OnGoAway(const spdy::SpdySerializedFrame& frame) {} void OnPing(const spdy::SpdySerializedFrame& frame) {} void OnWindowUpdate(spdy::SpdyStreamId stream_id, int delta_window_size) override {} void OnPushPromise(spdy::SpdyStreamId stream_id, spdy::SpdyStreamId promised_stream_id, spdy::SpdyHeaderBlock headers) override { header_stream_id_ = stream_id; push_promise_frame_count_++; promised_stream_id_ = promised_stream_id; headers_ = std::move(headers); } void OnAltSvc(spdy::SpdyStreamId stream_id, base::StringPiece origin, const spdy::SpdyAltSvcWireFormat::AlternativeServiceVector& altsvc_vector) override { altsvc_count_++; altsvc_stream_id_ = stream_id; origin.CopyToString(&altsvc_origin_); altsvc_vector_ = altsvc_vector; } bool OnUnknownFrame(spdy::SpdyStreamId stream_id, uint8_t frame_type) override { return true; } // Convenience function which runs a framer simulation with particular input. void SimulateInFramer(const spdy::SpdySerializedFrame& frame) { const char* input_ptr = frame.data(); size_t input_remaining = frame.size(); buffered_spdy_framer_.set_visitor(this); while (input_remaining > 0 && buffered_spdy_framer_.spdy_framer_error() == http2::Http2DecoderAdapter::SPDY_NO_ERROR) { // To make the tests more interesting, we feed random (amd small) chunks // into the framer. This simulates getting strange-sized reads from // the socket. const size_t kMaxReadSize = 32; size_t bytes_read = (rand() % std::min(input_remaining, kMaxReadSize)) + 1; size_t bytes_processed = buffered_spdy_framer_.ProcessInput(input_ptr, bytes_read); input_remaining -= bytes_processed; input_ptr += bytes_processed; } } BufferedSpdyFramer buffered_spdy_framer_; // Counters from the visitor callbacks. int error_count_; int setting_count_; int headers_frame_count_; int push_promise_frame_count_; int goaway_count_; int altsvc_count_; // Header block streaming state: spdy::SpdyStreamId header_stream_id_; spdy::SpdyStreamId promised_stream_id_; // Headers from OnHeaders and OnPushPromise for verification. spdy::SpdyHeaderBlock headers_; // OnGoAway parameters. spdy::SpdyStreamId goaway_last_accepted_stream_id_; spdy::SpdyErrorCode goaway_error_code_; std::string goaway_debug_data_; // OnAltSvc parameters. spdy::SpdyStreamId altsvc_stream_id_; std::string altsvc_origin_; spdy::SpdyAltSvcWireFormat::AlternativeServiceVector altsvc_vector_; }; } // namespace class BufferedSpdyFramerTest : public PlatformTest {}; TEST_F(BufferedSpdyFramerTest, OnSetting) { spdy::SpdyFramer framer(spdy::SpdyFramer::ENABLE_COMPRESSION); spdy::SpdySettingsIR settings_ir; settings_ir.AddSetting(spdy::SETTINGS_INITIAL_WINDOW_SIZE, 2); settings_ir.AddSetting(spdy::SETTINGS_MAX_CONCURRENT_STREAMS, 3); spdy::SpdySerializedFrame control_frame( framer.SerializeSettings(settings_ir)); TestBufferedSpdyVisitor visitor; visitor.SimulateInFramer(control_frame); EXPECT_EQ(0, visitor.error_count_); EXPECT_EQ(2, visitor.setting_count_); } TEST_F(BufferedSpdyFramerTest, HeaderListTooLarge) { spdy::SpdyHeaderBlock headers; std::string long_header_value(256 * 1024, 'x'); headers["foo"] = long_header_value; spdy::SpdyHeadersIR headers_ir(/*stream_id=*/1, std::move(headers)); NetLogWithSource net_log; BufferedSpdyFramer framer(kMaxHeaderListSizeForTest, net_log); spdy::SpdySerializedFrame control_frame = framer.SerializeFrame(headers_ir); TestBufferedSpdyVisitor visitor; visitor.SimulateInFramer(control_frame); EXPECT_EQ(1, visitor.error_count_); EXPECT_EQ(0, visitor.headers_frame_count_); EXPECT_EQ(0, visitor.push_promise_frame_count_); EXPECT_EQ(spdy::SpdyHeaderBlock(), visitor.headers_); } TEST_F(BufferedSpdyFramerTest, ValidHeadersAfterInvalidHeaders) { spdy::SpdyHeaderBlock headers; headers["invalid"] = "\r\n\r\n"; spdy::SpdyHeaderBlock headers2; headers["alpha"] = "beta"; SpdyTestUtil spdy_test_util; spdy::SpdySerializedFrame headers_frame( spdy_test_util.ConstructSpdyReply(1, std::move(headers))); spdy::SpdySerializedFrame headers_frame2( spdy_test_util.ConstructSpdyReply(2, std::move(headers2))); TestBufferedSpdyVisitor visitor; visitor.SimulateInFramer(headers_frame); EXPECT_EQ(1, visitor.error_count_); EXPECT_EQ(0, visitor.headers_frame_count_); visitor.SimulateInFramer(headers_frame2); EXPECT_EQ(1, visitor.error_count_); EXPECT_EQ(1, visitor.headers_frame_count_); } TEST_F(BufferedSpdyFramerTest, ReadHeadersHeaderBlock) { spdy::SpdyHeaderBlock headers; headers["alpha"] = "beta"; headers["gamma"] = "delta"; spdy::SpdyHeadersIR headers_ir(/*stream_id=*/1, headers.Clone()); NetLogWithSource net_log; BufferedSpdyFramer framer(kMaxHeaderListSizeForTest, net_log); spdy::SpdySerializedFrame control_frame = framer.SerializeFrame(headers_ir); TestBufferedSpdyVisitor visitor; visitor.SimulateInFramer(control_frame); EXPECT_EQ(0, visitor.error_count_); EXPECT_EQ(1, visitor.headers_frame_count_); EXPECT_EQ(0, visitor.push_promise_frame_count_); EXPECT_EQ(headers, visitor.headers_); } TEST_F(BufferedSpdyFramerTest, ReadPushPromiseHeaderBlock) { spdy::SpdyHeaderBlock headers; headers["alpha"] = "beta"; headers["gamma"] = "delta"; NetLogWithSource net_log; BufferedSpdyFramer framer(kMaxHeaderListSizeForTest, net_log); spdy::SpdyPushPromiseIR push_promise_ir( /*stream_id=*/1, /*promised_stream_id=*/2, headers.Clone()); spdy::SpdySerializedFrame control_frame = framer.SerializeFrame(push_promise_ir); TestBufferedSpdyVisitor visitor; visitor.SimulateInFramer(control_frame); EXPECT_EQ(0, visitor.error_count_); EXPECT_EQ(0, visitor.headers_frame_count_); EXPECT_EQ(1, visitor.push_promise_frame_count_); EXPECT_EQ(headers, visitor.headers_); EXPECT_EQ(1u, visitor.header_stream_id_); EXPECT_EQ(2u, visitor.promised_stream_id_); } TEST_F(BufferedSpdyFramerTest, GoAwayDebugData) { spdy::SpdyGoAwayIR go_ir(/*last_accepted_stream_id=*/2, spdy::ERROR_CODE_FRAME_SIZE_ERROR, "foo"); NetLogWithSource net_log; BufferedSpdyFramer framer(kMaxHeaderListSizeForTest, net_log); spdy::SpdySerializedFrame goaway_frame = framer.SerializeFrame(go_ir); TestBufferedSpdyVisitor visitor; visitor.SimulateInFramer(goaway_frame); EXPECT_EQ(0, visitor.error_count_); EXPECT_EQ(1, visitor.goaway_count_); EXPECT_EQ(2u, visitor.goaway_last_accepted_stream_id_); EXPECT_EQ(spdy::ERROR_CODE_FRAME_SIZE_ERROR, visitor.goaway_error_code_); EXPECT_EQ("foo", visitor.goaway_debug_data_); } // ALTSVC frame on stream 0 must have an origin. TEST_F(BufferedSpdyFramerTest, OnAltSvcOnStreamZero) { const spdy::SpdyStreamId altsvc_stream_id(0); spdy::SpdyAltSvcIR altsvc_ir(altsvc_stream_id); spdy::SpdyAltSvcWireFormat::AlternativeService alternative_service( "quic", "alternative.example.org", 443, 86400, spdy::SpdyAltSvcWireFormat::VersionVector()); altsvc_ir.add_altsvc(alternative_service); const char altsvc_origin[] = "https://www.example.org"; altsvc_ir.set_origin(altsvc_origin); NetLogWithSource net_log; BufferedSpdyFramer framer(kMaxHeaderListSizeForTest, net_log); spdy::SpdySerializedFrame altsvc_frame(framer.SerializeFrame(altsvc_ir)); TestBufferedSpdyVisitor visitor; visitor.SimulateInFramer(altsvc_frame); EXPECT_EQ(0, visitor.error_count_); EXPECT_EQ(1, visitor.altsvc_count_); EXPECT_EQ(altsvc_stream_id, visitor.altsvc_stream_id_); EXPECT_EQ(altsvc_origin, visitor.altsvc_origin_); ASSERT_EQ(1u, visitor.altsvc_vector_.size()); EXPECT_EQ(alternative_service, visitor.altsvc_vector_[0]); } // ALTSVC frame on a non-zero stream must not have an origin. TEST_F(BufferedSpdyFramerTest, OnAltSvcOnNonzeroStream) { const spdy::SpdyStreamId altsvc_stream_id(1); spdy::SpdyAltSvcIR altsvc_ir(altsvc_stream_id); spdy::SpdyAltSvcWireFormat::AlternativeService alternative_service( "quic", "alternative.example.org", 443, 86400, spdy::SpdyAltSvcWireFormat::VersionVector()); altsvc_ir.add_altsvc(alternative_service); NetLogWithSource net_log; BufferedSpdyFramer framer(kMaxHeaderListSizeForTest, net_log); spdy::SpdySerializedFrame altsvc_frame(framer.SerializeFrame(altsvc_ir)); TestBufferedSpdyVisitor visitor; visitor.SimulateInFramer(altsvc_frame); EXPECT_EQ(0, visitor.error_count_); EXPECT_EQ(1, visitor.altsvc_count_); EXPECT_EQ(altsvc_stream_id, visitor.altsvc_stream_id_); EXPECT_TRUE(visitor.altsvc_origin_.empty()); ASSERT_EQ(1u, visitor.altsvc_vector_.size()); EXPECT_EQ(alternative_service, visitor.altsvc_vector_[0]); } } // namespace net