quic_client.cc revision 0f1bc08d4cfcc34181b0b5cbf065c40f687bf740
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 "net/tools/quic/quic_client.h"
6
7#include <errno.h>
8#include <netinet/in.h>
9#include <string.h>
10#include <sys/epoll.h>
11#include <sys/socket.h>
12#include <unistd.h>
13
14#include "base/logging.h"
15#include "net/quic/crypto/quic_random.h"
16#include "net/quic/quic_connection.h"
17#include "net/quic/quic_data_reader.h"
18#include "net/quic/quic_protocol.h"
19#include "net/tools/balsa/balsa_headers.h"
20#include "net/tools/quic/quic_epoll_connection_helper.h"
21#include "net/tools/quic/quic_reliable_client_stream.h"
22#include "net/tools/quic/quic_socket_utils.h"
23
24#ifndef SO_RXQ_OVFL
25#define SO_RXQ_OVFL 40
26#endif
27
28namespace net {
29namespace tools {
30
31const int kEpollFlags = EPOLLIN | EPOLLOUT | EPOLLET;
32
33QuicClient::QuicClient(IPEndPoint server_address,
34                       const string& server_hostname,
35                       const QuicVersionVector& supported_versions,
36                       bool print_response)
37    : server_address_(server_address),
38      server_hostname_(server_hostname),
39      local_port_(0),
40      fd_(-1),
41      helper_(CreateQuicConnectionHelper()),
42      initialized_(false),
43      packets_dropped_(0),
44      overflow_supported_(false),
45      supported_versions_(supported_versions),
46      print_response_(print_response) {
47  config_.SetDefaults();
48  // TODO(ianswett): Allow the client to change the server's max packet size and
49  // initial congestion window.
50  config_.set_server_max_packet_size(kDefaultMaxPacketSize,
51                                     kDefaultMaxPacketSize);
52  config_.set_server_initial_congestion_window(kDefaultInitialWindow,
53                                               kDefaultInitialWindow);
54}
55
56QuicClient::QuicClient(IPEndPoint server_address,
57                       const string& server_hostname,
58                       const QuicConfig& config,
59                       const QuicVersionVector& supported_versions)
60    : server_address_(server_address),
61      server_hostname_(server_hostname),
62      config_(config),
63      local_port_(0),
64      fd_(-1),
65      helper_(CreateQuicConnectionHelper()),
66      initialized_(false),
67      packets_dropped_(0),
68      overflow_supported_(false),
69      supported_versions_(supported_versions),
70      print_response_(false) {
71}
72
73QuicClient::~QuicClient() {
74  if (connected()) {
75    session()->connection()->SendConnectionClosePacket(
76        QUIC_PEER_GOING_AWAY, "");
77  }
78}
79
80bool QuicClient::Initialize() {
81  DCHECK(!initialized_);
82
83  epoll_server_.set_timeout_in_us(50 * 1000);
84  crypto_config_.SetDefaults();
85
86  int address_family = server_address_.GetSockAddrFamily();
87  fd_ = socket(address_family, SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP);
88  if (fd_ < 0) {
89    LOG(ERROR) << "CreateSocket() failed: " << strerror(errno);
90    return false;
91  }
92
93  int get_overflow = 1;
94  int rc = setsockopt(fd_, SOL_SOCKET, SO_RXQ_OVFL, &get_overflow,
95                      sizeof(get_overflow));
96  if (rc < 0) {
97    DLOG(WARNING) << "Socket overflow detection not supported";
98  } else {
99    overflow_supported_ = true;
100  }
101
102  int get_local_ip = 1;
103  if (address_family == AF_INET) {
104    rc = setsockopt(fd_, IPPROTO_IP, IP_PKTINFO,
105                    &get_local_ip, sizeof(get_local_ip));
106  } else {
107    rc =  setsockopt(fd_, IPPROTO_IPV6, IPV6_RECVPKTINFO,
108                     &get_local_ip, sizeof(get_local_ip));
109  }
110
111  if (rc < 0) {
112    LOG(ERROR) << "IP detection not supported" << strerror(errno);
113    return false;
114  }
115
116  if (bind_to_address_.size() != 0) {
117    client_address_ = IPEndPoint(bind_to_address_, local_port_);
118  } else if (address_family == AF_INET) {
119    IPAddressNumber any4;
120    CHECK(net::ParseIPLiteralToNumber("0.0.0.0", &any4));
121    client_address_ = IPEndPoint(any4, local_port_);
122  } else {
123    IPAddressNumber any6;
124    CHECK(net::ParseIPLiteralToNumber("::", &any6));
125    client_address_ = IPEndPoint(any6, local_port_);
126  }
127
128  sockaddr_storage raw_addr;
129  socklen_t raw_addr_len = sizeof(raw_addr);
130  CHECK(client_address_.ToSockAddr(reinterpret_cast<sockaddr*>(&raw_addr),
131                           &raw_addr_len));
132  rc = bind(fd_,
133            reinterpret_cast<const sockaddr*>(&raw_addr),
134            sizeof(raw_addr));
135  if (rc < 0) {
136    LOG(ERROR) << "Bind failed: " << strerror(errno);
137    return false;
138  }
139
140  SockaddrStorage storage;
141  if (getsockname(fd_, storage.addr, &storage.addr_len) != 0 ||
142      !client_address_.FromSockAddr(storage.addr, storage.addr_len)) {
143    LOG(ERROR) << "Unable to get self address.  Error: " << strerror(errno);
144  }
145
146  epoll_server_.RegisterFD(fd_, this, kEpollFlags);
147  initialized_ = true;
148  return true;
149}
150
151bool QuicClient::Connect() {
152  if (!StartConnect()) {
153    return false;
154  }
155  while (EncryptionBeingEstablished()) {
156    WaitForEvents();
157  }
158  return session_->connection()->connected();
159}
160
161bool QuicClient::StartConnect() {
162  DCHECK(!connected() && initialized_);
163
164  QuicPacketWriter* writer = CreateQuicPacketWriter();
165  if (writer_.get() != writer) {
166    writer_.reset(writer);
167  }
168
169  session_.reset(new QuicClientSession(
170      server_hostname_,
171      config_,
172      new QuicConnection(GenerateGuid(), server_address_, helper_.get(),
173                         writer_.get(), false, supported_versions_),
174      &crypto_config_));
175  return session_->CryptoConnect();
176}
177
178bool QuicClient::EncryptionBeingEstablished() {
179  return !session_->IsEncryptionEstablished() &&
180      session_->connection()->connected();
181}
182
183void QuicClient::Disconnect() {
184  DCHECK(connected());
185
186  session()->connection()->SendConnectionClose(QUIC_PEER_GOING_AWAY);
187  epoll_server_.UnregisterFD(fd_);
188  close(fd_);
189  fd_ = -1;
190  initialized_ = false;
191}
192
193void QuicClient::SendRequestsAndWaitForResponse(
194    const CommandLine::StringVector& args) {
195  for (size_t i = 0; i < args.size(); i++) {
196    BalsaHeaders headers;
197    headers.SetRequestFirstlineFromStringPieces("GET", args[i], "HTTP/1.1");
198    QuicReliableClientStream* stream = CreateReliableClientStream();
199    stream->SendRequest(headers, "", true);
200    stream->set_visitor(this);
201  }
202
203  while (WaitForEvents()) { }
204}
205
206QuicReliableClientStream* QuicClient::CreateReliableClientStream() {
207  if (!connected()) {
208    return NULL;
209  }
210
211  return session_->CreateOutgoingReliableStream();
212}
213
214void QuicClient::WaitForStreamToClose(QuicStreamId id) {
215  DCHECK(connected());
216
217  while (!session_->IsClosedStream(id)) {
218    epoll_server_.WaitForEventsAndExecuteCallbacks();
219  }
220}
221
222void QuicClient::WaitForCryptoHandshakeConfirmed() {
223  DCHECK(connected());
224
225  while (!session_->IsCryptoHandshakeConfirmed()) {
226    epoll_server_.WaitForEventsAndExecuteCallbacks();
227  }
228}
229
230bool QuicClient::WaitForEvents() {
231  DCHECK(connected());
232
233  epoll_server_.WaitForEventsAndExecuteCallbacks();
234  return session_->num_active_requests() != 0;
235}
236
237void QuicClient::OnEvent(int fd, EpollEvent* event) {
238  DCHECK_EQ(fd, fd_);
239
240  if (event->in_events & EPOLLIN) {
241    while (connected() && ReadAndProcessPacket()) {
242    }
243  }
244  if (connected() && (event->in_events & EPOLLOUT)) {
245    session_->connection()->OnCanWrite();
246  }
247  if (event->in_events & EPOLLERR) {
248    DLOG(INFO) << "Epollerr";
249  }
250}
251
252void QuicClient::OnClose(ReliableQuicStream* stream) {
253  if (!print_response_) {
254    return;
255  }
256
257  QuicReliableClientStream* client_stream =
258      static_cast<QuicReliableClientStream*>(stream);
259  const BalsaHeaders& headers = client_stream->headers();
260  printf("%s\n", headers.first_line().as_string().c_str());
261  for (BalsaHeaders::const_header_lines_iterator i =
262           headers.header_lines_begin();
263       i != headers.header_lines_end(); ++i) {
264    printf("%s: %s\n", i->first.as_string().c_str(),
265           i->second.as_string().c_str());
266  }
267  printf("%s\n", client_stream->data().c_str());
268}
269
270QuicPacketCreator::Options* QuicClient::options() {
271  if (session() == NULL) {
272    return NULL;
273  }
274  return session_->options();
275}
276
277bool QuicClient::connected() const {
278  return session_.get() && session_->connection() &&
279      session_->connection()->connected();
280}
281
282QuicGuid QuicClient::GenerateGuid() {
283  return QuicRandom::GetInstance()->RandUint64();
284}
285
286QuicEpollConnectionHelper* QuicClient::CreateQuicConnectionHelper() {
287  return new QuicEpollConnectionHelper(&epoll_server_);
288}
289
290QuicPacketWriter* QuicClient::CreateQuicPacketWriter() {
291  return new QuicDefaultPacketWriter(fd_);
292}
293
294bool QuicClient::ReadAndProcessPacket() {
295  // Allocate some extra space so we can send an error if the server goes over
296  // the limit.
297  char buf[2 * kMaxPacketSize];
298
299  IPEndPoint server_address;
300  IPAddressNumber client_ip;
301
302  int bytes_read = QuicSocketUtils::ReadPacket(
303      fd_, buf, arraysize(buf), overflow_supported_ ? &packets_dropped_ : NULL,
304      &client_ip, &server_address);
305
306  if (bytes_read < 0) {
307    return false;
308  }
309
310  QuicEncryptedPacket packet(buf, bytes_read, false);
311  QuicGuid our_guid = session_->connection()->guid();
312  QuicGuid packet_guid;
313
314  if (!QuicFramer::ReadGuidFromPacket(packet, &packet_guid)) {
315    DLOG(INFO) << "Could not read GUID from packet";
316    return true;
317  }
318  if (packet_guid != our_guid) {
319    DLOG(INFO) << "Ignoring packet from unexpected GUID: "
320               << packet_guid << " instead of " << our_guid;
321    return true;
322  }
323
324  IPEndPoint client_address(client_ip, client_address_.port());
325  session_->connection()->ProcessUdpPacket(
326      client_address, server_address, packet);
327  return true;
328}
329
330}  // namespace tools
331}  // namespace net
332