blob: 2ece2fa9bfaf049dd2ca8022c569c29b9d26c739 [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/string_number_conversions.h>
11#include <base/stringprintf.h>
12
13#include "shill/async_connection.h"
14#include "shill/connection.h"
15#include "shill/dns_client.h"
Paul Stewartbdb02e62012-02-22 16:24:33 -080016#include "shill/error.h"
Paul Stewart188a84a2012-01-20 16:28:15 -080017#include "shill/event_dispatcher.h"
18#include "shill/http_url.h"
19#include "shill/ip_address.h"
Christopher Wileyb691efd2012-08-09 13:51:51 -070020#include "shill/logging.h"
Paul Stewart188a84a2012-01-20 16:28:15 -080021#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) {
Ben Chanfad4a0b2012-04-18 15:49:59 -070079 SLOG(HTTP, 3) << "In " << __func__;
Paul Stewart188a84a2012-01-20 16:28:15 -080080
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 {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700101 SLOG(HTTP, 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() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700118 SLOG(HTTP, 3) << "In " << __func__ << "; running is " << is_running_;
Paul Stewart188a84a2012-01-20 16:28:15 -0800119
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) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700148 SLOG(HTTP, 3) << "In " << __func__;
Paul Stewart188a84a2012-01-20 16:28:15 -0800149 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) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700164 SLOG(HTTP, 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) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700183 SLOG(HTTP, 3) << "In " << __func__;
Paul Stewart188a84a2012-01-20 16:28:15 -0800184 if (!success) {
185 LOG(ERROR) << "Socket connection delayed failure to "
186 << server_hostname_
187 << ": "
188 << server_async_connection_->error();
Paul Stewart8585b202012-09-21 14:03:01 -0700189 // |this| could be freed as a result of calling SendStatus().
Paul Stewart188a84a2012-01-20 16:28:15 -0800190 SendStatus(kResultConnectionFailure);
191 return;
192 }
193 server_socket_ = fd;
194 write_server_handler_.reset(
195 dispatcher_->CreateReadyHandler(server_socket_,
196 IOHandler::kModeOutput,
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500197 write_server_callback_));
Paul Stewart188a84a2012-01-20 16:28:15 -0800198 StartIdleTimeout(kInputTimeoutSeconds, kResultRequestTimeout);
199}
200
Paul Stewart5f06a0e2012-12-20 11:11:33 -0800201void HTTPRequest::OnServerReadError(const Error &/*error*/) {
202 SendStatus(kResultResponseFailure);
203}
204
Paul Stewart188a84a2012-01-20 16:28:15 -0800205// IOInputHandler callback which fires when data has been read from the
206// server.
207void HTTPRequest::ReadFromServer(InputData *data) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700208 SLOG(HTTP, 3) << "In " << __func__ << " length " << data->len;
Paul Stewart188a84a2012-01-20 16:28:15 -0800209 if (data->len == 0) {
210 SendStatus(kResultSuccess);
211 return;
212 }
213
214 response_data_.Append(ByteString(data->buf, data->len));
Paul Stewart188a84a2012-01-20 16:28:15 -0800215 StartIdleTimeout(kInputTimeoutSeconds, kResultResponseTimeout);
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500216 if (!read_event_callback_.is_null()) {
217 read_event_callback_.Run(response_data_);
Paul Stewartbdb02e62012-02-22 16:24:33 -0800218 }
Paul Stewart188a84a2012-01-20 16:28:15 -0800219}
220
221void HTTPRequest::SendStatus(Result result) {
Paul Stewartbdb02e62012-02-22 16:24:33 -0800222 // Save copies on the stack, since Stop() will remove them.
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500223 Callback<void(Result, const ByteString &)> result_callback = result_callback_;
Paul Stewartbdb02e62012-02-22 16:24:33 -0800224 const ByteString response_data(response_data_);
Paul Stewart188a84a2012-01-20 16:28:15 -0800225 Stop();
Paul Stewartbdb02e62012-02-22 16:24:33 -0800226
227 // Call the callback last, since it may delete us and |this| may no longer
228 // be valid.
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500229 if (!result_callback.is_null()) {
230 result_callback.Run(result, response_data);
Paul Stewartbdb02e62012-02-22 16:24:33 -0800231 }
Paul Stewart188a84a2012-01-20 16:28:15 -0800232}
233
234// Start a timeout for "the next event".
235void HTTPRequest::StartIdleTimeout(int timeout_seconds, Result timeout_result) {
Paul Stewart188a84a2012-01-20 16:28:15 -0800236 timeout_result_ = timeout_result;
Paul Stewartf582b502012-04-04 21:39:22 -0700237 timeout_closure_.Reset(
238 Bind(&HTTPRequest::TimeoutTask, weak_ptr_factory_.GetWeakPtr()));
239 dispatcher_->PostDelayedTask(timeout_closure_.callback(),
240 timeout_seconds * 1000);
Paul Stewart188a84a2012-01-20 16:28:15 -0800241}
242
243void HTTPRequest::TimeoutTask() {
244 LOG(ERROR) << "Connection with "
245 << server_hostname_
246 << " timed out";
247 SendStatus(timeout_result_);
248}
249
250// Output ReadyHandler callback which fires when the server socket is
251// ready for data to be sent to it.
252void HTTPRequest::WriteToServer(int fd) {
253 CHECK_EQ(server_socket_, fd);
254 int ret = sockets_->Send(fd, request_data_.GetConstData(),
255 request_data_.GetLength(), 0);
Ben Chan2697aa92012-10-22 22:25:10 -0700256 CHECK(ret < 0 || static_cast<size_t>(ret) <= request_data_.GetLength());
Paul Stewart188a84a2012-01-20 16:28:15 -0800257
Ben Chanfad4a0b2012-04-18 15:49:59 -0700258 SLOG(HTTP, 3) << "In " << __func__ << " wrote " << ret << " of "
259 << request_data_.GetLength();
Paul Stewart188a84a2012-01-20 16:28:15 -0800260
261 if (ret < 0) {
262 LOG(ERROR) << "Client write failed to "
263 << server_hostname_;
264 SendStatus(kResultRequestFailure);
265 return;
266 }
267
268 request_data_ = ByteString(request_data_.GetConstData() + ret,
269 request_data_.GetLength() - ret);
270
271 if (request_data_.IsEmpty()) {
272 write_server_handler_->Stop();
Paul Stewart5f06a0e2012-12-20 11:11:33 -0800273 read_server_handler_.reset(dispatcher_->CreateInputHandler(
274 server_socket_,
275 read_server_callback_,
276 Bind(&HTTPRequest::OnServerReadError, weak_ptr_factory_.GetWeakPtr())));
Paul Stewart188a84a2012-01-20 16:28:15 -0800277 StartIdleTimeout(kInputTimeoutSeconds, kResultResponseTimeout);
278 } else {
279 StartIdleTimeout(kInputTimeoutSeconds, kResultRequestTimeout);
280 }
281}
282
283} // namespace shill