• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 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 "chrome/browser/predictors/resource_prefetcher.h"
6 
7 #include <iterator>
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 
Request(const GURL & i_resource_url)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 
Request(const Request & other)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 
ResourcePrefetcher(Delegate * delegate,const ResourcePrefetchPredictorConfig & config,const NavigationID & navigation_id,PrefetchKeyType key_type,scoped_ptr<RequestVector> requests)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   DCHECK_CURRENTLY_ON(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 
~ResourcePrefetcher()55 ResourcePrefetcher::~ResourcePrefetcher() {
56   // Delete any pending net::URLRequests.
57   STLDeleteContainerPairFirstPointers(inflight_requests_.begin(),
58                                       inflight_requests_.end());
59 }
60 
Start()61 void ResourcePrefetcher::Start() {
62   DCHECK(thread_checker_.CalledOnValidThread());
63 
64   CHECK_EQ(state_, INITIALIZED);
65   state_ = RUNNING;
66 
67   TryToLaunchPrefetchRequests();
68 }
69 
Stop()70 void ResourcePrefetcher::Stop() {
71   DCHECK(thread_checker_.CalledOnValidThread());
72 
73   if (state_ == FINISHED)
74     return;
75 
76   state_ = STOPPED;
77 }
78 
TryToLaunchPrefetchRequests()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 ((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, size_t>::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 
SendRequest(Request * request)123 void ResourcePrefetcher::SendRequest(Request* request) {
124   request->prefetch_status = Request::PREFETCH_STATUS_STARTED;
125 
126   net::URLRequest* url_request =
127       delegate_->GetURLRequestContext()->CreateRequest(
128           request->resource_url, net::LOW, this, NULL).release();
129 
130   inflight_requests_[url_request] = request;
131   host_inflight_counts_[url_request->original_url().host()] += 1;
132 
133   url_request->set_method("GET");
134   url_request->set_first_party_for_cookies(navigation_id_.main_frame_url);
135   url_request->SetReferrer(navigation_id_.main_frame_url.spec());
136   StartURLRequest(url_request);
137 }
138 
StartURLRequest(net::URLRequest * request)139 void ResourcePrefetcher::StartURLRequest(net::URLRequest* request) {
140   request->Start();
141 }
142 
FinishRequest(net::URLRequest * request,Request::PrefetchStatus status)143 void ResourcePrefetcher::FinishRequest(net::URLRequest* request,
144                                        Request::PrefetchStatus status) {
145   std::map<net::URLRequest*, Request*>::iterator request_it =
146       inflight_requests_.find(request);
147   CHECK(request_it != inflight_requests_.end());
148 
149   const std::string host = request->original_url().host();
150   std::map<std::string, size_t>::iterator host_it = host_inflight_counts_.find(
151       host);
152   CHECK_GT(host_it->second, 0U);
153   host_it->second -= 1;
154   if (host_it->second == 0)
155     host_inflight_counts_.erase(host);
156 
157   request_it->second->prefetch_status = status;
158   inflight_requests_.erase(request_it);
159 
160   delete request;
161 
162   TryToLaunchPrefetchRequests();
163 }
164 
ReadFullResponse(net::URLRequest * request)165 void ResourcePrefetcher::ReadFullResponse(net::URLRequest* request) {
166   bool status = true;
167   while (status) {
168     int bytes_read = 0;
169     scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(
170         kResourceBufferSizeBytes));
171     status = request->Read(buffer.get(), kResourceBufferSizeBytes, &bytes_read);
172 
173     if (status) {
174       status = ShouldContinueReadingRequest(request, bytes_read);
175     } else if (request->status().error()) {
176       FinishRequest(request, Request::PREFETCH_STATUS_FAILED);
177       return;
178     }
179   }
180 }
181 
ShouldContinueReadingRequest(net::URLRequest * request,int bytes_read)182 bool ResourcePrefetcher::ShouldContinueReadingRequest(net::URLRequest* request,
183                                                       int bytes_read) {
184   if (bytes_read == 0) {  // When bytes_read == 0, no more data.
185     if (request->was_cached())
186       FinishRequest(request, Request::PREFETCH_STATUS_FROM_CACHE);
187     else
188       FinishRequest(request, Request::PREFETCH_STATUS_FROM_NETWORK);
189     return false;
190   }
191 
192   return true;
193 }
194 
OnReceivedRedirect(net::URLRequest * request,const net::RedirectInfo & redirect_info,bool * defer_redirect)195 void ResourcePrefetcher::OnReceivedRedirect(
196     net::URLRequest* request,
197     const net::RedirectInfo& redirect_info,
198     bool* defer_redirect) {
199   FinishRequest(request, Request::PREFETCH_STATUS_REDIRECTED);
200 }
201 
OnAuthRequired(net::URLRequest * request,net::AuthChallengeInfo * auth_info)202 void ResourcePrefetcher::OnAuthRequired(net::URLRequest* request,
203                                         net::AuthChallengeInfo* auth_info) {
204   FinishRequest(request, Request::PREFETCH_STATUS_AUTH_REQUIRED);
205 }
206 
OnCertificateRequested(net::URLRequest * request,net::SSLCertRequestInfo * cert_request_info)207 void ResourcePrefetcher::OnCertificateRequested(
208     net::URLRequest* request,
209     net::SSLCertRequestInfo* cert_request_info) {
210   FinishRequest(request, Request::PREFETCH_STATUS_CERT_REQUIRED);
211 }
212 
OnSSLCertificateError(net::URLRequest * request,const net::SSLInfo & ssl_info,bool fatal)213 void ResourcePrefetcher::OnSSLCertificateError(net::URLRequest* request,
214                                                const net::SSLInfo& ssl_info,
215                                                bool fatal) {
216   FinishRequest(request, Request::PREFETCH_STATUS_CERT_ERROR);
217 }
218 
OnResponseStarted(net::URLRequest * request)219 void ResourcePrefetcher::OnResponseStarted(net::URLRequest* request) {
220   if (request->status().error()) {
221     FinishRequest(request, Request::PREFETCH_STATUS_FAILED);
222     return;
223   }
224 
225   // TODO(shishir): Do not read cached entries, or ones that are not cacheable.
226   ReadFullResponse(request);
227 }
228 
OnReadCompleted(net::URLRequest * request,int bytes_read)229 void ResourcePrefetcher::OnReadCompleted(net::URLRequest* request,
230                                          int bytes_read) {
231   if (request->status().error()) {
232     FinishRequest(request, Request::PREFETCH_STATUS_FAILED);
233     return;
234   }
235 
236   if (ShouldContinueReadingRequest(request, bytes_read))
237     ReadFullResponse(request);
238 }
239 
240 }  // namespace predictors
241