fetch_client.cc revision c407dc5cd9bdc5668497f21b26b09d988ab439de
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/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_auth_handler_factory.h"
19#include "net/http/http_cache.h"
20#include "net/http/http_network_layer.h"
21#include "net/http/http_request_info.h"
22#include "net/http/http_transaction.h"
23#include "net/proxy/proxy_service.h"
24#include "net/socket/client_socket_factory.h"
25
26void usage(const char* program_name) {
27  printf("usage: %s --url=<url>  [--n=<clients>] [--stats] [--use_cache]\n",
28         program_name);
29  exit(1);
30}
31
32// Test Driver
33class Driver {
34 public:
35  Driver()
36      : clients_(0) {}
37
38  void ClientStarted() { clients_++; }
39  void ClientStopped() {
40    if (!--clients_) {
41      MessageLoop::current()->Quit();
42    }
43  }
44
45 private:
46  int clients_;
47};
48
49// A network client
50class Client {
51 public:
52  Client(net::HttpTransactionFactory* factory, const std::string& url) :
53      url_(url),
54      buffer_(new net::IOBuffer(kBufferSize)),
55      ALLOW_THIS_IN_INITIALIZER_LIST(
56          connect_callback_(this, &Client::OnConnectComplete)),
57      ALLOW_THIS_IN_INITIALIZER_LIST(
58          read_callback_(this, &Client::OnReadComplete)) {
59    int rv = factory->CreateTransaction(&transaction_);
60    DCHECK_EQ(net::OK, rv);
61    buffer_->AddRef();
62    driver_->ClientStarted();
63    request_info_.url = url_;
64    request_info_.method = "GET";
65    int state = transaction_->Start(
66        &request_info_, &connect_callback_, net::BoundNetLog());
67    DCHECK(state == net::ERR_IO_PENDING);
68  };
69
70 private:
71  void OnConnectComplete(int result) {
72    // Do work here.
73    int state = transaction_->Read(buffer_.get(), kBufferSize, &read_callback_);
74    if (state == net::ERR_IO_PENDING)
75      return;  // IO has started.
76    if (state < 0)
77      return;  // ERROR!
78    OnReadComplete(state);
79  }
80
81  void OnReadComplete(int result) {
82    if (result == 0) {
83      OnRequestComplete(result);
84      return;
85    }
86
87    // Deal with received data here.
88    static StatsCounter bytes_read("FetchClient.bytes_read");
89    bytes_read.Add(result);
90
91    // Issue a read for more data.
92    int state = transaction_->Read(buffer_.get(), kBufferSize, &read_callback_);
93    if (state == net::ERR_IO_PENDING)
94      return;  // IO has started.
95    if (state < 0)
96      return;  // ERROR!
97    OnReadComplete(state);
98  }
99
100  void OnRequestComplete(int result) {
101    static StatsCounter requests("FetchClient.requests");
102    requests.Increment();
103    driver_->ClientStopped();
104    printf(".");
105  }
106
107  static const int kBufferSize = (16 * 1024);
108  GURL url_;
109  net::HttpRequestInfo request_info_;
110  scoped_ptr<net::HttpTransaction> transaction_;
111  scoped_refptr<net::IOBuffer> buffer_;
112  net::CompletionCallbackImpl<Client> connect_callback_;
113  net::CompletionCallbackImpl<Client> read_callback_;
114  Singleton<Driver> driver_;
115};
116
117int main(int argc, char**argv) {
118  base::AtExitManager exit;
119  StatsTable table("fetchclient", 50, 1000);
120  table.set_current(&table);
121
122  CommandLine::Init(argc, argv);
123  const CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess();
124  std::string url = parsed_command_line.GetSwitchValueASCII("url");
125  if (!url.length())
126    usage(argv[0]);
127  int client_limit = 1;
128  if (parsed_command_line.HasSwitch("n"))
129    StringToInt(parsed_command_line.GetSwitchValueASCII("n"), &client_limit);
130  bool use_cache = parsed_command_line.HasSwitch("use-cache");
131
132  // Do work here.
133  MessageLoop loop(MessageLoop::TYPE_IO);
134
135  scoped_refptr<net::HostResolver> host_resolver(
136      net::CreateSystemHostResolver(net::HostResolver::kDefaultParallelism));
137
138  scoped_refptr<net::ProxyService> proxy_service(
139      net::ProxyService::CreateNull());
140  scoped_refptr<net::SSLConfigService> ssl_config_service(
141      net::SSLConfigService::CreateSystemSSLConfigService());
142  net::HttpTransactionFactory* factory = NULL;
143  scoped_ptr<net::HttpAuthHandlerFactory> http_auth_handler_factory(
144      net::HttpAuthHandlerFactory::CreateDefault());
145  if (use_cache) {
146    factory = new net::HttpCache(host_resolver, proxy_service,
147        ssl_config_service, http_auth_handler_factory.get(), NULL, NULL,
148        net::HttpCache::DefaultBackend::InMemory(0));
149  } else {
150    factory = new net::HttpNetworkLayer(
151        net::ClientSocketFactory::GetDefaultFactory(), host_resolver,
152        proxy_service, ssl_config_service, http_auth_handler_factory.get(),
153        NULL, NULL);
154  }
155
156  {
157    StatsCounterTimer driver_time("FetchClient.total_time");
158    StatsScope<StatsCounterTimer> scope(driver_time);
159
160    Client** clients = new Client*[client_limit];
161    for (int i = 0; i < client_limit; i++)
162      clients[i] = new Client(factory, url);
163
164    MessageLoop::current()->Run();
165  }
166
167  // Print Statistics here.
168  int num_clients = table.GetCounterValue("c:FetchClient.requests");
169  int test_time = table.GetCounterValue("t:FetchClient.total_time");
170  int bytes_read = table.GetCounterValue("c:FetchClient.bytes_read");
171
172  printf("\n");
173  printf("Clients     : %d\n", num_clients);
174  printf("Time        : %dms\n", test_time);
175  printf("Bytes Read  : %d\n", bytes_read);
176  if (test_time > 0) {
177    const char *units = "bps";
178    double bps = static_cast<float>(bytes_read * 8) /
179        (static_cast<float>(test_time) / 1000.0);
180
181    if (bps > (1024*1024)) {
182      bps /= (1024*1024);
183      units = "Mbps";
184    } else if (bps > 1024) {
185      bps /= 1024;
186      units = "Kbps";
187    }
188    printf("Bandwidth   : %.2f%s\n", bps, units);
189  }
190
191  if (parsed_command_line.HasSwitch("stats")) {
192    // Dump the stats table.
193    printf("<stats>\n");
194    int counter_max = table.GetMaxCounters();
195    for (int index=0; index < counter_max; index++) {
196      std::string name(table.GetRowName(index));
197      if (name.length() > 0) {
198        int value = table.GetRowValue(index);
199        printf("%s:\t%d\n", name.c_str(), value);
200      }
201    }
202    printf("</stats>\n");
203  }
204  return 0;
205}
206