blob: 5161c9f223cc8fdf397343a4cdff4a948e5866fb [file] [log] [blame]
// Copyright (c) 2013 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/tools/quic/quic_simple_server_stream.h"
#include <utility>
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
#include "net/quic/quic_connection.h"
#include "net/quic/quic_flags.h"
#include "net/quic/quic_protocol.h"
#include "net/quic/quic_utils.h"
#include "net/quic/spdy_utils.h"
#include "net/quic/test_tools/crypto_test_utils.h"
#include "net/quic/test_tools/quic_test_utils.h"
#include "net/quic/test_tools/reliable_quic_stream_peer.h"
#include "net/test/gtest_util.h"
#include "net/tools/epoll_server/epoll_server.h"
#include "net/tools/quic/quic_in_memory_cache.h"
#include "net/tools/quic/quic_simple_server_session.h"
#include "net/tools/quic/spdy_balsa_utils.h"
#include "net/tools/quic/test_tools/quic_in_memory_cache_peer.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
using base::StringPiece;
using net::test::MockQuicConnection;
using net::test::MockQuicConnectionHelper;
using net::test::MockQuicSpdySession;
using net::test::ReliableQuicStreamPeer;
using net::test::SupportedVersions;
using net::test::kInitialSessionFlowControlWindowForTest;
using net::test::kInitialStreamFlowControlWindowForTest;
using std::string;
using testing::_;
using testing::AnyNumber;
using testing::Invoke;
using testing::InvokeArgument;
using testing::InSequence;
using testing::Return;
using testing::StrictMock;
using testing::WithArgs;
namespace net {
namespace test {
class QuicSimpleServerStreamPeer : public QuicSimpleServerStream {
public:
QuicSimpleServerStreamPeer(QuicStreamId stream_id, QuicSpdySession* session)
: QuicSimpleServerStream(stream_id, session) {}
~QuicSimpleServerStreamPeer() override{};
using QuicSimpleServerStream::SendResponse;
using QuicSimpleServerStream::SendErrorResponse;
SpdyHeaderBlock* mutable_headers() { return &request_headers_; }
static void SendResponse(QuicSimpleServerStream* stream) {
stream->SendResponse();
}
static void SendErrorResponse(QuicSimpleServerStream* stream) {
stream->SendErrorResponse();
}
static const string& body(QuicSimpleServerStream* stream) {
return stream->body_;
}
static int content_length(QuicSimpleServerStream* stream) {
return stream->content_length_;
}
static SpdyHeaderBlock& headers(QuicSimpleServerStream* stream) {
return stream->request_headers_;
}
};
class MockQuicSimpleServerSession : public QuicSimpleServerSession {
public:
const size_t kMaxStreamsForTest = 100;
explicit MockQuicSimpleServerSession(
QuicConnection* connection,
MockQuicServerSessionVisitor* owner,
MockQuicServerSessionHelper* helper,
QuicCryptoServerConfig* crypto_config,
QuicCompressedCertsCache* compressed_certs_cache)
: QuicSimpleServerSession(DefaultQuicConfig(),
connection,
owner,
helper,
crypto_config,
compressed_certs_cache) {
set_max_open_incoming_streams(kMaxStreamsForTest);
set_max_open_outgoing_streams(kMaxStreamsForTest);
ON_CALL(*this, WritevData(_, _, _, _, _, _))
.WillByDefault(testing::Return(QuicConsumedData(0, false)));
}
~MockQuicSimpleServerSession() override {}
MOCK_METHOD3(OnConnectionClosed,
void(QuicErrorCode error,
const string& error_details,
ConnectionCloseSource source));
MOCK_METHOD1(CreateIncomingDynamicStream, QuicSpdyStream*(QuicStreamId id));
MOCK_METHOD6(WritevData,
QuicConsumedData(ReliableQuicStream* stream,
QuicStreamId id,
QuicIOVector data,
QuicStreamOffset offset,
bool fin,
QuicAckListenerInterface*));
MOCK_METHOD2(OnStreamHeaders,
void(QuicStreamId stream_id, StringPiece headers_data));
MOCK_METHOD2(OnStreamHeadersPriority,
void(QuicStreamId stream_id, SpdyPriority priority));
MOCK_METHOD3(OnStreamHeadersComplete,
void(QuicStreamId stream_id, bool fin, size_t frame_len));
// Methods taking non-copyable types like SpdyHeaderBlock by value cannot be
// mocked directly.
size_t WriteHeaders(
QuicStreamId id,
SpdyHeaderBlock headers,
bool fin,
SpdyPriority priority,
QuicAckListenerInterface* ack_notifier_delegate) override {
return WriteHeadersMock(id, headers, fin, priority, ack_notifier_delegate);
}
MOCK_METHOD5(WriteHeadersMock,
size_t(QuicStreamId id,
const SpdyHeaderBlock& headers,
bool fin,
SpdyPriority priority,
QuicAckListenerInterface* ack_notifier_delegate));
MOCK_METHOD3(SendRstStream,
void(QuicStreamId stream_id,
QuicRstStreamErrorCode error,
QuicStreamOffset bytes_written));
MOCK_METHOD1(OnHeadersHeadOfLineBlocking, void(QuicTime::Delta delta));
// Matchers cannot be used on non-copyable types like SpdyHeaderBlock.
void PromisePushResources(
const string& request_url,
const std::list<QuicInMemoryCache::ServerPushInfo>& resources,
QuicStreamId original_stream_id,
const SpdyHeaderBlock& original_request_headers) override {
original_request_headers_ = original_request_headers.Clone();
PromisePushResourcesMock(request_url, resources, original_stream_id,
original_request_headers);
}
MOCK_METHOD4(PromisePushResourcesMock,
void(const string&,
const std::list<QuicInMemoryCache::ServerPushInfo>&,
QuicStreamId,
const SpdyHeaderBlock&));
using QuicSession::ActivateStream;
SpdyHeaderBlock original_request_headers_;
private:
DISALLOW_COPY_AND_ASSIGN(MockQuicSimpleServerSession);
};
namespace {
class QuicSimpleServerStreamTest
: public ::testing::TestWithParam<QuicVersion> {
public:
QuicSimpleServerStreamTest()
: connection_(
new StrictMock<MockQuicConnection>(&helper_,
&alarm_factory_,
Perspective::IS_SERVER,
SupportedVersions(GetParam()))),
crypto_config_(new QuicCryptoServerConfig(
QuicCryptoServerConfig::TESTING,
QuicRandom::GetInstance(),
::net::test::CryptoTestUtils::ProofSourceForTesting())),
compressed_certs_cache_(
QuicCompressedCertsCache::kQuicCompressedCertsCacheSize),
session_(connection_,
&session_owner_,
&session_helper_,
crypto_config_.get(),
&compressed_certs_cache_),
body_("hello world") {
FLAGS_quic_always_log_bugs_for_tests = true;
SpdyHeaderBlock request_headers;
request_headers[":host"] = "";
request_headers[":authority"] = "www.google.com";
request_headers[":path"] = "/";
request_headers[":method"] = "POST";
request_headers[":version"] = "HTTP/1.1";
request_headers["content-length"] = "11";
headers_string_ =
net::SpdyUtils::SerializeUncompressedHeaders(request_headers);
// New streams rely on having the peer's flow control receive window
// negotiated in the config.
session_.config()->SetInitialStreamFlowControlWindowToSend(
kInitialStreamFlowControlWindowForTest);
session_.config()->SetInitialSessionFlowControlWindowToSend(
kInitialSessionFlowControlWindowForTest);
stream_ = new QuicSimpleServerStreamPeer(::net::test::kClientDataStreamId1,
&session_);
// Register stream_ in dynamic_stream_map_ and pass ownership to session_.
session_.ActivateStream(stream_);
QuicInMemoryCachePeer::ResetForTests();
}
~QuicSimpleServerStreamTest() override {
QuicInMemoryCachePeer::ResetForTests();
}
const string& StreamBody() {
return QuicSimpleServerStreamPeer::body(stream_);
}
StringPiece StreamHeadersValue(const string& key) {
return (*stream_->mutable_headers())[key];
}
SpdyHeaderBlock response_headers_;
MockQuicConnectionHelper helper_;
MockAlarmFactory alarm_factory_;
StrictMock<MockQuicConnection>* connection_;
StrictMock<MockQuicServerSessionVisitor> session_owner_;
StrictMock<MockQuicServerSessionHelper> session_helper_;
std::unique_ptr<QuicCryptoServerConfig> crypto_config_;
QuicCompressedCertsCache compressed_certs_cache_;
StrictMock<MockQuicSimpleServerSession> session_;
QuicSimpleServerStreamPeer* stream_; // Owned by session_.
string headers_string_;
string body_;
};
INSTANTIATE_TEST_CASE_P(Tests,
QuicSimpleServerStreamTest,
::testing::ValuesIn(QuicSupportedVersions()));
TEST_P(QuicSimpleServerStreamTest, TestFraming) {
EXPECT_CALL(session_, WritevData(_, _, _, _, _, _))
.Times(AnyNumber())
.WillRepeatedly(Invoke(MockQuicSession::ConsumeAllData));
stream_->OnStreamHeaders(headers_string_);
stream_->OnStreamHeadersComplete(false, headers_string_.size());
stream_->OnStreamFrame(
QuicStreamFrame(stream_->id(), /*fin=*/false, /*offset=*/0, body_));
EXPECT_EQ("11", StreamHeadersValue("content-length"));
EXPECT_EQ("/", StreamHeadersValue(":path"));
EXPECT_EQ("POST", StreamHeadersValue(":method"));
EXPECT_EQ(body_, StreamBody());
}
TEST_P(QuicSimpleServerStreamTest, TestFramingOnePacket) {
EXPECT_CALL(session_, WritevData(_, _, _, _, _, _))
.Times(AnyNumber())
.WillRepeatedly(Invoke(MockQuicSession::ConsumeAllData));
stream_->OnStreamHeaders(headers_string_);
stream_->OnStreamHeadersComplete(false, headers_string_.size());
stream_->OnStreamFrame(
QuicStreamFrame(stream_->id(), /*fin=*/false, /*offset=*/0, body_));
EXPECT_EQ("11", StreamHeadersValue("content-length"));
EXPECT_EQ("/", StreamHeadersValue(":path"));
EXPECT_EQ("POST", StreamHeadersValue(":method"));
EXPECT_EQ(body_, StreamBody());
}
TEST_P(QuicSimpleServerStreamTest, SendQuicRstStreamNoErrorInStopReading) {
EXPECT_CALL(session_, WritevData(_, _, _, _, _, _))
.Times(AnyNumber())
.WillRepeatedly(Invoke(MockQuicSession::ConsumeAllData));
EXPECT_FALSE(stream_->fin_received());
EXPECT_FALSE(stream_->rst_received());
stream_->set_fin_sent(true);
stream_->CloseWriteSide();
if (GetParam() > QUIC_VERSION_28) {
EXPECT_CALL(session_, SendRstStream(_, QUIC_STREAM_NO_ERROR, _)).Times(1);
} else {
EXPECT_CALL(session_, SendRstStream(_, QUIC_STREAM_NO_ERROR, _)).Times(0);
}
stream_->StopReading();
}
TEST_P(QuicSimpleServerStreamTest, TestFramingExtraData) {
string large_body = "hello world!!!!!!";
// We'll automatically write out an error (headers + body)
EXPECT_CALL(session_, WriteHeadersMock(_, _, _, _, _));
EXPECT_CALL(session_, WritevData(_, _, _, _, _, _))
.WillOnce(Invoke(MockQuicSession::ConsumeAllData));
EXPECT_CALL(session_, SendRstStream(_, QUIC_STREAM_NO_ERROR, _)).Times(0);
stream_->OnStreamHeaders(headers_string_);
stream_->OnStreamHeadersComplete(false, headers_string_.size());
stream_->OnStreamFrame(
QuicStreamFrame(stream_->id(), /*fin=*/false, /*offset=*/0, body_));
// Content length is still 11. This will register as an error and we won't
// accept the bytes.
stream_->OnStreamFrame(
QuicStreamFrame(stream_->id(), /*fin=*/true, body_.size(), large_body));
EXPECT_EQ("11", StreamHeadersValue("content-length"));
EXPECT_EQ("/", StreamHeadersValue(":path"));
EXPECT_EQ("POST", StreamHeadersValue(":method"));
}
TEST_P(QuicSimpleServerStreamTest, SendResponseWithIllegalResponseStatus) {
// Send an illegal response with response status not supported by HTTP/2.
SpdyHeaderBlock* request_headers = stream_->mutable_headers();
(*request_headers)[":path"] = "/bar";
(*request_headers)[":authority"] = "www.google.com";
(*request_headers)[":version"] = "HTTP/1.1";
(*request_headers)[":method"] = "GET";
response_headers_[":version"] = "HTTP/1.1";
// HTTP/2 only supports integer responsecode, so "200 OK" is illegal.
response_headers_[":status"] = "200 OK";
response_headers_["content-length"] = "5";
string body = "Yummm";
QuicInMemoryCache::GetInstance()->AddResponse(
"www.google.com", "/bar", std::move(response_headers_), body);
stream_->set_fin_received(true);
InSequence s;
EXPECT_CALL(session_, WriteHeadersMock(stream_->id(), _, false, _, nullptr));
EXPECT_CALL(session_, WritevData(_, _, _, _, _, _))
.Times(1)
.WillOnce(Return(QuicConsumedData(
strlen(QuicSimpleServerStream::kErrorResponseBody), true)));
QuicSimpleServerStreamPeer::SendResponse(stream_);
EXPECT_FALSE(ReliableQuicStreamPeer::read_side_closed(stream_));
EXPECT_TRUE(stream_->reading_stopped());
EXPECT_TRUE(stream_->write_side_closed());
}
TEST_P(QuicSimpleServerStreamTest, SendResponseWithIllegalResponseStatus2) {
// Send an illegal response with response status not supported by HTTP/2.
SpdyHeaderBlock* request_headers = stream_->mutable_headers();
(*request_headers)[":path"] = "/bar";
(*request_headers)[":authority"] = "www.google.com";
(*request_headers)[":version"] = "HTTP/1.1";
(*request_headers)[":method"] = "GET";
response_headers_[":version"] = "HTTP/1.1";
// HTTP/2 only supports 3-digit-integer, so "+200" is illegal.
response_headers_[":status"] = "+200";
response_headers_["content-length"] = "5";
string body = "Yummm";
QuicInMemoryCache::GetInstance()->AddResponse(
"www.google.com", "/bar", std::move(response_headers_), body);
stream_->set_fin_received(true);
InSequence s;
EXPECT_CALL(session_, WriteHeadersMock(stream_->id(), _, false, _, nullptr));
EXPECT_CALL(session_, WritevData(_, _, _, _, _, _))
.Times(1)
.WillOnce(Return(QuicConsumedData(
strlen(QuicSimpleServerStream::kErrorResponseBody), true)));
QuicSimpleServerStreamPeer::SendResponse(stream_);
EXPECT_FALSE(ReliableQuicStreamPeer::read_side_closed(stream_));
EXPECT_TRUE(stream_->reading_stopped());
EXPECT_TRUE(stream_->write_side_closed());
}
TEST_P(QuicSimpleServerStreamTest, SendPushResponseWith404Response) {
// Create a new promised stream with even id().
QuicSimpleServerStreamPeer* promised_stream =
new QuicSimpleServerStreamPeer(2, &session_);
session_.ActivateStream(promised_stream);
// Send a push response with response status 404, which will be regarded as
// invalid server push response.
SpdyHeaderBlock* request_headers = promised_stream->mutable_headers();
(*request_headers)[":path"] = "/bar";
(*request_headers)[":authority"] = "www.google.com";
(*request_headers)[":version"] = "HTTP/1.1";
(*request_headers)[":method"] = "GET";
response_headers_[":version"] = "HTTP/1.1";
response_headers_[":status"] = "404";
response_headers_["content-length"] = "8";
string body = "NotFound";
QuicInMemoryCache::GetInstance()->AddResponse(
"www.google.com", "/bar", std::move(response_headers_), body);
InSequence s;
EXPECT_CALL(session_,
SendRstStream(promised_stream->id(), QUIC_STREAM_CANCELLED, 0));
QuicSimpleServerStreamPeer::SendResponse(promised_stream);
}
TEST_P(QuicSimpleServerStreamTest, SendResponseWithValidHeaders) {
// Add a request and response with valid headers.
SpdyHeaderBlock* request_headers = stream_->mutable_headers();
(*request_headers)[":path"] = "/bar";
(*request_headers)[":authority"] = "www.google.com";
(*request_headers)[":version"] = "HTTP/1.1";
(*request_headers)[":method"] = "GET";
response_headers_[":version"] = "HTTP/1.1";
response_headers_[":status"] = "200";
response_headers_["content-length"] = "5";
string body = "Yummm";
QuicInMemoryCache::GetInstance()->AddResponse(
"www.google.com", "/bar", std::move(response_headers_), body);
stream_->set_fin_received(true);
InSequence s;
EXPECT_CALL(session_, WriteHeadersMock(stream_->id(), _, false, _, nullptr));
EXPECT_CALL(session_, WritevData(_, _, _, _, _, _))
.Times(1)
.WillOnce(Return(QuicConsumedData(body.length(), true)));
QuicSimpleServerStreamPeer::SendResponse(stream_);
EXPECT_FALSE(ReliableQuicStreamPeer::read_side_closed(stream_));
EXPECT_TRUE(stream_->reading_stopped());
EXPECT_TRUE(stream_->write_side_closed());
}
TEST_P(QuicSimpleServerStreamTest, SendReponseWithPushResources) {
// Tests that if a reponse has push resources to be send, SendResponse() will
// call PromisePushResources() to handle these resources.
// Add a request and response with valid headers into cache.
string host = "www.google.com";
string request_path = "/foo";
string body = "Yummm";
string url = host + "/bar";
QuicInMemoryCache::ServerPushInfo push_info(GURL(url), SpdyHeaderBlock(),
kDefaultPriority, "Push body");
std::list<QuicInMemoryCache::ServerPushInfo> push_resources;
push_resources.push_back(push_info);
QuicInMemoryCache::GetInstance()->AddSimpleResponseWithServerPushResources(
host, request_path, 200, body, push_resources);
SpdyHeaderBlock* request_headers = stream_->mutable_headers();
(*request_headers)[":path"] = request_path;
(*request_headers)[":authority"] = host;
(*request_headers)[":version"] = "HTTP/1.1";
(*request_headers)[":method"] = "GET";
stream_->set_fin_received(true);
InSequence s;
EXPECT_CALL(session_,
PromisePushResourcesMock(host + request_path, _,
::net::test::kClientDataStreamId1, _));
EXPECT_CALL(session_, WriteHeadersMock(stream_->id(), _, false, _, nullptr));
EXPECT_CALL(session_, WritevData(_, _, _, _, _, _))
.Times(1)
.WillOnce(Return(QuicConsumedData(body.length(), true)));
QuicSimpleServerStreamPeer::SendResponse(stream_);
EXPECT_EQ(*request_headers, session_.original_request_headers_);
}
TEST_P(QuicSimpleServerStreamTest, PushResponseOnClientInitiatedStream) {
// Calling PushResponse() on a client initialted stream is never supposed to
// happen.
EXPECT_DFATAL(stream_->PushResponse(SpdyHeaderBlock()),
"Client initiated stream"
" shouldn't be used as promised stream.");
}
TEST_P(QuicSimpleServerStreamTest, PushResponseOnServerInitiatedStream) {
// Tests that PushResponse() should take the given headers as request headers
// and fetch response from cache, and send it out.
// Create a stream with even stream id and test against this stream.
const QuicStreamId kServerInitiatedStreamId = 2;
// Create a server initiated stream and pass it to session_.
QuicSimpleServerStreamPeer* server_initiated_stream =
new QuicSimpleServerStreamPeer(kServerInitiatedStreamId, &session_);
session_.ActivateStream(server_initiated_stream);
const string kHost = "www.foo.com";
const string kPath = "/bar";
SpdyHeaderBlock headers;
headers[":path"] = kPath;
headers[":authority"] = kHost;
headers[":version"] = "HTTP/1.1";
headers[":method"] = "GET";
response_headers_[":version"] = "HTTP/1.1";
response_headers_[":status"] = "200";
response_headers_["content-length"] = "5";
const string kBody = "Hello";
QuicInMemoryCache::GetInstance()->AddResponse(
kHost, kPath, std::move(response_headers_), kBody);
// Call PushResponse() should trigger stream to fetch response from cache
// and send it back.
EXPECT_CALL(session_,
WriteHeadersMock(kServerInitiatedStreamId, _, false,
server_initiated_stream->priority(), nullptr));
EXPECT_CALL(session_, WritevData(_, kServerInitiatedStreamId, _, _, _, _))
.Times(1)
.WillOnce(Return(QuicConsumedData(kBody.size(), true)));
server_initiated_stream->PushResponse(std::move(headers));
EXPECT_EQ(kPath, QuicSimpleServerStreamPeer::headers(
server_initiated_stream)[":path"]
.as_string());
EXPECT_EQ("GET", QuicSimpleServerStreamPeer::headers(
server_initiated_stream)[":method"]
.as_string());
}
TEST_P(QuicSimpleServerStreamTest, TestSendErrorResponse) {
EXPECT_CALL(session_, SendRstStream(_, QUIC_STREAM_NO_ERROR, _)).Times(0);
stream_->set_fin_received(true);
InSequence s;
EXPECT_CALL(session_, WriteHeadersMock(_, _, _, _, _));
EXPECT_CALL(session_, WritevData(_, _, _, _, _, _))
.Times(1)
.WillOnce(Return(QuicConsumedData(3, true)));
QuicSimpleServerStreamPeer::SendErrorResponse(stream_);
EXPECT_FALSE(ReliableQuicStreamPeer::read_side_closed(stream_));
EXPECT_TRUE(stream_->reading_stopped());
EXPECT_TRUE(stream_->write_side_closed());
}
TEST_P(QuicSimpleServerStreamTest, InvalidMultipleContentLength) {
EXPECT_CALL(session_, SendRstStream(_, QUIC_STREAM_NO_ERROR, _)).Times(0);
SpdyHeaderBlock request_headers;
// \000 is a way to write the null byte when followed by a literal digit.
request_headers["content-length"] = StringPiece("11\00012", 5);
headers_string_ = SpdyUtils::SerializeUncompressedHeaders(request_headers);
EXPECT_CALL(session_, WriteHeadersMock(_, _, _, _, _));
EXPECT_CALL(session_, WritevData(_, _, _, _, _, _))
.Times(AnyNumber())
.WillRepeatedly(Invoke(MockQuicSession::ConsumeAllData));
stream_->OnStreamHeaders(headers_string_);
stream_->OnStreamHeadersComplete(true, headers_string_.size());
EXPECT_TRUE(ReliableQuicStreamPeer::read_side_closed(stream_));
EXPECT_TRUE(stream_->reading_stopped());
EXPECT_TRUE(stream_->write_side_closed());
}
TEST_P(QuicSimpleServerStreamTest, InvalidLeadingNullContentLength) {
EXPECT_CALL(session_, SendRstStream(_, QUIC_STREAM_NO_ERROR, _)).Times(0);
SpdyHeaderBlock request_headers;
// \000 is a way to write the null byte when followed by a literal digit.
request_headers["content-length"] = StringPiece("\00012", 3);
headers_string_ = SpdyUtils::SerializeUncompressedHeaders(request_headers);
EXPECT_CALL(session_, WriteHeadersMock(_, _, _, _, _));
EXPECT_CALL(session_, WritevData(_, _, _, _, _, _))
.Times(AnyNumber())
.WillRepeatedly(Invoke(MockQuicSession::ConsumeAllData));
stream_->OnStreamHeaders(headers_string_);
stream_->OnStreamHeadersComplete(true, headers_string_.size());
EXPECT_TRUE(ReliableQuicStreamPeer::read_side_closed(stream_));
EXPECT_TRUE(stream_->reading_stopped());
EXPECT_TRUE(stream_->write_side_closed());
}
TEST_P(QuicSimpleServerStreamTest, ValidMultipleContentLength) {
SpdyHeaderBlock request_headers;
// \000 is a way to write the null byte when followed by a literal digit.
request_headers["content-length"] = StringPiece("11\00011", 5);
headers_string_ = SpdyUtils::SerializeUncompressedHeaders(request_headers);
stream_->OnStreamHeaders(headers_string_);
stream_->OnStreamHeadersComplete(false, headers_string_.size());
EXPECT_EQ(11, QuicSimpleServerStreamPeer::content_length(stream_));
EXPECT_FALSE(ReliableQuicStreamPeer::read_side_closed(stream_));
EXPECT_FALSE(stream_->reading_stopped());
EXPECT_FALSE(stream_->write_side_closed());
}
TEST_P(QuicSimpleServerStreamTest, SendQuicRstStreamNoErrorWithEarlyResponse) {
InSequence s;
EXPECT_CALL(session_, WriteHeadersMock(stream_->id(), _, false, _, nullptr));
EXPECT_CALL(session_, WritevData(_, _, _, _, _, _))
.Times(1)
.WillOnce(Return(QuicConsumedData(3, true)));
if (GetParam() > QUIC_VERSION_28) {
EXPECT_CALL(session_, SendRstStream(_, QUIC_STREAM_NO_ERROR, _)).Times(1);
} else {
EXPECT_CALL(session_, SendRstStream(_, QUIC_STREAM_NO_ERROR, _)).Times(0);
}
EXPECT_FALSE(stream_->fin_received());
QuicSimpleServerStreamPeer::SendErrorResponse(stream_);
EXPECT_TRUE(stream_->reading_stopped());
EXPECT_TRUE(stream_->write_side_closed());
}
TEST_P(QuicSimpleServerStreamTest,
DoNotSendQuicRstStreamNoErrorWithRstReceived) {
InSequence s;
EXPECT_FALSE(stream_->reading_stopped());
EXPECT_CALL(session_, SendRstStream(_, QUIC_STREAM_NO_ERROR, _)).Times(0);
EXPECT_CALL(session_, SendRstStream(_, QUIC_RST_ACKNOWLEDGEMENT, _)).Times(1);
QuicRstStreamFrame rst_frame(stream_->id(), QUIC_STREAM_CANCELLED, 1234);
stream_->OnStreamReset(rst_frame);
EXPECT_TRUE(stream_->reading_stopped());
EXPECT_TRUE(stream_->write_side_closed());
}
TEST_P(QuicSimpleServerStreamTest, InvalidHeadersWithFin) {
char arr[] = {
0x3a, 0x68, 0x6f, 0x73, // :hos
0x74, 0x00, 0x00, 0x00, // t...
0x00, 0x00, 0x00, 0x00, // ....
0x07, 0x3a, 0x6d, 0x65, // .:me
0x74, 0x68, 0x6f, 0x64, // thod
0x00, 0x00, 0x00, 0x03, // ....
0x47, 0x45, 0x54, 0x00, // GET.
0x00, 0x00, 0x05, 0x3a, // ...:
0x70, 0x61, 0x74, 0x68, // path
0x00, 0x00, 0x00, 0x04, // ....
0x2f, 0x66, 0x6f, 0x6f, // /foo
0x00, 0x00, 0x00, 0x07, // ....
0x3a, 0x73, 0x63, 0x68, // :sch
0x65, 0x6d, 0x65, 0x00, // eme.
0x00, 0x00, 0x00, 0x00, // ....
0x00, 0x00, 0x08, 0x3a, // ...:
0x76, 0x65, 0x72, 0x73, // vers
'\x96', 0x6f, 0x6e, 0x00, // <i(69)>on.
0x00, 0x00, 0x08, 0x48, // ...H
0x54, 0x54, 0x50, 0x2f, // TTP/
0x31, 0x2e, 0x31, // 1.1
};
StringPiece data(arr, arraysize(arr));
QuicStreamFrame frame(stream_->id(), true, 0, data);
// Verify that we don't crash when we get a invalid headers in stream frame.
stream_->OnStreamFrame(frame);
}
} // namespace
} // namespace test
} // namespace net