blob: 714c1c38a31ef3bd4dc7f97b5f51518fc67c0721 [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.
#ifndef CHROME_BROWSER_NET_NETWORK_STATS_H_
#define CHROME_BROWSER_NET_NETWORK_STATS_H_
#include <string>
#include <vector>
#include "base/basictypes.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/string_util.h"
#include "base/time.h"
#include "chrome/browser/io_thread.h"
#include "net/base/address_list.h"
#include "net/base/completion_callback.h"
#include "net/base/host_port_pair.h"
#include "net/base/io_buffer.h"
#include "net/base/ip_endpoint.h"
#include "net/base/test_data_stream.h"
#include "net/dns/host_resolver.h"
#include "net/proxy/proxy_info.h"
#include "net/socket/socket.h"
namespace chrome_browser_net {
// This class is used for live experiment of network connectivity (for UDP)
// metrics. A small percentage of users participate in this experiment. All
// users (who are in the experiment) must have enabled "UMA upload".
//
// This class collects the following stats from users who have opted in.
// a) Success/failure, to estimate reachability for users.
// b) RTT for some select set of messages.
// c) Perform packet loss tests for correlation and FEC experiments.
//
// The following is the overview of the echo message protocol.
//
// We send the "echo request" to the UDP echo servers in the following format:
// <version><checksum><payload_size><payload>. <version> is the version number
// of the "echo request". <checksum> is the checksum of the <payload>.
// <payload_size> specifies the number of bytes in the <payload>.
//
// UDP echo servers respond to the "echo request" by returning "echo response".
// "echo response" is of the format:
// "<version><checksum><payload_size><key><encoded_payload>". <payload_size>
// specifies the number of bytes in the <encoded_payload>. <key> is used to
// decode the <encoded_payload>.
class NetworkStats {
public:
enum Status { // Used in HISTOGRAM_ENUMERATION.
SUCCESS, // Successfully received bytes from the server.
IP_STRING_PARSE_FAILED, // Parsing of IP string failed.
SOCKET_CREATE_FAILED, // Socket creation failed.
RESOLVE_FAILED, // Host resolution failed.
CONNECT_FAILED, // Connection to the server failed.
WRITE_FAILED, // Sending an echo message to the server failed.
READ_TIMED_OUT, // Reading the reply from the server timed out.
READ_FAILED, // Reading the reply from the server failed.
ZERO_LENGTH_ERROR, // Zero length message.
NO_CHECKSUM_ERROR, // Message doesn't have a checksum.
NO_KEY_ERROR, // Message doesn't have a key.
NO_PAYLOAD_SIZE_ERROR, // Message doesn't have a payload size.
NO_PAYLOAD_ERROR, // Message doesn't have a payload.
INVALID_KEY_ERROR, // Invalid key in the message.
TOO_SHORT_PAYLOAD, // Message is shorter than payload.
TOO_LONG_PAYLOAD, // Message is longer than payload.
INVALID_CHECKSUM, // Checksum verification failed.
PATTERN_CHANGED, // Pattern in payload has changed.
INVALID_PACKET_NUMBER, // Packet number didn't match.
TOO_MANY_PACKETS, // Received more packets than the packets sent.
PREVIOUS_PACKET_NUMBER, // Received a packet from an earlier test.
DUPLICATE_PACKET, // Received a duplicate packet.
SOME_PACKETS_NOT_VERIFIED, // Some packets have failed verification.
STATUS_MAX, // Bounding value.
};
// |ProtocolValue| enumerates different protocols that are being tested.
enum ProtocolValue {
PROTOCOL_UDP,
};
// |HistogramPortSelector| enumerates list of ports that are used for network
// connectivity tests (for UDP). Currently we are testing port 443 only.
enum HistogramPortSelector {
PORT_443 = 0, // HTTPS
HISTOGRAM_PORT_MAX,
};
// |TestType| specifies the |current_test_| test we are currently running or
// the |next_test_| test we need to run next.
enum TestType {
START_PACKET_TEST, // Initial packet loss test.
NON_PACED_PACKET_TEST, // Packet loss test with no pacing.
PACED_PACKET_TEST, // Packet loss test with pacing.
TEST_TYPE_MAX,
};
// |PacketStatus| collects the Status and RTT statistics for each packet.
struct PacketStatus {
base::TimeTicks start_time_;
base::TimeTicks end_time_;
};
// Constructs a NetworkStats object that collects metrics for network
// connectivity (for UDP).
NetworkStats();
// NetworkStats is deleted when Finish() is called.
~NetworkStats();
// Starts the client, connecting to |server|.
// Client will send |bytes_to_send| bytes to |server|.
// When client has received all echoed bytes from the server, or
// when an error occurs causing the client to stop, |Finish| will be
// called with a net status code.
// |Finish| will collect histogram stats.
// Returns true if successful in starting the client.
bool Start(net::HostResolver* host_resolver,
const net::HostPortPair& server,
HistogramPortSelector histogram_port,
bool has_proxy_server,
uint32 bytes_to_send,
uint32 packets_to_send,
const net::CompletionCallback& callback);
private:
friend class NetworkStatsTest;
// Allow tests to access our innards for testing purposes.
FRIEND_TEST_ALL_PREFIXES(NetworkStatsTest, GetHistogramNames);
FRIEND_TEST_ALL_PREFIXES(NetworkStatsTestUDP, VerifyBytesInvalidHeaders);
FRIEND_TEST_ALL_PREFIXES(NetworkStatsTestUDP, VerifyBytesInvalidChecksum);
FRIEND_TEST_ALL_PREFIXES(NetworkStatsTestUDP, VerifyBytesPreviousPacket);
FRIEND_TEST_ALL_PREFIXES(NetworkStatsTestUDP, VerifyBytesInvalidPacketNumber);
FRIEND_TEST_ALL_PREFIXES(NetworkStatsTestUDP, VerifyBytesPatternChanged);
FRIEND_TEST_ALL_PREFIXES(NetworkStatsTestUDP, VerifyBytes);
// Starts the test specified by the |next_test_|. It also resets all the book
// keeping data, before starting the new test.
void RestartPacketTest();
// Initializes |finished_callback_| and the number of bytes to send to the
// server. |finished_callback| is called when we are done with the test.
// |finished_callback| is mainly useful for unittests.
void Initialize(uint32 bytes_to_send,
HistogramPortSelector histogram_port,
bool has_proxy_server,
uint32 packets_to_send,
const net::CompletionCallback& finished_callback);
// Resets all the counters and the collected stats.
void ResetData();
// Callback that is called when host resolution is completed.
void OnResolveComplete(int result);
// Called after host is resolved. Creates UDPClientSocket and connects to the
// server. If successfully connected, then calls ConnectComplete() to start
// the network connectivity tests. Returns |false| if there is any error.
bool DoConnect(int result);
// This method is called after socket connection is completed. It will start
// the process of sending packets to |server| by calling SendPacket(). Returns
// false if connection is not established (result is less than 0).
bool ConnectComplete(int result);
// This method is called whenever we need to send a packet. It is called from
// either ConnectComplete or OnWriteComplete. It will send a packet, based on
// load_size_, to |server| by calling SendData(). If there are no more packets
// to send, it calls ReadData() to read/verify the data from the |server|.
void SendPacket();
// Sends the next packet by calling |SendPacket| after a delay. Delay time is
// calculated based on the remaining packets and the remaining time.
// For START_PACKET_TEST and NON_PACED_PACKET_TEST delay is zero. It also
// yields for packets received between sends.
void SendNextPacketAfterDelay();
// Verifies the data and calls Finish() if there is a significant network
// error or if all packets are sent. Returns true if Finish() is called
// otherwise returns false.
bool ReadComplete(int result);
// Callbacks when an internal IO is completed.
void OnReadComplete(int result);
void OnWriteComplete(int result);
// Reads data from server until an error occurs.
void ReadData();
// Sends data to server until an error occurs.
int SendData();
// Determine the size of the packet from |load_size_|. The packet size
// includes |load_size_| plus the header size.
uint32 SendingPacketSize() const;
uint32 ReceivingPacketSize() const;
// This method decrements the |bytes_to_send_| by the |bytes_sent| and updates
// |packets_sent_| if all the bytes are sent. It also informs |write_buffer_|
// that data has been consumed.
void DidSendData(int bytes_sent);
// We set a timeout for responses from the echo servers.
void StartReadDataTimer(int milliseconds);
// Called when the ReadData Timer fires. |test_base_packet_number| specifies
// the |base_packet_number_| when we have started the timer. If we haven't
// received all the packets for the test (test took longer than 30 secs to
// run), then we call Finish() to finish processing. If we have already
// received all packets from the server, then this method is a no-op.
void OnReadDataTimeout(uint32 test_base_packet_number);
// Returns the checksum for the message.
uint32 GetChecksum(const char* message, uint32 message_length);
// Encrypts/decrypts the data with the key and returns encrypted/decrypted
// data in |encoded_data|.
void Crypt(const char* key,
uint32 key_length,
const char* data,
uint32 data_length,
char* encoded_data);
// Fills the |io_buffer| with the "echo request" message. This gets the
// <payload> from |stream_| and calculates the <checksum> of the <payload> and
// returns the "echo request" that has <version>, <checksum>, <payload_size>
// and <payload>. Every <payload> has a unique packet number stored in it.
void GetEchoRequest(net::IOBufferWithSize* io_buffer);
// This method verifies that we have received all the packets we have sent. It
// verifies the |encoded_message_| by calling VerifyBytes() for each packet
// that is in it. It returns SUCCESS, if all the packets are verified.
NetworkStats::Status VerifyPackets();
// This method parses the "echo response" message in the |response| to verify
// that the <payload> is same as what we had sent in "echo request" message.
// As it verifies the response in each packet, it also extracts the packet
// number, and records that said packet number responded. It returns SUCCESS,
// if all the bytes are verified.
NetworkStats::Status VerifyBytes(const std::string& response);
// Collects network connectivity stats. This is called when all the data from
// server is read or when there is a failure during connect/read/write. It
// will either restart the second phase of the test, or it will self destruct
// at the end of this method.
void Finish(Status status, int result);
// This method is called from Finish() and calls |finished_callback_| callback
// to indicate that the test has finished.
void DoFinishCallback(int result);
// Collect the network connectivity stats by calling RecordRTTHistograms(),
// RecordAcksReceivedHistograms() and RecordPacketLossSeriesHistograms(). See
// below for details.
void RecordHistograms(const ProtocolValue& protocol,
const Status& status,
int result);
// Collect the following network connectivity stats when
// kMaximumSequentialPackets (21) packets are sent.
// a) Received the "echo response" for at least one packet.
// b) Received the "echo response" for the nth packet.
// c) Count the number of "echo responses" received for each of the initial
// sequences of packets 1...n.
void RecordAcksReceivedHistograms(const std::string& load_size_string);
// Collect the following network connectivity stats for
// kMaximumCorrelationPackets (6) packets test.
// a) Success/failure of each packet, to estimate reachability for users.
// b) Records if there is a probabalistic dependency in packet loss when
// kMaximumCorrelationPackets packets are sent consecutively.
void RecordPacketLossSeriesHistograms(const ProtocolValue& protocol,
const std::string& load_size_string,
const Status& status,
int result);
// Collects the RTT for the packet specified by the |index|.
void RecordRTTHistograms(const ProtocolValue& protocol,
const std::string& load_size_string,
uint32 index);
uint32 load_size() const { return load_size_; }
// Returns string representation of test_type_.
const char* TestName();
uint32 packet_number() const { return packet_number_; }
uint32 base_packet_number() const { return base_packet_number_; }
uint32 set_base_packet_number(uint32 packet_number) {
return base_packet_number_ = packet_number;
}
net::Socket* socket() { return socket_.get(); }
void set_socket(net::Socket* socket);
const net::AddressList& addresses() const { return addresses_; }
uint32 packets_received_mask() const { return packets_received_mask_; }
TestType next_test() const { return next_test_; }
// The socket handle for this session.
scoped_ptr<net::Socket> socket_;
// The read buffer used to read data from the socket.
scoped_refptr<net::IOBuffer> read_buffer_;
// The write buffer used to write data to the socket.
scoped_refptr<net::DrainableIOBuffer> write_buffer_;
// Some counters for the session.
uint32 load_size_;
uint32 bytes_to_read_;
uint32 bytes_to_send_;
// |stream_| is used to generate data to be sent to the server and it is also
// used to verify the data received from the server.
net::TestDataStream stream_;
// |histogram_port_| specifies the port for which we are testing the network
// connectivity.
HistogramPortSelector histogram_port_;
// |has_proxy_server_| specifies if there is a proxy server or not.
bool has_proxy_server_;
// HostResolver fills out the |addresses_| after host resolution is completed.
net::AddressList addresses_;
// Callback to call when echo protocol is successefully finished or whenever
// there is an error (this allows unittests to wait until echo protocol's
// round trip is finished).
net::CompletionCallback finished_callback_;
// Collects the Status and RTT for each packet.
std::vector<PacketStatus> packet_status_;
// The time when we have received 1st byte from the server.
base::TimeTicks packet_1st_byte_read_time_;
// The last time when we have received data from the server.
base::TimeTicks packet_last_byte_read_time_;
// This is the average time it took to receive a packet from the server.
base::TimeDelta average_time_;
// Data to track number of packets to send to the server and the packets we
// have received from the server.
uint32 packets_to_send_; // Numbers of packets that are to be sent.
uint32 packets_sent_; // Numbers of packets sent to the server.
uint32 packets_received_; // Number of packets successfully received.
uint32 packets_received_mask_; // Tracks the received status of each packet.
uint32 packet_number_; // The packet number being sent to the server.
uint32 base_packet_number_; // The packet number before the test.
bool sending_complete_; // Finished sending all packets.
TestType current_test_; // Current test that is running.
TestType next_test_; // Next test that is to be run.
// We use this factory to create timeout tasks for socket's ReadData.
base::WeakPtrFactory<NetworkStats> weak_factory_;
};
class ProxyDetector {
public:
// Used for the callback that is called from |OnResolveProxyComplete|.
typedef base::Callback<void(bool)> OnResolvedCallback;
// Constructs a ProxyDetector object that finds out if access to
// |server_address| goes through a proxy server or not. Calls the |callback|
// after proxy resolution is completed by currying the proxy resolution
// status.
ProxyDetector(net::ProxyService* proxy_service,
const net::HostPortPair& server_address,
OnResolvedCallback callback);
// This method uses |proxy_service_| to resolve the proxy for
// |server_address_|.
void StartResolveProxy();
private:
// This object is deleted from |OnResolveProxyComplete|.
~ProxyDetector();
// Calls the |callback_| by currying the proxy resolution status.
void OnResolveProxyComplete(int result);
// |proxy_service_| specifies the proxy service that is to be used to find
// if access to |server_address_| goes through proxy server or not.
net::ProxyService* proxy_service_;
// |server_address_| specifies the server host and port pair for which we are
// trying to see if access to it, goes through proxy or not.
net::HostPortPair server_address_;
// |callback_| will be called after proxy resolution is completed.
OnResolvedCallback callback_;
// |proxy_info_| holds proxy information returned by ResolveProxy.
net::ProxyInfo proxy_info_;
// Indicates if there is a pending a proxy resolution. We use this to assert
// that there is no in-progress proxy resolution request.
bool has_pending_proxy_resolution_;
};
// This collects the network connectivity stats for UDP protocol for small
// percentage of users who are participating in the experiment. All users must
// have enabled "UMA upload". This method gets called only if UMA upload to the
// server has succeeded.
void CollectNetworkStats(const std::string& network_stats_server_url,
IOThread* io_thread);
// This starts a test randomly selected among "6 packets correlation test for
// 1200 bytes packet", "21 packets bursty test with 1200 bytes packet",
// "21 packets bursty test with 500 bytes packet", and "21 packets bursty test
// with 100 bytes packet" tests.
void StartNetworkStatsTest(net::HostResolver* host_resolver,
const net::HostPortPair& server_address,
NetworkStats::HistogramPortSelector histogram_port,
bool has_proxy_server);
} // namespace chrome_browser_net
#endif // CHROME_BROWSER_NET_NETWORK_STATS_H_