blob: be40b39e3cef2571d4d7617f59930ef1d8fb8615 [file] [log] [blame]
// 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 "base/callback_helpers.h"
#include "base/message_loop.h"
#include "base/metrics/histogram.h"
#include "base/stl_util.h"
#include "base/string_number_conversions.h"
#include "base/values.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
#include "net/quic/quic_connection_helper.h"
#include "net/quic/quic_crypto_client_stream_factory.h"
#include "net/quic/quic_stream_factory.h"
#include "net/udp/datagram_client_socket.h"
namespace net {
namespace {
// Note: these values must be kept in sync with the corresponding values in:
// tools/metrics/histograms/histograms.xml
enum HandshakeState {
STATE_STARTED = 0,
STATE_ENCRYPTION_ESTABLISHED = 1,
STATE_HANDSHAKE_CONFIRMED = 2,
STATE_FAILED = 3,
NUM_HANDSHAKE_STATES = 4
};
void RecordHandshakeState(HandshakeState state) {
UMA_HISTOGRAM_ENUMERATION("Net.QuicHandshakeState", state,
NUM_HANDSHAKE_STATES);
}
} // namespace
QuicClientSession::QuicClientSession(
QuicConnection* connection,
DatagramClientSocket* socket,
QuicStreamFactory* stream_factory,
QuicCryptoClientStreamFactory* crypto_client_stream_factory,
const string& server_hostname,
const QuicConfig& config,
QuicCryptoClientConfig* crypto_config,
NetLog* net_log)
: QuicSession(connection, config, false),
weak_factory_(this),
stream_factory_(stream_factory),
socket_(socket),
read_buffer_(new IOBufferWithSize(kMaxPacketSize)),
read_pending_(false),
num_total_streams_(0),
net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_QUIC_SESSION)),
logger_(net_log_) {
crypto_stream_.reset(
crypto_client_stream_factory ?
crypto_client_stream_factory->CreateQuicCryptoClientStream(
server_hostname, this, crypto_config) :
new QuicCryptoClientStream(server_hostname, this, crypto_config));
connection->set_debug_visitor(&logger_);
// TODO(rch): pass in full host port proxy pair
net_log_.BeginEvent(
NetLog::TYPE_QUIC_SESSION,
NetLog::StringCallback("host", &server_hostname));
}
QuicClientSession::~QuicClientSession() {
DCHECK(callback_.is_null());
connection()->set_debug_visitor(NULL);
net_log_.EndEvent(NetLog::TYPE_QUIC_SESSION);
if (IsEncryptionEstablished())
RecordHandshakeState(STATE_ENCRYPTION_ESTABLISHED);
if (IsCryptoHandshakeConfirmed())
RecordHandshakeState(STATE_HANDSHAKE_CONFIRMED);
else
RecordHandshakeState(STATE_FAILED);
UMA_HISTOGRAM_COUNTS("Net.QuicNumSentClientHellos",
crypto_stream_->num_sent_client_hellos());
if (IsCryptoHandshakeConfirmed()) {
UMA_HISTOGRAM_COUNTS("Net.QuicNumSentClientHellosCryptoHandshakeConfirmed",
crypto_stream_->num_sent_client_hellos());
}
}
QuicReliableClientStream* QuicClientSession::CreateOutgoingReliableStream() {
if (!crypto_stream_->encryption_established()) {
DLOG(INFO) << "Encryption not active so no outgoing stream created.";
return NULL;
}
if (GetNumOpenStreams() >= get_max_open_streams()) {
DLOG(INFO) << "Failed to create a new outgoing stream. "
<< "Already " << GetNumOpenStreams() << " open.";
return NULL;
}
if (goaway_received()) {
DLOG(INFO) << "Failed to create a new outgoing stream. "
<< "Already received goaway.";
return NULL;
}
QuicReliableClientStream* stream =
new QuicReliableClientStream(GetNextStreamId(), this, net_log_);
ActivateStream(stream);
++num_total_streams_;
return stream;
}
QuicCryptoClientStream* QuicClientSession::GetCryptoStream() {
return crypto_stream_.get();
};
int QuicClientSession::CryptoConnect(const CompletionCallback& callback) {
RecordHandshakeState(STATE_STARTED);
if (!crypto_stream_->CryptoConnect()) {
// TODO(wtc): change crypto_stream_.CryptoConnect() to return a
// QuicErrorCode and map it to a net error code.
return ERR_CONNECTION_FAILED;
}
if (IsEncryptionEstablished()) {
return OK;
}
callback_ = callback;
return ERR_IO_PENDING;
}
ReliableQuicStream* QuicClientSession::CreateIncomingReliableStream(
QuicStreamId id) {
DLOG(ERROR) << "Server push not supported";
return NULL;
}
void QuicClientSession::CloseStream(QuicStreamId stream_id) {
QuicSession::CloseStream(stream_id);
if (GetNumOpenStreams() == 0) {
stream_factory_->OnIdleSession(this);
}
}
void QuicClientSession::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) {
if (!callback_.is_null()) {
// TODO(rtenneti): Currently for all CryptoHandshakeEvent events, callback_
// could be called because there are no error events in CryptoHandshakeEvent
// enum. If error events are added to CryptoHandshakeEvent, then the
// following code needs to changed.
base::ResetAndReturn(&callback_).Run(OK);
}
}
void QuicClientSession::ConnectionClose(QuicErrorCode error, bool from_peer) {
if (!callback_.is_null()) {
base::ResetAndReturn(&callback_).Run(ERR_QUIC_PROTOCOL_ERROR);
}
QuicSession::ConnectionClose(error, from_peer);
}
void QuicClientSession::StartReading() {
if (read_pending_) {
return;
}
read_pending_ = true;
int rv = socket_->Read(read_buffer_.get(),
read_buffer_->size(),
base::Bind(&QuicClientSession::OnReadComplete,
weak_factory_.GetWeakPtr()));
if (rv == ERR_IO_PENDING) {
return;
}
// Data was read, process it.
// Schedule the work through the message loop to avoid recursive
// callbacks.
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&QuicClientSession::OnReadComplete,
weak_factory_.GetWeakPtr(), rv));
}
void QuicClientSession::CloseSessionOnError(int error) {
if (!callback_.is_null()) {
base::ResetAndReturn(&callback_).Run(error);
}
while (!streams()->empty()) {
ReliableQuicStream* stream = streams()->begin()->second;
QuicStreamId id = stream->id();
static_cast<QuicReliableClientStream*>(stream)->OnError(error);
CloseStream(id);
}
net_log_.BeginEvent(
NetLog::TYPE_QUIC_SESSION,
NetLog::IntegerCallback("net_error", error));
// Will delete |this|.
stream_factory_->OnSessionClose(this);
}
Value* QuicClientSession::GetInfoAsValue(const HostPortPair& pair) const {
DictionaryValue* dict = new DictionaryValue();
dict->SetString("host_port_pair", pair.ToString());
dict->SetInteger("open_streams", GetNumOpenStreams());
dict->SetInteger("total_streams", num_total_streams_);
dict->SetString("peer_address", peer_address().ToString());
dict->SetString("guid", base::Uint64ToString(guid()));
return dict;
}
void QuicClientSession::OnReadComplete(int result) {
read_pending_ = false;
if (result == 0)
result = ERR_CONNECTION_CLOSED;
if (result < 0) {
DLOG(INFO) << "Closing session on read error: " << result;
CloseSessionOnError(result);
return;
}
scoped_refptr<IOBufferWithSize> buffer(read_buffer_);
read_buffer_ = new IOBufferWithSize(kMaxPacketSize);
QuicEncryptedPacket packet(buffer->data(), result);
IPEndPoint local_address;
IPEndPoint peer_address;
socket_->GetLocalAddress(&local_address);
socket_->GetPeerAddress(&peer_address);
// ProcessUdpPacket might result in |this| being deleted, so we
// use a weak pointer to be safe.
connection()->ProcessUdpPacket(local_address, peer_address, packet);
if (!connection()->connected()) {
stream_factory_->OnSessionClose(this);
return;
}
StartReading();
}
} // namespace net