Home | History | Annotate | Download | only in predictors
      1 // Copyright (c) 2012 The Chromium 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 <iterator>
      6 
      7 #include "chrome/browser/predictors/resource_prefetcher.h"
      8 
      9 #include "base/stl_util.h"
     10 #include "content/public/browser/browser_thread.h"
     11 #include "net/base/io_buffer.h"
     12 #include "net/base/request_priority.h"
     13 #include "net/url_request/url_request_context.h"
     14 
     15 namespace {
     16 
     17 // The size of the buffer used to read the resource.
     18 static const size_t kResourceBufferSizeBytes = 50000;
     19 
     20 }  // namespace
     21 
     22 namespace predictors {
     23 
     24 ResourcePrefetcher::Request::Request(const GURL& i_resource_url)
     25     : resource_url(i_resource_url),
     26       prefetch_status(PREFETCH_STATUS_NOT_STARTED),
     27       usage_status(USAGE_STATUS_NOT_REQUESTED) {
     28 }
     29 
     30 ResourcePrefetcher::Request::Request(const Request& other)
     31     : resource_url(other.resource_url),
     32       prefetch_status(other.prefetch_status),
     33       usage_status(other.usage_status) {
     34 }
     35 
     36 ResourcePrefetcher::ResourcePrefetcher(
     37     Delegate* delegate,
     38     const ResourcePrefetchPredictorConfig& config,
     39     const NavigationID& navigation_id,
     40     PrefetchKeyType key_type,
     41     scoped_ptr<RequestVector> requests)
     42         : state_(INITIALIZED),
     43           delegate_(delegate),
     44           config_(config),
     45           navigation_id_(navigation_id),
     46           key_type_(key_type),
     47           request_vector_(requests.Pass()) {
     48   CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
     49   DCHECK(request_vector_.get());
     50 
     51   std::copy(request_vector_->begin(), request_vector_->end(),
     52             std::back_inserter(request_queue_));
     53 }
     54 
     55 ResourcePrefetcher::~ResourcePrefetcher() {
     56   // Delete any pending net::URLRequests.
     57   STLDeleteContainerPairFirstPointers(inflight_requests_.begin(),
     58                                       inflight_requests_.end());
     59 }
     60 
     61 void ResourcePrefetcher::Start() {
     62   CHECK(CalledOnValidThread());
     63 
     64   CHECK_EQ(state_, INITIALIZED);
     65   state_ = RUNNING;
     66 
     67   TryToLaunchPrefetchRequests();
     68 }
     69 
     70 void ResourcePrefetcher::Stop() {
     71   CHECK(CalledOnValidThread());
     72 
     73   if (state_ == FINISHED)
     74     return;
     75 
     76   state_ = STOPPED;
     77 }
     78 
     79 void ResourcePrefetcher::TryToLaunchPrefetchRequests() {
     80   CHECK(state_ == RUNNING || state_ == STOPPED);
     81 
     82   // Try to launch new requests if the state is RUNNING.
     83   if (state_ == RUNNING) {
     84     bool request_available = true;
     85 
     86     // Loop through the requests while we are under the
     87     // max_prefetches_inflight_per_host_per_navigation limit, looking for a URL
     88     // for which the max_prefetches_inflight_per_host_per_navigation limit has
     89     // not been reached. Try to launch as many requests as possible.
     90     while ((static_cast<int>(inflight_requests_.size()) <
     91                 config_.max_prefetches_inflight_per_navigation) &&
     92            request_available) {
     93       std::list<Request*>::iterator request_it = request_queue_.begin();
     94       for (; request_it != request_queue_.end(); ++request_it) {
     95         const std::string& host = (*request_it)->resource_url.host();
     96 
     97         std::map<std::string, int>::iterator host_it =
     98             host_inflight_counts_.find(host);
     99         if (host_it == host_inflight_counts_.end() ||
    100             host_it->second <
    101                 config_.max_prefetches_inflight_per_host_per_navigation)
    102           break;
    103       }
    104       request_available = request_it != request_queue_.end();
    105 
    106       if (request_available) {
    107         SendRequest(*request_it);
    108         request_queue_.erase(request_it);
    109       }
    110     }
    111   }
    112 
    113   // If the inflight_requests_ is empty, we cant launch any more. Finish.
    114   if (inflight_requests_.empty()) {
    115     CHECK(host_inflight_counts_.empty());
    116     CHECK(request_queue_.empty() || state_ == STOPPED);
    117 
    118     state_ = FINISHED;
    119     delegate_->ResourcePrefetcherFinished(this, request_vector_.release());
    120   }
    121 }
    122 
    123 void ResourcePrefetcher::SendRequest(Request* request) {
    124   request->prefetch_status = Request::PREFETCH_STATUS_STARTED;
    125 
    126   net::URLRequest* url_request = new net::URLRequest(
    127       request->resource_url, net::LOW, this, delegate_->GetURLRequestContext());
    128   inflight_requests_[url_request] = request;
    129   host_inflight_counts_[url_request->original_url().host()] += 1;
    130 
    131   url_request->set_method("GET");
    132   url_request->set_first_party_for_cookies(navigation_id_.main_frame_url);
    133   url_request->SetReferrer(navigation_id_.main_frame_url.spec());
    134   StartURLRequest(url_request);
    135 }
    136 
    137 void ResourcePrefetcher::StartURLRequest(net::URLRequest* request) {
    138   request->Start();
    139 }
    140 
    141 void ResourcePrefetcher::FinishRequest(net::URLRequest* request,
    142                                        Request::PrefetchStatus status) {
    143   std::map<net::URLRequest*, Request*>::iterator request_it =
    144       inflight_requests_.find(request);
    145   CHECK(request_it != inflight_requests_.end());
    146 
    147   const std::string host = request->original_url().host();
    148   std::map<std::string, int>::iterator host_it = host_inflight_counts_.find(
    149       host);
    150   CHECK_GT(host_it->second, 0);
    151   host_it->second -= 1;
    152   if (host_it->second == 0)
    153     host_inflight_counts_.erase(host);
    154 
    155   request_it->second->prefetch_status = status;
    156   inflight_requests_.erase(request_it);
    157 
    158   delete request;
    159 
    160   TryToLaunchPrefetchRequests();
    161 }
    162 
    163 void ResourcePrefetcher::ReadFullResponse(net::URLRequest* request) {
    164   bool status = true;
    165   while (status) {
    166     int bytes_read = 0;
    167     scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(
    168         kResourceBufferSizeBytes));
    169     status = request->Read(buffer.get(), kResourceBufferSizeBytes, &bytes_read);
    170 
    171     if (status) {
    172       status = ShouldContinueReadingRequest(request, bytes_read);
    173     } else if (request->status().error()) {
    174       FinishRequest(request, Request::PREFETCH_STATUS_FAILED);
    175       return;
    176     }
    177   }
    178 }
    179 
    180 bool ResourcePrefetcher::ShouldContinueReadingRequest(net::URLRequest* request,
    181                                                       int bytes_read) {
    182   if (bytes_read == 0) {  // When bytes_read == 0, no more data.
    183     if (request->was_cached())
    184       FinishRequest(request, Request::PREFETCH_STATUS_FROM_CACHE);
    185     else
    186       FinishRequest(request, Request::PREFETCH_STATUS_FROM_NETWORK);
    187     return false;
    188   }
    189 
    190   return true;
    191 }
    192 
    193 void ResourcePrefetcher::OnReceivedRedirect(net::URLRequest* request,
    194                                             const GURL& new_url,
    195                                             bool* defer_redirect) {
    196   FinishRequest(request, Request::PREFETCH_STATUS_REDIRECTED);
    197 }
    198 
    199 void ResourcePrefetcher::OnAuthRequired(net::URLRequest* request,
    200                                         net::AuthChallengeInfo* auth_info) {
    201   FinishRequest(request, Request::PREFETCH_STATUS_AUTH_REQUIRED);
    202 }
    203 
    204 void ResourcePrefetcher::OnCertificateRequested(
    205     net::URLRequest* request,
    206     net::SSLCertRequestInfo* cert_request_info) {
    207   FinishRequest(request, Request::PREFETCH_STATUS_CERT_REQUIRED);
    208 }
    209 
    210 void ResourcePrefetcher::OnSSLCertificateError(net::URLRequest* request,
    211                                                const net::SSLInfo& ssl_info,
    212                                                bool fatal) {
    213   FinishRequest(request, Request::PREFETCH_STATUS_CERT_ERROR);
    214 }
    215 
    216 void ResourcePrefetcher::OnResponseStarted(net::URLRequest* request) {
    217   if (request->status().error()) {
    218     FinishRequest(request, Request::PREFETCH_STATUS_FAILED);
    219     return;
    220   }
    221 
    222   // TODO(shishir): Do not read cached entries, or ones that are not cacheable.
    223   ReadFullResponse(request);
    224 }
    225 
    226 void ResourcePrefetcher::OnReadCompleted(net::URLRequest* request,
    227                                          int bytes_read) {
    228   if (request->status().error()) {
    229     FinishRequest(request, Request::PREFETCH_STATUS_FAILED);
    230     return;
    231   }
    232 
    233   if (ShouldContinueReadingRequest(request, bytes_read))
    234     ReadFullResponse(request);
    235 }
    236 
    237 }  // namespace predictors
    238