| Index: net/socket/transport_client_socket_pool.cc
|
| ===================================================================
|
| --- net/socket/transport_client_socket_pool.cc (revision 85841)
|
| +++ net/socket/transport_client_socket_pool.cc (working copy)
|
| @@ -10,8 +10,10 @@
|
| #include "base/metrics/histogram.h"
|
| #include "base/string_util.h"
|
| #include "base/time.h"
|
| +#include "net/base/ip_endpoint.h"
|
| #include "net/base/net_log.h"
|
| #include "net/base/net_errors.h"
|
| +#include "net/base/sys_addrinfo.h"
|
| #include "net/socket/client_socket_factory.h"
|
| #include "net/socket/client_socket_handle.h"
|
| #include "net/socket/client_socket_pool_base.h"
|
| @@ -21,10 +23,46 @@
|
|
|
| namespace net {
|
|
|
| -TransportSocketParams::TransportSocketParams(const HostPortPair& host_port_pair,
|
| - RequestPriority priority, const GURL& referrer,
|
| - bool disable_resolver_cache,
|
| - bool ignore_limits)
|
| +// TODO(willchan): Base this off RTT instead of statically setting it. Note we
|
| +// choose a timeout that is different from the backup connect job timer so they
|
| +// don't synchronize.
|
| +const int TransportConnectJob::kIPv6FallbackTimerInMs = 300;
|
| +
|
| +namespace {
|
| +
|
| +bool AddressListStartsWithIPv6AndHasAnIPv4Addr(const AddressList& addrlist) {
|
| + const struct addrinfo* ai = addrlist.head();
|
| + if (ai->ai_family != AF_INET6)
|
| + return false;
|
| +
|
| + ai = ai->ai_next;
|
| + while (ai) {
|
| + if (ai->ai_family != AF_INET6)
|
| + return true;
|
| + ai = ai->ai_next;
|
| + }
|
| +
|
| + return false;
|
| +}
|
| +
|
| +bool AddressListOnlyContainsIPv6Addresses(const AddressList& addrlist) {
|
| + DCHECK(addrlist.head());
|
| + for (const struct addrinfo* ai = addrlist.head(); ai; ai = ai->ai_next) {
|
| + if (ai->ai_family != AF_INET6)
|
| + return false;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +TransportSocketParams::TransportSocketParams(
|
| + const HostPortPair& host_port_pair,
|
| + RequestPriority priority,
|
| + const GURL& referrer,
|
| + bool disable_resolver_cache,
|
| + bool ignore_limits)
|
| : destination_(host_port_pair), ignore_limits_(ignore_limits) {
|
| Initialize(priority, referrer, disable_resolver_cache);
|
| }
|
| @@ -32,8 +70,8 @@
|
| TransportSocketParams::~TransportSocketParams() {}
|
|
|
| void TransportSocketParams::Initialize(RequestPriority priority,
|
| - const GURL& referrer,
|
| - bool disable_resolver_cache) {
|
| + const GURL& referrer,
|
| + bool disable_resolver_cache) {
|
| // The referrer is used by the DNS prefetch system to correlate resolutions
|
| // with the page that triggered them. It doesn't impact the actual addresses
|
| // that we resolve to.
|
| @@ -69,7 +107,11 @@
|
| ALLOW_THIS_IN_INITIALIZER_LIST(
|
| callback_(this,
|
| &TransportConnectJob::OnIOComplete)),
|
| - resolver_(host_resolver) {}
|
| + resolver_(host_resolver),
|
| + ALLOW_THIS_IN_INITIALIZER_LIST(
|
| + fallback_callback_(
|
| + this,
|
| + &TransportConnectJob::DoIPv6FallbackTransportConnectComplete)) {}
|
|
|
| TransportConnectJob::~TransportConnectJob() {
|
| // We don't worry about cancelling the host resolution and TCP connect, since
|
| @@ -90,6 +132,38 @@
|
| }
|
| }
|
|
|
| +// static
|
| +void TransportConnectJob::MakeAddrListStartWithIPv4(AddressList* addrlist) {
|
| + if (addrlist->head()->ai_family != AF_INET6)
|
| + return;
|
| + bool has_ipv4 = false;
|
| + for (const struct addrinfo* ai = addrlist->head(); ai; ai = ai->ai_next) {
|
| + if (ai->ai_family != AF_INET6) {
|
| + has_ipv4 = true;
|
| + break;
|
| + }
|
| + }
|
| + if (!has_ipv4)
|
| + return;
|
| +
|
| + struct addrinfo* head = CreateCopyOfAddrinfo(addrlist->head(), true);
|
| + struct addrinfo* tail = head;
|
| + while (tail->ai_next)
|
| + tail = tail->ai_next;
|
| + char* canonname = head->ai_canonname;
|
| + head->ai_canonname = NULL;
|
| + while (head->ai_family == AF_INET6) {
|
| + tail->ai_next = head;
|
| + tail = head;
|
| + head = head->ai_next;
|
| + tail->ai_next = NULL;
|
| + }
|
| + head->ai_canonname = canonname;
|
| +
|
| + addrlist->Copy(head, true);
|
| + FreeCopyOfAddrinfo(head);
|
| +}
|
| +
|
| void TransportConnectJob::OnIOComplete(int result) {
|
| int rv = DoLoop(result);
|
| if (rv != ERR_IO_PENDING)
|
| @@ -142,14 +216,22 @@
|
|
|
| int TransportConnectJob::DoTransportConnect() {
|
| next_state_ = STATE_TRANSPORT_CONNECT_COMPLETE;
|
| - set_socket(client_socket_factory_->CreateTransportClientSocket(
|
| + transport_socket_.reset(client_socket_factory_->CreateTransportClientSocket(
|
| addresses_, net_log().net_log(), net_log().source()));
|
| connect_start_time_ = base::TimeTicks::Now();
|
| - return socket()->Connect(&callback_);
|
| + int rv = transport_socket_->Connect(&callback_);
|
| + if (rv == ERR_IO_PENDING &&
|
| + AddressListStartsWithIPv6AndHasAnIPv4Addr(addresses_)) {
|
| + fallback_timer_.Start(
|
| + base::TimeDelta::FromMilliseconds(kIPv6FallbackTimerInMs),
|
| + this, &TransportConnectJob::DoIPv6FallbackTransportConnect);
|
| + }
|
| + return rv;
|
| }
|
|
|
| int TransportConnectJob::DoTransportConnectComplete(int result) {
|
| if (result == OK) {
|
| + bool is_ipv4 = addresses_.head()->ai_family != AF_INET6;
|
| DCHECK(connect_start_time_ != base::TimeTicks());
|
| DCHECK(start_time_ != base::TimeTicks());
|
| base::TimeTicks now = base::TimeTicks::Now();
|
| @@ -167,14 +249,107 @@
|
| base::TimeDelta::FromMilliseconds(1),
|
| base::TimeDelta::FromMinutes(10),
|
| 100);
|
| +
|
| + if (is_ipv4) {
|
| + UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency_IPv4_No_Race",
|
| + connect_duration,
|
| + base::TimeDelta::FromMilliseconds(1),
|
| + base::TimeDelta::FromMinutes(10),
|
| + 100);
|
| + } else {
|
| + if (AddressListOnlyContainsIPv6Addresses(addresses_)) {
|
| + UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency_IPv6_Solo",
|
| + connect_duration,
|
| + base::TimeDelta::FromMilliseconds(1),
|
| + base::TimeDelta::FromMinutes(10),
|
| + 100);
|
| + } else {
|
| + UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency_IPv6_Raceable",
|
| + connect_duration,
|
| + base::TimeDelta::FromMilliseconds(1),
|
| + base::TimeDelta::FromMinutes(10),
|
| + 100);
|
| + }
|
| + }
|
| + set_socket(transport_socket_.release());
|
| + fallback_timer_.Stop();
|
| } else {
|
| - // Delete the socket on error.
|
| - set_socket(NULL);
|
| + // Be a bit paranoid and kill off the fallback members to prevent reuse.
|
| + fallback_transport_socket_.reset();
|
| + fallback_addresses_.reset();
|
| }
|
|
|
| return result;
|
| }
|
|
|
| +void TransportConnectJob::DoIPv6FallbackTransportConnect() {
|
| + // The timer should only fire while we're waiting for the main connect to
|
| + // succeed.
|
| + if (next_state_ != STATE_TRANSPORT_CONNECT_COMPLETE) {
|
| + NOTREACHED();
|
| + return;
|
| + }
|
| +
|
| + DCHECK(!fallback_transport_socket_.get());
|
| + DCHECK(!fallback_addresses_.get());
|
| +
|
| + fallback_addresses_.reset(new AddressList(addresses_));
|
| + MakeAddrListStartWithIPv4(fallback_addresses_.get());
|
| + fallback_transport_socket_.reset(
|
| + client_socket_factory_->CreateTransportClientSocket(
|
| + *fallback_addresses_, net_log().net_log(), net_log().source()));
|
| + fallback_connect_start_time_ = base::TimeTicks::Now();
|
| + int rv = fallback_transport_socket_->Connect(&fallback_callback_);
|
| + if (rv != ERR_IO_PENDING)
|
| + DoIPv6FallbackTransportConnectComplete(rv);
|
| +}
|
| +
|
| +void TransportConnectJob::DoIPv6FallbackTransportConnectComplete(int result) {
|
| + // This should only happen when we're waiting for the main connect to succeed.
|
| + if (next_state_ != STATE_TRANSPORT_CONNECT_COMPLETE) {
|
| + NOTREACHED();
|
| + return;
|
| + }
|
| +
|
| + DCHECK_NE(ERR_IO_PENDING, result);
|
| + DCHECK(fallback_transport_socket_.get());
|
| + DCHECK(fallback_addresses_.get());
|
| +
|
| + if (result == OK) {
|
| + DCHECK(fallback_connect_start_time_ != base::TimeTicks());
|
| + DCHECK(start_time_ != base::TimeTicks());
|
| + base::TimeTicks now = base::TimeTicks::Now();
|
| + base::TimeDelta total_duration = now - start_time_;
|
| + UMA_HISTOGRAM_CUSTOM_TIMES(
|
| + "Net.DNS_Resolution_And_TCP_Connection_Latency2",
|
| + total_duration,
|
| + base::TimeDelta::FromMilliseconds(1),
|
| + base::TimeDelta::FromMinutes(10),
|
| + 100);
|
| +
|
| + base::TimeDelta connect_duration = now - fallback_connect_start_time_;
|
| + UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency",
|
| + connect_duration,
|
| + base::TimeDelta::FromMilliseconds(1),
|
| + base::TimeDelta::FromMinutes(10),
|
| + 100);
|
| +
|
| + UMA_HISTOGRAM_CUSTOM_TIMES("Net.TCP_Connection_Latency_IPv4_Wins_Race",
|
| + connect_duration,
|
| + base::TimeDelta::FromMilliseconds(1),
|
| + base::TimeDelta::FromMinutes(10),
|
| + 100);
|
| + set_socket(fallback_transport_socket_.release());
|
| + next_state_ = STATE_NONE;
|
| + transport_socket_.reset();
|
| + } else {
|
| + // Be a bit paranoid and kill off the fallback members to prevent reuse.
|
| + fallback_transport_socket_.reset();
|
| + fallback_addresses_.reset();
|
| + }
|
| + NotifyDelegateOfCompletion(result); // Deletes |this|
|
| +}
|
| +
|
| int TransportConnectJob::ConnectInternal() {
|
| next_state_ = STATE_RESOLVE_HOST;
|
| start_time_ = base::TimeTicks::Now();
|
|
|