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