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