blob: e5937f15f37c74336796f3b8d90095ce1f236b5b [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
Rebecca Silbersteinc9c31d82014-10-21 15:01:00 -070030namespace Logging {
31static auto kModuleLogScope = ScopeLogger::kHTTP;
Paul Stewart8ae18742015-06-16 13:13:10 -070032static string ObjectID(Connection* c) { return c->interface_name(); }
Rebecca Silbersteinc9c31d82014-10-21 15:01:00 -070033}
34
Paul Stewart188a84a2012-01-20 16:28:15 -080035const int HTTPRequest::kConnectTimeoutSeconds = 10;
36const int HTTPRequest::kDNSTimeoutSeconds = 5;
37const int HTTPRequest::kInputTimeoutSeconds = 10;
38
39const char HTTPRequest::kHTTPRequestTemplate[] =
40 "GET %s HTTP/1.1\r\n"
41 "Host: %s:%d\r\n"
42 "Connection: Close\r\n\r\n";
43
44HTTPRequest::HTTPRequest(ConnectionRefPtr connection,
Paul Stewart8ae18742015-06-16 13:13:10 -070045 EventDispatcher* dispatcher,
46 Sockets* sockets)
Paul Stewart188a84a2012-01-20 16:28:15 -080047 : connection_(connection),
48 dispatcher_(dispatcher),
49 sockets_(sockets),
Eric Shienbrood3e20a232012-02-16 11:35:56 -050050 weak_ptr_factory_(this),
Paul Stewart188a84a2012-01-20 16:28:15 -080051 connect_completion_callback_(
Eric Shienbrood9a245532012-03-07 14:20:39 -050052 Bind(&HTTPRequest::OnConnectCompletion,
53 weak_ptr_factory_.GetWeakPtr())),
54 dns_client_callback_(Bind(&HTTPRequest::GetDNSResult,
55 weak_ptr_factory_.GetWeakPtr())),
Eric Shienbrood3e20a232012-02-16 11:35:56 -050056 read_server_callback_(Bind(&HTTPRequest::ReadFromServer,
Eric Shienbrood9a245532012-03-07 14:20:39 -050057 weak_ptr_factory_.GetWeakPtr())),
Eric Shienbrood3e20a232012-02-16 11:35:56 -050058 write_server_callback_(Bind(&HTTPRequest::WriteToServer,
Eric Shienbrood9a245532012-03-07 14:20:39 -050059 weak_ptr_factory_.GetWeakPtr())),
Paul Stewart188a84a2012-01-20 16:28:15 -080060 dns_client_(
Peter Qiuf3a8f902014-08-20 10:05:42 -070061 new DNSClient(connection->IsIPv6() ? IPAddress::kFamilyIPv6
62 : IPAddress::kFamilyIPv4,
Paul Stewart188a84a2012-01-20 16:28:15 -080063 connection->interface_name(),
64 connection->dns_servers(),
65 kDNSTimeoutSeconds * 1000,
66 dispatcher,
Eric Shienbrood3e20a232012-02-16 11:35:56 -050067 dns_client_callback_)),
Paul Stewart188a84a2012-01-20 16:28:15 -080068 server_async_connection_(
69 new AsyncConnection(connection_->interface_name(),
70 dispatcher_, sockets,
Eric Shienbrood3e20a232012-02-16 11:35:56 -050071 connect_completion_callback_)),
Paul Stewart188a84a2012-01-20 16:28:15 -080072 server_port_(-1),
73 server_socket_(-1),
74 timeout_result_(kResultUnknown),
75 is_running_(false) { }
76
77HTTPRequest::~HTTPRequest() {
78 Stop();
79}
80
81HTTPRequest::Result HTTPRequest::Start(
Paul Stewart8ae18742015-06-16 13:13:10 -070082 const HTTPURL& url,
83 const Callback<void(const ByteString&)>& read_event_callback,
84 const Callback<void(Result, const ByteString&)>& result_callback) {
Alex Vakulenko0951ccb2014-12-10 12:52:31 -080085 SLOG(connection_.get(), 3) << "In " << __func__;
Paul Stewart188a84a2012-01-20 16:28:15 -080086
87 DCHECK(!is_running_);
88
89 is_running_ = true;
90 request_data_ = ByteString(StringPrintf(kHTTPRequestTemplate,
91 url.path().c_str(),
92 url.host().c_str(),
93 url.port()), false);
94 server_hostname_ = url.host();
95 server_port_ = url.port();
96 connection_->RequestRouting();
97
98 IPAddress addr(IPAddress::kFamilyIPv4);
Peter Qiuf3a8f902014-08-20 10:05:42 -070099 if (connection_->IsIPv6()) {
100 addr.set_family(IPAddress::kFamilyIPv6);
101 }
Paul Stewart188a84a2012-01-20 16:28:15 -0800102 if (addr.SetAddressFromString(server_hostname_)) {
103 if (!ConnectServer(addr, server_port_)) {
104 LOG(ERROR) << "Connect to "
105 << server_hostname_
106 << " failed synchronously";
107 return kResultConnectionFailure;
108 }
109 } else {
Alex Vakulenko0951ccb2014-12-10 12:52:31 -0800110 SLOG(connection_.get(), 3) << "Looking up host: " << server_hostname_;
Paul Stewartbdb02e62012-02-22 16:24:33 -0800111 Error error;
112 if (!dns_client_->Start(server_hostname_, &error)) {
113 LOG(ERROR) << "Failed to start DNS client: " << error.message();
Paul Stewart188a84a2012-01-20 16:28:15 -0800114 Stop();
115 return kResultDNSFailure;
116 }
117 }
118
119 // Only install callbacks after connection succeeds in starting.
120 read_event_callback_ = read_event_callback;
121 result_callback_ = result_callback;
122
123 return kResultInProgress;
124}
125
126void HTTPRequest::Stop() {
Alex Vakulenko0951ccb2014-12-10 12:52:31 -0800127 SLOG(connection_.get(), 3) << "In " << __func__ << "; running is "
128 << is_running_;
Paul Stewart188a84a2012-01-20 16:28:15 -0800129
130 if (!is_running_) {
131 return;
132 }
133
134 // Clear IO handlers first so that closing the socket doesn't cause
135 // events to fire.
136 write_server_handler_.reset();
137 read_server_handler_.reset();
138
139 connection_->ReleaseRouting();
140 dns_client_->Stop();
Paul Stewart188a84a2012-01-20 16:28:15 -0800141 is_running_ = false;
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500142 result_callback_.Reset();
143 read_event_callback_.Reset();
Paul Stewart188a84a2012-01-20 16:28:15 -0800144 request_data_.Clear();
145 response_data_.Clear();
146 server_async_connection_->Stop();
147 server_hostname_.clear();
148 server_port_ = -1;
149 if (server_socket_ != -1) {
150 sockets_->Close(server_socket_);
151 server_socket_ = -1;
152 }
Paul Stewartf582b502012-04-04 21:39:22 -0700153 timeout_closure_.Cancel();
Paul Stewart188a84a2012-01-20 16:28:15 -0800154 timeout_result_ = kResultUnknown;
155}
156
Paul Stewart8ae18742015-06-16 13:13:10 -0700157bool HTTPRequest::ConnectServer(const IPAddress& address, int port) {
Alex Vakulenko0951ccb2014-12-10 12:52:31 -0800158 SLOG(connection_.get(), 3) << "In " << __func__;
Paul Stewart188a84a2012-01-20 16:28:15 -0800159 if (!server_async_connection_->Start(address, port)) {
Paul Stewart188a84a2012-01-20 16:28:15 -0800160 LOG(ERROR) << "Could not create socket to connect to server at "
mukesh agrawal2c15d2c2012-02-21 16:09:21 -0800161 << address.ToString();
Paul Stewart188a84a2012-01-20 16:28:15 -0800162 SendStatus(kResultConnectionFailure);
163 return false;
164 }
165 // Start a connection timeout only if we didn't synchronously connect.
166 if (server_socket_ == -1) {
167 StartIdleTimeout(kConnectTimeoutSeconds, kResultConnectionTimeout);
168 }
169 return true;
170}
171
172// DNSClient callback that fires when the DNS request completes.
Paul Stewart8ae18742015-06-16 13:13:10 -0700173void HTTPRequest::GetDNSResult(const Error& error, const IPAddress& address) {
Alex Vakulenko0951ccb2014-12-10 12:52:31 -0800174 SLOG(connection_.get(), 3) << "In " << __func__;
Paul Stewartbdb02e62012-02-22 16:24:33 -0800175 if (!error.IsSuccess()) {
Paul Stewart188a84a2012-01-20 16:28:15 -0800176 LOG(ERROR) << "Could not resolve hostname "
177 << server_hostname_
178 << ": "
Paul Stewartbdb02e62012-02-22 16:24:33 -0800179 << error.message();
180 if (error.message() == DNSClient::kErrorTimedOut) {
Paul Stewart188a84a2012-01-20 16:28:15 -0800181 SendStatus(kResultDNSTimeout);
182 } else {
183 SendStatus(kResultDNSFailure);
184 }
185 return;
186 }
Paul Stewartbdb02e62012-02-22 16:24:33 -0800187 ConnectServer(address, server_port_);
Paul Stewart188a84a2012-01-20 16:28:15 -0800188}
189
190// AsyncConnection callback routine which fires when the asynchronous Connect()
191// to the remote server completes (or fails).
192void HTTPRequest::OnConnectCompletion(bool success, int fd) {
Alex Vakulenko0951ccb2014-12-10 12:52:31 -0800193 SLOG(connection_.get(), 3) << "In " << __func__;
Paul Stewart188a84a2012-01-20 16:28:15 -0800194 if (!success) {
195 LOG(ERROR) << "Socket connection delayed failure to "
196 << server_hostname_
197 << ": "
198 << server_async_connection_->error();
Paul Stewart8585b202012-09-21 14:03:01 -0700199 // |this| could be freed as a result of calling SendStatus().
Paul Stewart188a84a2012-01-20 16:28:15 -0800200 SendStatus(kResultConnectionFailure);
201 return;
202 }
203 server_socket_ = fd;
204 write_server_handler_.reset(
205 dispatcher_->CreateReadyHandler(server_socket_,
206 IOHandler::kModeOutput,
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500207 write_server_callback_));
Paul Stewart188a84a2012-01-20 16:28:15 -0800208 StartIdleTimeout(kInputTimeoutSeconds, kResultRequestTimeout);
209}
210
Paul Stewart8ae18742015-06-16 13:13:10 -0700211void HTTPRequest::OnServerReadError(const string& /*error_msg*/) {
Paul Stewart5f06a0e2012-12-20 11:11:33 -0800212 SendStatus(kResultResponseFailure);
213}
214
Paul Stewart188a84a2012-01-20 16:28:15 -0800215// IOInputHandler callback which fires when data has been read from the
216// server.
Paul Stewart8ae18742015-06-16 13:13:10 -0700217void HTTPRequest::ReadFromServer(InputData* data) {
Alex Vakulenko0951ccb2014-12-10 12:52:31 -0800218 SLOG(connection_.get(), 3) << "In " << __func__ << " length " << data->len;
Paul Stewart188a84a2012-01-20 16:28:15 -0800219 if (data->len == 0) {
220 SendStatus(kResultSuccess);
221 return;
222 }
223
224 response_data_.Append(ByteString(data->buf, data->len));
Paul Stewart188a84a2012-01-20 16:28:15 -0800225 StartIdleTimeout(kInputTimeoutSeconds, kResultResponseTimeout);
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500226 if (!read_event_callback_.is_null()) {
227 read_event_callback_.Run(response_data_);
Paul Stewartbdb02e62012-02-22 16:24:33 -0800228 }
Paul Stewart188a84a2012-01-20 16:28:15 -0800229}
230
231void HTTPRequest::SendStatus(Result result) {
Paul Stewartbdb02e62012-02-22 16:24:33 -0800232 // Save copies on the stack, since Stop() will remove them.
Paul Stewart8ae18742015-06-16 13:13:10 -0700233 Callback<void(Result, const ByteString&)> result_callback = result_callback_;
Paul Stewartbdb02e62012-02-22 16:24:33 -0800234 const ByteString response_data(response_data_);
Paul Stewart188a84a2012-01-20 16:28:15 -0800235 Stop();
Paul Stewartbdb02e62012-02-22 16:24:33 -0800236
237 // Call the callback last, since it may delete us and |this| may no longer
238 // be valid.
Eric Shienbrood3e20a232012-02-16 11:35:56 -0500239 if (!result_callback.is_null()) {
240 result_callback.Run(result, response_data);
Paul Stewartbdb02e62012-02-22 16:24:33 -0800241 }
Paul Stewart188a84a2012-01-20 16:28:15 -0800242}
243
244// Start a timeout for "the next event".
245void HTTPRequest::StartIdleTimeout(int timeout_seconds, Result timeout_result) {
Paul Stewart188a84a2012-01-20 16:28:15 -0800246 timeout_result_ = timeout_result;
Paul Stewartf582b502012-04-04 21:39:22 -0700247 timeout_closure_.Reset(
248 Bind(&HTTPRequest::TimeoutTask, weak_ptr_factory_.GetWeakPtr()));
249 dispatcher_->PostDelayedTask(timeout_closure_.callback(),
250 timeout_seconds * 1000);
Paul Stewart188a84a2012-01-20 16:28:15 -0800251}
252
253void HTTPRequest::TimeoutTask() {
254 LOG(ERROR) << "Connection with "
255 << server_hostname_
256 << " timed out";
257 SendStatus(timeout_result_);
258}
259
260// Output ReadyHandler callback which fires when the server socket is
261// ready for data to be sent to it.
262void HTTPRequest::WriteToServer(int fd) {
263 CHECK_EQ(server_socket_, fd);
264 int ret = sockets_->Send(fd, request_data_.GetConstData(),
265 request_data_.GetLength(), 0);
Ben Chan2697aa92012-10-22 22:25:10 -0700266 CHECK(ret < 0 || static_cast<size_t>(ret) <= request_data_.GetLength());
Paul Stewart188a84a2012-01-20 16:28:15 -0800267
Alex Vakulenko0951ccb2014-12-10 12:52:31 -0800268 SLOG(connection_.get(), 3) << "In " << __func__ << " wrote " << ret << " of "
269 << request_data_.GetLength();
Paul Stewart188a84a2012-01-20 16:28:15 -0800270
271 if (ret < 0) {
272 LOG(ERROR) << "Client write failed to "
273 << server_hostname_;
274 SendStatus(kResultRequestFailure);
275 return;
276 }
277
278 request_data_ = ByteString(request_data_.GetConstData() + ret,
279 request_data_.GetLength() - ret);
280
281 if (request_data_.IsEmpty()) {
282 write_server_handler_->Stop();
Paul Stewart5f06a0e2012-12-20 11:11:33 -0800283 read_server_handler_.reset(dispatcher_->CreateInputHandler(
284 server_socket_,
285 read_server_callback_,
286 Bind(&HTTPRequest::OnServerReadError, weak_ptr_factory_.GetWeakPtr())));
Paul Stewart188a84a2012-01-20 16:28:15 -0800287 StartIdleTimeout(kInputTimeoutSeconds, kResultResponseTimeout);
288 } else {
289 StartIdleTimeout(kInputTimeoutSeconds, kResultRequestTimeout);
290 }
291}
292
293} // namespace shill