Home | History | Annotate | Download | only in fetch
      1 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "build/build_config.h"
      6 
      7 #include "base/at_exit.h"
      8 #include "base/command_line.h"
      9 #include "base/lazy_instance.h"
     10 #include "base/message_loop.h"
     11 #include "base/metrics/stats_counters.h"
     12 #include "base/string_number_conversions.h"
     13 #include "base/string_util.h"
     14 #include "net/base/cert_verifier.h"
     15 #include "net/base/completion_callback.h"
     16 #include "net/base/host_resolver.h"
     17 #include "net/base/io_buffer.h"
     18 #include "net/base/net_errors.h"
     19 #include "net/base/ssl_config_service.h"
     20 #include "net/http/http_auth_handler_factory.h"
     21 #include "net/http/http_cache.h"
     22 #include "net/http/http_network_layer.h"
     23 #include "net/http/http_network_session.h"
     24 #include "net/http/http_request_info.h"
     25 #include "net/http/http_transaction.h"
     26 #include "net/proxy/proxy_service.h"
     27 
     28 void usage(const char* program_name) {
     29   printf("usage: %s --url=<url>  [--n=<clients>] [--stats] [--use_cache]\n",
     30          program_name);
     31   exit(1);
     32 }
     33 
     34 // Test Driver
     35 class Driver {
     36  public:
     37   Driver()
     38       : clients_(0) {}
     39 
     40   void ClientStarted() { clients_++; }
     41   void ClientStopped() {
     42     if (!--clients_) {
     43       MessageLoop::current()->Quit();
     44     }
     45   }
     46 
     47  private:
     48   int clients_;
     49 };
     50 
     51 static base::LazyInstance<Driver> g_driver(base::LINKER_INITIALIZED);
     52 
     53 // A network client
     54 class Client {
     55  public:
     56   Client(net::HttpTransactionFactory* factory, const std::string& url) :
     57       url_(url),
     58       buffer_(new net::IOBuffer(kBufferSize)),
     59       ALLOW_THIS_IN_INITIALIZER_LIST(
     60           connect_callback_(this, &Client::OnConnectComplete)),
     61       ALLOW_THIS_IN_INITIALIZER_LIST(
     62           read_callback_(this, &Client::OnReadComplete)) {
     63     int rv = factory->CreateTransaction(&transaction_);
     64     DCHECK_EQ(net::OK, rv);
     65     buffer_->AddRef();
     66     g_driver.Get().ClientStarted();
     67     request_info_.url = url_;
     68     request_info_.method = "GET";
     69     int state = transaction_->Start(
     70         &request_info_, &connect_callback_, net::BoundNetLog());
     71     DCHECK(state == net::ERR_IO_PENDING);
     72   };
     73 
     74  private:
     75   void OnConnectComplete(int result) {
     76     // Do work here.
     77     int state = transaction_->Read(buffer_.get(), kBufferSize, &read_callback_);
     78     if (state == net::ERR_IO_PENDING)
     79       return;  // IO has started.
     80     if (state < 0)
     81       return;  // ERROR!
     82     OnReadComplete(state);
     83   }
     84 
     85   void OnReadComplete(int result) {
     86     if (result == 0) {
     87       OnRequestComplete(result);
     88       return;
     89     }
     90 
     91     // Deal with received data here.
     92     base::StatsCounter bytes_read("FetchClient.bytes_read");
     93     bytes_read.Add(result);
     94 
     95     // Issue a read for more data.
     96     int state = transaction_->Read(buffer_.get(), kBufferSize, &read_callback_);
     97     if (state == net::ERR_IO_PENDING)
     98       return;  // IO has started.
     99     if (state < 0)
    100       return;  // ERROR!
    101     OnReadComplete(state);
    102   }
    103 
    104   void OnRequestComplete(int result) {
    105     base::StatsCounter requests("FetchClient.requests");
    106     requests.Increment();
    107     g_driver.Get().ClientStopped();
    108     printf(".");
    109   }
    110 
    111   static const int kBufferSize = (16 * 1024);
    112   GURL url_;
    113   net::HttpRequestInfo request_info_;
    114   scoped_ptr<net::HttpTransaction> transaction_;
    115   scoped_refptr<net::IOBuffer> buffer_;
    116   net::CompletionCallbackImpl<Client> connect_callback_;
    117   net::CompletionCallbackImpl<Client> read_callback_;
    118 };
    119 
    120 int main(int argc, char**argv) {
    121   base::AtExitManager exit;
    122   base::StatsTable table("fetchclient", 50, 1000);
    123   table.set_current(&table);
    124 
    125   CommandLine::Init(argc, argv);
    126   const CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess();
    127   std::string url = parsed_command_line.GetSwitchValueASCII("url");
    128   if (!url.length())
    129     usage(argv[0]);
    130   int client_limit = 1;
    131   if (parsed_command_line.HasSwitch("n")) {
    132     base::StringToInt(parsed_command_line.GetSwitchValueASCII("n"),
    133                       &client_limit);
    134   }
    135   bool use_cache = parsed_command_line.HasSwitch("use-cache");
    136 
    137   // Do work here.
    138   MessageLoop loop(MessageLoop::TYPE_IO);
    139 
    140   scoped_ptr<net::HostResolver> host_resolver(
    141       net::CreateSystemHostResolver(net::HostResolver::kDefaultParallelism,
    142                                     NULL, NULL));
    143 
    144   scoped_ptr<net::CertVerifier> cert_verifier(new net::CertVerifier);
    145   scoped_refptr<net::ProxyService> proxy_service(
    146       net::ProxyService::CreateDirect());
    147   scoped_refptr<net::SSLConfigService> ssl_config_service(
    148       net::SSLConfigService::CreateSystemSSLConfigService());
    149   net::HttpTransactionFactory* factory = NULL;
    150   scoped_ptr<net::HttpAuthHandlerFactory> http_auth_handler_factory(
    151       net::HttpAuthHandlerFactory::CreateDefault(host_resolver.get()));
    152 
    153   net::HttpNetworkSession::Params session_params;
    154   session_params.host_resolver = host_resolver.get();
    155   session_params.cert_verifier = cert_verifier.get();
    156   session_params.proxy_service = proxy_service;
    157   session_params.http_auth_handler_factory = http_auth_handler_factory.get();
    158   session_params.ssl_config_service = ssl_config_service;
    159 
    160   scoped_refptr<net::HttpNetworkSession> network_session(
    161       new net::HttpNetworkSession(session_params));
    162   if (use_cache) {
    163     factory = new net::HttpCache(network_session,
    164                                  net::HttpCache::DefaultBackend::InMemory(0));
    165   } else {
    166     factory = new net::HttpNetworkLayer(network_session);
    167   }
    168 
    169   {
    170     base::StatsCounterTimer driver_time("FetchClient.total_time");
    171     base::StatsScope<base::StatsCounterTimer> scope(driver_time);
    172 
    173     Client** clients = new Client*[client_limit];
    174     for (int i = 0; i < client_limit; i++)
    175       clients[i] = new Client(factory, url);
    176 
    177     MessageLoop::current()->Run();
    178   }
    179 
    180   // Print Statistics here.
    181   int num_clients = table.GetCounterValue("c:FetchClient.requests");
    182   int test_time = table.GetCounterValue("t:FetchClient.total_time");
    183   int bytes_read = table.GetCounterValue("c:FetchClient.bytes_read");
    184 
    185   printf("\n");
    186   printf("Clients     : %d\n", num_clients);
    187   printf("Time        : %dms\n", test_time);
    188   printf("Bytes Read  : %d\n", bytes_read);
    189   if (test_time > 0) {
    190     const char *units = "bps";
    191     double bps = static_cast<float>(bytes_read * 8) /
    192         (static_cast<float>(test_time) / 1000.0);
    193 
    194     if (bps > (1024*1024)) {
    195       bps /= (1024*1024);
    196       units = "Mbps";
    197     } else if (bps > 1024) {
    198       bps /= 1024;
    199       units = "Kbps";
    200     }
    201     printf("Bandwidth   : %.2f%s\n", bps, units);
    202   }
    203 
    204   if (parsed_command_line.HasSwitch("stats")) {
    205     // Dump the stats table.
    206     printf("<stats>\n");
    207     int counter_max = table.GetMaxCounters();
    208     for (int index = 0; index < counter_max; index++) {
    209       std::string name(table.GetRowName(index));
    210       if (name.length() > 0) {
    211         int value = table.GetRowValue(index);
    212         printf("%s:\t%d\n", name.c_str(), value);
    213       }
    214     }
    215     printf("</stats>\n");
    216   }
    217   return 0;
    218 }
    219