blob: bd66ea3dcf835c55c5b6f048eee2d63968af69f5 [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"
Ben Chanfad4a0b2012-04-18 15:49:59 -070021#include "shill/scope_logger.h"
Paul Stewart188a84a2012-01-20 16:28:15 -080022#include "shill/sockets.h"
23
Eric Shienbrood3e20a232012-02-16 11:35:56 -050024using base::Bind;
25using base::Callback;
Paul Stewart188a84a2012-01-20 16:28:15 -080026using base::StringPrintf;
27using std::string;
28
29namespace shill {
30
31const int HTTPRequest::kConnectTimeoutSeconds = 10;
32const int HTTPRequest::kDNSTimeoutSeconds = 5;
33const int HTTPRequest::kInputTimeoutSeconds = 10;
34
35const char HTTPRequest::kHTTPRequestTemplate[] =
36 "GET %s HTTP/1.1\r\n"
37 "Host: %s:%d\r\n"
38 "Connection: Close\r\n\r\n";
39
40HTTPRequest::HTTPRequest(ConnectionRefPtr connection,
41 EventDispatcher *dispatcher,
42 Sockets *sockets)
43 : connection_(connection),
44 dispatcher_(dispatcher),
45 sockets_(sockets),
Eric Shienbrood3e20a232012-02-16 11:35:56 -050046 weak_ptr_factory_(this),
Paul Stewart188a84a2012-01-20 16:28:15 -080047 connect_completion_callback_(
Eric Shienbrood9a245532012-03-07 14:20:39 -050048 Bind(&HTTPRequest::OnConnectCompletion,
49 weak_ptr_factory_.GetWeakPtr())),
50 dns_client_callback_(Bind(&HTTPRequest::GetDNSResult,
51 weak_ptr_factory_.GetWeakPtr())),
Eric Shienbrood3e20a232012-02-16 11:35:56 -050052 read_server_callback_(Bind(&HTTPRequest::ReadFromServer,
Eric Shienbrood9a245532012-03-07 14:20:39 -050053 weak_ptr_factory_.GetWeakPtr())),
Eric Shienbrood3e20a232012-02-16 11:35:56 -050054 write_server_callback_(Bind(&HTTPRequest::WriteToServer,
Eric Shienbrood9a245532012-03-07 14:20:39 -050055 weak_ptr_factory_.GetWeakPtr())),
Paul Stewart188a84a2012-01-20 16:28:15 -080056 dns_client_(
57 new DNSClient(IPAddress::kFamilyIPv4,
58 connection->interface_name(),
59 connection->dns_servers(),
60 kDNSTimeoutSeconds * 1000,
61 dispatcher,
Eric Shienbrood3e20a232012-02-16 11:35:56 -050062 dns_client_callback_)),
Paul Stewart188a84a2012-01-20 16:28:15 -080063 server_async_connection_(
64 new AsyncConnection(connection_->interface_name(),
65 dispatcher_, sockets,
Eric Shienbrood3e20a232012-02-16 11:35:56 -050066 connect_completion_callback_)),
Paul Stewart188a84a2012-01-20 16:28:15 -080067 server_port_(-1),
68 server_socket_(-1),
69 timeout_result_(kResultUnknown),
70 is_running_(false) { }
71
72HTTPRequest::~HTTPRequest() {
73 Stop();
74}
75
76HTTPRequest::Result HTTPRequest::Start(
77 const HTTPURL &url,
Eric Shienbrood3e20a232012-02-16 11:35:56 -050078 const Callback<void(const ByteString &)> &read_event_callback,
79 const Callback<void(Result, const ByteString &)> &result_callback) {
Ben Chanfad4a0b2012-04-18 15:49:59 -070080 SLOG(HTTP, 3) << "In " << __func__;
Paul Stewart188a84a2012-01-20 16:28:15 -080081
82 DCHECK(!is_running_);
83
84 is_running_ = true;
85 request_data_ = ByteString(StringPrintf(kHTTPRequestTemplate,
86 url.path().c_str(),
87 url.host().c_str(),
88 url.port()), false);
89 server_hostname_ = url.host();
90 server_port_ = url.port();
91 connection_->RequestRouting();
92
93 IPAddress addr(IPAddress::kFamilyIPv4);
94 if (addr.SetAddressFromString(server_hostname_)) {
95 if (!ConnectServer(addr, server_port_)) {
96 LOG(ERROR) << "Connect to "
97 << server_hostname_
98 << " failed synchronously";
99 return kResultConnectionFailure;
100 }
101 } else {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700102 SLOG(HTTP, 3) << "Looking up host: " << server_hostname_;
Paul Stewartbdb02e62012-02-22 16:24:33 -0800103 Error error;
104 if (!dns_client_->Start(server_hostname_, &error)) {
105 LOG(ERROR) << "Failed to start DNS client: " << error.message();
Paul Stewart188a84a2012-01-20 16:28:15 -0800106 Stop();
107 return kResultDNSFailure;
108 }
109 }
110
111 // Only install callbacks after connection succeeds in starting.
112 read_event_callback_ = read_event_callback;
113 result_callback_ = result_callback;
114
115 return kResultInProgress;
116}
117
118void HTTPRequest::Stop() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700119 SLOG(HTTP, 3) << "In " << __func__ << "; running is " << is_running_;
Paul Stewart188a84a2012-01-20 16:28:15 -0800120
121 if (!is_running_) {
122 return;
123 }
124
125 // Clear IO handlers first so that closing the socket doesn't cause
126 // events to fire.
127 write_server_handler_.reset();
128 read_server_handler_.reset();
129
130 connection_->ReleaseRouting();
131 dns_client_->Stop();
Paul Stewart188a84a2012-01-20 16:28:15 -0800132 is_running_ = false;
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500133 result_callback_.Reset();
134 read_event_callback_.Reset();
Paul Stewart188a84a2012-01-20 16:28:15 -0800135 request_data_.Clear();
136 response_data_.Clear();
137 server_async_connection_->Stop();
138 server_hostname_.clear();
139 server_port_ = -1;
140 if (server_socket_ != -1) {
141 sockets_->Close(server_socket_);
142 server_socket_ = -1;
143 }
Paul Stewartf582b502012-04-04 21:39:22 -0700144 timeout_closure_.Cancel();
Paul Stewart188a84a2012-01-20 16:28:15 -0800145 timeout_result_ = kResultUnknown;
146}
147
148bool HTTPRequest::ConnectServer(const IPAddress &address, int port) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700149 SLOG(HTTP, 3) << "In " << __func__;
Paul Stewart188a84a2012-01-20 16:28:15 -0800150 if (!server_async_connection_->Start(address, port)) {
Paul Stewart188a84a2012-01-20 16:28:15 -0800151 LOG(ERROR) << "Could not create socket to connect to server at "
mukesh agrawal2c15d2c2012-02-21 16:09:21 -0800152 << address.ToString();
Paul Stewart188a84a2012-01-20 16:28:15 -0800153 SendStatus(kResultConnectionFailure);
154 return false;
155 }
156 // Start a connection timeout only if we didn't synchronously connect.
157 if (server_socket_ == -1) {
158 StartIdleTimeout(kConnectTimeoutSeconds, kResultConnectionTimeout);
159 }
160 return true;
161}
162
163// DNSClient callback that fires when the DNS request completes.
Paul Stewartbdb02e62012-02-22 16:24:33 -0800164void HTTPRequest::GetDNSResult(const Error &error, const IPAddress &address) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700165 SLOG(HTTP, 3) << "In " << __func__;
Paul Stewartbdb02e62012-02-22 16:24:33 -0800166 if (!error.IsSuccess()) {
Paul Stewart188a84a2012-01-20 16:28:15 -0800167 LOG(ERROR) << "Could not resolve hostname "
168 << server_hostname_
169 << ": "
Paul Stewartbdb02e62012-02-22 16:24:33 -0800170 << error.message();
171 if (error.message() == DNSClient::kErrorTimedOut) {
Paul Stewart188a84a2012-01-20 16:28:15 -0800172 SendStatus(kResultDNSTimeout);
173 } else {
174 SendStatus(kResultDNSFailure);
175 }
176 return;
177 }
Paul Stewartbdb02e62012-02-22 16:24:33 -0800178 ConnectServer(address, server_port_);
Paul Stewart188a84a2012-01-20 16:28:15 -0800179}
180
181// AsyncConnection callback routine which fires when the asynchronous Connect()
182// to the remote server completes (or fails).
183void HTTPRequest::OnConnectCompletion(bool success, int fd) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700184 SLOG(HTTP, 3) << "In " << __func__;
Paul Stewart188a84a2012-01-20 16:28:15 -0800185 if (!success) {
186 LOG(ERROR) << "Socket connection delayed failure to "
187 << server_hostname_
188 << ": "
189 << server_async_connection_->error();
190 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
201// IOInputHandler callback which fires when data has been read from the
202// server.
203void HTTPRequest::ReadFromServer(InputData *data) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700204 SLOG(HTTP, 3) << "In " << __func__ << " length " << data->len;
Paul Stewart188a84a2012-01-20 16:28:15 -0800205 if (data->len == 0) {
206 SendStatus(kResultSuccess);
207 return;
208 }
209
210 response_data_.Append(ByteString(data->buf, data->len));
Paul Stewart188a84a2012-01-20 16:28:15 -0800211 StartIdleTimeout(kInputTimeoutSeconds, kResultResponseTimeout);
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500212 if (!read_event_callback_.is_null()) {
213 read_event_callback_.Run(response_data_);
Paul Stewartbdb02e62012-02-22 16:24:33 -0800214 }
Paul Stewart188a84a2012-01-20 16:28:15 -0800215}
216
217void HTTPRequest::SendStatus(Result result) {
Paul Stewartbdb02e62012-02-22 16:24:33 -0800218 // Save copies on the stack, since Stop() will remove them.
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500219 Callback<void(Result, const ByteString &)> result_callback = result_callback_;
Paul Stewartbdb02e62012-02-22 16:24:33 -0800220 const ByteString response_data(response_data_);
Paul Stewart188a84a2012-01-20 16:28:15 -0800221 Stop();
Paul Stewartbdb02e62012-02-22 16:24:33 -0800222
223 // Call the callback last, since it may delete us and |this| may no longer
224 // be valid.
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500225 if (!result_callback.is_null()) {
226 result_callback.Run(result, response_data);
Paul Stewartbdb02e62012-02-22 16:24:33 -0800227 }
Paul Stewart188a84a2012-01-20 16:28:15 -0800228}
229
230// Start a timeout for "the next event".
231void HTTPRequest::StartIdleTimeout(int timeout_seconds, Result timeout_result) {
Paul Stewart188a84a2012-01-20 16:28:15 -0800232 timeout_result_ = timeout_result;
Paul Stewartf582b502012-04-04 21:39:22 -0700233 timeout_closure_.Reset(
234 Bind(&HTTPRequest::TimeoutTask, weak_ptr_factory_.GetWeakPtr()));
235 dispatcher_->PostDelayedTask(timeout_closure_.callback(),
236 timeout_seconds * 1000);
Paul Stewart188a84a2012-01-20 16:28:15 -0800237}
238
239void HTTPRequest::TimeoutTask() {
240 LOG(ERROR) << "Connection with "
241 << server_hostname_
242 << " timed out";
243 SendStatus(timeout_result_);
244}
245
246// Output ReadyHandler callback which fires when the server socket is
247// ready for data to be sent to it.
248void HTTPRequest::WriteToServer(int fd) {
249 CHECK_EQ(server_socket_, fd);
250 int ret = sockets_->Send(fd, request_data_.GetConstData(),
251 request_data_.GetLength(), 0);
252 CHECK(static_cast<size_t>(ret) <= request_data_.GetLength());
253
Ben Chanfad4a0b2012-04-18 15:49:59 -0700254 SLOG(HTTP, 3) << "In " << __func__ << " wrote " << ret << " of "
255 << request_data_.GetLength();
Paul Stewart188a84a2012-01-20 16:28:15 -0800256
257 if (ret < 0) {
258 LOG(ERROR) << "Client write failed to "
259 << server_hostname_;
260 SendStatus(kResultRequestFailure);
261 return;
262 }
263
264 request_data_ = ByteString(request_data_.GetConstData() + ret,
265 request_data_.GetLength() - ret);
266
267 if (request_data_.IsEmpty()) {
268 write_server_handler_->Stop();
269 read_server_handler_.reset(
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500270 dispatcher_->CreateInputHandler(server_socket_, read_server_callback_));
Paul Stewart188a84a2012-01-20 16:28:15 -0800271 StartIdleTimeout(kInputTimeoutSeconds, kResultResponseTimeout);
272 } else {
273 StartIdleTimeout(kInputTimeoutSeconds, kResultRequestTimeout);
274 }
275}
276
277} // namespace shill