Home | History | Annotate | Download | only in shill
      1 //
      2 // Copyright (C) 2012 The Android Open Source Project
      3 //
      4 // Licensed under the Apache License, Version 2.0 (the "License");
      5 // you may not use this file except in compliance with the License.
      6 // You may obtain a copy of the License at
      7 //
      8 //      http://www.apache.org/licenses/LICENSE-2.0
      9 //
     10 // Unless required by applicable law or agreed to in writing, software
     11 // distributed under the License is distributed on an "AS IS" BASIS,
     12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 // See the License for the specific language governing permissions and
     14 // limitations under the License.
     15 //
     16 
     17 #include "shill/http_request.h"
     18 
     19 #include <string>
     20 
     21 #include <base/bind.h>
     22 #include <base/strings/string_number_conversions.h>
     23 #include <base/strings/stringprintf.h>
     24 
     25 #include "shill/async_connection.h"
     26 #include "shill/connection.h"
     27 #include "shill/dns_client.h"
     28 #include "shill/error.h"
     29 #include "shill/event_dispatcher.h"
     30 #include "shill/http_url.h"
     31 #include "shill/logging.h"
     32 #include "shill/net/ip_address.h"
     33 #include "shill/net/sockets.h"
     34 
     35 using base::Bind;
     36 using base::Callback;
     37 using base::StringPrintf;
     38 using std::string;
     39 
     40 namespace shill {
     41 
     42 namespace Logging {
     43 static auto kModuleLogScope = ScopeLogger::kHTTP;
     44 static string ObjectID(Connection* c) { return c->interface_name(); }
     45 }
     46 
     47 const int HTTPRequest::kConnectTimeoutSeconds = 10;
     48 const int HTTPRequest::kDNSTimeoutSeconds = 5;
     49 const int HTTPRequest::kInputTimeoutSeconds = 10;
     50 
     51 const char HTTPRequest::kHTTPRequestTemplate[] =
     52     "GET %s HTTP/1.1\r\n"
     53     "Host: %s:%d\r\n"
     54     "Connection: Close\r\n\r\n";
     55 
     56 HTTPRequest::HTTPRequest(ConnectionRefPtr connection,
     57                          EventDispatcher* dispatcher,
     58                          Sockets* sockets)
     59     : connection_(connection),
     60       dispatcher_(dispatcher),
     61       sockets_(sockets),
     62       weak_ptr_factory_(this),
     63       connect_completion_callback_(
     64           Bind(&HTTPRequest::OnConnectCompletion,
     65                weak_ptr_factory_.GetWeakPtr())),
     66       dns_client_callback_(Bind(&HTTPRequest::GetDNSResult,
     67                                 weak_ptr_factory_.GetWeakPtr())),
     68       read_server_callback_(Bind(&HTTPRequest::ReadFromServer,
     69                                  weak_ptr_factory_.GetWeakPtr())),
     70       write_server_callback_(Bind(&HTTPRequest::WriteToServer,
     71                                   weak_ptr_factory_.GetWeakPtr())),
     72       dns_client_(
     73           new DNSClient(connection->IsIPv6() ? IPAddress::kFamilyIPv6
     74                                              : IPAddress::kFamilyIPv4,
     75                         connection->interface_name(),
     76                         connection->dns_servers(),
     77                         kDNSTimeoutSeconds * 1000,
     78                         dispatcher,
     79                         dns_client_callback_)),
     80       server_async_connection_(
     81           new AsyncConnection(connection_->interface_name(),
     82                               dispatcher_, sockets,
     83                               connect_completion_callback_)),
     84       server_port_(-1),
     85       server_socket_(-1),
     86       timeout_result_(kResultUnknown),
     87       is_running_(false) { }
     88 
     89 HTTPRequest::~HTTPRequest() {
     90   Stop();
     91 }
     92 
     93 HTTPRequest::Result HTTPRequest::Start(
     94     const HTTPURL& url,
     95     const Callback<void(const ByteString&)>& read_event_callback,
     96     const Callback<void(Result, const ByteString&)>& result_callback) {
     97   SLOG(connection_.get(), 3) << "In " << __func__;
     98 
     99   DCHECK(!is_running_);
    100 
    101   is_running_ = true;
    102   request_data_ = ByteString(StringPrintf(kHTTPRequestTemplate,
    103                                           url.path().c_str(),
    104                                           url.host().c_str(),
    105                                           url.port()), false);
    106   server_hostname_ = url.host();
    107   server_port_ = url.port();
    108   connection_->RequestRouting();
    109 
    110   IPAddress addr(IPAddress::kFamilyIPv4);
    111   if (connection_->IsIPv6()) {
    112     addr.set_family(IPAddress::kFamilyIPv6);
    113   }
    114   if (addr.SetAddressFromString(server_hostname_)) {
    115     if (!ConnectServer(addr, server_port_)) {
    116       LOG(ERROR) << "Connect to "
    117                  << server_hostname_
    118                  << " failed synchronously";
    119       return kResultConnectionFailure;
    120     }
    121   } else {
    122     SLOG(connection_.get(), 3) << "Looking up host: " << server_hostname_;
    123     Error error;
    124     if (!dns_client_->Start(server_hostname_, &error)) {
    125       LOG(ERROR) << "Failed to start DNS client: " << error.message();
    126       Stop();
    127       return kResultDNSFailure;
    128     }
    129   }
    130 
    131   // Only install callbacks after connection succeeds in starting.
    132   read_event_callback_ = read_event_callback;
    133   result_callback_ = result_callback;
    134 
    135   return kResultInProgress;
    136 }
    137 
    138 void HTTPRequest::Stop() {
    139   SLOG(connection_.get(), 3) << "In " << __func__ << "; running is "
    140                              << is_running_;
    141 
    142   if (!is_running_) {
    143     return;
    144   }
    145 
    146   // Clear IO handlers first so that closing the socket doesn't cause
    147   // events to fire.
    148   write_server_handler_.reset();
    149   read_server_handler_.reset();
    150 
    151   connection_->ReleaseRouting();
    152   dns_client_->Stop();
    153   is_running_ = false;
    154   result_callback_.Reset();
    155   read_event_callback_.Reset();
    156   request_data_.Clear();
    157   response_data_.Clear();
    158   server_async_connection_->Stop();
    159   server_hostname_.clear();
    160   server_port_ = -1;
    161   if (server_socket_ != -1) {
    162     sockets_->Close(server_socket_);
    163     server_socket_ = -1;
    164   }
    165   timeout_closure_.Cancel();
    166   timeout_result_ = kResultUnknown;
    167 }
    168 
    169 bool HTTPRequest::ConnectServer(const IPAddress& address, int port) {
    170   SLOG(connection_.get(), 3) << "In " << __func__;
    171   if (!server_async_connection_->Start(address, port)) {
    172     LOG(ERROR) << "Could not create socket to connect to server at "
    173                << address.ToString();
    174     SendStatus(kResultConnectionFailure);
    175     return false;
    176   }
    177   // Start a connection timeout only if we didn't synchronously connect.
    178   if (server_socket_ == -1) {
    179     StartIdleTimeout(kConnectTimeoutSeconds, kResultConnectionTimeout);
    180   }
    181   return true;
    182 }
    183 
    184 // DNSClient callback that fires when the DNS request completes.
    185 void HTTPRequest::GetDNSResult(const Error& error, const IPAddress& address) {
    186   SLOG(connection_.get(), 3) << "In " << __func__;
    187   if (!error.IsSuccess()) {
    188     LOG(ERROR) << "Could not resolve hostname "
    189                << server_hostname_
    190                << ": "
    191                << error.message();
    192     if (error.message() == DNSClient::kErrorTimedOut) {
    193       SendStatus(kResultDNSTimeout);
    194     } else {
    195       SendStatus(kResultDNSFailure);
    196     }
    197     return;
    198   }
    199   ConnectServer(address, server_port_);
    200 }
    201 
    202 // AsyncConnection callback routine which fires when the asynchronous Connect()
    203 // to the remote server completes (or fails).
    204 void HTTPRequest::OnConnectCompletion(bool success, int fd) {
    205   SLOG(connection_.get(), 3) << "In " << __func__;
    206   if (!success) {
    207     LOG(ERROR) << "Socket connection delayed failure to "
    208                << server_hostname_
    209                << ": "
    210                << server_async_connection_->error();
    211     // |this| could be freed as a result of calling SendStatus().
    212     SendStatus(kResultConnectionFailure);
    213     return;
    214   }
    215   server_socket_ = fd;
    216   write_server_handler_.reset(
    217       dispatcher_->CreateReadyHandler(server_socket_,
    218                                       IOHandler::kModeOutput,
    219                                       write_server_callback_));
    220   StartIdleTimeout(kInputTimeoutSeconds, kResultRequestTimeout);
    221 }
    222 
    223 void HTTPRequest::OnServerReadError(const string& /*error_msg*/) {
    224   SendStatus(kResultResponseFailure);
    225 }
    226 
    227 // IOInputHandler callback which fires when data has been read from the
    228 // server.
    229 void HTTPRequest::ReadFromServer(InputData* data) {
    230   SLOG(connection_.get(), 3) << "In " << __func__ << " length " << data->len;
    231   if (data->len == 0) {
    232     SendStatus(kResultSuccess);
    233     return;
    234   }
    235 
    236   response_data_.Append(ByteString(data->buf, data->len));
    237   StartIdleTimeout(kInputTimeoutSeconds, kResultResponseTimeout);
    238   if (!read_event_callback_.is_null()) {
    239     read_event_callback_.Run(response_data_);
    240   }
    241 }
    242 
    243 void HTTPRequest::SendStatus(Result result) {
    244   // Save copies on the stack, since Stop() will remove them.
    245   Callback<void(Result, const ByteString&)> result_callback = result_callback_;
    246   const ByteString response_data(response_data_);
    247   Stop();
    248 
    249   // Call the callback last, since it may delete us and |this| may no longer
    250   // be valid.
    251   if (!result_callback.is_null()) {
    252     result_callback.Run(result, response_data);
    253   }
    254 }
    255 
    256 // Start a timeout for "the next event".
    257 void HTTPRequest::StartIdleTimeout(int timeout_seconds, Result timeout_result) {
    258   timeout_result_ = timeout_result;
    259   timeout_closure_.Reset(
    260       Bind(&HTTPRequest::TimeoutTask, weak_ptr_factory_.GetWeakPtr()));
    261   dispatcher_->PostDelayedTask(timeout_closure_.callback(),
    262                                timeout_seconds * 1000);
    263 }
    264 
    265 void HTTPRequest::TimeoutTask() {
    266   LOG(ERROR) << "Connection with "
    267              << server_hostname_
    268              << " timed out";
    269   SendStatus(timeout_result_);
    270 }
    271 
    272 // Output ReadyHandler callback which fires when the server socket is
    273 // ready for data to be sent to it.
    274 void HTTPRequest::WriteToServer(int fd) {
    275   CHECK_EQ(server_socket_, fd);
    276   int ret = sockets_->Send(fd, request_data_.GetConstData(),
    277                            request_data_.GetLength(), 0);
    278   CHECK(ret < 0 || static_cast<size_t>(ret) <= request_data_.GetLength());
    279 
    280   SLOG(connection_.get(), 3) << "In " << __func__ << " wrote " << ret << " of "
    281                              << request_data_.GetLength();
    282 
    283   if (ret < 0) {
    284     LOG(ERROR) << "Client write failed to "
    285                << server_hostname_;
    286     SendStatus(kResultRequestFailure);
    287     return;
    288   }
    289 
    290   request_data_ = ByteString(request_data_.GetConstData() + ret,
    291                              request_data_.GetLength() - ret);
    292 
    293   if (request_data_.IsEmpty()) {
    294     write_server_handler_->Stop();
    295     read_server_handler_.reset(dispatcher_->CreateInputHandler(
    296         server_socket_,
    297         read_server_callback_,
    298         Bind(&HTTPRequest::OnServerReadError, weak_ptr_factory_.GetWeakPtr())));
    299     StartIdleTimeout(kInputTimeoutSeconds, kResultResponseTimeout);
    300   } else {
    301     StartIdleTimeout(kInputTimeoutSeconds, kResultRequestTimeout);
    302   }
    303 }
    304 
    305 }  // namespace shill
    306