blob: c8d035eb1ad3ac25adb9184014c774efda2805c1 [file] [log] [blame]
// Copyright 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_spdy_client_stream.h"
#include <memory>
#include "base/macros.h"
#include "net/quic/core/quic_utils.h"
#include "net/quic/core/spdy_utils.h"
#include "net/quic/core/tls_client_handshaker.h"
#include "net/quic/platform/api/quic_logging.h"
#include "net/quic/platform/api/quic_socket_address.h"
#include "net/quic/platform/api/quic_test.h"
#include "net/quic/platform/api/quic_text_utils.h"
#include "net/quic/test_tools/crypto_test_utils.h"
#include "net/quic/test_tools/quic_spdy_session_peer.h"
#include "net/quic/test_tools/quic_test_utils.h"
#include "net/tools/quic/quic_spdy_client_session.h"
using base::IntToString;
using std::string;
using testing::StrictMock;
namespace net {
namespace test {
namespace {
class MockQuicSpdyClientSession : public QuicSpdyClientSession {
public:
explicit MockQuicSpdyClientSession(
QuicConnection* connection,
QuicClientPushPromiseIndex* push_promise_index)
: QuicSpdyClientSession(
DefaultQuicConfig(),
connection,
QuicServerId("example.com", 443, PRIVACY_MODE_DISABLED),
&crypto_config_,
push_promise_index),
crypto_config_(crypto_test_utils::ProofVerifierForTesting(),
TlsClientHandshaker::CreateSslCtx()) {}
~MockQuicSpdyClientSession() override = default;
MOCK_METHOD1(CloseStream, void(QuicStreamId stream_id));
private:
QuicCryptoClientConfig crypto_config_;
DISALLOW_COPY_AND_ASSIGN(MockQuicSpdyClientSession);
};
class QuicSpdyClientStreamTest : public QuicTest {
public:
class StreamVisitor;
QuicSpdyClientStreamTest()
: connection_(new StrictMock<MockQuicConnection>(&helper_,
&alarm_factory_,
Perspective::IS_CLIENT)),
session_(connection_, &push_promise_index_),
body_("hello world") {
session_.Initialize();
headers_[":status"] = "200";
headers_["content-length"] = "11";
stream_.reset(new QuicSpdyClientStream(
QuicSpdySessionPeer::GetNthClientInitiatedStreamId(session_, 0),
&session_));
stream_visitor_.reset(new StreamVisitor());
stream_->set_visitor(stream_visitor_.get());
}
class StreamVisitor : public QuicSpdyClientStream::Visitor {
void OnClose(QuicSpdyStream* stream) override {
QUIC_DVLOG(1) << "stream " << stream->id();
}
};
MockQuicConnectionHelper helper_;
MockAlarmFactory alarm_factory_;
StrictMock<MockQuicConnection>* connection_;
QuicClientPushPromiseIndex push_promise_index_;
MockQuicSpdyClientSession session_;
std::unique_ptr<QuicSpdyClientStream> stream_;
std::unique_ptr<StreamVisitor> stream_visitor_;
SpdyHeaderBlock headers_;
string body_;
};
TEST_F(QuicSpdyClientStreamTest, TestReceivingIllegalResponseStatusCode) {
headers_[":status"] = "200 ok";
EXPECT_CALL(*connection_,
SendRstStream(stream_->id(), QUIC_BAD_APPLICATION_PAYLOAD, 0));
auto headers = AsHeaderList(headers_);
stream_->OnStreamHeaderList(false, headers.uncompressed_header_bytes(),
headers);
EXPECT_EQ(QUIC_BAD_APPLICATION_PAYLOAD, stream_->stream_error());
}
TEST_F(QuicSpdyClientStreamTest, TestFraming) {
auto headers = AsHeaderList(headers_);
stream_->OnStreamHeaderList(false, headers.uncompressed_header_bytes(),
headers);
stream_->OnStreamFrame(
QuicStreamFrame(stream_->id(), /*fin=*/false, /*offset=*/0, body_));
EXPECT_EQ("200", stream_->response_headers().find(":status")->second);
EXPECT_EQ(200, stream_->response_code());
EXPECT_EQ(body_, stream_->data());
}
TEST_F(QuicSpdyClientStreamTest, TestFraming100Continue) {
headers_[":status"] = "100";
auto headers = AsHeaderList(headers_);
stream_->OnStreamHeaderList(false, headers.uncompressed_header_bytes(),
headers);
stream_->OnStreamFrame(
QuicStreamFrame(stream_->id(), /*fin=*/false, /*offset=*/0, body_));
EXPECT_EQ("100", stream_->preliminary_headers().find(":status")->second);
EXPECT_EQ(0u, stream_->response_headers().size());
EXPECT_EQ(100, stream_->response_code());
EXPECT_EQ("", stream_->data());
}
TEST_F(QuicSpdyClientStreamTest, TestFramingOnePacket) {
auto headers = AsHeaderList(headers_);
stream_->OnStreamHeaderList(false, headers.uncompressed_header_bytes(),
headers);
stream_->OnStreamFrame(
QuicStreamFrame(stream_->id(), /*fin=*/false, /*offset=*/0, body_));
EXPECT_EQ("200", stream_->response_headers().find(":status")->second);
EXPECT_EQ(200, stream_->response_code());
EXPECT_EQ(body_, stream_->data());
}
TEST_F(QuicSpdyClientStreamTest, DISABLED_TestFramingExtraData) {
string large_body = "hello world!!!!!!";
auto headers = AsHeaderList(headers_);
stream_->OnStreamHeaderList(false, headers.uncompressed_header_bytes(),
headers);
// The headers should parse successfully.
EXPECT_EQ(QUIC_STREAM_NO_ERROR, stream_->stream_error());
EXPECT_EQ("200", stream_->response_headers().find(":status")->second);
EXPECT_EQ(200, stream_->response_code());
EXPECT_CALL(*connection_,
SendRstStream(stream_->id(), QUIC_BAD_APPLICATION_PAYLOAD, 0));
stream_->OnStreamFrame(
QuicStreamFrame(stream_->id(), /*fin=*/false, /*offset=*/0, large_body));
EXPECT_NE(QUIC_STREAM_NO_ERROR, stream_->stream_error());
}
TEST_F(QuicSpdyClientStreamTest, ReceivingTrailers) {
// Test that receiving trailing headers, containing a final offset, results in
// the stream being closed at that byte offset.
// Send headers as usual.
auto headers = AsHeaderList(headers_);
stream_->OnStreamHeaderList(false, headers.uncompressed_header_bytes(),
headers);
// Send trailers before sending the body. Even though a FIN has been received
// the stream should not be closed, as it does not yet have all the data bytes
// promised by the final offset field.
SpdyHeaderBlock trailer_block;
trailer_block["trailer key"] = "trailer value";
trailer_block[kFinalOffsetHeaderKey] =
QuicTextUtils::Uint64ToString(body_.size());
auto trailers = AsHeaderList(trailer_block);
stream_->OnStreamHeaderList(true, trailers.uncompressed_header_bytes(),
trailers);
// Now send the body, which should close the stream as the FIN has been
// received, as well as all data.
stream_->OnStreamFrame(
QuicStreamFrame(stream_->id(), /*fin=*/false, /*offset=*/0, body_));
EXPECT_TRUE(stream_->reading_stopped());
}
} // namespace
} // namespace test
} // namespace net