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