// 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/quic/quic_client_session.h"
#include <vector>
#include "base/rand_util.h"
#include "net/base/capturing_net_log.h"
#include "net/base/test_completion_callback.h"
#include "net/quic/crypto/aes_128_gcm_12_encrypter.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/quic_default_packet_writer.h"
#include "net/quic/test_tools/crypto_test_utils.h"
#include "net/quic/test_tools/quic_client_session_peer.h"
#include "net/quic/test_tools/quic_test_utils.h"
#include "net/socket/socket_test_util.h"
#include "net/udp/datagram_client_socket.h"
using testing::_;
namespace net {
namespace test {
namespace {
const char kServerHostname[] = "www.example.com";
class TestPacketWriter : public QuicDefaultPacketWriter {
public:
TestPacketWriter() {
}
// QuicPacketWriter
virtual WriteResult WritePacket(
const char* buffer, size_t buf_len,
const IPAddressNumber& self_address,
const IPEndPoint& peer_address,
QuicBlockedWriterInterface* blocked_writer) OVERRIDE {
QuicFramer framer(QuicSupportedVersions(), QuicTime::Zero(), true);
FramerVisitorCapturingFrames visitor;
framer.set_visitor(&visitor);
QuicEncryptedPacket packet(buffer, buf_len);
EXPECT_TRUE(framer.ProcessPacket(packet));
header_ = *visitor.header();
return WriteResult(WRITE_STATUS_OK, packet.length());
}
virtual bool IsWriteBlockedDataBuffered() const OVERRIDE {
// Chrome sockets' Write() methods buffer the data until the Write is
// permitted.
return true;
}
// Returns the header from the last packet written.
const QuicPacketHeader& header() { return header_; }
private:
QuicPacketHeader header_;
};
class QuicClientSessionTest : public ::testing::Test {
protected:
QuicClientSessionTest()
: writer_(new TestPacketWriter()),
connection_(new PacketSavingConnection(false)),
session_(connection_, GetSocket().Pass(), writer_.Pass(), NULL, NULL,
kServerHostname, DefaultQuicConfig(), &crypto_config_,
&net_log_) {
session_.config()->SetDefaults();
crypto_config_.SetDefaults();
}
virtual void TearDown() OVERRIDE {
session_.CloseSessionOnError(ERR_ABORTED);
}
scoped_ptr<DatagramClientSocket> GetSocket() {
socket_factory_.AddSocketDataProvider(&socket_data_);
return socket_factory_.CreateDatagramClientSocket(
DatagramSocket::DEFAULT_BIND, base::Bind(&base::RandInt),
&net_log_, NetLog::Source());
}
void CompleteCryptoHandshake() {
ASSERT_EQ(ERR_IO_PENDING,
session_.CryptoConnect(false, callback_.callback()));
CryptoTestUtils::HandshakeWithFakeServer(
connection_, session_.GetCryptoStream());
ASSERT_EQ(OK, callback_.WaitForResult());
}
scoped_ptr<QuicDefaultPacketWriter> writer_;
PacketSavingConnection* connection_;
CapturingNetLog net_log_;
MockClientSocketFactory socket_factory_;
StaticSocketDataProvider socket_data_;
QuicClientSession session_;
MockClock clock_;
MockRandom random_;
QuicConnectionVisitorInterface* visitor_;
TestCompletionCallback callback_;
QuicCryptoClientConfig crypto_config_;
};
TEST_F(QuicClientSessionTest, CryptoConnect) {
CompleteCryptoHandshake();
}
TEST_F(QuicClientSessionTest, MaxNumStreams) {
CompleteCryptoHandshake();
std::vector<QuicReliableClientStream*> streams;
for (size_t i = 0; i < kDefaultMaxStreamsPerConnection; i++) {
QuicReliableClientStream* stream = session_.CreateOutgoingDataStream();
EXPECT_TRUE(stream);
streams.push_back(stream);
}
EXPECT_FALSE(session_.CreateOutgoingDataStream());
// Close a stream and ensure I can now open a new one.
session_.CloseStream(streams[0]->id());
EXPECT_TRUE(session_.CreateOutgoingDataStream());
}
TEST_F(QuicClientSessionTest, MaxNumStreamsViaRequest) {
CompleteCryptoHandshake();
std::vector<QuicReliableClientStream*> streams;
for (size_t i = 0; i < kDefaultMaxStreamsPerConnection; i++) {
QuicReliableClientStream* stream = session_.CreateOutgoingDataStream();
EXPECT_TRUE(stream);
streams.push_back(stream);
}
QuicReliableClientStream* stream;
QuicClientSession::StreamRequest stream_request;
TestCompletionCallback callback;
ASSERT_EQ(ERR_IO_PENDING,
stream_request.StartRequest(session_.GetWeakPtr(), &stream,
callback.callback()));
// Close a stream and ensure I can now open a new one.
session_.CloseStream(streams[0]->id());
ASSERT_TRUE(callback.have_result());
EXPECT_EQ(OK, callback.WaitForResult());
EXPECT_TRUE(stream != NULL);
}
TEST_F(QuicClientSessionTest, GoAwayReceived) {
CompleteCryptoHandshake();
// After receiving a GoAway, I should no longer be able to create outgoing
// streams.
session_.OnGoAway(QuicGoAwayFrame(QUIC_PEER_GOING_AWAY, 1u, "Going away."));
EXPECT_EQ(NULL, session_.CreateOutgoingDataStream());
}
} // namespace
} // namespace test
} // namespace net