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