0

QUIC - reorganize common code between quic_client and quic_simple_client

into quic_client_base.* files.

R=rch@chromium.org

Review URL: https://codereview.chromium.org/1297853002

Cr-Commit-Position: refs/heads/master@{#343810}
This commit is contained in:
rtenneti
2015-08-17 22:15:31 -07:00
committed by Commit bot
parent c925519f09
commit d67b3a7256
11 changed files with 481 additions and 671 deletions

@ -1248,6 +1248,8 @@ if (is_android || is_linux) {
source_set("simple_quic_tools") {
sources = [
"tools/quic/quic_client_base.cc",
"tools/quic/quic_client_base.h",
"tools/quic/quic_client_session.cc",
"tools/quic/quic_client_session.h",
"tools/quic/quic_dispatcher.cc",

@ -821,6 +821,8 @@
'net_quic_proto',
],
'sources': [
'tools/quic/quic_client_base.cc',
'tools/quic/quic_client_base.h',
'tools/quic/quic_client_session.cc',
'tools/quic/quic_client_session.h',
'tools/quic/quic_dispatcher.cc',

@ -15,16 +15,12 @@
#include "net/base/net_util.h"
#include "net/quic/crypto/quic_random.h"
#include "net/quic/quic_connection.h"
#include "net/quic/quic_crypto_client_stream.h"
#include "net/quic/quic_data_reader.h"
#include "net/quic/quic_flags.h"
#include "net/quic/quic_protocol.h"
#include "net/quic/quic_server_id.h"
#include "net/tools/balsa/balsa_headers.h"
#include "net/tools/epoll_server/epoll_server.h"
#include "net/tools/quic/quic_epoll_connection_helper.h"
#include "net/tools/quic/quic_socket_utils.h"
#include "net/tools/quic/quic_spdy_client_stream.h"
#include "net/tools/quic/spdy_balsa_utils.h"
#ifndef SO_RXQ_OVFL
@ -60,9 +56,8 @@ QuicClient::QuicClient(IPEndPoint server_address,
const QuicVersionVector& supported_versions,
const QuicConfig& config,
EpollServer* epoll_server)
: server_address_(server_address),
server_id_(server_id),
config_(config),
: QuicClientBase(server_id, supported_versions, config),
server_address_(server_address),
local_port_(0),
epoll_server_(epoll_server),
fd_(-1),
@ -70,19 +65,14 @@ QuicClient::QuicClient(IPEndPoint server_address,
initialized_(false),
packets_dropped_(0),
overflow_supported_(false),
supported_versions_(supported_versions),
store_response_(false),
latest_response_code_(-1),
initial_max_packet_length_(0),
num_stateless_rejects_received_(0),
num_sent_client_hellos_(0),
connection_error_(QUIC_NO_ERROR),
connected_or_attempting_connect_(false) {}
latest_response_code_(-1) {}
QuicClient::~QuicClient() {
if (connected()) {
session()->connection()->SendConnectionClose(QUIC_PEER_GOING_AWAY);
}
STLDeleteElements(&data_to_resend_on_connect_);
STLDeleteElements(&data_sent_before_handshake_);
@ -90,24 +80,20 @@ QuicClient::~QuicClient() {
}
bool QuicClient::Initialize() {
DCHECK(!initialized_);
num_sent_client_hellos_ = 0;
num_stateless_rejects_received_ = 0;
connection_error_ = QUIC_NO_ERROR;
connected_or_attempting_connect_ = false;
QuicClientBase::Initialize();
// If an initial flow control window has not explicitly been set, then use the
// same values that Chrome uses.
const uint32 kSessionMaxRecvWindowSize = 15 * 1024 * 1024; // 15 MB
const uint32 kStreamMaxRecvWindowSize = 6 * 1024 * 1024; // 6 MB
if (config_.GetInitialStreamFlowControlWindowToSend() ==
if (config()->GetInitialStreamFlowControlWindowToSend() ==
kMinimumFlowControlSendWindow) {
config_.SetInitialStreamFlowControlWindowToSend(kStreamMaxRecvWindowSize);
config()->SetInitialStreamFlowControlWindowToSend(kStreamMaxRecvWindowSize);
}
if (config_.GetInitialSessionFlowControlWindowToSend() ==
if (config()->GetInitialSessionFlowControlWindowToSend() ==
kMinimumFlowControlSendWindow) {
config_.SetInitialSessionFlowControlWindowToSend(kSessionMaxRecvWindowSize);
config()->SetInitialSessionFlowControlWindowToSend(
kSessionMaxRecvWindowSize);
}
epoll_server_->set_timeout_in_us(50 * 1000);
@ -121,17 +107,6 @@ bool QuicClient::Initialize() {
return true;
}
QuicClient::DummyPacketWriterFactory::DummyPacketWriterFactory(
QuicPacketWriter* writer)
: writer_(writer) {}
QuicClient::DummyPacketWriterFactory::~DummyPacketWriterFactory() {}
QuicPacketWriter* QuicClient::DummyPacketWriterFactory::Create(
QuicConnection* /*connection*/) const {
return writer_;
}
QuicClient::QuicDataToResend::QuicDataToResend(BalsaHeaders* headers,
StringPiece body,
bool fin)
@ -226,8 +201,8 @@ bool QuicClient::Connect() {
}
STLDeleteElements(&data_to_resend_on_connect_);
}
if (session_.get() != nullptr &&
session_->error() != QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT) {
if (session() != nullptr &&
session()->error() != QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT) {
// We've successfully created a session but we're not connected, and there
// is no stateless reject to recover from. Give up trying.
break;
@ -235,20 +210,12 @@ bool QuicClient::Connect() {
}
if (!connected() &&
GetNumSentClientHellos() > QuicCryptoClientStream::kMaxClientHellos &&
session_ != nullptr &&
session_->error() == QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT) {
session() != nullptr &&
session()->error() == QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT) {
// The overall connection failed due too many stateless rejects.
connection_error_ = QUIC_CRYPTO_TOO_MANY_REJECTS;
set_connection_error(QUIC_CRYPTO_TOO_MANY_REJECTS);
}
return session_->connection()->connected();
}
QuicClientSession* QuicClient::CreateQuicClientSession(
const QuicConfig& config,
QuicConnection* connection,
const QuicServerId& server_id,
QuicCryptoClientConfig* crypto_config) {
return new QuicClientSession(config, connection, server_id_, &crypto_config_);
return session()->connection()->connected();
}
void QuicClient::StartConnect() {
@ -259,45 +226,31 @@ void QuicClient::StartConnect() {
DummyPacketWriterFactory factory(writer);
if (connected_or_attempting_connect_) {
if (connected_or_attempting_connect()) {
// Before we destroy the last session and create a new one, gather its stats
// and update the stats for the overall connection.
num_sent_client_hellos_ += session_->GetNumSentClientHellos();
if (session_->error() == QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT) {
UpdateStats();
if (session()->error() == QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT) {
// If the last error was due to a stateless reject, queue up the data to
// be resent on the next successful connection.
// TODO(jokulik): I'm a little bit concerned about ordering here. Maybe
// we should just maintain one queue?
++num_stateless_rejects_received_;
DCHECK(data_to_resend_on_connect_.empty());
data_to_resend_on_connect_.swap(data_sent_before_handshake_);
}
}
session_.reset(CreateQuicClientSession(
config_,
new QuicConnection(GetNextConnectionId(), server_address_, helper_.get(),
factory,
/* owns_writer= */ false, Perspective::IS_CLIENT,
server_id_.is_https(), supported_versions_),
server_id_, &crypto_config_));
if (initial_max_packet_length_ != 0) {
session_->connection()->set_max_packet_length(initial_max_packet_length_);
}
CreateQuicClientSession(new QuicConnection(
GetNextConnectionId(), server_address_, helper_.get(), factory,
/* owns_writer= */ false, Perspective::IS_CLIENT, server_id().is_https(),
supported_versions()));
// Reset |writer_| after |session_| so that the old writer outlives the old
// Reset |writer_| after |session()| so that the old writer outlives the old
// session.
if (writer_.get() != writer) {
writer_.reset(writer);
}
session_->Initialize();
session_->CryptoConnect();
connected_or_attempting_connect_ = true;
}
bool QuicClient::EncryptionBeingEstablished() {
return !session_->IsEncryptionEstablished() &&
session_->connection()->connected();
set_writer(writer);
session()->Initialize();
session()->CryptoConnect();
set_connected_or_attempting_connect(true);
}
void QuicClient::Disconnect() {
@ -351,7 +304,7 @@ void QuicClient::SendRequest(const BalsaHeaders& headers,
void QuicClient::MaybeAddQuicDataToResend(QuicDataToResend* data_to_resend) {
DCHECK(FLAGS_enable_quic_stateless_reject_support);
if (session_->IsCryptoHandshakeConfirmed()) {
if (session()->IsCryptoHandshakeConfirmed()) {
// The handshake is confirmed. No need to continue saving requests to
// resend.
STLDeleteElements(&data_sent_before_handshake_);
@ -382,45 +335,21 @@ void QuicClient::SendRequestsAndWaitForResponse(
while (WaitForEvents()) {}
}
QuicSpdyClientStream* QuicClient::CreateReliableClientStream() {
if (!connected()) {
return nullptr;
}
return session_->CreateOutgoingDynamicStream();
}
void QuicClient::WaitForStreamToClose(QuicStreamId id) {
DCHECK(connected());
while (connected() && !session_->IsClosedStream(id)) {
WaitForEvents();
}
}
void QuicClient::WaitForCryptoHandshakeConfirmed() {
DCHECK(connected());
while (connected() && !session_->IsCryptoHandshakeConfirmed()) {
WaitForEvents();
}
}
bool QuicClient::WaitForEvents() {
DCHECK(connected());
epoll_server_->WaitForEventsAndExecuteCallbacks();
DCHECK(session_ != nullptr);
DCHECK(session() != nullptr);
if (!connected() &&
session_->error() == QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT) {
session()->error() == QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT) {
DCHECK(FLAGS_enable_quic_stateless_reject_support);
DVLOG(1) << "Detected stateless reject while waiting for events. "
<< "Attempting to reconnect.";
Connect();
}
return session_->num_active_requests() != 0;
return session()->num_active_requests() != 0;
}
bool QuicClient::MigrateSocket(const IPAddressNumber& new_host) {
@ -436,14 +365,12 @@ bool QuicClient::MigrateSocket(const IPAddressNumber& new_host) {
}
epoll_server_->RegisterFD(fd_, this, kEpollFlags);
session_->connection()->SetSelfAddress(client_address_);
session()->connection()->SetSelfAddress(client_address_);
QuicPacketWriter* writer = CreateQuicPacketWriter();
DummyPacketWriterFactory factory(writer);
if (writer_.get() != writer) {
writer_.reset(writer);
}
session_->connection()->SetQuicPacketWriter(writer, false);
set_writer(writer);
session()->connection()->SetQuicPacketWriter(writer, false);
return true;
}
@ -456,8 +383,8 @@ void QuicClient::OnEvent(int fd, EpollEvent* event) {
}
}
if (connected() && (event->in_events & EPOLLOUT)) {
writer_->SetWritable();
session_->connection()->OnCanWrite();
writer()->SetWritable();
session()->connection()->OnCanWrite();
}
if (event->in_events & EPOLLERR) {
DVLOG(1) << "Epollerr";
@ -485,15 +412,6 @@ void QuicClient::OnClose(QuicDataStream* stream) {
}
}
bool QuicClient::connected() const {
return session_.get() && session_->connection() &&
session_->connection()->connected();
}
bool QuicClient::goaway_received() const {
return session_ != nullptr && session_->goaway_received();
}
size_t QuicClient::latest_response_code() const {
LOG_IF(DFATAL, !store_response_) << "Response not stored!";
return latest_response_code_;
@ -509,49 +427,6 @@ const string& QuicClient::latest_response_body() const {
return latest_response_body_;
}
int QuicClient::GetNumSentClientHellos() {
// If we are not actively attempting to connect, the session object
// corresponds to the previous connection and should not be used.
const int current_session_hellos = !connected_or_attempting_connect_
? 0
: session_->GetNumSentClientHellos();
return num_sent_client_hellos_ + current_session_hellos;
}
QuicErrorCode QuicClient::connection_error() const {
// Return the high-level error if there was one. Otherwise, return the
// connection error from the last session.
if (connection_error_ != QUIC_NO_ERROR) {
return connection_error_;
}
if (session_.get() == nullptr) {
return QUIC_NO_ERROR;
}
return session_->error();
}
QuicConnectionId QuicClient::GetNextConnectionId() {
QuicConnectionId server_designated_id = GetNextServerDesignatedConnectionId();
return server_designated_id ? server_designated_id
: GenerateNewConnectionId();
}
QuicConnectionId QuicClient::GetNextServerDesignatedConnectionId() {
QuicCryptoClientConfig::CachedState* cached =
crypto_config_.LookupOrCreate(server_id_);
// If the cached state indicates that we should use a server-designated
// connection ID, then return that connection ID.
CHECK(cached != nullptr) << "QuicClientCryptoConfig::LookupOrCreate returned "
<< "unexpected nullptr.";
return cached->has_server_designated_connection_id()
? cached->GetNextServerDesignatedConnectionId()
: 0;
}
QuicConnectionId QuicClient::GenerateNewConnectionId() {
return QuicRandom::GetInstance()->RandUint64();
}
QuicEpollConnectionHelper* QuicClient::CreateQuicConnectionHelper() {
return new QuicEpollConnectionHelper(epoll_server_);
}
@ -587,8 +462,8 @@ bool QuicClient::ReadAndProcessPacket() {
QuicEncryptedPacket packet(buf, bytes_read, false);
IPEndPoint client_address(client_ip, client_address_.port());
session_->connection()->ProcessUdpPacket(
client_address, server_address, packet);
session()->connection()->ProcessUdpPacket(client_address, server_address,
packet);
return true;
}

@ -15,18 +15,14 @@
#include "base/memory/scoped_ptr.h"
#include "base/strings/string_piece.h"
#include "net/base/ip_endpoint.h"
#include "net/quic/crypto/crypto_handshake.h"
#include "net/quic/quic_config.h"
#include "net/quic/quic_framer.h"
#include "net/quic/quic_packet_creator.h"
#include "net/quic/quic_data_stream.h"
#include "net/tools/balsa/balsa_headers.h"
#include "net/tools/epoll_server/epoll_server.h"
#include "net/tools/quic/quic_client_session.h"
#include "net/tools/quic/quic_spdy_client_stream.h"
#include "net/tools/quic/quic_client_base.h"
namespace net {
class ProofVerifier;
class QuicServerId;
namespace tools {
@ -37,7 +33,8 @@ namespace test {
class QuicClientPeer;
} // namespace test
class QuicClient : public EpollCallbackInterface,
class QuicClient : public QuicClientBase,
public EpollCallbackInterface,
public QuicDataStream::Visitor {
public:
class ResponseListener {
@ -49,18 +46,6 @@ class QuicClient : public EpollCallbackInterface,
const std::string& response_body) = 0;
};
// A packet writer factory that always returns the same writer.
class DummyPacketWriterFactory : public QuicConnection::PacketWriterFactory {
public:
explicit DummyPacketWriterFactory(QuicPacketWriter* writer);
~DummyPacketWriterFactory() override;
QuicPacketWriter* Create(QuicConnection* connection) const override;
private:
QuicPacketWriter* writer_;
};
// The client uses these objects to keep track of any data to resend upon
// receipt of a stateless reject. Recall that the client API allows callers
// to optimistically send data to the server prior to handshake-confirmation.
@ -103,10 +88,9 @@ class QuicClient : public EpollCallbackInterface,
~QuicClient() override;
// Initializes the client to create a connection. Should be called exactly
// once before calling StartConnect or Connect. Returns true if the
// initialization succeeds, false otherwise.
bool Initialize();
// From QuicClientBase
bool Initialize() override;
bool WaitForEvents() override;
// "Connect" to the QUIC server, including performing synchronous crypto
// handshake.
@ -117,11 +101,6 @@ class QuicClient : public EpollCallbackInterface,
// completes.
void StartConnect();
// Returns true if the crypto handshake has yet to establish encryption.
// Returns false if encryption is active (even if the server hasn't confirmed
// the handshake) or if the connection has been closed.
bool EncryptionBeingEstablished();
// Disconnects from the QUIC server.
void Disconnect();
@ -140,20 +119,6 @@ class QuicClient : public EpollCallbackInterface,
void SendRequestsAndWaitForResponse(
const std::vector<std::string>& url_list);
// Returns a newly created QuicSpdyClientStream, owned by the
// QuicClient.
QuicSpdyClientStream* CreateReliableClientStream();
// Wait for events until the stream with the given ID is closed.
void WaitForStreamToClose(QuicStreamId id);
// Wait for events until the handshake is confirmed.
void WaitForCryptoHandshakeConfirmed();
// Wait up to 50ms, and handle any events which occur.
// Returns true if there are any outstanding requests.
bool WaitForEvents();
// Migrate to a new socket during an active connection.
bool MigrateSocket(const IPAddressNumber& new_host);
@ -175,11 +140,6 @@ class QuicClient : public EpollCallbackInterface,
// Otherwise, deletes the data. Takes ownerership of |data_to_resend|.
void MaybeAddQuicDataToResend(QuicDataToResend* data_to_resend);
QuicClientSession* session() { return session_.get(); }
bool connected() const;
bool goaway_received() const;
void set_bind_to_address(IPAddressNumber address) {
bind_to_address_ = address;
}
@ -194,87 +154,18 @@ class QuicClient : public EpollCallbackInterface,
int fd() { return fd_; }
const QuicServerId& server_id() const { return server_id_; }
// This should only be set before the initial Connect()
void set_server_id(const QuicServerId& server_id) {
server_id_ = server_id;
}
void SetUserAgentID(const std::string& user_agent_id) {
crypto_config_.set_user_agent_id(user_agent_id);
}
// SetProofVerifier sets the ProofVerifier that will be used to verify the
// server's certificate and takes ownership of |verifier|.
void SetProofVerifier(ProofVerifier* verifier) {
// TODO(rtenneti): We should set ProofVerifier in QuicClientSession.
crypto_config_.SetProofVerifier(verifier);
}
// SetChannelIDSource sets a ChannelIDSource that will be called, when the
// server supports channel IDs, to obtain a channel ID for signing a message
// proving possession of the channel ID. This object takes ownership of
// |source|.
void SetChannelIDSource(ChannelIDSource* source) {
crypto_config_.SetChannelIDSource(source);
}
void SetSupportedVersions(const QuicVersionVector& versions) {
supported_versions_ = versions;
}
// Takes ownership of the listener.
void set_response_listener(ResponseListener* listener) {
response_listener_.reset(listener);
}
QuicConfig* config() { return &config_; }
void set_store_response(bool val) { store_response_ = val; }
size_t latest_response_code() const;
const std::string& latest_response_headers() const;
const std::string& latest_response_body() const;
// Change the initial maximum packet size of the connection. Has to be called
// before Connect()/StartConnect() in order to have any effect.
void set_initial_max_packet_length(QuicByteCount initial_max_packet_length) {
initial_max_packet_length_ = initial_max_packet_length;
}
int num_stateless_rejects_received() const {
return num_stateless_rejects_received_;
}
// The number of client hellos sent, taking stateless rejects into
// account. In the case of a stateless reject, the initial
// connection object may be torn down and a new one created. The
// user cannot rely upon the latest connection object to get the
// total number of client hellos sent, and should use this function
// instead.
int GetNumSentClientHellos();
// Returns any errors that occurred at the connection-level (as
// opposed to the session-level). When a stateless reject occurs,
// the error of the last session may not reflect the overall state
// of the connection.
QuicErrorCode connection_error() const;
protected:
// Generates the next ConnectionId for |server_id_|. By default, if the
// cached server config contains a server-designated ID, that ID will be
// returned. Otherwise, the next random ID will be returned.
QuicConnectionId GetNextConnectionId();
// Returns the next server-designated ConnectionId from the cached config for
// |server_id_|, if it exists. Otherwise, returns 0.
QuicConnectionId GetNextServerDesignatedConnectionId();
// Generates a new, random connection ID (as opposed to a server-designated
// connection ID).
virtual QuicConnectionId GenerateNewConnectionId();
virtual QuicEpollConnectionHelper* CreateQuicConnectionHelper();
virtual QuicPacketWriter* CreateQuicPacketWriter();
@ -283,12 +174,6 @@ class QuicClient : public EpollCallbackInterface,
IPEndPoint* server_address,
IPAddressNumber* client_ip);
virtual QuicClientSession* CreateQuicClientSession(
const QuicConfig& config,
QuicConnection* connection,
const QuicServerId& server_id,
QuicCryptoClientConfig* crypto_config);
EpollServer* epoll_server() { return epoll_server_; }
// If the socket has been created, then unregister and close() the FD.
@ -333,14 +218,6 @@ class QuicClient : public EpollCallbackInterface,
// Address of the server.
const IPEndPoint server_address_;
// |server_id_| is a tuple (hostname, port, is_https) of the server.
QuicServerId server_id_;
// config_ and crypto_config_ contain configuration and cached state about
// servers.
QuicConfig config_;
QuicCryptoClientConfig crypto_config_;
// Address of the client if the client is connected to the server.
IPEndPoint client_address_;
@ -349,12 +226,6 @@ class QuicClient : public EpollCallbackInterface,
// Local port to bind to. Initialize to 0.
int local_port_;
// Writer used to actually send packets to the wire. Needs to outlive
// |session_|.
scoped_ptr<QuicPacketWriter> writer_;
// Session which manages streams.
scoped_ptr<QuicClientSession> session_;
// Listens for events on the client socket.
EpollServer* epoll_server_;
// UDP socket.
@ -377,13 +248,6 @@ class QuicClient : public EpollCallbackInterface,
// because the socket would otherwise overflow.
bool overflow_supported_;
// This vector contains QUIC versions which we currently support.
// This should be ordered such that the highest supported version is the first
// element, with subsequent elements in descending order (versions can be
// skipped as necessary). We will always pick supported_versions_[0] as the
// initial version to use.
QuicVersionVector supported_versions_;
// If true, store the latest response code, headers, and body.
bool store_response_;
// HTTP response code from most recent response.
@ -393,31 +257,6 @@ class QuicClient : public EpollCallbackInterface,
// Body of most recent response.
std::string latest_response_body_;
// The initial value of maximum packet size of the connection. If set to
// zero, the default is used.
QuicByteCount initial_max_packet_length_;
// The number of stateless rejects received during the current/latest
// connection.
// TODO(jokulik): Consider some consistent naming scheme (or other) for member
// variables that are kept per-request, per-connection, and over the client's
// lifetime.
int num_stateless_rejects_received_;
// The number of hellos sent during the current/latest connection.
int num_sent_client_hellos_;
// Used to store any errors that occurred with the overall connection (as
// opposed to that associated with the last session object).
QuicErrorCode connection_error_;
// True when the client is attempting to connect or re-connect the session (in
// the case of a stateless reject). Set to false between a call to
// Disconnect() and the subsequent call to StartConnect(). When
// connected_or_attempting_connect_ is false, the session object corresponds
// to the previous client-level connection.
bool connected_or_attempting_connect_;
// Keeps track of any data sent before the handshake.
std::vector<QuicDataToResend*> data_sent_before_handshake_;

@ -0,0 +1,145 @@
// Copyright (c) 2015 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_client_base.h"
#include "net/quic/crypto/quic_random.h"
#include "net/quic/quic_server_id.h"
namespace net {
namespace tools {
QuicClientBase::QuicClientBase(const QuicServerId& server_id,
const QuicVersionVector& supported_versions,
const QuicConfig& config)
: server_id_(server_id),
config_(config),
supported_versions_(supported_versions),
initial_max_packet_length_(0),
num_stateless_rejects_received_(0),
num_sent_client_hellos_(0),
connection_error_(QUIC_NO_ERROR),
connected_or_attempting_connect_(false) {}
QuicClientBase::~QuicClientBase() {}
bool QuicClientBase::Initialize() {
num_sent_client_hellos_ = 0;
num_stateless_rejects_received_ = 0;
connection_error_ = QUIC_NO_ERROR;
connected_or_attempting_connect_ = false;
return true;
}
QuicClientBase::DummyPacketWriterFactory::DummyPacketWriterFactory(
QuicPacketWriter* writer)
: writer_(writer) {}
QuicClientBase::DummyPacketWriterFactory::~DummyPacketWriterFactory() {}
QuicPacketWriter* QuicClientBase::DummyPacketWriterFactory::Create(
QuicConnection* /*connection*/) const {
return writer_;
}
QuicClientSession* QuicClientBase::CreateQuicClientSession(
QuicConnection* connection) {
session_.reset(
new QuicClientSession(config_, connection, server_id_, &crypto_config_));
if (initial_max_packet_length_ != 0) {
session()->connection()->set_max_packet_length(initial_max_packet_length_);
}
return session_.get();
}
bool QuicClientBase::EncryptionBeingEstablished() {
return !session_->IsEncryptionEstablished() &&
session_->connection()->connected();
}
QuicSpdyClientStream* QuicClientBase::CreateReliableClientStream() {
if (!connected()) {
return nullptr;
}
return session_->CreateOutgoingDynamicStream();
}
void QuicClientBase::WaitForStreamToClose(QuicStreamId id) {
DCHECK(connected());
while (connected() && !session_->IsClosedStream(id)) {
WaitForEvents();
}
}
void QuicClientBase::WaitForCryptoHandshakeConfirmed() {
DCHECK(connected());
while (connected() && !session_->IsCryptoHandshakeConfirmed()) {
WaitForEvents();
}
}
bool QuicClientBase::connected() const {
return session_.get() && session_->connection() &&
session_->connection()->connected();
}
bool QuicClientBase::goaway_received() const {
return session_ != nullptr && session_->goaway_received();
}
int QuicClientBase::GetNumSentClientHellos() {
// If we are not actively attempting to connect, the session object
// corresponds to the previous connection and should not be used.
const int current_session_hellos = !connected_or_attempting_connect_
? 0
: session_->GetNumSentClientHellos();
return num_sent_client_hellos_ + current_session_hellos;
}
void QuicClientBase::UpdateStats() {
num_sent_client_hellos_ += session()->GetNumSentClientHellos();
if (session()->error() == QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT) {
++num_stateless_rejects_received_;
}
}
QuicErrorCode QuicClientBase::connection_error() const {
// Return the high-level error if there was one. Otherwise, return the
// connection error from the last session.
if (connection_error_ != QUIC_NO_ERROR) {
return connection_error_;
}
if (session_.get() == nullptr) {
return QUIC_NO_ERROR;
}
return session_->error();
}
QuicConnectionId QuicClientBase::GetNextConnectionId() {
QuicConnectionId server_designated_id = GetNextServerDesignatedConnectionId();
return server_designated_id ? server_designated_id
: GenerateNewConnectionId();
}
QuicConnectionId QuicClientBase::GetNextServerDesignatedConnectionId() {
QuicCryptoClientConfig::CachedState* cached =
crypto_config_.LookupOrCreate(server_id_);
// If the cached state indicates that we should use a server-designated
// connection ID, then return that connection ID.
CHECK(cached != nullptr) << "QuicClientCryptoConfig::LookupOrCreate returned "
<< "unexpected nullptr.";
return cached->has_server_designated_connection_id()
? cached->GetNextServerDesignatedConnectionId()
: 0;
}
QuicConnectionId QuicClientBase::GenerateNewConnectionId() {
return QuicRandom::GetInstance()->RandUint64();
}
} // namespace tools
} // namespace net

@ -0,0 +1,241 @@
// Copyright (c) 2015 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.
//
// A base class for the toy client, which connects to a specified port and sends
// QUIC request to that endpoint.
#ifndef NET_TOOLS_QUIC_QUIC_CLIENT_BASE_H_
#define NET_TOOLS_QUIC_QUIC_CLIENT_BASE_H_
#include <string>
#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
#include "net/base/ip_endpoint.h"
#include "net/log/net_log.h"
#include "net/quic/crypto/crypto_handshake.h"
#include "net/quic/crypto/quic_crypto_client_config.h"
#include "net/quic/quic_bandwidth.h"
#include "net/quic/quic_config.h"
#include "net/quic/quic_connection.h"
#include "net/quic/quic_packet_writer.h"
#include "net/quic/quic_protocol.h"
#include "net/tools/quic/quic_client_session.h"
#include "net/tools/quic/quic_spdy_client_stream.h"
namespace net {
class ProofVerifier;
class QuicServerId;
namespace tools {
class QuicClientBase {
public:
// A packet writer factory that always returns the same writer.
class DummyPacketWriterFactory : public QuicConnection::PacketWriterFactory {
public:
explicit DummyPacketWriterFactory(QuicPacketWriter* writer);
~DummyPacketWriterFactory() override;
QuicPacketWriter* Create(QuicConnection* connection) const override;
private:
QuicPacketWriter* writer_;
};
QuicClientBase(const QuicServerId& server_id,
const QuicVersionVector& supported_versions,
const QuicConfig& config);
~QuicClientBase();
// Initializes the client to create a connection. Should be called exactly
// once before calling StartConnect or Connect. Returns true if the
// initialization succeeds, false otherwise.
virtual bool Initialize();
// Returns true if the crypto handshake has yet to establish encryption.
// Returns false if encryption is active (even if the server hasn't confirmed
// the handshake) or if the connection has been closed.
bool EncryptionBeingEstablished();
// Returns a newly created QuicSpdyClientStream, owned by the
// QuicSimpleClient.
QuicSpdyClientStream* CreateReliableClientStream();
// Wait for events until the stream with the given ID is closed.
void WaitForStreamToClose(QuicStreamId id);
// Wait for events until the handshake is confirmed.
void WaitForCryptoHandshakeConfirmed();
// Wait up to 50ms, and handle any events which occur.
// Returns true if there are any outstanding requests.
virtual bool WaitForEvents() = 0;
QuicClientSession* session() { return session_.get(); }
bool connected() const;
bool goaway_received() const;
const QuicServerId& server_id() const { return server_id_; }
// This should only be set before the initial Connect()
void set_server_id(const QuicServerId& server_id) { server_id_ = server_id; }
void SetUserAgentID(const std::string& user_agent_id) {
crypto_config_.set_user_agent_id(user_agent_id);
}
// SetProofVerifier sets the ProofVerifier that will be used to verify the
// server's certificate and takes ownership of |verifier|.
void SetProofVerifier(ProofVerifier* verifier) {
// TODO(rtenneti): We should set ProofVerifier in QuicClientSession.
crypto_config_.SetProofVerifier(verifier);
}
// SetChannelIDSource sets a ChannelIDSource that will be called, when the
// server supports channel IDs, to obtain a channel ID for signing a message
// proving possession of the channel ID. This object takes ownership of
// |source|.
void SetChannelIDSource(ChannelIDSource* source) {
crypto_config_.SetChannelIDSource(source);
}
const QuicVersionVector& supported_versions() const {
return supported_versions_;
}
void SetSupportedVersions(const QuicVersionVector& versions) {
supported_versions_ = versions;
}
QuicConfig* config() { return &config_; }
QuicCryptoClientConfig* crypto_config() { return &crypto_config_; }
// Change the initial maximum packet size of the connection. Has to be called
// before Connect()/StartConnect() in order to have any effect.
void set_initial_max_packet_length(QuicByteCount initial_max_packet_length) {
initial_max_packet_length_ = initial_max_packet_length;
}
int num_stateless_rejects_received() const {
return num_stateless_rejects_received_;
}
// The number of client hellos sent, taking stateless rejects into
// account. In the case of a stateless reject, the initial
// connection object may be torn down and a new one created. The
// user cannot rely upon the latest connection object to get the
// total number of client hellos sent, and should use this function
// instead.
int GetNumSentClientHellos();
// Gather the stats for the last session and update the stats for the overall
// connection.
void UpdateStats();
// Returns any errors that occurred at the connection-level (as
// opposed to the session-level). When a stateless reject occurs,
// the error of the last session may not reflect the overall state
// of the connection.
QuicErrorCode connection_error() const;
void set_connection_error(QuicErrorCode connection_error) {
connection_error_ = connection_error;
}
bool connected_or_attempting_connect() const {
return connected_or_attempting_connect_;
}
void set_connected_or_attempting_connect(
bool connected_or_attempting_connect) {
connected_or_attempting_connect_ = connected_or_attempting_connect;
}
QuicPacketWriter* writer() { return writer_.get(); }
void set_writer(QuicPacketWriter* writer) {
if (writer_.get() != writer) {
writer_.reset(writer);
}
}
void reset_writer() { writer_.reset(); }
QuicByteCount initial_max_packet_length() {
return initial_max_packet_length_;
}
protected:
virtual QuicClientSession* CreateQuicClientSession(
QuicConnection* connection);
// Generates the next ConnectionId for |server_id_|. By default, if the
// cached server config contains a server-designated ID, that ID will be
// returned. Otherwise, the next random ID will be returned.
QuicConnectionId GetNextConnectionId();
// Returns the next server-designated ConnectionId from the cached config for
// |server_id_|, if it exists. Otherwise, returns 0.
QuicConnectionId GetNextServerDesignatedConnectionId();
// Generates a new, random connection ID (as opposed to a server-designated
// connection ID).
virtual QuicConnectionId GenerateNewConnectionId();
private:
// |server_id_| is a tuple (hostname, port, is_https) of the server.
QuicServerId server_id_;
// config_ and crypto_config_ contain configuration and cached state about
// servers.
QuicConfig config_;
QuicCryptoClientConfig crypto_config_;
// Writer used to actually send packets to the wire. Needs to outlive
// |session_|.
scoped_ptr<QuicPacketWriter> writer_;
// Session which manages streams.
scoped_ptr<QuicClientSession> session_;
// This vector contains QUIC versions which we currently support.
// This should be ordered such that the highest supported version is the first
// element, with subsequent elements in descending order (versions can be
// skipped as necessary). We will always pick supported_versions_[0] as the
// initial version to use.
QuicVersionVector supported_versions_;
// The initial value of maximum packet size of the connection. If set to
// zero, the default is used.
QuicByteCount initial_max_packet_length_;
// The number of stateless rejects received during the current/latest
// connection.
// TODO(jokulik): Consider some consistent naming scheme (or other) for member
// variables that are kept per-request, per-connection, and over the client's
// lifetime.
int num_stateless_rejects_received_;
// The number of hellos sent during the current/latest connection.
int num_sent_client_hellos_;
// Used to store any errors that occurred with the overall connection (as
// opposed to that associated with the last session object).
QuicErrorCode connection_error_;
// True when the client is attempting to connect or re-connect the session (in
// the case of a stateless reject). Set to false between a call to
// Disconnect() and the subsequent call to StartConnect(). When
// connected_or_attempting_connect_ is false, the session object corresponds
// to the previous client-level connection.
bool connected_or_attempting_connect_;
DISALLOW_COPY_AND_ASSIGN(QuicClientBase);
};
} // namespace tools
} // namespace net
#endif // NET_TOOLS_QUIC_QUIC_CLIENT_BASE_H_

@ -13,7 +13,6 @@
#include "net/quic/crypto/quic_random.h"
#include "net/quic/quic_connection.h"
#include "net/quic/quic_connection_helper.h"
#include "net/quic/quic_crypto_client_stream.h"
#include "net/quic/quic_default_packet_writer.h"
#include "net/quic/quic_flags.h"
#include "net/quic/quic_protocol.h"
@ -37,12 +36,11 @@ void QuicSimpleClient::ClientQuicDataToResend::Resend() {
QuicSimpleClient::QuicSimpleClient(IPEndPoint server_address,
const QuicServerId& server_id,
const QuicVersionVector& supported_versions)
: server_address_(server_address),
server_id_(server_id),
: QuicClientBase(server_id, supported_versions, QuicConfig()),
server_address_(server_address),
local_port_(0),
helper_(CreateQuicConnectionHelper()),
initialized_(false),
supported_versions_(supported_versions),
packet_reader_started_(false),
weak_factory_(this) {}
@ -50,18 +48,11 @@ QuicSimpleClient::QuicSimpleClient(IPEndPoint server_address,
const QuicServerId& server_id,
const QuicVersionVector& supported_versions,
const QuicConfig& config)
: server_address_(server_address),
server_id_(server_id),
config_(config),
: QuicClientBase(server_id, supported_versions, config),
server_address_(server_address),
local_port_(0),
helper_(CreateQuicConnectionHelper()),
initialized_(false),
supported_versions_(supported_versions),
initial_max_packet_length_(0),
num_stateless_rejects_received_(0),
num_sent_client_hellos_(0),
connection_error_(QUIC_NO_ERROR),
connected_or_attempting_connect_(false),
packet_reader_started_(false),
weak_factory_(this) {}
@ -77,10 +68,7 @@ QuicSimpleClient::~QuicSimpleClient() {
bool QuicSimpleClient::Initialize() {
DCHECK(!initialized_);
num_sent_client_hellos_ = 0;
num_stateless_rejects_received_ = 0;
connection_error_ = QUIC_NO_ERROR;
connected_or_attempting_connect_ = false;
QuicClientBase::Initialize();
if (!CreateUDPSocket()) {
return false;
@ -90,17 +78,6 @@ bool QuicSimpleClient::Initialize() {
return true;
}
QuicSimpleClient::DummyPacketWriterFactory::DummyPacketWriterFactory(
QuicPacketWriter* writer)
: writer_(writer) {}
QuicSimpleClient::DummyPacketWriterFactory::~DummyPacketWriterFactory() {}
QuicPacketWriter* QuicSimpleClient::DummyPacketWriterFactory::Create(
QuicConnection* /*connection*/) const {
return writer_;
}
QuicSimpleClient::QuicDataToResend::QuicDataToResend(HttpRequestInfo* headers,
StringPiece body,
bool fin)
@ -193,8 +170,8 @@ bool QuicSimpleClient::Connect() {
}
STLDeleteElements(&data_to_resend_on_connect_);
}
if (session_.get() != nullptr &&
session_->error() != QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT) {
if (session() != nullptr &&
session()->error() != QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT) {
// We've successfully created a session but we're not connected, and there
// is no stateless reject to recover from. Give up trying.
break;
@ -202,63 +179,44 @@ bool QuicSimpleClient::Connect() {
}
if (!connected() &&
GetNumSentClientHellos() > QuicCryptoClientStream::kMaxClientHellos &&
session_ != nullptr &&
session_->error() == QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT) {
session() != nullptr &&
session()->error() == QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT) {
// The overall connection failed due too many stateless rejects.
connection_error_ = QUIC_CRYPTO_TOO_MANY_REJECTS;
set_connection_error(QUIC_CRYPTO_TOO_MANY_REJECTS);
}
return session_->connection()->connected();
}
QuicClientSession* QuicSimpleClient::CreateQuicClientSession(
const QuicConfig& config,
QuicConnection* connection,
const QuicServerId& server_id,
QuicCryptoClientConfig* crypto_config) {
return new QuicClientSession(config, connection, server_id_, &crypto_config_);
return session()->connection()->connected();
}
void QuicSimpleClient::StartConnect() {
DCHECK(initialized_);
DCHECK(!connected());
writer_.reset(CreateQuicPacketWriter());
set_writer(CreateQuicPacketWriter());
DummyPacketWriterFactory factory(writer_.get());
DummyPacketWriterFactory factory(writer());
if (connected_or_attempting_connect_) {
if (connected_or_attempting_connect()) {
// Before we destroy the last session and create a new one, gather its stats
// and update the stats for the overall connection.
num_sent_client_hellos_ += session_->GetNumSentClientHellos();
if (session_->error() == QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT) {
UpdateStats();
if (session()->error() == QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT) {
// If the last error was due to a stateless reject, queue up the data to
// be resent on the next successful connection.
// TODO(jokulik): I'm a little bit concerned about ordering here. Maybe
// we should just maintain one queue?
++num_stateless_rejects_received_;
DCHECK(data_to_resend_on_connect_.empty());
data_to_resend_on_connect_.swap(data_sent_before_handshake_);
}
}
session_.reset(CreateQuicClientSession(
config_,
new QuicConnection(GetNextConnectionId(), server_address_, helper_.get(),
factory,
/* owns_writer= */ false, Perspective::IS_CLIENT,
server_id_.is_https(), supported_versions_),
server_id_, &crypto_config_));
if (initial_max_packet_length_ != 0) {
session_->connection()->set_max_packet_length(initial_max_packet_length_);
}
session_->Initialize();
session_->CryptoConnect();
connected_or_attempting_connect_ = true;
}
CreateQuicClientSession(new QuicConnection(
GetNextConnectionId(), server_address_, helper_.get(), factory,
/* owns_writer= */ false, Perspective::IS_CLIENT, server_id().is_https(),
supported_versions()));
bool QuicSimpleClient::EncryptionBeingEstablished() {
return !session_->IsEncryptionEstablished() &&
session_->connection()->connected();
session()->Initialize();
session()->CryptoConnect();
set_connected_or_attempting_connect(true);
}
void QuicSimpleClient::Disconnect() {
@ -270,14 +228,14 @@ void QuicSimpleClient::Disconnect() {
STLDeleteElements(&data_to_resend_on_connect_);
STLDeleteElements(&data_sent_before_handshake_);
writer_.reset();
reset_writer();
packet_reader_.reset();
initialized_ = false;
}
void QuicSimpleClient::SendRequest(const HttpRequestInfo& headers,
base::StringPiece body,
StringPiece body,
bool fin) {
QuicSpdyClientStream* stream = CreateReliableClientStream();
if (stream == nullptr) {
@ -304,7 +262,7 @@ void QuicSimpleClient::SendRequest(const HttpRequestInfo& headers,
void QuicSimpleClient::MaybeAddQuicDataToResend(
QuicDataToResend* data_to_resend) {
DCHECK(FLAGS_enable_quic_stateless_reject_support);
if (session_->IsCryptoHandshakeConfirmed()) {
if (session()->IsCryptoHandshakeConfirmed()) {
// The handshake is confirmed. No need to continue saving requests to
// resend.
STLDeleteElements(&data_sent_before_handshake_);
@ -319,7 +277,7 @@ void QuicSimpleClient::MaybeAddQuicDataToResend(
void QuicSimpleClient::SendRequestAndWaitForResponse(
const HttpRequestInfo& request,
base::StringPiece body,
StringPiece body,
bool fin) {
SendRequest(request, body, fin);
while (WaitForEvents()) {}
@ -337,45 +295,21 @@ void QuicSimpleClient::SendRequestsAndWaitForResponse(
while (WaitForEvents()) {}
}
QuicSpdyClientStream* QuicSimpleClient::CreateReliableClientStream() {
if (!connected()) {
return nullptr;
}
return session_->CreateOutgoingDynamicStream();
}
void QuicSimpleClient::WaitForStreamToClose(QuicStreamId id) {
DCHECK(connected());
while (connected() && !session_->IsClosedStream(id)) {
WaitForEvents();
}
}
void QuicSimpleClient::WaitForCryptoHandshakeConfirmed() {
DCHECK(connected());
while (connected() && !session_->IsCryptoHandshakeConfirmed()) {
WaitForEvents();
}
}
bool QuicSimpleClient::WaitForEvents() {
DCHECK(connected());
base::RunLoop().RunUntilIdle();
DCHECK(session_ != nullptr);
DCHECK(session() != nullptr);
if (!connected() &&
session_->error() == QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT) {
session()->error() == QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT) {
DCHECK(FLAGS_enable_quic_stateless_reject_support);
DVLOG(1) << "Detected stateless reject while waiting for events. "
<< "Attempting to reconnect.";
Connect();
}
return session_->num_active_requests() != 0;
return session()->num_active_requests() != 0;
}
bool QuicSimpleClient::MigrateSocket(const IPAddressNumber& new_host) {
@ -388,14 +322,12 @@ bool QuicSimpleClient::MigrateSocket(const IPAddressNumber& new_host) {
return false;
}
session_->connection()->SetSelfAddress(client_address_);
session()->connection()->SetSelfAddress(client_address_);
QuicPacketWriter* writer = CreateQuicPacketWriter();
DummyPacketWriterFactory factory(writer);
if (writer_.get() != writer) {
writer_.reset(writer);
}
session_->connection()->SetQuicPacketWriter(writer, false);
set_writer(writer);
session()->connection()->SetQuicPacketWriter(writer, false);
return true;
}
@ -421,15 +353,6 @@ void QuicSimpleClient::OnClose(QuicDataStream* stream) {
}
}
bool QuicSimpleClient::connected() const {
return session_.get() && session_->connection() &&
session_->connection()->connected();
}
bool QuicSimpleClient::goaway_received() const {
return session_ != nullptr && session_->goaway_received();
}
size_t QuicSimpleClient::latest_response_code() const {
LOG_IF(DFATAL, !store_response_) << "Response not stored!";
return latest_response_code_;
@ -445,45 +368,6 @@ const string& QuicSimpleClient::latest_response_body() const {
return latest_response_body_;
}
int QuicSimpleClient::GetNumSentClientHellos() {
// If we are not actively attempting to connect, the session object
// corresponds to the previous connection and should not be used.
const int current_session_hellos = !connected_or_attempting_connect_
? 0
: session_->GetNumSentClientHellos();
return num_sent_client_hellos_ + current_session_hellos;
}
QuicErrorCode QuicSimpleClient::connection_error() const {
// Return the high-level error if there was one. Otherwise, return the
// connection error from the last session.
if (connection_error_ != QUIC_NO_ERROR) {
return connection_error_;
}
if (session_.get() == nullptr) {
return QUIC_NO_ERROR;
}
return session_->error();
}
QuicConnectionId QuicSimpleClient::GetNextConnectionId() {
QuicConnectionId server_designated_id = GetNextServerDesignatedConnectionId();
return server_designated_id ? server_designated_id
: GenerateNewConnectionId();
}
QuicConnectionId QuicSimpleClient::GetNextServerDesignatedConnectionId() {
QuicCryptoClientConfig::CachedState* cached =
crypto_config_.LookupOrCreate(server_id_);
// If the cached state indicates that we should use a server-designated
// connection ID, then return that connection ID.
CHECK(cached != nullptr) << "QuicClientCryptoConfig::LookupOrCreate returned "
<< "unexpected nullptr.";
return cached->has_server_designated_connection_id()
? cached->GetNextServerDesignatedConnectionId()
: 0;
}
QuicConnectionId QuicSimpleClient::GenerateNewConnectionId() {
return helper_->GetRandomGenerator()->RandUint64();
}
@ -505,8 +389,9 @@ void QuicSimpleClient::OnReadError(int result) {
bool QuicSimpleClient::OnPacket(const QuicEncryptedPacket& packet,
IPEndPoint local_address,
IPEndPoint peer_address) {
session_->connection()->ProcessUdpPacket(local_address, peer_address, packet);
if (!session_->connection()->connected()) {
session()->connection()->ProcessUdpPacket(local_address, peer_address,
packet);
if (!session()->connection()->connected()) {
return false;
}

@ -14,23 +14,17 @@
#include "base/command_line.h"
#include "base/memory/scoped_ptr.h"
#include "base/strings/string_piece.h"
#include "net/base/io_buffer.h"
#include "net/base/ip_endpoint.h"
#include "net/http/http_response_headers.h"
#include "net/log/net_log.h"
#include "net/quic/crypto/crypto_handshake.h"
#include "net/quic/quic_config.h"
#include "net/quic/quic_framer.h"
#include "net/quic/quic_packet_creator.h"
#include "net/quic/quic_data_stream.h"
#include "net/quic/quic_packet_reader.h"
#include "net/tools/quic/quic_client_session.h"
#include "net/tools/quic/quic_spdy_client_stream.h"
#include "net/tools/quic/quic_client_base.h"
namespace net {
struct HttpRequestInfo;
class ProofVerifier;
class QuicServerId;
class QuicConnectionHelper;
class UDPClientSocket;
@ -40,7 +34,8 @@ namespace test {
class QuicClientPeer;
} // namespace test
class QuicSimpleClient : public QuicDataStream::Visitor,
class QuicSimpleClient : public QuicClientBase,
public QuicDataStream::Visitor,
public QuicPacketReader::Visitor {
public:
class ResponseListener {
@ -94,10 +89,10 @@ class QuicSimpleClient : public QuicDataStream::Visitor,
~QuicSimpleClient() override;
// Initializes the client to create a connection. Should be called exactly
// once before calling StartConnect or Connect. Returns true if the
// initialization succeeds, false otherwise.
bool Initialize();
// From QuicClientBase
bool Initialize() override;
bool WaitForEvents() override;
QuicConnectionId GenerateNewConnectionId() override;
// "Connect" to the QUIC server, including performing synchronous crypto
// handshake.
@ -108,11 +103,6 @@ class QuicSimpleClient : public QuicDataStream::Visitor,
// completes.
void StartConnect();
// Returns true if the crypto handshake has yet to establish encryption.
// Returns false if encryption is active (even if the server hasn't confirmed
// the handshake) or if the connection has been closed.
bool EncryptionBeingEstablished();
// Disconnects from the QUIC server.
void Disconnect();
@ -131,20 +121,6 @@ class QuicSimpleClient : public QuicDataStream::Visitor,
void SendRequestsAndWaitForResponse(
const base::CommandLine::StringVector& url_list);
// Returns a newly created QuicSpdyClientStream, owned by the
// QuicSimpleClient.
QuicSpdyClientStream* CreateReliableClientStream();
// Wait for events until the stream with the given ID is closed.
void WaitForStreamToClose(QuicStreamId id);
// Wait for events until the handshake is confirmed.
void WaitForCryptoHandshakeConfirmed();
// Wait up to 50ms, and handle any events which occur.
// Returns true if there are any outstanding requests.
bool WaitForEvents();
// Migrate to a new socket during an active connection.
bool MigrateSocket(const IPAddressNumber& new_host);
@ -162,11 +138,6 @@ class QuicSimpleClient : public QuicDataStream::Visitor,
// Otherwise, deletes the data. Takes ownerership of |data_to_resend|.
void MaybeAddQuicDataToResend(QuicDataToResend* data_to_resend);
QuicClientSession* session() { return session_.get(); }
bool connected() const;
bool goaway_received() const;
void set_bind_to_address(IPAddressNumber address) {
bind_to_address_ = address;
}
@ -179,111 +150,24 @@ class QuicSimpleClient : public QuicDataStream::Visitor,
const IPEndPoint& client_address() const { return client_address_; }
const QuicServerId& server_id() const { return server_id_; }
// This should only be set before the initial Connect()
void set_server_id(const QuicServerId& server_id) {
server_id_ = server_id;
}
void SetUserAgentID(const std::string& user_agent_id) {
crypto_config_.set_user_agent_id(user_agent_id);
}
// SetProofVerifier sets the ProofVerifier that will be used to verify the
// server's certificate and takes ownership of |verifier|.
void SetProofVerifier(ProofVerifier* verifier) {
// TODO(rtenneti): We should set ProofVerifier in QuicClientSession.
crypto_config_.SetProofVerifier(verifier);
}
// SetChannelIDSource sets a ChannelIDSource that will be called, when the
// server supports channel IDs, to obtain a channel ID for signing a message
// proving possession of the channel ID. This object takes ownership of
// |source|.
void SetChannelIDSource(ChannelIDSource* source) {
crypto_config_.SetChannelIDSource(source);
}
void SetSupportedVersions(const QuicVersionVector& versions) {
supported_versions_ = versions;
}
// Takes ownership of the listener.
void set_response_listener(ResponseListener* listener) {
response_listener_.reset(listener);
}
QuicConfig* config() { return &config_; }
void set_store_response(bool val) { store_response_ = val; }
size_t latest_response_code() const;
const std::string& latest_response_headers() const;
const std::string& latest_response_body() const;
// Change the initial maximum packet size of the connection. Has to be called
// before Connect()/StartConnect() in order to have any effect.
void set_initial_max_packet_length(QuicByteCount initial_max_packet_length) {
initial_max_packet_length_ = initial_max_packet_length;
}
int num_stateless_rejects_received() const {
return num_stateless_rejects_received_;
}
// The number of client hellos sent, taking stateless rejects into
// account. In the case of a stateless reject, the initial
// connection object may be torn down and a new one created. The
// user cannot rely upon the latest connection object to get the
// total number of client hellos sent, and should use this function
// instead.
int GetNumSentClientHellos();
// Returns any errors that occurred at the connection-level (as
// opposed to the session-level). When a stateless reject occurs,
// the error of the last session may not reflect the overall state
// of the connection.
QuicErrorCode connection_error() const;
protected:
// Generates the next ConnectionId for |server_id_|. By default, if the
// cached server config contains a server-designated ID, that ID will be
// returned. Otherwise, the next random ID will be returned.
QuicConnectionId GetNextConnectionId();
// Returns the next server-designated ConnectionId from the cached config for
// |server_id_|, if it exists. Otherwise, returns 0.
QuicConnectionId GetNextServerDesignatedConnectionId();
// Generates a new, random connection ID (as opposed to a server-designated
// connection ID).
virtual QuicConnectionId GenerateNewConnectionId();
virtual QuicConnectionHelper* CreateQuicConnectionHelper();
virtual QuicPacketWriter* CreateQuicPacketWriter();
virtual QuicClientSession* CreateQuicClientSession(
const QuicConfig& config,
QuicConnection* connection,
const QuicServerId& server_id,
QuicCryptoClientConfig* crypto_config);
private:
friend class net::tools::test::QuicClientPeer;
// A packet writer factory that always returns the same writer
class DummyPacketWriterFactory : public QuicConnection::PacketWriterFactory {
public:
DummyPacketWriterFactory(QuicPacketWriter* writer);
~DummyPacketWriterFactory() override;
QuicPacketWriter* Create(QuicConnection* connection) const override;
private:
QuicPacketWriter* writer_;
};
// Specific QuicClient class for storing data to resend.
class ClientQuicDataToResend : public QuicDataToResend {
public:
@ -322,14 +206,6 @@ class QuicSimpleClient : public QuicDataStream::Visitor,
// Address of the server.
const IPEndPoint server_address_;
// |server_id_| is a tuple (hostname, port, is_https) of the server.
QuicServerId server_id_;
// config_ and crypto_config_ contain configuration and cached state about
// servers.
QuicConfig config_;
QuicCryptoClientConfig crypto_config_;
// Address of the client if the client is connected to the server.
IPEndPoint client_address_;
@ -338,19 +214,9 @@ class QuicSimpleClient : public QuicDataStream::Visitor,
// Local port to bind to. Initialize to 0.
int local_port_;
// Writer used to actually send packets to the wire. Needs to outlive
// |session_|.
scoped_ptr<QuicPacketWriter> writer_;
// Session which manages streams.
scoped_ptr<QuicClientSession> session_;
// UDP socket connected to the server.
scoped_ptr<UDPClientSocket> socket_;
// Connection on the socket. Owned by |session_|.
QuicConnection* connection_;
// Helper to be used by created connections.
scoped_ptr<QuicConnectionHelper> helper_;
@ -368,13 +234,6 @@ class QuicSimpleClient : public QuicDataStream::Visitor,
// because the socket would otherwise overflow.
bool overflow_supported_;
// This vector contains QUIC versions which we currently support.
// This should be ordered such that the highest supported version is the first
// element, with subsequent elements in descending order (versions can be
// skipped as necessary). We will always pick supported_versions_[0] as the
// initial version to use.
QuicVersionVector supported_versions_;
// If true, store the latest response code, headers, and body.
bool store_response_;
// HTTP response code from most recent response.
@ -384,31 +243,6 @@ class QuicSimpleClient : public QuicDataStream::Visitor,
// Body of most recent response.
std::string latest_response_body_;
// The initial value of maximum packet size of the connection. If set to
// zero, the default is used.
QuicByteCount initial_max_packet_length_;
// The number of stateless rejects received during the current/latest
// connection.
// TODO(jokulik): Consider some consistent naming scheme (or other) for member
// variables that are kept per-request, per-connection, and over the client's
// lifetime.
int num_stateless_rejects_received_;
// The number of hellos sent during the current/latest connection.
int num_sent_client_hellos_;
// Used to store any errors that occurred with the overall connection (as
// opposed to that associated with the last session object).
QuicErrorCode connection_error_;
// True when the client is attempting to connect or re-connect the session (in
// the case of a stateless reject). Set to false between a call to
// Disconnect() and the subsequent call to StartConnect(). When
// connected_or_attempting_connect_ is false, the session object corresponds
// to the previous client-level connection.
bool connected_or_attempting_connect_;
// Keeps track of any data sent before the handshake.
std::vector<QuicDataToResend*> data_sent_before_handshake_;

@ -10,11 +10,6 @@ namespace net {
namespace tools {
namespace test {
// static
QuicCryptoClientConfig* QuicClientPeer::GetCryptoConfig(QuicClient* client) {
return &client->crypto_config_;
}
// static
bool QuicClientPeer::CreateUDPSocket(QuicClient* client) {
return client->CreateUDPSocket();
@ -25,11 +20,6 @@ void QuicClientPeer::SetClientPort(QuicClient* client, int port) {
client->client_address_ = IPEndPoint(client->client_address_.address(), port);
}
// static
void QuicClientPeer::SetWriter(QuicClient* client, QuicPacketWriter* writer) {
client->writer_.reset(writer);
}
} // namespace test
} // namespace tools
} // namespace net

@ -20,10 +20,8 @@ namespace test {
class QuicClientPeer {
public:
static QuicCryptoClientConfig* GetCryptoConfig(QuicClient* client);
static bool CreateUDPSocket(QuicClient* client);
static void SetClientPort(QuicClient* client, int port);
static void SetWriter(QuicClient* client, QuicPacketWriter* writer);
private:
DISALLOW_COPY_AND_ASSIGN(QuicClientPeer);

@ -410,8 +410,7 @@ const string& QuicTestClient::cert_common_name() const {
}
QuicTagValueMap QuicTestClient::GetServerConfig() const {
QuicCryptoClientConfig* config =
QuicClientPeer::GetCryptoConfig(client_.get());
QuicCryptoClientConfig* config = client_->crypto_config();
QuicCryptoClientConfig::CachedState* state =
config->LookupOrCreate(client_->server_id());
const CryptoHandshakeMessage* handshake_msg = state->GetServerConfig();