text: // 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/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:
  // <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 used to find the IP addresses.
    scoped_ptr<net::SingleRequestHostResolver> 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<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_
  