blob: 74fa541d4a832b4264734f1d37ced8c0589c71b5 [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/tools/quic/quic_simple_client.h"
#include "base/logging.h"
#include "base/run_loop.h"
#include "net/base/net_errors.h"
#include "net/http/http_request_info.h"
#include "net/quic/crypto/quic_random.h"
#include "net/quic/quic_connection.h"
#include "net/quic/quic_connection_helper.h"
#include "net/quic/quic_default_packet_writer.h"
#include "net/quic/quic_protocol.h"
#include "net/quic/quic_server_id.h"
#include "net/udp/udp_client_socket.h"
using std::string;
using std::vector;
namespace net {
namespace tools {
namespace {
// Allocate some extra space so we can send an error if the server goes over
// the limit.
const int kReadBufferSize = 2 * kMaxPacketSize;
} // namespace
QuicSimpleClient::QuicSimpleClient(IPEndPoint server_address,
const QuicServerId& server_id,
const QuicVersionVector& supported_versions)
: server_address_(server_address),
server_id_(server_id),
local_port_(0),
helper_(CreateQuicConnectionHelper()),
initialized_(false),
supported_versions_(supported_versions),
read_pending_(false),
synchronous_read_count_(0),
read_buffer_(new IOBufferWithSize(kReadBufferSize)),
weak_factory_(this) {
}
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),
local_port_(0),
helper_(CreateQuicConnectionHelper()),
initialized_(false),
supported_versions_(supported_versions),
read_pending_(false),
synchronous_read_count_(0),
read_buffer_(new IOBufferWithSize(kReadBufferSize)),
weak_factory_(this) {
}
QuicSimpleClient::~QuicSimpleClient() {
if (connected()) {
session()->connection()->SendConnectionClosePacket(
QUIC_PEER_GOING_AWAY, "");
}
}
bool QuicSimpleClient::Initialize() {
DCHECK(!initialized_);
if (!CreateUDPSocket()) {
return false;
}
initialized_ = true;
return true;
}
QuicSimpleClient::DummyPacketWriterFactory::DummyPacketWriterFactory(
QuicPacketWriter* writer)
: writer_(writer) {}
QuicSimpleClient::DummyPacketWriterFactory::~DummyPacketWriterFactory() {}
QuicPacketWriter* QuicSimpleClient::DummyPacketWriterFactory::Create(
QuicConnection* /*connection*/) const {
return writer_;
}
bool QuicSimpleClient::CreateUDPSocket() {
scoped_ptr<UDPClientSocket> socket(
new UDPClientSocket(DatagramSocket::DEFAULT_BIND,
RandIntCallback(),
&net_log_,
NetLog::Source()));
int address_family = server_address_.GetSockAddrFamily();
if (bind_to_address_.size() != 0) {
client_address_ = IPEndPoint(bind_to_address_, local_port_);
} else if (address_family == AF_INET) {
IPAddressNumber any4;
CHECK(net::ParseIPLiteralToNumber("0.0.0.0", &any4));
client_address_ = IPEndPoint(any4, local_port_);
} else {
IPAddressNumber any6;
CHECK(net::ParseIPLiteralToNumber("::", &any6));
client_address_ = IPEndPoint(any6, local_port_);
}
int rc = socket->Connect(server_address_);
if (rc != OK) {
LOG(ERROR) << "Connect failed: " << ErrorToString(rc);
return false;
}
rc = socket->SetReceiveBufferSize(kDefaultSocketReceiveBuffer);
if (rc != OK) {
LOG(ERROR) << "SetReceiveBufferSize() failed: " << ErrorToString(rc);
return false;
}
rc = socket->SetSendBufferSize(kDefaultSocketReceiveBuffer);
if (rc != OK) {
LOG(ERROR) << "SetSendBufferSize() failed: " << ErrorToString(rc);
return false;
}
rc = socket->GetLocalAddress(&client_address_);
if (rc != OK) {
LOG(ERROR) << "GetLocalAddress failed: " << ErrorToString(rc);
return false;
}
socket_.swap(socket);
read_pending_ = false;
if (socket != nullptr) {
socket->Close();
}
return true;
}
bool QuicSimpleClient::Connect() {
StartConnect();
StartReading();
while (EncryptionBeingEstablished()) {
WaitForEvents();
}
return session_->connection()->connected();
}
void QuicSimpleClient::StartConnect() {
DCHECK(initialized_);
DCHECK(!connected());
writer_.reset(CreateQuicPacketWriter());
connection_ = new QuicConnection(GenerateConnectionId(),
server_address_,
helper_.get(),
DummyPacketWriterFactory(writer_.get()),
/* owns_writer= */ false,
Perspective::IS_CLIENT,
server_id_.is_https(),
supported_versions_);
session_.reset(new QuicSimpleClientSession(config_, connection_));
session_->InitializeSession(server_id_, &crypto_config_);
session_->CryptoConnect();
}
bool QuicSimpleClient::EncryptionBeingEstablished() {
return !session_->IsEncryptionEstablished() &&
session_->connection()->connected();
}
void QuicSimpleClient::Disconnect() {
DCHECK(initialized_);
if (connected()) {
session()->connection()->SendConnectionClose(QUIC_PEER_GOING_AWAY);
}
writer_.reset();
read_pending_ = false;
initialized_ = false;
}
void QuicSimpleClient::SendRequest(const HttpRequestInfo& headers,
base::StringPiece body,
bool fin) {
QuicSimpleClientStream* stream = CreateReliableClientStream();
if (stream == nullptr) {
LOG(DFATAL) << "stream creation failed!";
return;
}
stream->SendRequest(headers, body, fin);
stream->set_visitor(this);
}
void QuicSimpleClient::SendRequestAndWaitForResponse(
const HttpRequestInfo& request,
base::StringPiece body,
bool fin) {
SendRequest(request, body, fin);
while (WaitForEvents()) {}
}
void QuicSimpleClient::SendRequestsAndWaitForResponse(
const base::CommandLine::StringVector& url_list) {
for (size_t i = 0; i < url_list.size(); ++i) {
HttpRequestInfo request;
request.method = "GET";
request.url = GURL(url_list[i]);
SendRequest(request, "", true);
}
while (WaitForEvents()) {}
}
QuicSimpleClientStream* QuicSimpleClient::CreateReliableClientStream() {
if (!connected()) {
return nullptr;
}
return session_->CreateOutgoingDataStream();
}
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();
return session_->num_active_requests() != 0;
}
void QuicSimpleClient::OnClose(QuicDataStream* stream) {
QuicSimpleClientStream* client_stream =
static_cast<QuicSimpleClientStream*>(stream);
if (response_listener_.get() != nullptr) {
response_listener_->OnCompleteResponse(
stream->id(), *client_stream->headers(), client_stream->data());
}
// Store response headers and body.
if (store_response_) {
latest_response_code_ = client_stream->headers()->response_code();
client_stream->headers()->GetNormalizedHeaders(&latest_response_headers_);
latest_response_body_ = client_stream->data();
}
}
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_;
}
const string& QuicSimpleClient::latest_response_headers() const {
LOG_IF(DFATAL, !store_response_) << "Response not stored!";
return latest_response_headers_;
}
const string& QuicSimpleClient::latest_response_body() const {
LOG_IF(DFATAL, !store_response_) << "Response not stored!";
return latest_response_body_;
}
QuicConnectionId QuicSimpleClient::GenerateConnectionId() {
return helper_->GetRandomGenerator()->RandUint64();
}
QuicConnectionHelper* QuicSimpleClient::CreateQuicConnectionHelper() {
return new QuicConnectionHelper(
base::MessageLoop::current()->message_loop_proxy().get(),
&clock_,
QuicRandom::GetInstance());
}
QuicPacketWriter* QuicSimpleClient::CreateQuicPacketWriter() {
return new QuicDefaultPacketWriter(socket_.get());
}
void QuicSimpleClient::StartReading() {
if (read_pending_) {
return;
}
read_pending_ = true;
int result = socket_->Read(
read_buffer_.get(),
read_buffer_->size(),
base::Bind(&QuicSimpleClient::OnReadComplete,
weak_factory_.GetWeakPtr()));
if (result == ERR_IO_PENDING) {
synchronous_read_count_ = 0;
return;
}
if (++synchronous_read_count_ > 32) {
synchronous_read_count_ = 0;
// Schedule the processing through the message loop to 1) prevent infinite
// recursion and 2) avoid blocking the thread for too long.
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&QuicSimpleClient::OnReadComplete,
weak_factory_.GetWeakPtr(), result));
} else {
OnReadComplete(result);
}
}
void QuicSimpleClient::OnReadComplete(int result) {
read_pending_ = false;
if (result == 0)
result = ERR_CONNECTION_CLOSED;
if (result < 0) {
LOG(ERROR) << "QuicSimpleClient read failed: " << ErrorToString(result);
Disconnect();
return;
}
QuicEncryptedPacket packet(read_buffer_->data(), result);
IPEndPoint local_address;
IPEndPoint peer_address;
socket_->GetLocalAddress(&local_address);
socket_->GetPeerAddress(&peer_address);
session_->connection()->ProcessUdpPacket(local_address, peer_address, packet);
if (!session_->connection()->connected()) {
return;
}
StartReading();
}
} // namespace tools
} // namespace net