// 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 #include #include "base/basictypes.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/strings/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/proxy/proxy_info.h" #include "net/socket/socket.h" namespace net { class HostResolver; class SingleRequestHostResolver; } 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: // . is the version number // of the "echo request". is the checksum of the . // specifies the number of bytes in the . // // UDP echo servers respond to the "echo request" by returning "echo response". // "echo response" is of the format: // "". // specifies the number of bytes in the . is used to // decode the . 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 // from |stream_| and calculates the of the and // returns the "echo request" that has , , // and . Every 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 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 socket_; // The read buffer used to read data from the socket. scoped_refptr read_buffer_; // The write buffer used to write data to the socket. scoped_refptr 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 used to find the IP addresses. scoped_ptr resolver_; // 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 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 weak_factory_; }; class ProxyDetector { public: // Used for the callback that is called from |OnResolveProxyComplete|. typedef base::Callback 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_