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
34void 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
41class 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
57static base::LazyInstance<Driver> g_driver = LAZY_INSTANCE_INITIALIZER;
58
59// A network client
60class 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
127int 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