blob: bf22225fd1aa080805efb88f8ad9fdaf33051ee0 [file] [log] [blame]
Paul Stewart188a84a2012-01-20 16:28:15 -08001// Copyright (c) 2012 The Chromium OS 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 "shill/http_request.h"
6
7#include <string>
8
Eric Shienbrood3e20a232012-02-16 11:35:56 -05009#include <base/bind.h>
Paul Stewart188a84a2012-01-20 16:28:15 -080010#include <base/logging.h>
11#include <base/string_number_conversions.h>
12#include <base/stringprintf.h>
13
14#include "shill/async_connection.h"
15#include "shill/connection.h"
16#include "shill/dns_client.h"
Paul Stewartbdb02e62012-02-22 16:24:33 -080017#include "shill/error.h"
Paul Stewart188a84a2012-01-20 16:28:15 -080018#include "shill/event_dispatcher.h"
19#include "shill/http_url.h"
20#include "shill/ip_address.h"
21#include "shill/sockets.h"
22
Eric Shienbrood3e20a232012-02-16 11:35:56 -050023using base::Bind;
24using base::Callback;
Paul Stewart188a84a2012-01-20 16:28:15 -080025using base::StringPrintf;
26using std::string;
27
28namespace shill {
29
30const int HTTPRequest::kConnectTimeoutSeconds = 10;
31const int HTTPRequest::kDNSTimeoutSeconds = 5;
32const int HTTPRequest::kInputTimeoutSeconds = 10;
33
34const char HTTPRequest::kHTTPRequestTemplate[] =
35 "GET %s HTTP/1.1\r\n"
36 "Host: %s:%d\r\n"
37 "Connection: Close\r\n\r\n";
38
39HTTPRequest::HTTPRequest(ConnectionRefPtr connection,
40 EventDispatcher *dispatcher,
41 Sockets *sockets)
42 : connection_(connection),
43 dispatcher_(dispatcher),
44 sockets_(sockets),
Eric Shienbrood3e20a232012-02-16 11:35:56 -050045 weak_ptr_factory_(this),
Paul Stewart188a84a2012-01-20 16:28:15 -080046 connect_completion_callback_(
Eric Shienbrood9a245532012-03-07 14:20:39 -050047 Bind(&HTTPRequest::OnConnectCompletion,
48 weak_ptr_factory_.GetWeakPtr())),
49 dns_client_callback_(Bind(&HTTPRequest::GetDNSResult,
50 weak_ptr_factory_.GetWeakPtr())),
Eric Shienbrood3e20a232012-02-16 11:35:56 -050051 read_server_callback_(Bind(&HTTPRequest::ReadFromServer,
Eric Shienbrood9a245532012-03-07 14:20:39 -050052 weak_ptr_factory_.GetWeakPtr())),
Eric Shienbrood3e20a232012-02-16 11:35:56 -050053 write_server_callback_(Bind(&HTTPRequest::WriteToServer,
Eric Shienbrood9a245532012-03-07 14:20:39 -050054 weak_ptr_factory_.GetWeakPtr())),
Paul Stewart188a84a2012-01-20 16:28:15 -080055 dns_client_(
56 new DNSClient(IPAddress::kFamilyIPv4,
57 connection->interface_name(),
58 connection->dns_servers(),
59 kDNSTimeoutSeconds * 1000,
60 dispatcher,
Eric Shienbrood3e20a232012-02-16 11:35:56 -050061 dns_client_callback_)),
Paul Stewart188a84a2012-01-20 16:28:15 -080062 server_async_connection_(
63 new AsyncConnection(connection_->interface_name(),
64 dispatcher_, sockets,
Eric Shienbrood3e20a232012-02-16 11:35:56 -050065 connect_completion_callback_)),
Paul Stewart188a84a2012-01-20 16:28:15 -080066 server_port_(-1),
67 server_socket_(-1),
68 timeout_result_(kResultUnknown),
69 is_running_(false) { }
70
71HTTPRequest::~HTTPRequest() {
72 Stop();
73}
74
75HTTPRequest::Result HTTPRequest::Start(
76 const HTTPURL &url,
Eric Shienbrood3e20a232012-02-16 11:35:56 -050077 const Callback<void(const ByteString &)> &read_event_callback,
78 const Callback<void(Result, const ByteString &)> &result_callback) {
Paul Stewart188a84a2012-01-20 16:28:15 -080079 VLOG(3) << "In " << __func__;
80
81 DCHECK(!is_running_);
82
83 is_running_ = true;
84 request_data_ = ByteString(StringPrintf(kHTTPRequestTemplate,
85 url.path().c_str(),
86 url.host().c_str(),
87 url.port()), false);
88 server_hostname_ = url.host();
89 server_port_ = url.port();
90 connection_->RequestRouting();
91
92 IPAddress addr(IPAddress::kFamilyIPv4);
93 if (addr.SetAddressFromString(server_hostname_)) {
94 if (!ConnectServer(addr, server_port_)) {
95 LOG(ERROR) << "Connect to "
96 << server_hostname_
97 << " failed synchronously";
98 return kResultConnectionFailure;
99 }
100 } else {
101 VLOG(3) << "Looking up host: " << server_hostname_;
Paul Stewartbdb02e62012-02-22 16:24:33 -0800102 Error error;
103 if (!dns_client_->Start(server_hostname_, &error)) {
104 LOG(ERROR) << "Failed to start DNS client: " << error.message();
Paul Stewart188a84a2012-01-20 16:28:15 -0800105 Stop();
106 return kResultDNSFailure;
107 }
108 }
109
110 // Only install callbacks after connection succeeds in starting.
111 read_event_callback_ = read_event_callback;
112 result_callback_ = result_callback;
113
114 return kResultInProgress;
115}
116
117void HTTPRequest::Stop() {
118 VLOG(3) << "In " << __func__ << "; running is " << is_running_;
119
120 if (!is_running_) {
121 return;
122 }
123
124 // Clear IO handlers first so that closing the socket doesn't cause
125 // events to fire.
126 write_server_handler_.reset();
127 read_server_handler_.reset();
128
129 connection_->ReleaseRouting();
130 dns_client_->Stop();
Paul Stewart188a84a2012-01-20 16:28:15 -0800131 is_running_ = false;
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500132 result_callback_.Reset();
133 read_event_callback_.Reset();
Paul Stewart188a84a2012-01-20 16:28:15 -0800134 request_data_.Clear();
135 response_data_.Clear();
136 server_async_connection_->Stop();
137 server_hostname_.clear();
138 server_port_ = -1;
139 if (server_socket_ != -1) {
140 sockets_->Close(server_socket_);
141 server_socket_ = -1;
142 }
Paul Stewartf582b502012-04-04 21:39:22 -0700143 timeout_closure_.Cancel();
Paul Stewart188a84a2012-01-20 16:28:15 -0800144 timeout_result_ = kResultUnknown;
145}
146
147bool HTTPRequest::ConnectServer(const IPAddress &address, int port) {
148 VLOG(3) << "In " << __func__;
149 if (!server_async_connection_->Start(address, port)) {
Paul Stewart188a84a2012-01-20 16:28:15 -0800150 LOG(ERROR) << "Could not create socket to connect to server at "
mukesh agrawal2c15d2c2012-02-21 16:09:21 -0800151 << address.ToString();
Paul Stewart188a84a2012-01-20 16:28:15 -0800152 SendStatus(kResultConnectionFailure);
153 return false;
154 }
155 // Start a connection timeout only if we didn't synchronously connect.
156 if (server_socket_ == -1) {
157 StartIdleTimeout(kConnectTimeoutSeconds, kResultConnectionTimeout);
158 }
159 return true;
160}
161
162// DNSClient callback that fires when the DNS request completes.
Paul Stewartbdb02e62012-02-22 16:24:33 -0800163void HTTPRequest::GetDNSResult(const Error &error, const IPAddress &address) {
Paul Stewart188a84a2012-01-20 16:28:15 -0800164 VLOG(3) << "In " << __func__;
Paul Stewartbdb02e62012-02-22 16:24:33 -0800165 if (!error.IsSuccess()) {
Paul Stewart188a84a2012-01-20 16:28:15 -0800166 LOG(ERROR) << "Could not resolve hostname "
167 << server_hostname_
168 << ": "
Paul Stewartbdb02e62012-02-22 16:24:33 -0800169 << error.message();
170 if (error.message() == DNSClient::kErrorTimedOut) {
Paul Stewart188a84a2012-01-20 16:28:15 -0800171 SendStatus(kResultDNSTimeout);
172 } else {
173 SendStatus(kResultDNSFailure);
174 }
175 return;
176 }
Paul Stewartbdb02e62012-02-22 16:24:33 -0800177 ConnectServer(address, server_port_);
Paul Stewart188a84a2012-01-20 16:28:15 -0800178}
179
180// AsyncConnection callback routine which fires when the asynchronous Connect()
181// to the remote server completes (or fails).
182void HTTPRequest::OnConnectCompletion(bool success, int fd) {
183 VLOG(3) << "In " << __func__;
184 if (!success) {
185 LOG(ERROR) << "Socket connection delayed failure to "
186 << server_hostname_
187 << ": "
188 << server_async_connection_->error();
189 SendStatus(kResultConnectionFailure);
190 return;
191 }
192 server_socket_ = fd;
193 write_server_handler_.reset(
194 dispatcher_->CreateReadyHandler(server_socket_,
195 IOHandler::kModeOutput,
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500196 write_server_callback_));
Paul Stewart188a84a2012-01-20 16:28:15 -0800197 StartIdleTimeout(kInputTimeoutSeconds, kResultRequestTimeout);
198}
199
200// IOInputHandler callback which fires when data has been read from the
201// server.
202void HTTPRequest::ReadFromServer(InputData *data) {
203 VLOG(3) << "In " << __func__ << " length " << data->len;
204 if (data->len == 0) {
205 SendStatus(kResultSuccess);
206 return;
207 }
208
209 response_data_.Append(ByteString(data->buf, data->len));
Paul Stewart188a84a2012-01-20 16:28:15 -0800210 StartIdleTimeout(kInputTimeoutSeconds, kResultResponseTimeout);
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500211 if (!read_event_callback_.is_null()) {
212 read_event_callback_.Run(response_data_);
Paul Stewartbdb02e62012-02-22 16:24:33 -0800213 }
Paul Stewart188a84a2012-01-20 16:28:15 -0800214}
215
216void HTTPRequest::SendStatus(Result result) {
Paul Stewartbdb02e62012-02-22 16:24:33 -0800217 // Save copies on the stack, since Stop() will remove them.
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500218 Callback<void(Result, const ByteString &)> result_callback = result_callback_;
Paul Stewartbdb02e62012-02-22 16:24:33 -0800219 const ByteString response_data(response_data_);
Paul Stewart188a84a2012-01-20 16:28:15 -0800220 Stop();
Paul Stewartbdb02e62012-02-22 16:24:33 -0800221
222 // Call the callback last, since it may delete us and |this| may no longer
223 // be valid.
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500224 if (!result_callback.is_null()) {
225 result_callback.Run(result, response_data);
Paul Stewartbdb02e62012-02-22 16:24:33 -0800226 }
Paul Stewart188a84a2012-01-20 16:28:15 -0800227}
228
229// Start a timeout for "the next event".
230void HTTPRequest::StartIdleTimeout(int timeout_seconds, Result timeout_result) {
Paul Stewart188a84a2012-01-20 16:28:15 -0800231 timeout_result_ = timeout_result;
Paul Stewartf582b502012-04-04 21:39:22 -0700232 timeout_closure_.Reset(
233 Bind(&HTTPRequest::TimeoutTask, weak_ptr_factory_.GetWeakPtr()));
234 dispatcher_->PostDelayedTask(timeout_closure_.callback(),
235 timeout_seconds * 1000);
Paul Stewart188a84a2012-01-20 16:28:15 -0800236}
237
238void HTTPRequest::TimeoutTask() {
239 LOG(ERROR) << "Connection with "
240 << server_hostname_
241 << " timed out";
242 SendStatus(timeout_result_);
243}
244
245// Output ReadyHandler callback which fires when the server socket is
246// ready for data to be sent to it.
247void HTTPRequest::WriteToServer(int fd) {
248 CHECK_EQ(server_socket_, fd);
249 int ret = sockets_->Send(fd, request_data_.GetConstData(),
250 request_data_.GetLength(), 0);
251 CHECK(static_cast<size_t>(ret) <= request_data_.GetLength());
252
253 VLOG(3) << "In " << __func__ << " wrote " << ret << " of " <<
254 request_data_.GetLength();
255
256 if (ret < 0) {
257 LOG(ERROR) << "Client write failed to "
258 << server_hostname_;
259 SendStatus(kResultRequestFailure);
260 return;
261 }
262
263 request_data_ = ByteString(request_data_.GetConstData() + ret,
264 request_data_.GetLength() - ret);
265
266 if (request_data_.IsEmpty()) {
267 write_server_handler_->Stop();
268 read_server_handler_.reset(
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500269 dispatcher_->CreateInputHandler(server_socket_, read_server_callback_));
Paul Stewart188a84a2012-01-20 16:28:15 -0800270 StartIdleTimeout(kInputTimeoutSeconds, kResultResponseTimeout);
271 } else {
272 StartIdleTimeout(kInputTimeoutSeconds, kResultRequestTimeout);
273 }
274}
275
276} // namespace shill