| Index: net/quic/bidirectional_stream_quic_job_unittest.cc
|
| diff --git a/net/quic/bidirectional_stream_quic_job_unittest.cc b/net/quic/bidirectional_stream_quic_job_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..c55547aa0222f57689231dbc78fdf1228a4a075d
|
| --- /dev/null
|
| +++ b/net/quic/bidirectional_stream_quic_job_unittest.cc
|
| @@ -0,0 +1,1105 @@
|
| +// Copyright 2016 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/quic/bidirectional_stream_quic_job.h"
|
| +
|
| +#include <stdint.h>
|
| +#include <vector>
|
| +
|
| +#include "base/memory/scoped_ptr.h"
|
| +#include "base/message_loop/message_loop.h"
|
| +#include "base/run_loop.h"
|
| +#include "net/base/net_errors.h"
|
| +#include "net/http/transport_security_state.h"
|
| +#include "net/quic/crypto/crypto_protocol.h"
|
| +#include "net/quic/crypto/quic_decrypter.h"
|
| +#include "net/quic/crypto/quic_encrypter.h"
|
| +#include "net/quic/crypto/quic_server_info.h"
|
| +#include "net/quic/quic_chromium_client_session.h"
|
| +#include "net/quic/quic_chromium_client_stream.h"
|
| +#include "net/quic/quic_chromium_connection_helper.h"
|
| +#include "net/quic/quic_chromium_packet_reader.h"
|
| +#include "net/quic/quic_chromium_packet_writer.h"
|
| +#include "net/quic/quic_connection.h"
|
| +#include "net/quic/quic_http_utils.h"
|
| +#include "net/quic/spdy_utils.h"
|
| +#include "net/quic/test_tools/crypto_test_utils.h"
|
| +#include "net/quic/test_tools/mock_clock.h"
|
| +#include "net/quic/test_tools/mock_crypto_client_stream_factory.h"
|
| +#include "net/quic/test_tools/mock_random.h"
|
| +#include "net/quic/test_tools/quic_connection_peer.h"
|
| +#include "net/quic/test_tools/quic_test_packet_maker.h"
|
| +#include "net/quic/test_tools/quic_test_utils.h"
|
| +#include "net/quic/test_tools/test_task_runner.h"
|
| +#include "net/socket/socket_test_util.h"
|
| +#include "testing/gmock/include/gmock/gmock.h"
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +
|
| +namespace net {
|
| +
|
| +namespace test {
|
| +
|
| +namespace {
|
| +
|
| +const char kUploadData[] = "Really nifty data!";
|
| +const char kDefaultServerHostName[] = "www.google.com";
|
| +const uint16_t kDefaultServerPort = 80;
|
| +// Size of the buffer to be allocated for each read.
|
| +const size_t kReadBufferSize = 4096;
|
| +
|
| +class TestDelegateBase : public BidirectionalStreamJob::Delegate {
|
| + public:
|
| + TestDelegateBase(IOBuffer* read_buf, int read_buf_len)
|
| + : TestDelegateBase(read_buf,
|
| + read_buf_len,
|
| + make_scoped_ptr(new base::Timer(false, false))) {}
|
| +
|
| + TestDelegateBase(IOBuffer* read_buf,
|
| + int read_buf_len,
|
| + scoped_ptr<base::Timer> timer)
|
| + : read_buf_(read_buf),
|
| + read_buf_len_(read_buf_len),
|
| + timer_(std::move(timer)),
|
| + loop_(nullptr),
|
| + error_(OK),
|
| + on_data_read_count_(0),
|
| + on_data_sent_count_(0),
|
| + not_expect_callback_(false) {
|
| + loop_.reset(new base::RunLoop);
|
| + }
|
| +
|
| + ~TestDelegateBase() override {}
|
| +
|
| + void OnHeadersSent() override {
|
| + CHECK(!not_expect_callback_);
|
| + loop_->Quit();
|
| + }
|
| +
|
| + void OnHeadersReceived(const SpdyHeaderBlock& response_headers) override {
|
| + CHECK(!not_expect_callback_);
|
| +
|
| + response_headers_ = response_headers;
|
| + loop_->Quit();
|
| + }
|
| +
|
| + void OnDataRead(int bytes_read) override {
|
| + CHECK(!not_expect_callback_);
|
| +
|
| + ++on_data_read_count_;
|
| + CHECK_GE(bytes_read, OK);
|
| + data_received_.append(read_buf_->data(), bytes_read);
|
| + loop_->Quit();
|
| + }
|
| +
|
| + void OnDataSent() override {
|
| + CHECK(!not_expect_callback_);
|
| +
|
| + ++on_data_sent_count_;
|
| + loop_->Quit();
|
| + }
|
| +
|
| + void OnTrailersReceived(const SpdyHeaderBlock& trailers) override {
|
| + CHECK(!not_expect_callback_);
|
| +
|
| + trailers_ = trailers;
|
| + loop_->Quit();
|
| + }
|
| +
|
| + void OnFailed(int error) override {
|
| + CHECK(!not_expect_callback_);
|
| + CHECK_EQ(OK, error_);
|
| + CHECK_NE(OK, error);
|
| +
|
| + error_ = error;
|
| + loop_->Quit();
|
| + }
|
| +
|
| + void Start(const BidirectionalStreamRequestInfo* request_info,
|
| + const BoundNetLog& net_log,
|
| + const base::WeakPtr<QuicChromiumClientSession> session) {
|
| + stream_job_.reset(new BidirectionalStreamQuicJob(session));
|
| + stream_job_->Start(request_info, net_log, this, nullptr);
|
| + }
|
| +
|
| + void SendData(IOBuffer* data, int length, bool end_of_stream) {
|
| + not_expect_callback_ = true;
|
| + stream_job_->SendData(data, length, end_of_stream);
|
| + not_expect_callback_ = false;
|
| + }
|
| +
|
| + // Waits until next Delegate callback.
|
| + void WaitUntilNextCallback() {
|
| + loop_->Run();
|
| + loop_.reset(new base::RunLoop);
|
| + }
|
| +
|
| + // Calls ReadData on the |stream_| and updates |data_received_|.
|
| + int ReadData() {
|
| + not_expect_callback_ = true;
|
| + int rv = stream_job_->ReadData(read_buf_.get(), read_buf_len_);
|
| + not_expect_callback_ = false;
|
| + if (rv > 0)
|
| + data_received_.append(read_buf_->data(), rv);
|
| + return rv;
|
| + }
|
| +
|
| + // Cancels |stream_|.
|
| + void CancelStream() { stream_job_->Cancel(); }
|
| +
|
| + NextProto GetProtocol() const { return stream_job_->GetProtocol(); }
|
| +
|
| + int64_t GetTotalReceivedBytes() const {
|
| + return stream_job_->GetTotalReceivedBytes();
|
| + }
|
| +
|
| + int64_t GetTotalSentBytes() const { return stream_job_->GetTotalSentBytes(); }
|
| +
|
| + // Const getters for internal states.
|
| + const std::string& data_received() const { return data_received_; }
|
| + int error() const { return error_; }
|
| + const SpdyHeaderBlock& response_headers() const { return response_headers_; }
|
| + const SpdyHeaderBlock& trailers() const { return trailers_; }
|
| + int on_data_read_count() const { return on_data_read_count_; }
|
| + int on_data_sent_count() const { return on_data_sent_count_; }
|
| +
|
| + protected:
|
| + // Quits |loop_|.
|
| + void QuitLoop() { loop_->Quit(); }
|
| +
|
| + // Deletes |stream_|.
|
| + void DeleteStream() { stream_job_.reset(); }
|
| +
|
| + private:
|
| + scoped_ptr<BidirectionalStreamQuicJob> stream_job_;
|
| + scoped_refptr<IOBuffer> read_buf_;
|
| + int read_buf_len_;
|
| + scoped_ptr<base::Timer> timer_;
|
| + std::string data_received_;
|
| + scoped_ptr<base::RunLoop> loop_;
|
| + SpdyHeaderBlock response_headers_;
|
| + SpdyHeaderBlock trailers_;
|
| + int error_;
|
| + int on_data_read_count_;
|
| + int on_data_sent_count_;
|
| + // This is to ensure that delegate callback is not invoked synchronously when
|
| + // calling into |stream_|.
|
| + bool not_expect_callback_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(TestDelegateBase);
|
| +};
|
| +
|
| +// A delegate that deletes the stream in a particular callback.
|
| +class DeleteStreamDelegate : public TestDelegateBase {
|
| + public:
|
| + // Specifies in which callback the stream can be deleted.
|
| + enum Phase {
|
| + ON_HEADERS_RECEIVED,
|
| + ON_DATA_READ,
|
| + ON_TRAILERS_RECEIVED,
|
| + ON_FAILED,
|
| + };
|
| +
|
| + DeleteStreamDelegate(IOBuffer* buf, int buf_len, Phase phase, bool do_cancel)
|
| + : TestDelegateBase(buf, buf_len), phase_(phase), do_cancel_(do_cancel) {}
|
| + ~DeleteStreamDelegate() override {}
|
| +
|
| + void OnHeadersReceived(const SpdyHeaderBlock& response_headers) override {
|
| + if (phase_ == ON_HEADERS_RECEIVED) {
|
| + DeleteStream();
|
| + }
|
| + TestDelegateBase::OnHeadersReceived(response_headers);
|
| + }
|
| +
|
| + void OnDataSent() override { NOTREACHED(); }
|
| +
|
| + void OnDataRead(int bytes_read) override {
|
| + DCHECK_NE(ON_HEADERS_RECEIVED, phase_);
|
| + if (phase_ == ON_DATA_READ)
|
| + DeleteStream();
|
| + TestDelegateBase::OnDataRead(bytes_read);
|
| + }
|
| +
|
| + void OnTrailersReceived(const SpdyHeaderBlock& trailers) override {
|
| + DCHECK_NE(ON_HEADERS_RECEIVED, phase_);
|
| + DCHECK_NE(ON_DATA_READ, phase_);
|
| + if (phase_ == ON_TRAILERS_RECEIVED)
|
| + DeleteStream();
|
| + TestDelegateBase::OnTrailersReceived(trailers);
|
| + }
|
| +
|
| + void OnFailed(int error) override {
|
| + DCHECK_EQ(ON_FAILED, phase_);
|
| + DeleteStream();
|
| + TestDelegateBase::OnFailed(error);
|
| + }
|
| +
|
| + private:
|
| + // Indicates in which callback the delegate should cancel or delete the
|
| + // stream.
|
| + Phase phase_;
|
| + // Indicates whether to cancel or delete the stream.
|
| + bool do_cancel_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(DeleteStreamDelegate);
|
| +};
|
| +
|
| +} // namespace
|
| +
|
| +class BidirectionalStreamQuicJobTest
|
| + : public ::testing::TestWithParam<QuicVersion> {
|
| + protected:
|
| + static const bool kFin = true;
|
| + static const bool kIncludeVersion = true;
|
| + static const bool kIncludeCongestionFeedback = true;
|
| +
|
| + // Holds a packet to be written to the wire, and the IO mode that should
|
| + // be used by the mock socket when performing the write.
|
| + struct PacketToWrite {
|
| + PacketToWrite(IoMode mode, QuicEncryptedPacket* packet)
|
| + : mode(mode), packet(packet) {}
|
| + PacketToWrite(IoMode mode, int rv) : mode(mode), packet(nullptr), rv(rv) {}
|
| + IoMode mode;
|
| + QuicEncryptedPacket* packet;
|
| + int rv;
|
| + };
|
| +
|
| + BidirectionalStreamQuicJobTest()
|
| + : net_log_(BoundNetLog()),
|
| + crypto_config_(CryptoTestUtils::ProofVerifierForTesting()),
|
| + read_buffer_(new IOBufferWithSize(4096)),
|
| + connection_id_(2),
|
| + stream_id_(kClientDataStreamId1),
|
| + maker_(GetParam(), connection_id_, &clock_, kDefaultServerHostName),
|
| + random_generator_(0) {
|
| + IPAddressNumber ip;
|
| + CHECK(ParseIPLiteralToNumber("192.0.2.33", &ip));
|
| + peer_addr_ = IPEndPoint(ip, 443);
|
| + self_addr_ = IPEndPoint(ip, 8435);
|
| + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(20));
|
| + }
|
| +
|
| + ~BidirectionalStreamQuicJobTest() {
|
| + session_->CloseSessionOnError(ERR_ABORTED, QUIC_INTERNAL_ERROR);
|
| + for (size_t i = 0; i < writes_.size(); i++) {
|
| + delete writes_[i].packet;
|
| + }
|
| + }
|
| +
|
| + void TearDown() override {
|
| + EXPECT_TRUE(socket_data_->AllReadDataConsumed());
|
| + EXPECT_TRUE(socket_data_->AllWriteDataConsumed());
|
| + }
|
| +
|
| + // Adds a packet to the list of expected writes.
|
| + void AddWrite(scoped_ptr<QuicEncryptedPacket> packet) {
|
| + writes_.push_back(PacketToWrite(SYNCHRONOUS, packet.release()));
|
| + }
|
| +
|
| + void ProcessPacket(scoped_ptr<QuicEncryptedPacket> packet) {
|
| + connection_->ProcessUdpPacket(self_addr_, peer_addr_, *packet);
|
| + }
|
| +
|
| + // Configures the test fixture to use the list of expected writes.
|
| + void Initialize() {
|
| + mock_writes_.reset(new MockWrite[writes_.size()]);
|
| + for (size_t i = 0; i < writes_.size(); i++) {
|
| + if (writes_[i].packet == nullptr) {
|
| + mock_writes_[i] = MockWrite(writes_[i].mode, writes_[i].rv, i);
|
| + } else {
|
| + mock_writes_[i] = MockWrite(writes_[i].mode, writes_[i].packet->data(),
|
| + writes_[i].packet->length());
|
| + }
|
| + };
|
| +
|
| + socket_data_.reset(new StaticSocketDataProvider(
|
| + nullptr, 0, mock_writes_.get(), writes_.size()));
|
| +
|
| + MockUDPClientSocket* socket =
|
| + new MockUDPClientSocket(socket_data_.get(), net_log_.net_log());
|
| + socket->Connect(peer_addr_);
|
| + runner_ = new TestTaskRunner(&clock_);
|
| + helper_.reset(new QuicChromiumConnectionHelper(runner_.get(), &clock_,
|
| + &random_generator_));
|
| + connection_ = new QuicConnection(
|
| + connection_id_, peer_addr_, helper_.get(),
|
| + new QuicChromiumPacketWriter(socket), true /* owns_writer */,
|
| + Perspective::IS_CLIENT, SupportedVersions(GetParam()));
|
| +
|
| + session_.reset(new QuicChromiumClientSession(
|
| + connection_, scoped_ptr<DatagramClientSocket>(socket),
|
| + /*stream_factory=*/nullptr, &crypto_client_stream_factory_, &clock_,
|
| + &transport_security_state_, make_scoped_ptr((QuicServerInfo*)nullptr),
|
| + QuicServerId(kDefaultServerHostName, kDefaultServerPort,
|
| + PRIVACY_MODE_DISABLED),
|
| + kQuicYieldAfterPacketsRead,
|
| + QuicTime::Delta::FromMilliseconds(kQuicYieldAfterDurationMilliseconds),
|
| + /*cert_verify_flags=*/0, DefaultQuicConfig(), &crypto_config_,
|
| + "CONNECTION_UNKNOWN", base::TimeTicks::Now(), &push_promise_index_,
|
| + base::ThreadTaskRunnerHandle::Get().get(),
|
| + /*socket_performance_watcher=*/nullptr, nullptr));
|
| + session_->Initialize();
|
| + session_->GetCryptoStream()->CryptoConnect();
|
| + EXPECT_TRUE(session_->IsCryptoHandshakeConfirmed());
|
| + }
|
| +
|
| + void SetRequest(const std::string& method,
|
| + const std::string& path,
|
| + RequestPriority priority) {
|
| + request_headers_ = maker_.GetRequestHeaders(method, "http", path);
|
| + }
|
| +
|
| + SpdyHeaderBlock ConstructResponseHeaders(const std::string& response_code) {
|
| + return maker_.GetResponseHeaders(response_code);
|
| + }
|
| +
|
| + scoped_ptr<QuicEncryptedPacket> ConstructDataPacket(
|
| + QuicPacketNumber packet_number,
|
| + bool should_include_version,
|
| + bool fin,
|
| + QuicStreamOffset offset,
|
| + base::StringPiece data) {
|
| + scoped_ptr<QuicEncryptedPacket> packet(maker_.MakeDataPacket(
|
| + packet_number, stream_id_, should_include_version, fin, offset, data));
|
| + DVLOG(2) << "packet(" << packet_number << "): " << std::endl
|
| + << QuicUtils::StringToHexASCIIDump(packet->AsStringPiece());
|
| + return packet;
|
| + }
|
| +
|
| + scoped_ptr<QuicEncryptedPacket> ConstructRequestHeadersPacket(
|
| + QuicPacketNumber packet_number,
|
| + bool fin,
|
| + RequestPriority request_priority,
|
| + size_t* spdy_headers_frame_length) {
|
| + SpdyPriority priority =
|
| + ConvertRequestPriorityToQuicPriority(request_priority);
|
| + return maker_.MakeRequestHeadersPacket(
|
| + packet_number, stream_id_, kIncludeVersion, fin, priority,
|
| + request_headers_, spdy_headers_frame_length);
|
| + }
|
| +
|
| + scoped_ptr<QuicEncryptedPacket> ConstructResponseHeadersPacket(
|
| + QuicPacketNumber packet_number,
|
| + bool fin,
|
| + const SpdyHeaderBlock& response_headers,
|
| + size_t* spdy_headers_frame_length,
|
| + QuicStreamOffset* offset) {
|
| + return maker_.MakeResponseHeadersPacket(
|
| + packet_number, stream_id_, !kIncludeVersion, fin, response_headers,
|
| + spdy_headers_frame_length, offset);
|
| + }
|
| +
|
| + scoped_ptr<QuicEncryptedPacket> ConstructResponseTrailersPacket(
|
| + QuicPacketNumber packet_number,
|
| + bool fin,
|
| + const SpdyHeaderBlock& trailers,
|
| + size_t* spdy_headers_frame_length,
|
| + QuicStreamOffset* offset) {
|
| + return maker_.MakeResponseHeadersPacket(packet_number, stream_id_,
|
| + !kIncludeVersion, fin, trailers,
|
| + spdy_headers_frame_length, offset);
|
| + }
|
| +
|
| + scoped_ptr<QuicEncryptedPacket> ConstructRstStreamPacket(
|
| + QuicPacketNumber packet_number) {
|
| + return ConstructRstStreamCancelledPacket(packet_number, 0);
|
| + }
|
| +
|
| + scoped_ptr<QuicEncryptedPacket> ConstructRstStreamCancelledPacket(
|
| + QuicPacketNumber packet_number,
|
| + size_t bytes_written) {
|
| + scoped_ptr<QuicEncryptedPacket> packet(
|
| + maker_.MakeRstPacket(packet_number, !kIncludeVersion, stream_id_,
|
| + QUIC_STREAM_CANCELLED, bytes_written));
|
| + DVLOG(2) << "packet(" << packet_number << "): " << std::endl
|
| + << QuicUtils::StringToHexASCIIDump(packet->AsStringPiece());
|
| + return packet;
|
| + }
|
| +
|
| + scoped_ptr<QuicEncryptedPacket> ConstructAckAndRstStreamPacket(
|
| + QuicPacketNumber packet_number,
|
| + QuicPacketNumber largest_received,
|
| + QuicPacketNumber ack_least_unacked,
|
| + QuicPacketNumber stop_least_unacked) {
|
| + return maker_.MakeAckAndRstPacket(
|
| + packet_number, !kIncludeVersion, stream_id_, QUIC_STREAM_CANCELLED,
|
| + largest_received, ack_least_unacked, stop_least_unacked,
|
| + !kIncludeCongestionFeedback);
|
| + }
|
| +
|
| + scoped_ptr<QuicEncryptedPacket> ConstructAckAndDataPacket(
|
| + QuicPacketNumber packet_number,
|
| + bool should_include_version,
|
| + QuicPacketNumber largest_received,
|
| + QuicPacketNumber least_unacked,
|
| + bool fin,
|
| + QuicStreamOffset offset,
|
| + base::StringPiece data) {
|
| + scoped_ptr<QuicEncryptedPacket> packet(maker_.MakeAckAndDataPacket(
|
| + packet_number, should_include_version, stream_id_, largest_received,
|
| + least_unacked, fin, offset, data));
|
| + DVLOG(2) << "packet(" << packet_number << "): " << std::endl
|
| + << QuicUtils::StringToHexASCIIDump(packet->AsStringPiece());
|
| + return packet;
|
| + }
|
| +
|
| + scoped_ptr<QuicEncryptedPacket> ConstructAckPacket(
|
| + QuicPacketNumber packet_number,
|
| + QuicPacketNumber largest_received,
|
| + QuicPacketNumber least_unacked) {
|
| + return maker_.MakeAckPacket(packet_number, largest_received, least_unacked,
|
| + !kIncludeCongestionFeedback);
|
| + }
|
| +
|
| + const BoundNetLog& net_log() const { return net_log_; }
|
| +
|
| + QuicChromiumClientSession* session() const { return session_.get(); }
|
| +
|
| + private:
|
| + BoundNetLog net_log_;
|
| + scoped_refptr<TestTaskRunner> runner_;
|
| + scoped_ptr<MockWrite[]> mock_writes_;
|
| + MockClock clock_;
|
| + QuicConnection* connection_;
|
| + scoped_ptr<QuicChromiumConnectionHelper> helper_;
|
| + TransportSecurityState transport_security_state_;
|
| + scoped_ptr<QuicChromiumClientSession> session_;
|
| + QuicCryptoClientConfig crypto_config_;
|
| + HttpRequestHeaders headers_;
|
| + HttpResponseInfo response_;
|
| + scoped_refptr<IOBufferWithSize> read_buffer_;
|
| + SpdyHeaderBlock request_headers_;
|
| + const QuicConnectionId connection_id_;
|
| + const QuicStreamId stream_id_;
|
| + QuicTestPacketMaker maker_;
|
| + IPEndPoint self_addr_;
|
| + IPEndPoint peer_addr_;
|
| + MockRandom random_generator_;
|
| + MockCryptoClientStreamFactory crypto_client_stream_factory_;
|
| + scoped_ptr<StaticSocketDataProvider> socket_data_;
|
| + std::vector<PacketToWrite> writes_;
|
| + QuicClientPushPromiseIndex push_promise_index_;
|
| +};
|
| +
|
| +INSTANTIATE_TEST_CASE_P(Version,
|
| + BidirectionalStreamQuicJobTest,
|
| + ::testing::ValuesIn(QuicSupportedVersions()));
|
| +
|
| +TEST_P(BidirectionalStreamQuicJobTest, GetRequest) {
|
| + SetRequest("GET", "/", DEFAULT_PRIORITY);
|
| + size_t spdy_request_headers_frame_length;
|
| + AddWrite(ConstructRequestHeadersPacket(1, kFin, DEFAULT_PRIORITY,
|
| + &spdy_request_headers_frame_length));
|
| +
|
| + AddWrite(ConstructAckPacket(2, 3, 1));
|
| + Initialize();
|
| +
|
| + BidirectionalStreamRequestInfo request;
|
| + request.method = "GET";
|
| + request.url = GURL("http://www.google.com/");
|
| + request.end_stream_on_headers = true;
|
| + request.priority = DEFAULT_PRIORITY;
|
| +
|
| + scoped_refptr<IOBuffer> read_buffer(new IOBuffer(kReadBufferSize));
|
| + scoped_ptr<TestDelegateBase> delegate(
|
| + new TestDelegateBase(read_buffer.get(), kReadBufferSize));
|
| + delegate->Start(&request, net_log(), session()->GetWeakPtr());
|
| + delegate->WaitUntilNextCallback(); // OnHeadersSent
|
| +
|
| + // Server acks the request.
|
| + ProcessPacket(ConstructAckPacket(1, 0, 0));
|
| +
|
| + // Server sends the response headers.
|
| + SpdyHeaderBlock response_headers = ConstructResponseHeaders("200");
|
| +
|
| + size_t spdy_response_headers_frame_length;
|
| + QuicStreamOffset offset = 0;
|
| + ProcessPacket(ConstructResponseHeadersPacket(
|
| + 2, !kFin, response_headers, &spdy_response_headers_frame_length,
|
| + &offset));
|
| +
|
| + delegate->WaitUntilNextCallback(); // OnHeadersReceived
|
| + int rv = delegate->ReadData();
|
| + EXPECT_EQ(ERR_IO_PENDING, rv);
|
| + EXPECT_EQ("200", delegate->response_headers().find(":status")->second);
|
| + const char kResponseBody[] = "Hello world!";
|
| + // Server sends data.
|
| + ProcessPacket(
|
| + ConstructDataPacket(3, !kIncludeVersion, !kFin, 0, kResponseBody));
|
| + delegate->WaitUntilNextCallback(); // OnDataRead
|
| + EXPECT_EQ(std::string(kResponseBody), delegate->data_received());
|
| +
|
| + SpdyHeaderBlock trailers;
|
| + size_t spdy_trailers_frame_length;
|
| + trailers["foo"] = "bar";
|
| + // Server sends trailers.
|
| + ProcessPacket(ConstructResponseTrailersPacket(
|
| + 4, kFin, trailers, &spdy_trailers_frame_length, &offset));
|
| +
|
| + delegate->WaitUntilNextCallback(); // OnTrailersReceived
|
| + EXPECT_EQ(trailers, delegate->trailers());
|
| +
|
| + EXPECT_EQ(OK, delegate->ReadData());
|
| +
|
| + base::MessageLoop::current()->RunUntilIdle();
|
| +
|
| + EXPECT_EQ(1, delegate->on_data_read_count());
|
| + EXPECT_EQ(0, delegate->on_data_sent_count());
|
| + EXPECT_EQ(kProtoQUIC1SPDY3, delegate->GetProtocol());
|
| + EXPECT_EQ(static_cast<int64_t>(spdy_request_headers_frame_length),
|
| + delegate->GetTotalSentBytes());
|
| + EXPECT_EQ(
|
| + static_cast<int64_t>(spdy_response_headers_frame_length +
|
| + strlen(kResponseBody) + spdy_trailers_frame_length),
|
| + delegate->GetTotalReceivedBytes());
|
| +}
|
| +
|
| +TEST_P(BidirectionalStreamQuicJobTest, PostRequest) {
|
| + SetRequest("POST", "/", DEFAULT_PRIORITY);
|
| + size_t spdy_request_headers_frame_length;
|
| + AddWrite(ConstructRequestHeadersPacket(1, !kFin, DEFAULT_PRIORITY,
|
| + &spdy_request_headers_frame_length));
|
| + AddWrite(ConstructDataPacket(2, kIncludeVersion, kFin, 0, kUploadData));
|
| + AddWrite(ConstructAckPacket(3, 3, 1));
|
| +
|
| + Initialize();
|
| +
|
| + BidirectionalStreamRequestInfo request;
|
| + request.method = "POST";
|
| + request.url = GURL("http://www.google.com/");
|
| + request.end_stream_on_headers = false;
|
| + request.priority = DEFAULT_PRIORITY;
|
| +
|
| + scoped_refptr<IOBuffer> read_buffer(new IOBuffer(kReadBufferSize));
|
| + scoped_ptr<TestDelegateBase> delegate(
|
| + new TestDelegateBase(read_buffer.get(), kReadBufferSize));
|
| + delegate->Start(&request, net_log(), session()->GetWeakPtr());
|
| + delegate->WaitUntilNextCallback(); // OnHeadersSent
|
| +
|
| + // Send a DATA frame.
|
| + scoped_refptr<StringIOBuffer> buf(new StringIOBuffer(kUploadData));
|
| +
|
| + delegate->SendData(buf.get(), buf->size(), true);
|
| + delegate->WaitUntilNextCallback(); // OnDataSent
|
| +
|
| + // Server acks the request.
|
| + ProcessPacket(ConstructAckPacket(1, 0, 0));
|
| +
|
| + // Server sends the response headers.
|
| + SpdyHeaderBlock response_headers = ConstructResponseHeaders("200");
|
| + size_t spdy_response_headers_frame_length;
|
| + QuicStreamOffset offset = 0;
|
| + ProcessPacket(ConstructResponseHeadersPacket(
|
| + 2, !kFin, response_headers, &spdy_response_headers_frame_length,
|
| + &offset));
|
| +
|
| + delegate->WaitUntilNextCallback(); // OnHeadersReceived
|
| + int rv = delegate->ReadData();
|
| + EXPECT_EQ(ERR_IO_PENDING, rv);
|
| + EXPECT_EQ("200", delegate->response_headers().find(":status")->second);
|
| + const char kResponseBody[] = "Hello world!";
|
| + // Server sends data.
|
| + ProcessPacket(
|
| + ConstructDataPacket(3, !kIncludeVersion, !kFin, 0, kResponseBody));
|
| +
|
| + delegate->WaitUntilNextCallback(); // OnDataReceived
|
| +
|
| + size_t spdy_trailers_frame_length;
|
| + SpdyHeaderBlock trailers;
|
| + trailers["foo"] = "bar";
|
| + // Server sends trailers.
|
| + ProcessPacket(ConstructResponseTrailersPacket(
|
| + 4, kFin, trailers, &spdy_trailers_frame_length, &offset));
|
| +
|
| + delegate->WaitUntilNextCallback(); // OnTrailersReceived
|
| + EXPECT_EQ(trailers, delegate->trailers());
|
| +
|
| + EXPECT_EQ(1, delegate->on_data_read_count());
|
| + EXPECT_EQ(1, delegate->on_data_sent_count());
|
| + EXPECT_EQ(kProtoQUIC1SPDY3, delegate->GetProtocol());
|
| + EXPECT_EQ(static_cast<int64_t>(spdy_request_headers_frame_length +
|
| + strlen(kUploadData)),
|
| + delegate->GetTotalSentBytes());
|
| + EXPECT_EQ(
|
| + static_cast<int64_t>(spdy_response_headers_frame_length +
|
| + strlen(kResponseBody) + spdy_trailers_frame_length),
|
| + delegate->GetTotalReceivedBytes());
|
| +}
|
| +
|
| +TEST_P(BidirectionalStreamQuicJobTest, InterleaveReadDataAndSendData) {
|
| + SetRequest("POST", "/", DEFAULT_PRIORITY);
|
| + size_t spdy_request_headers_frame_length;
|
| + AddWrite(ConstructRequestHeadersPacket(1, !kFin, DEFAULT_PRIORITY,
|
| + &spdy_request_headers_frame_length));
|
| + AddWrite(ConstructAckAndDataPacket(2, !kIncludeVersion, 2, 1, !kFin, 0,
|
| + kUploadData));
|
| + AddWrite(ConstructAckAndDataPacket(3, !kIncludeVersion, 3, 3, kFin,
|
| + strlen(kUploadData), kUploadData));
|
| + Initialize();
|
| +
|
| + BidirectionalStreamRequestInfo request;
|
| + request.method = "POST";
|
| + request.url = GURL("http://www.google.com/");
|
| + request.end_stream_on_headers = false;
|
| + request.priority = DEFAULT_PRIORITY;
|
| +
|
| + scoped_refptr<IOBuffer> read_buffer(new IOBuffer(kReadBufferSize));
|
| + scoped_ptr<TestDelegateBase> delegate(
|
| + new TestDelegateBase(read_buffer.get(), kReadBufferSize));
|
| + delegate->Start(&request, net_log(), session()->GetWeakPtr());
|
| + delegate->WaitUntilNextCallback(); // OnHeadersSent
|
| +
|
| + // Server acks the request.
|
| + ProcessPacket(ConstructAckPacket(1, 0, 0));
|
| +
|
| + // Server sends the response headers.
|
| + SpdyHeaderBlock response_headers = ConstructResponseHeaders("200");
|
| + size_t spdy_response_headers_frame_length;
|
| + ProcessPacket(ConstructResponseHeadersPacket(
|
| + 2, !kFin, response_headers, &spdy_response_headers_frame_length, 0));
|
| +
|
| + delegate->WaitUntilNextCallback(); // OnHeadersReceived
|
| + EXPECT_EQ("200", delegate->response_headers().find(":status")->second);
|
| +
|
| + // Client sends a data packet.
|
| + scoped_refptr<StringIOBuffer> buf(new StringIOBuffer(kUploadData));
|
| +
|
| + delegate->SendData(buf.get(), buf->size(), false);
|
| + delegate->WaitUntilNextCallback(); // OnDataSent
|
| +
|
| + int rv = delegate->ReadData();
|
| + EXPECT_EQ(ERR_IO_PENDING, rv);
|
| + const char kResponseBody[] = "Hello world!";
|
| +
|
| + // Server sends a data packet.
|
| + ProcessPacket(ConstructAckAndDataPacket(3, !kIncludeVersion, 2, 1, !kFin, 0,
|
| + kResponseBody));
|
| +
|
| + delegate->WaitUntilNextCallback(); // OnDataRead
|
| + EXPECT_EQ(std::string(kResponseBody), delegate->data_received());
|
| +
|
| + // Client sends a data packet.
|
| + delegate->SendData(buf.get(), buf->size(), true);
|
| + delegate->WaitUntilNextCallback(); // OnDataSent
|
| +
|
| + rv = delegate->ReadData();
|
| + EXPECT_EQ(ERR_IO_PENDING, rv);
|
| + ProcessPacket(ConstructAckAndDataPacket(
|
| + 4, !kIncludeVersion, 3, 1, kFin, strlen(kResponseBody), kResponseBody));
|
| +
|
| + delegate->WaitUntilNextCallback(); // OnDataRead
|
| + std::string expected_body(kResponseBody);
|
| + expected_body.append(kResponseBody);
|
| + EXPECT_EQ(expected_body, delegate->data_received());
|
| +
|
| + EXPECT_EQ(2, delegate->on_data_read_count());
|
| + EXPECT_EQ(2, delegate->on_data_sent_count());
|
| + EXPECT_EQ(kProtoQUIC1SPDY3, delegate->GetProtocol());
|
| + EXPECT_EQ(static_cast<int64_t>(spdy_request_headers_frame_length +
|
| + 2 * strlen(kUploadData)),
|
| + delegate->GetTotalSentBytes());
|
| + EXPECT_EQ(static_cast<int64_t>(spdy_response_headers_frame_length +
|
| + 2 * strlen(kResponseBody)),
|
| + delegate->GetTotalReceivedBytes());
|
| +}
|
| +
|
| +TEST_P(BidirectionalStreamQuicJobTest, ServerSendsRstAfterHeaders) {
|
| + SetRequest("GET", "/", DEFAULT_PRIORITY);
|
| + size_t spdy_request_headers_frame_length;
|
| + AddWrite(ConstructRequestHeadersPacket(1, kFin, DEFAULT_PRIORITY,
|
| + &spdy_request_headers_frame_length));
|
| + Initialize();
|
| +
|
| + BidirectionalStreamRequestInfo request;
|
| + request.method = "GET";
|
| + request.url = GURL("http://www.google.com/");
|
| + request.end_stream_on_headers = true;
|
| + request.priority = DEFAULT_PRIORITY;
|
| +
|
| + scoped_refptr<IOBuffer> read_buffer(new IOBuffer(kReadBufferSize));
|
| + scoped_ptr<TestDelegateBase> delegate(
|
| + new TestDelegateBase(read_buffer.get(), kReadBufferSize));
|
| + delegate->Start(&request, net_log(), session()->GetWeakPtr());
|
| + delegate->WaitUntilNextCallback(); // OnHeadersSent
|
| +
|
| + // Server sends a Rst.
|
| + ProcessPacket(ConstructRstStreamPacket(1));
|
| +
|
| + delegate->WaitUntilNextCallback(); // OnFailed
|
| +
|
| + base::MessageLoop::current()->RunUntilIdle();
|
| +
|
| + EXPECT_EQ(ERR_QUIC_PROTOCOL_ERROR, delegate->error());
|
| + EXPECT_EQ(0, delegate->on_data_read_count());
|
| + EXPECT_EQ(0, delegate->on_data_sent_count());
|
| + EXPECT_EQ(static_cast<int64_t>(spdy_request_headers_frame_length),
|
| + delegate->GetTotalSentBytes());
|
| + EXPECT_EQ(0, delegate->GetTotalReceivedBytes());
|
| +}
|
| +
|
| +TEST_P(BidirectionalStreamQuicJobTest, ServerSendsRstAfterReadData) {
|
| + SetRequest("GET", "/", DEFAULT_PRIORITY);
|
| + size_t spdy_request_headers_frame_length;
|
| + AddWrite(ConstructRequestHeadersPacket(1, kFin, DEFAULT_PRIORITY,
|
| + &spdy_request_headers_frame_length));
|
| + // Why does QUIC ack Rst? Is this expected?
|
| + AddWrite(ConstructAckPacket(2, 3, 1));
|
| +
|
| + Initialize();
|
| +
|
| + BidirectionalStreamRequestInfo request;
|
| + request.method = "GET";
|
| + request.url = GURL("http://www.google.com/");
|
| + request.end_stream_on_headers = true;
|
| + request.priority = DEFAULT_PRIORITY;
|
| +
|
| + scoped_refptr<IOBuffer> read_buffer(new IOBuffer(kReadBufferSize));
|
| + scoped_ptr<TestDelegateBase> delegate(
|
| + new TestDelegateBase(read_buffer.get(), kReadBufferSize));
|
| + delegate->Start(&request, net_log(), session()->GetWeakPtr());
|
| + delegate->WaitUntilNextCallback(); // OnHeadersSent
|
| +
|
| + // Server acks the request.
|
| + ProcessPacket(ConstructAckPacket(1, 0, 0));
|
| +
|
| + // Server sends the response headers.
|
| + SpdyHeaderBlock response_headers = ConstructResponseHeaders("200");
|
| +
|
| + size_t spdy_response_headers_frame_length;
|
| + QuicStreamOffset offset = 0;
|
| + ProcessPacket(ConstructResponseHeadersPacket(
|
| + 2, !kFin, response_headers, &spdy_response_headers_frame_length,
|
| + &offset));
|
| +
|
| + delegate->WaitUntilNextCallback(); // OnHeadersReceived
|
| + EXPECT_EQ("200", delegate->response_headers().find(":status")->second);
|
| +
|
| + int rv = delegate->ReadData();
|
| + EXPECT_EQ(ERR_IO_PENDING, rv);
|
| +
|
| + // Server sends a Rst.
|
| + ProcessPacket(ConstructRstStreamPacket(3));
|
| +
|
| + delegate->WaitUntilNextCallback(); // OnFailed
|
| +
|
| + EXPECT_EQ(ERR_QUIC_PROTOCOL_ERROR, delegate->error());
|
| + EXPECT_EQ(0, delegate->on_data_read_count());
|
| + EXPECT_EQ(0, delegate->on_data_sent_count());
|
| + EXPECT_EQ(static_cast<int64_t>(spdy_request_headers_frame_length),
|
| + delegate->GetTotalSentBytes());
|
| + EXPECT_EQ(static_cast<int64_t>(spdy_response_headers_frame_length),
|
| + delegate->GetTotalReceivedBytes());
|
| +}
|
| +
|
| +TEST_P(BidirectionalStreamQuicJobTest, CancelStreamAfterSendData) {
|
| + SetRequest("POST", "/", DEFAULT_PRIORITY);
|
| + size_t spdy_request_headers_frame_length;
|
| + AddWrite(ConstructRequestHeadersPacket(1, !kFin, DEFAULT_PRIORITY,
|
| + &spdy_request_headers_frame_length));
|
| + AddWrite(ConstructAckAndDataPacket(2, !kIncludeVersion, 2, 1, !kFin, 0,
|
| + kUploadData));
|
| + AddWrite(ConstructRstStreamCancelledPacket(3, strlen(kUploadData)));
|
| +
|
| + Initialize();
|
| +
|
| + BidirectionalStreamRequestInfo request;
|
| + request.method = "POST";
|
| + request.url = GURL("http://www.google.com/");
|
| + request.end_stream_on_headers = false;
|
| + request.priority = DEFAULT_PRIORITY;
|
| +
|
| + scoped_refptr<IOBuffer> read_buffer(new IOBuffer(kReadBufferSize));
|
| + scoped_ptr<TestDelegateBase> delegate(
|
| + new TestDelegateBase(read_buffer.get(), kReadBufferSize));
|
| + delegate->Start(&request, net_log(), session()->GetWeakPtr());
|
| + delegate->WaitUntilNextCallback(); // OnHeadersSent
|
| +
|
| + // Server acks the request.
|
| + ProcessPacket(ConstructAckPacket(1, 0, 0));
|
| +
|
| + // Server sends the response headers.
|
| + SpdyHeaderBlock response_headers = ConstructResponseHeaders("200");
|
| + size_t spdy_response_headers_frame_length;
|
| + ProcessPacket(ConstructResponseHeadersPacket(
|
| + 2, !kFin, response_headers, &spdy_response_headers_frame_length, 0));
|
| +
|
| + delegate->WaitUntilNextCallback(); // OnHeadersReceived
|
| + EXPECT_EQ("200", delegate->response_headers().find(":status")->second);
|
| +
|
| + // Send a DATA frame.
|
| + scoped_refptr<StringIOBuffer> buf(new StringIOBuffer(kUploadData));
|
| +
|
| + delegate->SendData(buf.get(), buf->size(), false);
|
| + delegate->WaitUntilNextCallback(); // OnDataSent
|
| +
|
| + delegate->CancelStream();
|
| + base::MessageLoop::current()->RunUntilIdle();
|
| +
|
| + EXPECT_EQ(0, delegate->on_data_read_count());
|
| + EXPECT_EQ(1, delegate->on_data_sent_count());
|
| + EXPECT_EQ(kProtoQUIC1SPDY3, delegate->GetProtocol());
|
| + EXPECT_EQ(static_cast<int64_t>(spdy_request_headers_frame_length +
|
| + strlen(kUploadData)),
|
| + delegate->GetTotalSentBytes());
|
| + EXPECT_EQ(static_cast<int64_t>(spdy_response_headers_frame_length),
|
| + delegate->GetTotalReceivedBytes());
|
| +}
|
| +TEST_P(BidirectionalStreamQuicJobTest, SessionClosedBeforeReadData) {
|
| + SetRequest("GET", "/", DEFAULT_PRIORITY);
|
| + size_t spdy_request_headers_frame_length;
|
| + AddWrite(ConstructRequestHeadersPacket(1, kFin, DEFAULT_PRIORITY,
|
| + &spdy_request_headers_frame_length));
|
| + Initialize();
|
| +
|
| + BidirectionalStreamRequestInfo request;
|
| + request.method = "GET";
|
| + request.url = GURL("http://www.google.com/");
|
| + request.end_stream_on_headers = true;
|
| + request.priority = DEFAULT_PRIORITY;
|
| +
|
| + scoped_refptr<IOBuffer> read_buffer(new IOBuffer(kReadBufferSize));
|
| + scoped_ptr<TestDelegateBase> delegate(
|
| + new TestDelegateBase(read_buffer.get(), kReadBufferSize));
|
| + delegate->Start(&request, net_log(), session()->GetWeakPtr());
|
| + delegate->WaitUntilNextCallback(); // OnHeadersSent
|
| +
|
| + // Server acks the request.
|
| + ProcessPacket(ConstructAckPacket(1, 0, 0));
|
| +
|
| + // Server sends the response headers.
|
| + SpdyHeaderBlock response_headers = ConstructResponseHeaders("200");
|
| +
|
| + size_t spdy_response_headers_frame_length;
|
| + QuicStreamOffset offset = 0;
|
| + ProcessPacket(ConstructResponseHeadersPacket(
|
| + 2, !kFin, response_headers, &spdy_response_headers_frame_length,
|
| + &offset));
|
| +
|
| + delegate->WaitUntilNextCallback(); // OnHeadersReceived
|
| + int rv = delegate->ReadData();
|
| + EXPECT_EQ(ERR_IO_PENDING, rv);
|
| + session()->connection()->CloseConnection(QUIC_NO_ERROR,
|
| + ConnectionCloseSource::FROM_PEER);
|
| + delegate->WaitUntilNextCallback(); // OnFailed
|
| +
|
| + base::MessageLoop::current()->RunUntilIdle();
|
| +
|
| + EXPECT_EQ(ERR_UNEXPECTED, delegate->error());
|
| + EXPECT_EQ(0, delegate->on_data_read_count());
|
| + EXPECT_EQ(0, delegate->on_data_sent_count());
|
| + EXPECT_EQ(kProtoQUIC1SPDY3, delegate->GetProtocol());
|
| + EXPECT_EQ(static_cast<int64_t>(spdy_request_headers_frame_length),
|
| + delegate->GetTotalSentBytes());
|
| + EXPECT_EQ(static_cast<int64_t>(spdy_response_headers_frame_length),
|
| + delegate->GetTotalReceivedBytes());
|
| +}
|
| +
|
| +TEST_P(BidirectionalStreamQuicJobTest, CancelStreamAfterReadData) {
|
| + SetRequest("POST", "/", DEFAULT_PRIORITY);
|
| + size_t spdy_request_headers_frame_length;
|
| + AddWrite(ConstructRequestHeadersPacket(1, !kFin, DEFAULT_PRIORITY,
|
| + &spdy_request_headers_frame_length));
|
| + AddWrite(ConstructAckAndRstStreamPacket(2, 2, 1, 1));
|
| +
|
| + Initialize();
|
| +
|
| + BidirectionalStreamRequestInfo request;
|
| + request.method = "POST";
|
| + request.url = GURL("http://www.google.com/");
|
| + request.end_stream_on_headers = false;
|
| + request.priority = DEFAULT_PRIORITY;
|
| +
|
| + scoped_refptr<IOBuffer> read_buffer(new IOBuffer(kReadBufferSize));
|
| + scoped_ptr<TestDelegateBase> delegate(
|
| + new TestDelegateBase(read_buffer.get(), kReadBufferSize));
|
| + delegate->Start(&request, net_log(), session()->GetWeakPtr());
|
| + delegate->WaitUntilNextCallback(); // OnHeadersSent
|
| +
|
| + // Server acks the request.
|
| + ProcessPacket(ConstructAckPacket(1, 0, 0));
|
| +
|
| + // Server sends the response headers.
|
| + SpdyHeaderBlock response_headers = ConstructResponseHeaders("200");
|
| + size_t spdy_response_headers_frame_length;
|
| + ProcessPacket(ConstructResponseHeadersPacket(
|
| + 2, !kFin, response_headers, &spdy_response_headers_frame_length, 0));
|
| +
|
| + delegate->WaitUntilNextCallback(); // OnHeadersReceived
|
| + EXPECT_EQ("200", delegate->response_headers().find(":status")->second);
|
| +
|
| + // Cancel the stream after ReadData returns ERR_IO_PENDING.
|
| + EXPECT_EQ(ERR_IO_PENDING, delegate->ReadData());
|
| + delegate->CancelStream();
|
| +
|
| + base::MessageLoop::current()->RunUntilIdle();
|
| +
|
| + EXPECT_EQ(0, delegate->on_data_read_count());
|
| + EXPECT_EQ(0, delegate->on_data_sent_count());
|
| + EXPECT_EQ(kProtoQUIC1SPDY3, delegate->GetProtocol());
|
| + EXPECT_EQ(static_cast<int64_t>(spdy_request_headers_frame_length),
|
| + delegate->GetTotalSentBytes());
|
| + EXPECT_EQ(static_cast<int64_t>(spdy_response_headers_frame_length),
|
| + delegate->GetTotalReceivedBytes());
|
| +}
|
| +
|
| +TEST_P(BidirectionalStreamQuicJobTest, DeleteStreamDuringOnHeadersReceived) {
|
| + SetRequest("POST", "/", DEFAULT_PRIORITY);
|
| + size_t spdy_request_headers_frame_length;
|
| + AddWrite(ConstructRequestHeadersPacket(1, !kFin, DEFAULT_PRIORITY,
|
| + &spdy_request_headers_frame_length));
|
| + AddWrite(ConstructAckAndRstStreamPacket(2, 2, 1, 1));
|
| +
|
| + Initialize();
|
| +
|
| + BidirectionalStreamRequestInfo request;
|
| + request.method = "POST";
|
| + request.url = GURL("http://www.google.com/");
|
| + request.end_stream_on_headers = false;
|
| + request.priority = DEFAULT_PRIORITY;
|
| +
|
| + scoped_refptr<IOBuffer> read_buffer(new IOBuffer(kReadBufferSize));
|
| + scoped_ptr<DeleteStreamDelegate> delegate(new DeleteStreamDelegate(
|
| + read_buffer.get(), kReadBufferSize,
|
| + DeleteStreamDelegate::ON_HEADERS_RECEIVED, true));
|
| + delegate->Start(&request, net_log(), session()->GetWeakPtr());
|
| + delegate->WaitUntilNextCallback(); // OnHeadersSent
|
| +
|
| + // Server acks the request.
|
| + ProcessPacket(ConstructAckPacket(1, 0, 0));
|
| +
|
| + // Server sends the response headers.
|
| + SpdyHeaderBlock response_headers = ConstructResponseHeaders("200");
|
| +
|
| + size_t spdy_response_headers_frame_length;
|
| + ProcessPacket(ConstructResponseHeadersPacket(
|
| + 2, !kFin, response_headers, &spdy_response_headers_frame_length,
|
| + nullptr));
|
| +
|
| + delegate->WaitUntilNextCallback(); // OnHeadersReceived
|
| + EXPECT_EQ("200", delegate->response_headers().find(":status")->second);
|
| +
|
| + base::MessageLoop::current()->RunUntilIdle();
|
| +
|
| + EXPECT_EQ(0, delegate->on_data_read_count());
|
| + EXPECT_EQ(0, delegate->on_data_sent_count());
|
| +}
|
| +
|
| +TEST_P(BidirectionalStreamQuicJobTest, DeleteStreamDuringOnDataRead) {
|
| + SetRequest("POST", "/", DEFAULT_PRIORITY);
|
| + size_t spdy_request_headers_frame_length;
|
| + AddWrite(ConstructRequestHeadersPacket(1, !kFin, DEFAULT_PRIORITY,
|
| + &spdy_request_headers_frame_length));
|
| + AddWrite(ConstructAckPacket(2, 3, 1));
|
| + AddWrite(ConstructRstStreamPacket(3));
|
| +
|
| + Initialize();
|
| +
|
| + BidirectionalStreamRequestInfo request;
|
| + request.method = "POST";
|
| + request.url = GURL("http://www.google.com/");
|
| + request.end_stream_on_headers = false;
|
| + request.priority = DEFAULT_PRIORITY;
|
| +
|
| + scoped_refptr<IOBuffer> read_buffer(new IOBuffer(kReadBufferSize));
|
| + scoped_ptr<DeleteStreamDelegate> delegate(
|
| + new DeleteStreamDelegate(read_buffer.get(), kReadBufferSize,
|
| + DeleteStreamDelegate::ON_DATA_READ, true));
|
| + delegate->Start(&request, net_log(), session()->GetWeakPtr());
|
| + delegate->WaitUntilNextCallback(); // OnHeadersSent
|
| +
|
| + // Server acks the request.
|
| + ProcessPacket(ConstructAckPacket(1, 0, 0));
|
| +
|
| + // Server sends the response headers.
|
| + SpdyHeaderBlock response_headers = ConstructResponseHeaders("200");
|
| +
|
| + size_t spdy_response_headers_frame_length;
|
| + ProcessPacket(ConstructResponseHeadersPacket(
|
| + 2, !kFin, response_headers, &spdy_response_headers_frame_length,
|
| + nullptr));
|
| +
|
| + delegate->WaitUntilNextCallback(); // OnHeadersReceived
|
| +
|
| + EXPECT_EQ("200", delegate->response_headers().find(":status")->second);
|
| +
|
| + int rv = delegate->ReadData();
|
| + EXPECT_EQ(ERR_IO_PENDING, rv);
|
| + const char kResponseBody[] = "Hello world!";
|
| + // Server sends data.
|
| + ProcessPacket(
|
| + ConstructDataPacket(3, !kIncludeVersion, !kFin, 0, kResponseBody));
|
| + delegate->WaitUntilNextCallback(); // OnDataRead
|
| +
|
| + base::MessageLoop::current()->RunUntilIdle();
|
| +
|
| + EXPECT_EQ(1, delegate->on_data_read_count());
|
| + EXPECT_EQ(0, delegate->on_data_sent_count());
|
| +}
|
| +
|
| +TEST_P(BidirectionalStreamQuicJobTest, DeleteStreamDuringOnTrailersReceived) {
|
| + SetRequest("GET", "/", DEFAULT_PRIORITY);
|
| + size_t spdy_request_headers_frame_length;
|
| + AddWrite(ConstructRequestHeadersPacket(1, kFin, DEFAULT_PRIORITY,
|
| + &spdy_request_headers_frame_length));
|
| + AddWrite(ConstructAckPacket(2, 3, 1)); // Ack the data packet
|
| +
|
| + Initialize();
|
| +
|
| + BidirectionalStreamRequestInfo request;
|
| + request.method = "GET";
|
| + request.url = GURL("http://www.google.com/");
|
| + request.end_stream_on_headers = true;
|
| + request.priority = DEFAULT_PRIORITY;
|
| +
|
| + scoped_refptr<IOBuffer> read_buffer(new IOBuffer(kReadBufferSize));
|
| + scoped_ptr<DeleteStreamDelegate> delegate(new DeleteStreamDelegate(
|
| + read_buffer.get(), kReadBufferSize,
|
| + DeleteStreamDelegate::ON_TRAILERS_RECEIVED, true));
|
| + delegate->Start(&request, net_log(), session()->GetWeakPtr());
|
| + delegate->WaitUntilNextCallback(); // OnHeadersSent
|
| +
|
| + // Server acks the request.
|
| + ProcessPacket(ConstructAckPacket(1, 0, 0));
|
| +
|
| + // Server sends the response headers.
|
| + SpdyHeaderBlock response_headers = ConstructResponseHeaders("200");
|
| +
|
| + QuicStreamOffset offset = 0;
|
| + size_t spdy_response_headers_frame_length;
|
| + ProcessPacket(ConstructResponseHeadersPacket(
|
| + 2, !kFin, response_headers, &spdy_response_headers_frame_length,
|
| + &offset));
|
| +
|
| + delegate->WaitUntilNextCallback(); // OnHeadersReceived
|
| +
|
| + EXPECT_EQ("200", delegate->response_headers().find(":status")->second);
|
| +
|
| + int rv = delegate->ReadData();
|
| + EXPECT_EQ(ERR_IO_PENDING, rv);
|
| + const char kResponseBody[] = "Hello world!";
|
| + // Server sends data.
|
| + ProcessPacket(
|
| + ConstructDataPacket(3, !kIncludeVersion, !kFin, 0, kResponseBody));
|
| +
|
| + delegate->WaitUntilNextCallback(); // OnDataRead
|
| + EXPECT_EQ(std::string(kResponseBody), delegate->data_received());
|
| +
|
| + size_t spdy_trailers_frame_length;
|
| + SpdyHeaderBlock trailers;
|
| + trailers["foo"] = "bar";
|
| + // Server sends trailers.
|
| + ProcessPacket(ConstructResponseTrailersPacket(
|
| + 4, kFin, trailers, &spdy_trailers_frame_length, &offset));
|
| +
|
| + delegate->WaitUntilNextCallback(); // OnTrailersReceived
|
| + EXPECT_EQ(trailers, delegate->trailers());
|
| +
|
| + base::MessageLoop::current()->RunUntilIdle();
|
| +
|
| + EXPECT_EQ(1, delegate->on_data_read_count());
|
| + EXPECT_EQ(0, delegate->on_data_sent_count());
|
| +}
|
| +
|
| +} // namespace test
|
| +
|
| +} // namespace net
|
|
|