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