// 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_client.h" #include #include #include #include #include #include #include "base/logging.h" #include "net/quic/congestion_control/tcp_receiver.h" #include "net/quic/crypto/quic_random.h" #include "net/quic/quic_connection.h" #include "net/quic/quic_data_reader.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" #ifndef SO_RXQ_OVFL #define SO_RXQ_OVFL 40 #endif namespace net { namespace tools { const int kEpollFlags = EPOLLIN | EPOLLOUT | EPOLLET; QuicClient::QuicClient(IPEndPoint server_address, const QuicServerId& server_id, const QuicVersionVector& supported_versions, bool print_response, EpollServer* epoll_server) : server_address_(server_address), server_id_(server_id), local_port_(0), epoll_server_(epoll_server), fd_(-1), helper_(CreateQuicConnectionHelper()), initialized_(false), packets_dropped_(0), overflow_supported_(false), supported_versions_(supported_versions), print_response_(print_response) { } QuicClient::QuicClient(IPEndPoint server_address, const QuicServerId& server_id, const QuicVersionVector& supported_versions, bool print_response, const QuicConfig& config, EpollServer* epoll_server) : server_address_(server_address), server_id_(server_id), config_(config), local_port_(0), epoll_server_(epoll_server), fd_(-1), helper_(CreateQuicConnectionHelper()), initialized_(false), packets_dropped_(0), overflow_supported_(false), supported_versions_(supported_versions), print_response_(print_response) { } QuicClient::~QuicClient() { if (connected()) { session()->connection()->SendConnectionClosePacket( QUIC_PEER_GOING_AWAY, ""); } if (fd_ > 0) { epoll_server_->UnregisterFD(fd_); } } bool QuicClient::Initialize() { DCHECK(!initialized_); epoll_server_->set_timeout_in_us(50 * 1000); if (!CreateUDPSocket()) { return false; } epoll_server_->RegisterFD(fd_, this, kEpollFlags); initialized_ = true; return true; } QuicClient::DummyPacketWriterFactory::DummyPacketWriterFactory( QuicPacketWriter* writer) : writer_(writer) {} QuicClient::DummyPacketWriterFactory::~DummyPacketWriterFactory() {} QuicPacketWriter* QuicClient::DummyPacketWriterFactory::Create( QuicConnection* /*connection*/) const { return writer_; } bool QuicClient::CreateUDPSocket() { int address_family = server_address_.GetSockAddrFamily(); fd_ = socket(address_family, SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP); if (fd_ < 0) { LOG(ERROR) << "CreateSocket() failed: " << strerror(errno); return false; } int get_overflow = 1; int rc = setsockopt(fd_, SOL_SOCKET, SO_RXQ_OVFL, &get_overflow, sizeof(get_overflow)); if (rc < 0) { DLOG(WARNING) << "Socket overflow detection not supported"; } else { overflow_supported_ = true; } if (!QuicSocketUtils::SetReceiveBufferSize(fd_, TcpReceiver::kReceiveWindowTCP)) { return false; } if (!QuicSocketUtils::SetSendBufferSize(fd_, TcpReceiver::kReceiveWindowTCP)) { return false; } rc = QuicSocketUtils::SetGetAddressInfo(fd_, address_family); if (rc < 0) { LOG(ERROR) << "IP detection not supported" << strerror(errno); return false; } 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_); } sockaddr_storage raw_addr; socklen_t raw_addr_len = sizeof(raw_addr); CHECK(client_address_.ToSockAddr(reinterpret_cast(&raw_addr), &raw_addr_len)); rc = bind(fd_, reinterpret_cast(&raw_addr), sizeof(raw_addr)); if (rc < 0) { LOG(ERROR) << "Bind failed: " << strerror(errno); return false; } SockaddrStorage storage; if (getsockname(fd_, storage.addr, &storage.addr_len) != 0 || !client_address_.FromSockAddr(storage.addr, storage.addr_len)) { LOG(ERROR) << "Unable to get self address. Error: " << strerror(errno); } return true; } bool QuicClient::Connect() { if (!StartConnect()) { return false; } while (EncryptionBeingEstablished()) { WaitForEvents(); } return session_->connection()->connected(); } bool QuicClient::StartConnect() { DCHECK(initialized_); DCHECK(!connected()); QuicPacketWriter* writer = CreateQuicPacketWriter(); DummyPacketWriterFactory factory(writer); session_.reset(new QuicClientSession( config_, new QuicConnection(GenerateConnectionId(), server_address_, helper_.get(), factory, /* owns_writer= */ false, /* is_server= */ false, supported_versions_), server_id_.is_https())); // Reset |writer_| after |session_| so that the old writer outlives the old // session. if (writer_.get() != writer) { writer_.reset(writer); } session_->InitializeSession(server_id_, &crypto_config_); return session_->CryptoConnect(); } bool QuicClient::EncryptionBeingEstablished() { return !session_->IsEncryptionEstablished() && session_->connection()->connected(); } void QuicClient::Disconnect() { DCHECK(initialized_); if (connected()) { session()->connection()->SendConnectionClose(QUIC_PEER_GOING_AWAY); } epoll_server_->UnregisterFD(fd_); close(fd_); fd_ = -1; initialized_ = false; } void QuicClient::SendRequestsAndWaitForResponse( const base::CommandLine::StringVector& args) { for (size_t i = 0; i < args.size(); ++i) { BalsaHeaders headers; headers.SetRequestFirstlineFromStringPieces("GET", args[i], "HTTP/1.1"); QuicSpdyClientStream* stream = CreateReliableClientStream(); DCHECK(stream != nullptr); stream->SendRequest(headers, "", true); stream->set_visitor(this); } while (WaitForEvents()) {} } QuicSpdyClientStream* QuicClient::CreateReliableClientStream() { if (!connected()) { return nullptr; } return session_->CreateOutgoingDataStream(); } void QuicClient::WaitForStreamToClose(QuicStreamId id) { DCHECK(connected()); while (connected() && !session_->IsClosedStream(id)) { epoll_server_->WaitForEventsAndExecuteCallbacks(); } } void QuicClient::WaitForCryptoHandshakeConfirmed() { DCHECK(connected()); while (connected() && !session_->IsCryptoHandshakeConfirmed()) { epoll_server_->WaitForEventsAndExecuteCallbacks(); } } bool QuicClient::WaitForEvents() { DCHECK(connected()); epoll_server_->WaitForEventsAndExecuteCallbacks(); return session_->num_active_requests() != 0; } void QuicClient::OnEvent(int fd, EpollEvent* event) { DCHECK_EQ(fd, fd_); if (event->in_events & EPOLLIN) { while (connected() && ReadAndProcessPacket()) { } } if (connected() && (event->in_events & EPOLLOUT)) { writer_->SetWritable(); session_->connection()->OnCanWrite(); } if (event->in_events & EPOLLERR) { DVLOG(1) << "Epollerr"; } } void QuicClient::OnClose(QuicDataStream* stream) { QuicSpdyClientStream* client_stream = static_cast(stream); if (response_listener_.get() != nullptr) { response_listener_->OnCompleteResponse( stream->id(), client_stream->headers(), client_stream->data()); } if (!print_response_) { return; } const BalsaHeaders& headers = client_stream->headers(); printf("%s\n", headers.first_line().as_string().c_str()); for (BalsaHeaders::const_header_lines_iterator i = headers.header_lines_begin(); i != headers.header_lines_end(); ++i) { printf("%s: %s\n", i->first.as_string().c_str(), i->second.as_string().c_str()); } printf("%s\n", client_stream->data().c_str()); } bool QuicClient::connected() const { return session_.get() && session_->connection() && session_->connection()->connected(); } QuicConnectionId QuicClient::GenerateConnectionId() { return QuicRandom::GetInstance()->RandUint64(); } QuicEpollConnectionHelper* QuicClient::CreateQuicConnectionHelper() { return new QuicEpollConnectionHelper(epoll_server_); } QuicPacketWriter* QuicClient::CreateQuicPacketWriter() { return new QuicDefaultPacketWriter(fd_); } int QuicClient::ReadPacket(char* buffer, int buffer_len, IPEndPoint* server_address, IPAddressNumber* client_ip) { return QuicSocketUtils::ReadPacket( fd_, buffer, buffer_len, overflow_supported_ ? &packets_dropped_ : nullptr, client_ip, server_address); } bool QuicClient::ReadAndProcessPacket() { // Allocate some extra space so we can send an error if the server goes over // the limit. char buf[2 * kMaxPacketSize]; IPEndPoint server_address; IPAddressNumber client_ip; int bytes_read = ReadPacket(buf, arraysize(buf), &server_address, &client_ip); if (bytes_read < 0) { return false; } QuicEncryptedPacket packet(buf, bytes_read, false); IPEndPoint client_address(client_ip, client_address_.port()); session_->connection()->ProcessUdpPacket( client_address, server_address, packet); return true; } } // namespace tools } // namespace net