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 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 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 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 DCHECK(thread_checker_.CalledOnValidThread()); 63 64 CHECK_EQ(state_, INITIALIZED); 65 state_ = RUNNING; 66 67 TryToLaunchPrefetchRequests(); 68 } 69 70 void ResourcePrefetcher::Stop() { 71 DCHECK(thread_checker_.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 ((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 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 139 void ResourcePrefetcher::StartURLRequest(net::URLRequest* request) { 140 request->Start(); 141 } 142 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 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 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 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 202 void ResourcePrefetcher::OnAuthRequired(net::URLRequest* request, 203 net::AuthChallengeInfo* auth_info) { 204 FinishRequest(request, Request::PREFETCH_STATUS_AUTH_REQUIRED); 205 } 206 207 void ResourcePrefetcher::OnCertificateRequested( 208 net::URLRequest* request, 209 net::SSLCertRequestInfo* cert_request_info) { 210 FinishRequest(request, Request::PREFETCH_STATUS_CERT_REQUIRED); 211 } 212 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 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 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