blob: 2a6f3c84ce3daf4ae58db70ea3f779f64de9d31a [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>
Ben Chana0ddf462014-02-06 11:32:42 -080010#include <base/strings/string_number_conversions.h>
11#include <base/strings/stringprintf.h>
Paul Stewart188a84a2012-01-20 16:28:15 -080012
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"
Christopher Wileyb691efd2012-08-09 13:51:51 -070019#include "shill/logging.h"
Peter Qiu8d6b5972014-10-28 15:33:34 -070020#include "shill/net/ip_address.h"
21#include "shill/net/sockets.h"
Paul Stewart188a84a2012-01-20 16:28:15 -080022
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_(
Peter Qiuf3a8f902014-08-20 10:05:42 -070056 new DNSClient(connection->IsIPv6() ? IPAddress::kFamilyIPv6
57 : IPAddress::kFamilyIPv4,
Paul Stewart188a84a2012-01-20 16:28:15 -080058 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);
Peter Qiuf3a8f902014-08-20 10:05:42 -070094 if (connection_->IsIPv6()) {
95 addr.set_family(IPAddress::kFamilyIPv6);
96 }
Paul Stewart188a84a2012-01-20 16:28:15 -080097 if (addr.SetAddressFromString(server_hostname_)) {
98 if (!ConnectServer(addr, server_port_)) {
99 LOG(ERROR) << "Connect to "
100 << server_hostname_
101 << " failed synchronously";
102 return kResultConnectionFailure;
103 }
104 } else {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700105 SLOG(HTTP, 3) << "Looking up host: " << server_hostname_;
Paul Stewartbdb02e62012-02-22 16:24:33 -0800106 Error error;
107 if (!dns_client_->Start(server_hostname_, &error)) {
108 LOG(ERROR) << "Failed to start DNS client: " << error.message();
Paul Stewart188a84a2012-01-20 16:28:15 -0800109 Stop();
110 return kResultDNSFailure;
111 }
112 }
113
114 // Only install callbacks after connection succeeds in starting.
115 read_event_callback_ = read_event_callback;
116 result_callback_ = result_callback;
117
118 return kResultInProgress;
119}
120
121void HTTPRequest::Stop() {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700122 SLOG(HTTP, 3) << "In " << __func__ << "; running is " << is_running_;
Paul Stewart188a84a2012-01-20 16:28:15 -0800123
124 if (!is_running_) {
125 return;
126 }
127
128 // Clear IO handlers first so that closing the socket doesn't cause
129 // events to fire.
130 write_server_handler_.reset();
131 read_server_handler_.reset();
132
133 connection_->ReleaseRouting();
134 dns_client_->Stop();
Paul Stewart188a84a2012-01-20 16:28:15 -0800135 is_running_ = false;
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500136 result_callback_.Reset();
137 read_event_callback_.Reset();
Paul Stewart188a84a2012-01-20 16:28:15 -0800138 request_data_.Clear();
139 response_data_.Clear();
140 server_async_connection_->Stop();
141 server_hostname_.clear();
142 server_port_ = -1;
143 if (server_socket_ != -1) {
144 sockets_->Close(server_socket_);
145 server_socket_ = -1;
146 }
Paul Stewartf582b502012-04-04 21:39:22 -0700147 timeout_closure_.Cancel();
Paul Stewart188a84a2012-01-20 16:28:15 -0800148 timeout_result_ = kResultUnknown;
149}
150
151bool HTTPRequest::ConnectServer(const IPAddress &address, int port) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700152 SLOG(HTTP, 3) << "In " << __func__;
Paul Stewart188a84a2012-01-20 16:28:15 -0800153 if (!server_async_connection_->Start(address, port)) {
Paul Stewart188a84a2012-01-20 16:28:15 -0800154 LOG(ERROR) << "Could not create socket to connect to server at "
mukesh agrawal2c15d2c2012-02-21 16:09:21 -0800155 << address.ToString();
Paul Stewart188a84a2012-01-20 16:28:15 -0800156 SendStatus(kResultConnectionFailure);
157 return false;
158 }
159 // Start a connection timeout only if we didn't synchronously connect.
160 if (server_socket_ == -1) {
161 StartIdleTimeout(kConnectTimeoutSeconds, kResultConnectionTimeout);
162 }
163 return true;
164}
165
166// DNSClient callback that fires when the DNS request completes.
Paul Stewartbdb02e62012-02-22 16:24:33 -0800167void HTTPRequest::GetDNSResult(const Error &error, const IPAddress &address) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700168 SLOG(HTTP, 3) << "In " << __func__;
Paul Stewartbdb02e62012-02-22 16:24:33 -0800169 if (!error.IsSuccess()) {
Paul Stewart188a84a2012-01-20 16:28:15 -0800170 LOG(ERROR) << "Could not resolve hostname "
171 << server_hostname_
172 << ": "
Paul Stewartbdb02e62012-02-22 16:24:33 -0800173 << error.message();
174 if (error.message() == DNSClient::kErrorTimedOut) {
Paul Stewart188a84a2012-01-20 16:28:15 -0800175 SendStatus(kResultDNSTimeout);
176 } else {
177 SendStatus(kResultDNSFailure);
178 }
179 return;
180 }
Paul Stewartbdb02e62012-02-22 16:24:33 -0800181 ConnectServer(address, server_port_);
Paul Stewart188a84a2012-01-20 16:28:15 -0800182}
183
184// AsyncConnection callback routine which fires when the asynchronous Connect()
185// to the remote server completes (or fails).
186void HTTPRequest::OnConnectCompletion(bool success, int fd) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700187 SLOG(HTTP, 3) << "In " << __func__;
Paul Stewart188a84a2012-01-20 16:28:15 -0800188 if (!success) {
189 LOG(ERROR) << "Socket connection delayed failure to "
190 << server_hostname_
191 << ": "
192 << server_async_connection_->error();
Paul Stewart8585b202012-09-21 14:03:01 -0700193 // |this| could be freed as a result of calling SendStatus().
Paul Stewart188a84a2012-01-20 16:28:15 -0800194 SendStatus(kResultConnectionFailure);
195 return;
196 }
197 server_socket_ = fd;
198 write_server_handler_.reset(
199 dispatcher_->CreateReadyHandler(server_socket_,
200 IOHandler::kModeOutput,
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500201 write_server_callback_));
Paul Stewart188a84a2012-01-20 16:28:15 -0800202 StartIdleTimeout(kInputTimeoutSeconds, kResultRequestTimeout);
203}
204
Peter Qiu3161caa2014-10-29 09:47:22 -0700205void HTTPRequest::OnServerReadError(const string &/*error_msg*/) {
Paul Stewart5f06a0e2012-12-20 11:11:33 -0800206 SendStatus(kResultResponseFailure);
207}
208
Paul Stewart188a84a2012-01-20 16:28:15 -0800209// IOInputHandler callback which fires when data has been read from the
210// server.
211void HTTPRequest::ReadFromServer(InputData *data) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700212 SLOG(HTTP, 3) << "In " << __func__ << " length " << data->len;
Paul Stewart188a84a2012-01-20 16:28:15 -0800213 if (data->len == 0) {
214 SendStatus(kResultSuccess);
215 return;
216 }
217
218 response_data_.Append(ByteString(data->buf, data->len));
Paul Stewart188a84a2012-01-20 16:28:15 -0800219 StartIdleTimeout(kInputTimeoutSeconds, kResultResponseTimeout);
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500220 if (!read_event_callback_.is_null()) {
221 read_event_callback_.Run(response_data_);
Paul Stewartbdb02e62012-02-22 16:24:33 -0800222 }
Paul Stewart188a84a2012-01-20 16:28:15 -0800223}
224
225void HTTPRequest::SendStatus(Result result) {
Paul Stewartbdb02e62012-02-22 16:24:33 -0800226 // Save copies on the stack, since Stop() will remove them.
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500227 Callback<void(Result, const ByteString &)> result_callback = result_callback_;
Paul Stewartbdb02e62012-02-22 16:24:33 -0800228 const ByteString response_data(response_data_);
Paul Stewart188a84a2012-01-20 16:28:15 -0800229 Stop();
Paul Stewartbdb02e62012-02-22 16:24:33 -0800230
231 // Call the callback last, since it may delete us and |this| may no longer
232 // be valid.
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500233 if (!result_callback.is_null()) {
234 result_callback.Run(result, response_data);
Paul Stewartbdb02e62012-02-22 16:24:33 -0800235 }
Paul Stewart188a84a2012-01-20 16:28:15 -0800236}
237
238// Start a timeout for "the next event".
239void HTTPRequest::StartIdleTimeout(int timeout_seconds, Result timeout_result) {
Paul Stewart188a84a2012-01-20 16:28:15 -0800240 timeout_result_ = timeout_result;
Paul Stewartf582b502012-04-04 21:39:22 -0700241 timeout_closure_.Reset(
242 Bind(&HTTPRequest::TimeoutTask, weak_ptr_factory_.GetWeakPtr()));
243 dispatcher_->PostDelayedTask(timeout_closure_.callback(),
244 timeout_seconds * 1000);
Paul Stewart188a84a2012-01-20 16:28:15 -0800245}
246
247void HTTPRequest::TimeoutTask() {
248 LOG(ERROR) << "Connection with "
249 << server_hostname_
250 << " timed out";
251 SendStatus(timeout_result_);
252}
253
254// Output ReadyHandler callback which fires when the server socket is
255// ready for data to be sent to it.
256void HTTPRequest::WriteToServer(int fd) {
257 CHECK_EQ(server_socket_, fd);
258 int ret = sockets_->Send(fd, request_data_.GetConstData(),
259 request_data_.GetLength(), 0);
Ben Chan2697aa92012-10-22 22:25:10 -0700260 CHECK(ret < 0 || static_cast<size_t>(ret) <= request_data_.GetLength());
Paul Stewart188a84a2012-01-20 16:28:15 -0800261
Ben Chanfad4a0b2012-04-18 15:49:59 -0700262 SLOG(HTTP, 3) << "In " << __func__ << " wrote " << ret << " of "
263 << request_data_.GetLength();
Paul Stewart188a84a2012-01-20 16:28:15 -0800264
265 if (ret < 0) {
266 LOG(ERROR) << "Client write failed to "
267 << server_hostname_;
268 SendStatus(kResultRequestFailure);
269 return;
270 }
271
272 request_data_ = ByteString(request_data_.GetConstData() + ret,
273 request_data_.GetLength() - ret);
274
275 if (request_data_.IsEmpty()) {
276 write_server_handler_->Stop();
Paul Stewart5f06a0e2012-12-20 11:11:33 -0800277 read_server_handler_.reset(dispatcher_->CreateInputHandler(
278 server_socket_,
279 read_server_callback_,
280 Bind(&HTTPRequest::OnServerReadError, weak_ptr_factory_.GetWeakPtr())));
Paul Stewart188a84a2012-01-20 16:28:15 -0800281 StartIdleTimeout(kInputTimeoutSeconds, kResultResponseTimeout);
282 } else {
283 StartIdleTimeout(kInputTimeoutSeconds, kResultRequestTimeout);
284 }
285}
286
287} // namespace shill