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