mirror of
https://github.com/klzgrad/naiveproxy.git
synced 2024-11-24 22:36:09 +03:00
347 lines
12 KiB
C++
347 lines
12 KiB
C++
|
// 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 <algorithm>
|
||
|
#include <utility>
|
||
|
|
||
|
#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<spdy::SpdyStreamId>(-1)),
|
||
|
promised_stream_id_(static_cast<spdy::SpdyStreamId>(-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
|