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