fetch_client.cc revision 72a454cd3513ac24fbdd0e0cb9ad70b86a99b801
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
28void 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
35class 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
51static base::LazyInstance<Driver> g_driver(base::LINKER_INITIALIZED);
52
53// A network client
54class 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    static 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    static 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
120int 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