// 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 "chrome/browser/net/network_stats.h" #include "base/bind.h" #include "base/logging.h" #include "base/message_loop.h" #include "base/metrics/field_trial.h" #include "base/metrics/histogram.h" #include "base/rand_util.h" #include "base/stringprintf.h" #include "base/threading/platform_thread.h" #include "base/time.h" #include "base/tuple.h" #include "chrome/common/chrome_version_info.h" #include "content/public/browser/browser_thread.h" #include "googleurl/src/gurl.h" #include "net/base/net_errors.h" #include "net/base/net_util.h" #include "net/base/network_change_notifier.h" #include "net/base/test_completion_callback.h" #include "net/proxy/proxy_service.h" #include "net/socket/tcp_client_socket.h" #include "net/udp/udp_client_socket.h" #include "net/udp/udp_server_socket.h" using content::BrowserThread; namespace chrome_browser_net { // This specifies the number of bytes to be sent to the TCP/UDP servers as part // of small packet size test. static const uint32 kSmallTestBytesToSend = 100; // This specifies the number of bytes to be sent to the TCP/UDP servers as part // of medium packet size test. static const uint32 kMediumTestBytesToSend = 500; // This specifies the number of bytes to be sent to the TCP/UDP servers as part // of large packet size test. static const uint32 kLargeTestBytesToSend = 1200; // This specifies the maximum message (payload) size. static const uint32 kMaxMessage = 2048; // This specifies starting position of the and length of the // in "echo request" and "echo response". static const uint32 kVersionNumber = 1; static const uint32 kVersionStart = 0; static const uint32 kVersionLength = 2; static const uint32 kVersionEnd = kVersionStart + kVersionLength; // This specifies the starting position of the and length of the // in "echo request" and "echo response". Maximum value for the // is less than (2 ** 31 - 1). static const uint32 kChecksumStart = kVersionEnd; static const uint32 kChecksumLength = 10; static const uint32 kChecksumEnd = kChecksumStart + kChecksumLength; // This specifies the starting position of the and length of the // in "echo request" and "echo response". Maximum number of bytes // that can be sent in the is 9,999,999. static const uint32 kPayloadSizeStart = kChecksumEnd; static const uint32 kPayloadSizeLength = 7; static const uint32 kPayloadSizeEnd = kPayloadSizeStart + kPayloadSizeLength; // This specifies the starting position of the and length of the in // "echo response". static const uint32 kKeyStart = kPayloadSizeEnd; static const uint32 kKeyLength = 6; static const uint32 kKeyEnd = kKeyStart + kKeyLength; static const int32 kKeyMinValue = 0; static const int32 kKeyMaxValue = 999999; // This specifies the starting position of the in "echo request". static const uint32 kPayloadStart = kPayloadSizeEnd; // This specifies the length of the packet_number in the payload. static const uint32 kPacketNumberLength = 10; // This specifies the starting position of the in // "echo response". static const uint32 kEncodedPayloadStart = kKeyEnd; // HistogramPortSelector and kPorts should be kept in sync. static const int32 kPorts[] = {53, 80, 587, 6121, 8080, 999999}; // The packet number that is to be sent to the server. static uint32 g_packet_number_ = 0; // Maximum number of packets that can be sent to the server for packet loss // correlation test. static const uint32 kMaximumCorrelationPackets = 6; // Maximum number of packets that can be sent to the server. static const uint32 kMaximumSequentialPackets = 21; // NetworkStats methods and members. NetworkStats::NetworkStats() : load_size_(0), bytes_to_read_(0), bytes_to_send_(0), has_proxy_server_(false), packets_to_send_(0), packets_sent_(0), base_packet_number_(0), packets_received_mask_(0), ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) { } NetworkStats::~NetworkStats() { socket_.reset(); } bool NetworkStats::Start(net::HostResolver* host_resolver, const net::HostPortPair& server_host_port_pair, HistogramPortSelector histogram_port, bool has_proxy_server, uint32 bytes_to_send, uint32 packets_to_send, const net::CompletionCallback& finished_callback) { DCHECK(host_resolver); DCHECK(bytes_to_send); // We should have data to send. DCHECK_LE(packets_to_send, kMaximumSequentialPackets); Initialize(bytes_to_send, histogram_port, has_proxy_server, packets_to_send, finished_callback); net::HostResolver::RequestInfo request(server_host_port_pair); int rv = host_resolver->Resolve( request, &addresses_, base::Bind(&NetworkStats::OnResolveComplete, base::Unretained(this)), NULL, net::BoundNetLog()); if (rv == net::ERR_IO_PENDING) return true; return DoConnect(rv); } void NetworkStats::Initialize( uint32 bytes_to_send, HistogramPortSelector histogram_port, bool has_proxy_server, uint32 packets_to_send, const net::CompletionCallback& finished_callback) { DCHECK(bytes_to_send); // We should have data to send. DCHECK(packets_to_send); // We should send at least 1 packet. DCHECK_LE(bytes_to_send, kLargeTestBytesToSend); load_size_ = bytes_to_send; packets_to_send_ = packets_to_send; histogram_port_ = histogram_port; has_proxy_server_ = has_proxy_server; finished_callback_ = finished_callback; } bool NetworkStats::ConnectComplete(int result) { if (result < 0) { Finish(CONNECT_FAILED, result); return false; } base_packet_number_ = g_packet_number_; start_time_ = base::TimeTicks::Now(); SendPacket(); return true; } void NetworkStats::DoFinishCallback(int result) { if (!finished_callback_.is_null()) { net::CompletionCallback callback = finished_callback_; finished_callback_.Reset(); callback.Run(result); } } void NetworkStats::set_socket(net::Socket* socket) { DCHECK(socket); DCHECK(!socket_.get()); socket_.reset(socket); } bool NetworkStats::ReadComplete(int result) { DCHECK(socket_.get()); DCHECK_NE(net::ERR_IO_PENDING, result); if (result < 0) { Finish(READ_FAILED, result); return true; } encoded_message_.append(read_buffer_->data(), result); read_buffer_ = NULL; bytes_to_read_ -= result; // No more data to read. if (!bytes_to_read_ || result == 0) { Status status = VerifyPackets(); if (status == SUCCESS) Finish(status, net::OK); else Finish(status, net::ERR_INVALID_RESPONSE); return true; } return false; } void NetworkStats::OnResolveComplete(int result) { DoConnect(result); } void NetworkStats::SendPacket() { DCHECK_EQ(bytes_to_send_, 0u); uint32 sending_packet_size = SendingPacketSize(); while (packets_to_send_ > packets_sent_) { ++g_packet_number_; bytes_to_send_ = sending_packet_size; int rv = SendData(); if (rv < 0) { if (rv != net::ERR_IO_PENDING) Finish(WRITE_FAILED, rv); return; } DCHECK_EQ(bytes_to_send_, 0u); } // Timeout if we don't get response back from echo servers in 60 secs. const int kReadDataTimeoutMs = 60000; StartReadDataTimer(kReadDataTimeoutMs); bytes_to_read_ = packets_sent_ * ReceivingPacketSize(); ReadData(); } void NetworkStats::OnReadComplete(int result) { if (!ReadComplete(result)) { // Called ReadData() via PostDelayedTask() to avoid recursion. Added a delay // of 1ms so that the time-out will fire before we have time to really hog // the CPU too extensively (waiting for the time-out) in case of an infinite // loop. MessageLoop::current()->PostDelayedTask( FROM_HERE, base::Bind(&NetworkStats::ReadData, weak_factory_.GetWeakPtr()), base::TimeDelta::FromMilliseconds(1)); } } void NetworkStats::OnWriteComplete(int result) { DCHECK(socket_.get()); DCHECK_NE(net::ERR_IO_PENDING, result); if (result < 0) { Finish(WRITE_FAILED, result); return; } DidSendData(result); if (bytes_to_send_) { int rv = SendData(); if (rv < 0) { if (rv != net::ERR_IO_PENDING) Finish(WRITE_FAILED, rv); return; } DCHECK_EQ(rv, net::OK); DCHECK_EQ(bytes_to_send_, 0u); } MessageLoop::current()->PostTask( FROM_HERE, base::Bind(&NetworkStats::SendPacket, weak_factory_.GetWeakPtr())); } void NetworkStats::ReadData() { int rv; do { if (!socket_.get()) return; DCHECK(!read_buffer_.get()); // We release the read_buffer_ in the destructor if there is an error. read_buffer_ = new net::IOBuffer(kMaxMessage); rv = socket_->Read(read_buffer_, kMaxMessage, base::Bind(&NetworkStats::OnReadComplete, base::Unretained(this))); if (rv == net::ERR_IO_PENDING) return; // If we have read all the data then return. if (ReadComplete(rv)) return; } while (rv > 0); } int NetworkStats::SendData() { DCHECK(bytes_to_send_); // We should have data to send. do { if (!write_buffer_.get()) { scoped_refptr buffer( new net::IOBufferWithSize(bytes_to_send_)); GetEchoRequest(buffer); write_buffer_ = new net::DrainableIOBuffer(buffer, bytes_to_send_); } if (!socket_.get()) return net::ERR_UNEXPECTED; int rv = socket_->Write(write_buffer_, write_buffer_->BytesRemaining(), base::Bind(&NetworkStats::OnWriteComplete, base::Unretained(this))); if (rv < 0) return rv; DidSendData(rv); } while (bytes_to_send_); return net::OK; } uint32 NetworkStats::SendingPacketSize() const { return kVersionLength + kChecksumLength + kPayloadSizeLength + load_size_; } uint32 NetworkStats::ReceivingPacketSize() const { return kVersionLength + kChecksumLength + kPayloadSizeLength + kKeyLength + load_size_; } void NetworkStats::DidSendData(int bytes_sent) { write_buffer_->DidConsume(bytes_sent); if (!write_buffer_->BytesRemaining()) write_buffer_ = NULL; bytes_to_send_ -= bytes_sent; if (bytes_to_send_ == 0) ++packets_sent_; } void NetworkStats::StartReadDataTimer(int milliseconds) { MessageLoop::current()->PostDelayedTask( FROM_HERE, base::Bind(&NetworkStats::OnReadDataTimeout, weak_factory_.GetWeakPtr()), base::TimeDelta::FromMilliseconds(milliseconds)); } void NetworkStats::OnReadDataTimeout() { Status status = VerifyPackets(); if (status == SUCCESS) Finish(status, net::OK); else Finish(READ_TIMED_OUT, net::ERR_INVALID_ARGUMENT); } uint32 NetworkStats::GetChecksum(const char* message, uint32 message_length) { // Calculate the of the . uint32 sum = 0; for (uint32 i = 0; i < message_length; ++i) sum += message[i]; return sum; } void NetworkStats::Crypt(const char* key, uint32 key_length, const char* data, uint32 data_length, char* encoded_data) { // Decrypt the data by looping through the |data| and XOR each byte with the // |key| to get the decoded byte. Append the decoded byte to the // |encoded_data|. for (uint32 data_index = 0, key_index = 0; data_index < data_length; ++data_index) { char data_byte = data[data_index]; char key_byte = key[key_index]; char encoded_byte = data_byte ^ key_byte; encoded_data[data_index] = encoded_byte; key_index = (key_index + 1) % key_length; } } void NetworkStats::GetEchoRequest(net::IOBufferWithSize* io_buffer) { char* buffer = io_buffer->data(); uint32 buffer_size = static_cast(io_buffer->size()); // Copy the into the io_buffer starting from the kVersionStart // position. std::string version = base::StringPrintf("%02d", kVersionNumber); DCHECK(kVersionLength == version.length()); DCHECK_GE(buffer_size, kVersionStart + kVersionLength); memcpy(buffer + kVersionStart, version.c_str(), kVersionLength); // Copy the packet_number into the payload. std::string packet_number = base::StringPrintf("%010d", g_packet_number_); DCHECK(kPacketNumberLength == packet_number.length()); DCHECK_GE(buffer_size, kPayloadStart + kPacketNumberLength); memcpy(buffer + kPayloadStart, packet_number.c_str(), kPacketNumberLength); // Get the from the |stream_| and copy it into io_buffer after // packet_number. stream_.Reset(); DCHECK_GE(buffer_size, kPayloadStart + load_size_); stream_.GetBytes(buffer + kPayloadStart + kPacketNumberLength, load_size_ - kPacketNumberLength); // Calculate the of the . uint32 sum = GetChecksum(buffer + kPayloadStart, load_size_); // Copy the into the io_buffer starting from the kChecksumStart // position. std::string checksum = base::StringPrintf("%010d", sum); DCHECK(kChecksumLength == checksum.length()); DCHECK_GE(buffer_size, kChecksumStart + kChecksumLength); memcpy(buffer + kChecksumStart, checksum.c_str(), kChecksumLength); // Copy the size of the into the io_buffer starting from the // kPayloadSizeStart position. std::string payload_size = base::StringPrintf("%07d", load_size_); DCHECK(kPayloadSizeLength == payload_size.length()); DCHECK_GE(buffer_size, kPayloadSizeStart + kPayloadSizeLength); memcpy(buffer + kPayloadSizeStart, payload_size.c_str(), kPayloadSizeLength); } NetworkStats::Status NetworkStats::VerifyPackets() { uint32 packet_start = 0; size_t message_length = encoded_message_.length(); uint32 receiving_packet_size = ReceivingPacketSize(); Status status = SUCCESS; for (uint32 i = 0; i < packets_to_send_; i++) { if (message_length <= packet_start) { status = ZERO_LENGTH_ERROR; break; } std::string response = encoded_message_.substr(packet_start, receiving_packet_size); Status packet_status = VerifyBytes(response); if (packet_status != SUCCESS) status = packet_status; packet_start += receiving_packet_size; } if (message_length > packet_start) status = TOO_MANY_PACKETS; return status; } NetworkStats::Status NetworkStats::VerifyBytes(const std::string& response) { // If the "echo response" doesn't have enough bytes, then return false. if (response.length() <= kVersionStart) return ZERO_LENGTH_ERROR; if (response.length() <= kChecksumStart) return NO_CHECKSUM_ERROR; if (response.length() <= kPayloadSizeStart) return NO_PAYLOAD_SIZE_ERROR; if (response.length() <= kKeyStart) return NO_KEY_ERROR; if (response.length() <= kEncodedPayloadStart) return NO_PAYLOAD_ERROR; // Extract the |key| from the "echo response". std::string key_string = response.substr(kKeyStart, kKeyLength); const char* key = key_string.c_str(); int key_value = atoi(key); if (key_value < kKeyMinValue || key_value > kKeyMaxValue) return INVALID_KEY_ERROR; std::string encoded_payload = response.substr(kEncodedPayloadStart); const char* encoded_data = encoded_payload.c_str(); uint32 message_length = encoded_payload.length(); message_length = std::min(message_length, kMaxMessage); if (message_length < load_size_) return TOO_SHORT_PAYLOAD; if (message_length > load_size_) return TOO_LONG_PAYLOAD; // Decode/decrypt the |encoded_data| into |decoded_data|. char decoded_data[kMaxMessage + 1]; DCHECK_LE(message_length, kMaxMessage); memset(decoded_data, 0, kMaxMessage + 1); Crypt(key, kKeyLength, encoded_data, message_length, decoded_data); // Calculate the of the . uint32 sum = GetChecksum(decoded_data, message_length); // Extract the |checksum| from the "echo response". std::string checksum_string = response.substr(kChecksumStart, kChecksumLength); const char* checksum = checksum_string.c_str(); uint32 checksum_value = atoi(checksum); if (checksum_value != sum) return INVALID_CHECKSUM; // Verify the packet_number. char packet_number_data[kPacketNumberLength + 1]; memset(packet_number_data, 0, kPacketNumberLength + 1); memcpy(packet_number_data, decoded_data, kPacketNumberLength); uint32 packet_number = atoi(packet_number_data); uint32 packet_index = packet_number - base_packet_number_; if (packet_index > packets_to_send_) return INVALID_PACKET_NUMBER; stream_.Reset(); if (!stream_.VerifyBytes(&decoded_data[kPacketNumberLength], message_length - kPacketNumberLength)) { return PATTERN_CHANGED; } packets_received_mask_ |= 1 << (packet_index - 1); return SUCCESS; } void NetworkStats::RecordAcksReceivedHistograms(const char* load_size_string) { bool received_atleast_one_packet = packets_received_mask_ > 0; std::string histogram_name = base::StringPrintf( "NetConnectivity2.Sent%d.GotAnAck.%d.%s", kMaximumSequentialPackets, kPorts[histogram_port_], load_size_string); base::Histogram* got_an_ack_histogram = base::BooleanHistogram::FactoryGet( histogram_name, base::Histogram::kUmaTargetedHistogramFlag); got_an_ack_histogram->AddBoolean(received_atleast_one_packet); histogram_name = base::StringPrintf( "NetConnectivity2.Sent%d.PacketsSent.%d.%s", kMaximumSequentialPackets, kPorts[histogram_port_], load_size_string); base::Histogram* packets_sent_histogram = base::Histogram::FactoryGet( histogram_name, 1, kMaximumSequentialPackets, kMaximumSequentialPackets + 1, base::Histogram::kUmaTargetedHistogramFlag); packets_sent_histogram->Add(packets_sent_); if (!received_atleast_one_packet || packets_sent_ != packets_to_send_) return; histogram_name = base::StringPrintf( "NetConnectivity2.Sent%d.AckReceivedForNthPacket.%d.%s", kMaximumSequentialPackets, kPorts[histogram_port_], load_size_string); base::Histogram* ack_received_for_nth_packet_histogram = base::Histogram::FactoryGet( histogram_name, 1, kMaximumSequentialPackets, kMaximumSequentialPackets + 1, base::Histogram::kUmaTargetedHistogramFlag); int count = 0; for (size_t j = 0; j < packets_to_send_; j++) { int packet_number = j + 1; if (packets_received_mask_ & (1 << j)) { ack_received_for_nth_packet_histogram->Add(packet_number); count++; } if (packet_number < 2) continue; histogram_name = base::StringPrintf( "NetConnectivity2.Sent%d.AcksReceivedFromFirst%dPackets.%d.%s", kMaximumSequentialPackets, packet_number, kPorts[histogram_port_], load_size_string); base::Histogram* acks_received_count_histogram = base::Histogram::FactoryGet( histogram_name, 1, packet_number, packet_number + 1, base::Histogram::kUmaTargetedHistogramFlag); acks_received_count_histogram->Add(count); } } void NetworkStats::RecordPacketLossSeriesHistograms( const ProtocolValue& protocol, const Status& status, const char* load_size_string, int result) { if (packets_to_send_ < 2 || protocol != PROTOCOL_UDP) return; // Build "NetConnectivity2.Send6.SeriesAcked.." histogram // name. Total number of histograms are 5*2. std::string series_acked_histogram_name = base::StringPrintf( "NetConnectivity2.Send6.SeriesAcked.%d.%s", kPorts[histogram_port_], load_size_string); // Build "NetConnectivity2.Send6.PacketsSent.." histogram // name. Total number of histograms are 5*2. std::string packets_sent_histogram_name = base::StringPrintf( "NetConnectivity2.Send6.PacketsSent.%d.%s", kPorts[histogram_port_], load_size_string); // If we are running without a proxy, we'll generate 2 distinct histograms in // each case, one will have the ".NoProxy" suffix. size_t histogram_count = has_proxy_server_ ? 1 : 2; for (size_t i = 0; i < histogram_count; i++) { // For packet loss test, just record packet loss data. base::Histogram* series_acked_histogram = base::LinearHistogram::FactoryGet( series_acked_histogram_name, 1, 2 << kMaximumCorrelationPackets, (2 << kMaximumCorrelationPackets) + 1, base::Histogram::kUmaTargetedHistogramFlag); series_acked_histogram->Add(packets_received_mask_); series_acked_histogram_name.append(".NoProxy"); base::Histogram* packets_sent_histogram = base::Histogram::FactoryGet( packets_sent_histogram_name, 1, kMaximumCorrelationPackets, kMaximumCorrelationPackets + 1, base::Histogram::kUmaTargetedHistogramFlag); packets_sent_histogram->Add(packets_sent_); packets_sent_histogram_name.append(".NoProxy"); } } void NetworkStats::RecordHistograms(const ProtocolValue& protocol, const Status& status, int result) { // Build string. const char* kSmallLoadString = "100B"; const char* kMediumLoadString = "500B"; const char* kLargeLoadString = "1K"; const char* load_size_string; if (load_size_ == kSmallTestBytesToSend) load_size_string = kSmallLoadString; else if (load_size_ == kMediumTestBytesToSend) load_size_string = kMediumLoadString; else load_size_string = kLargeLoadString; if (packets_to_send_ == kMaximumSequentialPackets) { RecordAcksReceivedHistograms(load_size_string); return; } RecordPacketLossSeriesHistograms(protocol, status, load_size_string, result); } // UDPStatsClient methods and members. UDPStatsClient::UDPStatsClient() : NetworkStats() { } UDPStatsClient::~UDPStatsClient() { } bool UDPStatsClient::DoConnect(int result) { if (result != net::OK) { Finish(RESOLVE_FAILED, result); return false; } net::UDPClientSocket* udp_socket = new net::UDPClientSocket(net::DatagramSocket::DEFAULT_BIND, net::RandIntCallback(), NULL, net::NetLog::Source()); if (!udp_socket) { Finish(SOCKET_CREATE_FAILED, net::ERR_INVALID_ARGUMENT); return false; } set_socket(udp_socket); if (addresses().empty()) { Finish(RESOLVE_FAILED, net::ERR_INVALID_ARGUMENT); return false; } const net::IPEndPoint& endpoint = addresses().front(); int rv = udp_socket->Connect(endpoint); if (rv < 0) { Finish(CONNECT_FAILED, rv); return false; } return NetworkStats::ConnectComplete(rv); } bool UDPStatsClient::ReadComplete(int result) { DCHECK_NE(net::ERR_IO_PENDING, result); if (result <= 0) { Finish(READ_FAILED, result); return true; } return NetworkStats::ReadComplete(result); } void UDPStatsClient::Finish(Status status, int result) { RecordHistograms(PROTOCOL_UDP, status, result); DoFinishCallback(result); // Close the socket so that there are no more IO operations. net::UDPClientSocket* udp_socket = static_cast(socket()); if (udp_socket) udp_socket->Close(); delete this; } // TCPStatsClient methods and members. TCPStatsClient::TCPStatsClient() { } TCPStatsClient::~TCPStatsClient() { } bool TCPStatsClient::DoConnect(int result) { if (result != net::OK) { Finish(RESOLVE_FAILED, result); return false; } net::TCPClientSocket* tcp_socket = new net::TCPClientSocket(addresses(), NULL, net::NetLog::Source()); if (!tcp_socket) { Finish(SOCKET_CREATE_FAILED, net::ERR_INVALID_ARGUMENT); return false; } set_socket(tcp_socket); int rv = tcp_socket->Connect(base::Bind(&TCPStatsClient::OnConnectComplete, base::Unretained(this))); if (rv == net::ERR_IO_PENDING) return true; return NetworkStats::ConnectComplete(rv); } void TCPStatsClient::OnConnectComplete(int result) { NetworkStats::ConnectComplete(result); } bool TCPStatsClient::ReadComplete(int result) { DCHECK_NE(net::ERR_IO_PENDING, result); if (result < 0) { Finish(READ_FAILED, result); return true; } return NetworkStats::ReadComplete(result); } void TCPStatsClient::Finish(Status status, int result) { RecordHistograms(PROTOCOL_TCP, status, result); DoFinishCallback(result); // Disconnect the socket so that there are no more IO operations. net::TCPClientSocket* tcp_socket = static_cast(socket()); if (tcp_socket) tcp_socket->Disconnect(); delete this; } // ProxyDetector methods and members. ProxyDetector::ProxyDetector(net::ProxyService* proxy_service, const net::HostPortPair& server_address, OnResolvedCallback callback) : proxy_service_(proxy_service), server_address_(server_address), callback_(callback), has_pending_proxy_resolution_(false) { } ProxyDetector::~ProxyDetector() { CHECK(!has_pending_proxy_resolution_); } void ProxyDetector::StartResolveProxy() { std::string url = base::StringPrintf("https://%s", server_address_.ToString().c_str()); GURL gurl(url); has_pending_proxy_resolution_ = true; DCHECK(proxy_service_); int rv = proxy_service_->ResolveProxy( gurl, &proxy_info_, base::Bind(&ProxyDetector::OnResolveProxyComplete, base::Unretained(this)), NULL, net::BoundNetLog()); if (rv != net::ERR_IO_PENDING) OnResolveProxyComplete(rv); } void ProxyDetector::OnResolveProxyComplete(int result) { has_pending_proxy_resolution_ = false; bool has_proxy_server = (result == net::OK && proxy_info_.proxy_server().is_valid() && !proxy_info_.proxy_server().is_direct()); OnResolvedCallback callback = callback_; BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::Bind(callback, has_proxy_server)); // TODO(rtenneti): Will we leak if ProxyResolve is cancelled (or proxy // resolution never completes). delete this; } // static void CollectNetworkStats(const std::string& network_stats_server, IOThread* io_thread) { if (network_stats_server.empty()) return; // If we are not on IO Thread, then post a task to call CollectNetworkStats on // IO Thread. if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::Bind( &CollectNetworkStats, network_stats_server, io_thread)); return; } DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); // Check that there is a network connection. We get called only if UMA upload // to the server has succeeded. DCHECK(!net::NetworkChangeNotifier::IsOffline()); CR_DEFINE_STATIC_LOCAL(scoped_refptr, trial, ()); static bool collect_stats = false; static NetworkStats::HistogramPortSelector histogram_port = NetworkStats::PORT_6121; if (!trial.get()) { // Set up a field trial to collect network stats for UDP and TCP. const base::FieldTrial::Probability kDivisor = 1000; // Enable the connectivity testing for 0.5% of the users in stable channel. base::FieldTrial::Probability probability_per_group = 5; chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel(); if (channel == chrome::VersionInfo::CHANNEL_CANARY) probability_per_group = kDivisor; else if (channel == chrome::VersionInfo::CHANNEL_DEV) // Enable the connectivity testing for 10% of the users in dev channel. probability_per_group = 500; else if (channel == chrome::VersionInfo::CHANNEL_BETA) // Enable the connectivity testing for 5% of the users in beta channel. probability_per_group = 50; // After October 30, 2012 builds, it will always be in default group // (disable_network_stats). trial = base::FieldTrialList::FactoryGetFieldTrial( "NetworkConnectivity", kDivisor, "disable_network_stats", 2012, 10, 30, NULL); // Add option to collect_stats for NetworkConnectivity. int collect_stats_group = trial->AppendGroup("collect_stats", probability_per_group); if (trial->group() == collect_stats_group) collect_stats = true; } if (!collect_stats) return; // Run test kMaxNumberOfTests times. const size_t kMaxNumberOfTests = INT_MAX; static size_t number_of_tests_done = 0; if (number_of_tests_done > kMaxNumberOfTests) return; ++number_of_tests_done; net::HostResolver* host_resolver = io_thread->globals()->host_resolver.get(); DCHECK(host_resolver); net::HostPortPair server_address(network_stats_server, kPorts[histogram_port]); net::ProxyService* proxy_service = io_thread->globals()->system_proxy_service.get(); DCHECK(proxy_service); ProxyDetector::OnResolvedCallback callback = base::Bind(&StartNetworkStatsTest, host_resolver, server_address, histogram_port); ProxyDetector* proxy_client = new ProxyDetector( proxy_service, server_address, callback); proxy_client->StartResolveProxy(); } // static void StartNetworkStatsTest(net::HostResolver* host_resolver, const net::HostPortPair& server_address, NetworkStats::HistogramPortSelector histogram_port, bool has_proxy_server) { int experiment_to_run = base::RandInt(1, 4); switch (experiment_to_run) { case 1: { UDPStatsClient* udp_stats_client = new UDPStatsClient(); udp_stats_client->Start( host_resolver, server_address, histogram_port, has_proxy_server, kSmallTestBytesToSend, kMaximumCorrelationPackets, net::CompletionCallback()); } break; case 2: { UDPStatsClient* udp_stats_client = new UDPStatsClient(); udp_stats_client->Start( host_resolver, server_address, histogram_port, has_proxy_server, kSmallTestBytesToSend, kMaximumSequentialPackets, net::CompletionCallback()); } break; case 3: { UDPStatsClient* udp_stats_client = new UDPStatsClient(); udp_stats_client->Start( host_resolver, server_address, histogram_port, has_proxy_server, kMediumTestBytesToSend, kMaximumSequentialPackets, net::CompletionCallback()); } break; case 4: { UDPStatsClient* udp_stats_client = new UDPStatsClient(); udp_stats_client->Start( host_resolver, server_address, histogram_port, has_proxy_server, kLargeTestBytesToSend, kMaximumSequentialPackets, net::CompletionCallback()); } break; } } } // namespace chrome_browser_net