blob: 6bb23307646a6800fa94dda1cf8f9c3fe61b0732 [file] [log] [blame]
[email protected]74be069e82010-06-25 00:12:491// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved.
license.botbf09a502008-08-24 00:55:552// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
initial.commit09911bf2008-07-26 23:55:294
[email protected]3530cd92010-06-27 06:22:015#include "chrome/browser/net/predictor.h"
initial.commit09911bf2008-07-26 23:55:296
[email protected]1933eb202009-02-19 18:23:257#include <algorithm>
[email protected]319d9e6f2009-02-18 19:47:218#include <set>
[email protected]1933eb202009-02-19 18:23:259#include <sstream>
initial.commit09911bf2008-07-26 23:55:2910
[email protected]1933eb202009-02-19 18:23:2511#include "base/compiler_specific.h"
initial.commit09911bf2008-07-26 23:55:2912#include "base/histogram.h"
13#include "base/stats_counters.h"
[email protected]21dae9b2008-11-06 23:32:5314#include "base/string_util.h"
[email protected]1933eb202009-02-19 18:23:2515#include "base/time.h"
[email protected]6fad2632009-11-02 05:59:3716#include "chrome/browser/chrome_thread.h"
[email protected]760d970a2010-05-18 00:39:1817#include "chrome/browser/net/preconnect.h"
[email protected]1933eb202009-02-19 18:23:2518#include "net/base/address_list.h"
19#include "net/base/completion_callback.h"
[email protected]760d970a2010-05-18 00:39:1820#include "net/base/host_port_pair.h"
[email protected]1933eb202009-02-19 18:23:2521#include "net/base/host_resolver.h"
22#include "net/base/net_errors.h"
[email protected]9e743cd2010-03-16 07:03:5323#include "net/base/net_log.h"
[email protected]e1acf6f2008-10-27 20:43:3324
[email protected]602faf3c2009-06-27 14:35:4425using base::TimeDelta;
26
initial.commit09911bf2008-07-26 23:55:2927namespace chrome_browser_net {
28
[email protected]74be069e82010-06-25 00:12:4929class Predictor::LookupRequest {
[email protected]1933eb202009-02-19 18:23:2530 public:
[email protected]74be069e82010-06-25 00:12:4931 LookupRequest(Predictor* predictor,
[email protected]8a00f00a2009-06-12 00:49:3832 net::HostResolver* host_resolver,
[email protected]c5629c32010-06-23 01:22:4333 const GURL& url)
[email protected]1933eb202009-02-19 18:23:2534 : ALLOW_THIS_IN_INITIALIZER_LIST(
35 net_callback_(this, &LookupRequest::OnLookupFinished)),
[email protected]74be069e82010-06-25 00:12:4936 predictor_(predictor),
[email protected]c5629c32010-06-23 01:22:4337 url_(url),
[email protected]8a00f00a2009-06-12 00:49:3838 resolver_(host_resolver) {
initial.commit09911bf2008-07-26 23:55:2939 }
[email protected]1933eb202009-02-19 18:23:2540
[email protected]85398532009-06-16 21:32:1841 // Return underlying network resolver status.
42 // net::OK ==> Host was found synchronously.
43 // net:ERR_IO_PENDING ==> Network will callback later with result.
44 // anything else ==> Host was not found synchronously.
45 int Start() {
[email protected]c5629c32010-06-23 01:22:4346 net::HostResolver::RequestInfo resolve_info(url_.host(),
47 url_.EffectiveIntPort());
[email protected]2884a462009-06-15 05:08:4248
49 // Make a note that this is a speculative resolve request. This allows us
50 // to separate it from real navigations in the observer's callback, and
51 // lets the HostResolver know it can de-prioritize it.
52 resolve_info.set_is_speculative(true);
[email protected]ec08bb22009-08-12 00:25:1253 return resolver_.Resolve(
[email protected]9e743cd2010-03-16 07:03:5354 resolve_info, &addresses_, &net_callback_, net::BoundNetLog());
[email protected]1933eb202009-02-19 18:23:2555 }
56
57 private:
58 void OnLookupFinished(int result) {
[email protected]74be069e82010-06-25 00:12:4959 predictor_->OnLookupFinished(this, url_, result == net::OK);
[email protected]1933eb202009-02-19 18:23:2560 }
61
62 // HostResolver will call us using this callback when resolution is complete.
63 net::CompletionCallbackImpl<LookupRequest> net_callback_;
64
[email protected]74be069e82010-06-25 00:12:4965 Predictor* predictor_; // The predictor which started us.
[email protected]1933eb202009-02-19 18:23:2566
[email protected]c5629c32010-06-23 01:22:4367 const GURL url_; // Hostname to resolve.
[email protected]8a00f00a2009-06-12 00:49:3868 net::SingleRequestHostResolver resolver_;
[email protected]1933eb202009-02-19 18:23:2569 net::AddressList addresses_;
70
71 DISALLOW_COPY_AND_ASSIGN(LookupRequest);
72};
73
[email protected]74be069e82010-06-25 00:12:4974Predictor::Predictor(net::HostResolver* host_resolver,
75 base::TimeDelta max_dns_queue_delay,
[email protected]760d970a2010-05-18 00:39:1876 size_t max_concurrent,
77 bool preconnect_enabled)
78 : peak_pending_lookups_(0),
79 shutdown_(false),
[email protected]74be069e82010-06-25 00:12:4980 max_concurrent_dns_lookups_(max_concurrent),
81 max_dns_queue_delay_(max_dns_queue_delay),
[email protected]760d970a2010-05-18 00:39:1882 host_resolver_(host_resolver),
83 preconnect_enabled_(preconnect_enabled) {
84 Referrer::SetUsePreconnectValuations(preconnect_enabled);
[email protected]1933eb202009-02-19 18:23:2585}
86
[email protected]74be069e82010-06-25 00:12:4987Predictor::~Predictor() {
[email protected]1933eb202009-02-19 18:23:2588 DCHECK(shutdown_);
89}
90
[email protected]74be069e82010-06-25 00:12:4991void Predictor::Shutdown() {
[email protected]ec86bea2009-12-08 18:35:1492 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
[email protected]1933eb202009-02-19 18:23:2593 DCHECK(!shutdown_);
94 shutdown_ = true;
95
96 std::set<LookupRequest*>::iterator it;
97 for (it = pending_lookups_.begin(); it != pending_lookups_.end(); ++it)
98 delete *it;
initial.commit09911bf2008-07-26 23:55:2999}
100
101// Overloaded Resolve() to take a vector of names.
[email protected]74be069e82010-06-25 00:12:49102void Predictor::ResolveList(const UrlList& urls,
103 UrlInfo::ResolutionMotivation motivation) {
[email protected]ec86bea2009-12-08 18:35:14104 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
[email protected]fd2f8afe2009-06-11 21:53:55105
[email protected]c5629c32010-06-23 01:22:43106 for (UrlList::const_iterator it = urls.begin(); it < urls.end(); ++it) {
107 AppendToResolutionQueue(*it, motivation);
108 }
initial.commit09911bf2008-07-26 23:55:29109}
110
111// Basic Resolve() takes an invidual name, and adds it
112// to the queue.
[email protected]74be069e82010-06-25 00:12:49113void Predictor::Resolve(const GURL& url,
114 UrlInfo::ResolutionMotivation motivation) {
[email protected]ec86bea2009-12-08 18:35:14115 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
[email protected]c5629c32010-06-23 01:22:43116 if (!url.has_host())
initial.commit09911bf2008-07-26 23:55:29117 return;
[email protected]c5629c32010-06-23 01:22:43118 AppendToResolutionQueue(url, motivation);
initial.commit09911bf2008-07-26 23:55:29119}
120
[email protected]74be069e82010-06-25 00:12:49121bool Predictor::AccruePrefetchBenefits(const GURL& referrer,
122 UrlInfo* navigation_info) {
[email protected]ec86bea2009-12-08 18:35:14123 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
[email protected]c5629c32010-06-23 01:22:43124 GURL url = navigation_info->url();
125 Results::iterator it = results_.find(url);
[email protected]21dae9b2008-11-06 23:32:53126 if (it == results_.end()) {
[email protected]21dae9b2008-11-06 23:32:53127 // Use UMA histogram to quantify potential future gains here.
[email protected]553dba62009-02-24 19:08:23128 UMA_HISTOGRAM_LONG_TIMES("DNS.UnexpectedResolutionL",
[email protected]21dae9b2008-11-06 23:32:53129 navigation_info->resolve_duration());
130 navigation_info->DLogResultsStats("DNS UnexpectedResolution");
131
[email protected]74be069e82010-06-25 00:12:49132 LearnFromNavigation(referrer, navigation_info->url());
[email protected]21dae9b2008-11-06 23:32:53133 return false;
134 }
[email protected]74be069e82010-06-25 00:12:49135 UrlInfo& prefetched_host_info(it->second);
[email protected]21dae9b2008-11-06 23:32:53136
137 // Sometimes a host is used as a subresource by several referrers, so it is
138 // in our list, but was never motivated by a page-link-scan. In that case, it
139 // really is an "unexpected" navigation, and we should tally it, and augment
140 // our referrers_.
141 bool referrer_based_prefetch = !prefetched_host_info.was_linked();
142 if (referrer_based_prefetch) {
143 // This wasn't the first time this host refered to *some* referrer.
[email protected]74be069e82010-06-25 00:12:49144 LearnFromNavigation(referrer, navigation_info->url());
[email protected]21dae9b2008-11-06 23:32:53145 }
146
147 DnsBenefit benefit = prefetched_host_info.AccruePrefetchBenefits(
148 navigation_info);
149 switch (benefit) {
150 case PREFETCH_NAME_FOUND:
151 case PREFETCH_NAME_NONEXISTANT:
[email protected]74be069e82010-06-25 00:12:49152 dns_cache_hits_.push_back(*navigation_info);
[email protected]21dae9b2008-11-06 23:32:53153 if (referrer_based_prefetch) {
[email protected]c5629c32010-06-23 01:22:43154 if (referrer.has_host()) {
[email protected]760d970a2010-05-18 00:39:18155 referrers_[referrer].AccrueValue(
[email protected]c5629c32010-06-23 01:22:43156 navigation_info->benefits_remaining(), url);
[email protected]21dae9b2008-11-06 23:32:53157 }
158 }
159 return true;
160
161 case PREFETCH_CACHE_EVICTION:
[email protected]c5629c32010-06-23 01:22:43162 cache_eviction_map_[url] = *navigation_info;
[email protected]21dae9b2008-11-06 23:32:53163 return false;
164
165 case PREFETCH_NO_BENEFIT:
166 // Prefetch never hit the network. Name was pre-cached.
167 return false;
168
169 default:
[email protected]760d970a2010-05-18 00:39:18170 NOTREACHED();
[email protected]21dae9b2008-11-06 23:32:53171 return false;
172 }
173}
174
[email protected]74be069e82010-06-25 00:12:49175void Predictor::LearnFromNavigation(const GURL& referring_url,
176 const GURL& target_url) {
[email protected]ec86bea2009-12-08 18:35:14177 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
[email protected]c5629c32010-06-23 01:22:43178 if (referring_url.has_host() &&
179 referring_url != target_url) {
180 DCHECK(referring_url == referring_url.GetWithEmptyPath());
181 referrers_[referring_url].SuggestHost(target_url);
182 }
[email protected]21dae9b2008-11-06 23:32:53183}
184
[email protected]74be069e82010-06-25 00:12:49185void Predictor::PredictSubresources(const GURL& url) {
[email protected]ec86bea2009-12-08 18:35:14186 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
[email protected]c5629c32010-06-23 01:22:43187 Referrers::iterator it = referrers_.find(url);
[email protected]1933eb202009-02-19 18:23:25188 if (referrers_.end() == it)
189 return;
190 Referrer* referrer = &(it->second);
[email protected]760d970a2010-05-18 00:39:18191 referrer->IncrementUseCount();
[email protected]c5629c32010-06-23 01:22:43192 for (Referrer::iterator future_url = referrer->begin();
193 future_url != referrer->end(); ++future_url) {
[email protected]74be069e82010-06-25 00:12:49194 UrlInfo* queued_info = AppendToResolutionQueue(
[email protected]c5629c32010-06-23 01:22:43195 future_url->first,
[email protected]74be069e82010-06-25 00:12:49196 UrlInfo::LEARNED_REFERAL_MOTIVATED);
[email protected]1933eb202009-02-19 18:23:25197 if (queued_info)
[email protected]c5629c32010-06-23 01:22:43198 queued_info->SetReferringHostname(url);
199 }
200}
201
[email protected]74be069e82010-06-25 00:12:49202void Predictor::PredictFrameSubresources(const GURL& url) {
[email protected]c5629c32010-06-23 01:22:43203 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
204 DCHECK(url.GetWithEmptyPath() == url);
205 Referrers::iterator it = referrers_.find(url);
206 if (referrers_.end() == it)
207 return;
208 Referrer* referrer = &(it->second);
209 referrer->IncrementUseCount();
210 for (Referrer::iterator future_url = referrer->begin();
211 future_url != referrer->end(); ++future_url) {
212 if (future_url->second.IsPreconnectWorthDoing())
213 Preconnect::PreconnectOnIOThread(future_url->first);
initial.commit09911bf2008-07-26 23:55:29214 }
215}
216
initial.commit09911bf2008-07-26 23:55:29217// Provide sort order so all .com's are together, etc.
218struct RightToLeftStringSorter {
[email protected]760d970a2010-05-18 00:39:18219 bool operator()(const net::HostPortPair& left,
220 const net::HostPortPair& right) const {
221 return string_compare(left.host, right.host);
222 }
223 bool operator()(const GURL& left,
224 const GURL& right) const {
225 return string_compare(left.host(), right.host());
226 }
227
228 static bool string_compare(const std::string& left_host,
229 const std::string right_host) {
230 if (left_host == right_host) return true;
231 size_t left_already_matched = left_host.size();
232 size_t right_already_matched = right_host.size();
[email protected]21dae9b2008-11-06 23:32:53233
234 // Ensure both strings have characters.
235 if (!left_already_matched) return true;
236 if (!right_already_matched) return false;
237
238 // Watch for trailing dot, so we'll always be safe to go one beyond dot.
[email protected]760d970a2010-05-18 00:39:18239 if ('.' == left_host[left_already_matched - 1]) {
240 if ('.' != right_host[right_already_matched - 1])
initial.commit09911bf2008-07-26 23:55:29241 return true;
[email protected]21dae9b2008-11-06 23:32:53242 // Both have dots at end of string.
243 --left_already_matched;
244 --right_already_matched;
245 } else {
[email protected]760d970a2010-05-18 00:39:18246 if ('.' == right_host[right_already_matched - 1])
[email protected]21dae9b2008-11-06 23:32:53247 return false;
248 }
249
250 while (1) {
251 if (!left_already_matched) return true;
252 if (!right_already_matched) return false;
253
254 size_t left_length, right_length;
[email protected]760d970a2010-05-18 00:39:18255 size_t left_start = left_host.find_last_of('.', left_already_matched - 1);
[email protected]21dae9b2008-11-06 23:32:53256 if (std::string::npos == left_start) {
257 left_length = left_already_matched;
258 left_already_matched = left_start = 0;
259 } else {
260 left_length = left_already_matched - left_start;
261 left_already_matched = left_start;
262 ++left_start; // Don't compare the dot.
263 }
[email protected]760d970a2010-05-18 00:39:18264 size_t right_start = right_host.find_last_of('.',
265 right_already_matched - 1);
[email protected]21dae9b2008-11-06 23:32:53266 if (std::string::npos == right_start) {
267 right_length = right_already_matched;
268 right_already_matched = right_start = 0;
269 } else {
270 right_length = right_already_matched - right_start;
271 right_already_matched = right_start;
272 ++right_start; // Don't compare the dot.
273 }
274
[email protected]760d970a2010-05-18 00:39:18275 int diff = left_host.compare(left_start, left_host.size(),
276 right_host, right_start, right_host.size());
[email protected]21dae9b2008-11-06 23:32:53277 if (diff > 0) return false;
278 if (diff < 0) return true;
initial.commit09911bf2008-07-26 23:55:29279 }
280 }
281};
282
[email protected]74be069e82010-06-25 00:12:49283void Predictor::GetHtmlReferrerLists(std::string* output) {
[email protected]ec86bea2009-12-08 18:35:14284 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
[email protected]21dae9b2008-11-06 23:32:53285 if (referrers_.empty())
286 return;
287
288 // TODO(jar): Remove any plausible JavaScript from names before displaying.
289
[email protected]c5629c32010-06-23 01:22:43290 typedef std::set<GURL, struct RightToLeftStringSorter>
[email protected]760d970a2010-05-18 00:39:18291 SortedNames;
[email protected]21dae9b2008-11-06 23:32:53292 SortedNames sorted_names;
293
294 for (Referrers::iterator it = referrers_.begin();
295 referrers_.end() != it; ++it)
296 sorted_names.insert(it->first);
297
298 output->append("<br><table border>");
[email protected]760d970a2010-05-18 00:39:18299 output->append(
300 "<tr><th>Host for Page</th>"
301 "<th>Page Load<br>Count</th>"
302 "<th>Subresource<br>Navigations</th>"
303 "<th>Subresource<br>PreConnects</th>"
304 "<th>Expected<br>Connects</th>"
305 "<th>DNS<br>Savings</th>"
306 "<th>Subresource Spec</th></tr>");
[email protected]21dae9b2008-11-06 23:32:53307
308 for (SortedNames::iterator it = sorted_names.begin();
309 sorted_names.end() != it; ++it) {
310 Referrer* referrer = &(referrers_[*it]);
[email protected]760d970a2010-05-18 00:39:18311 bool first_set_of_futures = true;
[email protected]c5629c32010-06-23 01:22:43312 for (Referrer::iterator future_url = referrer->begin();
313 future_url != referrer->end(); ++future_url) {
[email protected]760d970a2010-05-18 00:39:18314 output->append("<tr align=right>");
315 if (first_set_of_futures)
316 StringAppendF(output, "<td rowspan=%d>%s</td><td rowspan=%d>%d</td>",
317 static_cast<int>(referrer->size()),
[email protected]c5629c32010-06-23 01:22:43318 it->spec().c_str(),
[email protected]760d970a2010-05-18 00:39:18319 static_cast<int>(referrer->size()),
320 static_cast<int>(referrer->use_count()));
321 first_set_of_futures = false;
322 StringAppendF(output,
323 "<td>%d</td><td>%d</td><td>%2.3f</td><td>%dms</td><td>%s</td></tr>",
[email protected]c5629c32010-06-23 01:22:43324 static_cast<int>(future_url->second.navigation_count()),
325 static_cast<int>(future_url->second.preconnection_count()),
326 static_cast<double>(future_url->second.subresource_use_rate()),
327 static_cast<int>(future_url->second.latency().InMilliseconds()),
328 future_url->first.spec().c_str());
[email protected]21dae9b2008-11-06 23:32:53329 }
[email protected]21dae9b2008-11-06 23:32:53330 }
331 output->append("</table>");
332}
333
[email protected]74be069e82010-06-25 00:12:49334void Predictor::GetHtmlInfo(std::string* output) {
[email protected]ec86bea2009-12-08 18:35:14335 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
[email protected]74be069e82010-06-25 00:12:49336 // Local lists for calling UrlInfo
337 UrlInfo::DnsInfoTable cache_hits;
338 UrlInfo::DnsInfoTable cache_evictions;
339 UrlInfo::DnsInfoTable name_not_found;
340 UrlInfo::DnsInfoTable network_hits;
341 UrlInfo::DnsInfoTable already_cached;
initial.commit09911bf2008-07-26 23:55:29342
[email protected]ec86bea2009-12-08 18:35:14343 // Get copies of all useful data.
[email protected]74be069e82010-06-25 00:12:49344 typedef std::map<GURL, UrlInfo, RightToLeftStringSorter>
[email protected]760d970a2010-05-18 00:39:18345 Snapshot;
initial.commit09911bf2008-07-26 23:55:29346 Snapshot snapshot;
347 {
[email protected]74be069e82010-06-25 00:12:49348 // UrlInfo supports value semantics, so we can do a shallow copy.
initial.commit09911bf2008-07-26 23:55:29349 for (Results::iterator it(results_.begin()); it != results_.end(); it++) {
350 snapshot[it->first] = it->second;
351 }
352 for (Results::iterator it(cache_eviction_map_.begin());
353 it != cache_eviction_map_.end();
354 it++) {
355 cache_evictions.push_back(it->second);
356 }
357 // Reverse list as we copy cache hits, so that new hits are at the top.
[email protected]74be069e82010-06-25 00:12:49358 size_t index = dns_cache_hits_.size();
initial.commit09911bf2008-07-26 23:55:29359 while (index > 0) {
360 index--;
[email protected]74be069e82010-06-25 00:12:49361 cache_hits.push_back(dns_cache_hits_[index]);
initial.commit09911bf2008-07-26 23:55:29362 }
363 }
364
[email protected]74be069e82010-06-25 00:12:49365 // Partition the UrlInfo's into categories.
initial.commit09911bf2008-07-26 23:55:29366 for (Snapshot::iterator it(snapshot.begin()); it != snapshot.end(); it++) {
367 if (it->second.was_nonexistant()) {
368 name_not_found.push_back(it->second);
369 continue;
370 }
371 if (!it->second.was_found())
372 continue; // Still being processed.
[email protected]602faf3c2009-06-27 14:35:44373 if (TimeDelta() != it->second.benefits_remaining()) {
initial.commit09911bf2008-07-26 23:55:29374 network_hits.push_back(it->second); // With no benefit yet.
375 continue;
376 }
[email protected]74be069e82010-06-25 00:12:49377 if (UrlInfo::kMaxNonNetworkDnsLookupDuration >
initial.commit09911bf2008-07-26 23:55:29378 it->second.resolve_duration()) {
379 already_cached.push_back(it->second);
380 continue;
381 }
382 // Remaining case is where prefetch benefit was significant, and was used.
383 // Since we shot those cases as historical hits, we won't bother here.
384 }
385
386 bool brief = false;
387#ifdef NDEBUG
388 brief = true;
389#endif // NDEBUG
390
391 // Call for display of each table, along with title.
[email protected]74be069e82010-06-25 00:12:49392 UrlInfo::GetHtmlTable(cache_hits,
initial.commit09911bf2008-07-26 23:55:29393 "Prefetching DNS records produced benefits for ", false, output);
[email protected]74be069e82010-06-25 00:12:49394 UrlInfo::GetHtmlTable(cache_evictions,
initial.commit09911bf2008-07-26 23:55:29395 "Cache evictions negated DNS prefetching benefits for ", brief, output);
[email protected]74be069e82010-06-25 00:12:49396 UrlInfo::GetHtmlTable(network_hits,
initial.commit09911bf2008-07-26 23:55:29397 "Prefetching DNS records was not yet beneficial for ", brief, output);
[email protected]74be069e82010-06-25 00:12:49398 UrlInfo::GetHtmlTable(already_cached,
initial.commit09911bf2008-07-26 23:55:29399 "Previously cached resolutions were found for ", brief, output);
[email protected]74be069e82010-06-25 00:12:49400 UrlInfo::GetHtmlTable(name_not_found,
initial.commit09911bf2008-07-26 23:55:29401 "Prefetching DNS records revealed non-existance for ", brief, output);
402}
403
[email protected]74be069e82010-06-25 00:12:49404UrlInfo* Predictor::AppendToResolutionQueue(
[email protected]c5629c32010-06-23 01:22:43405 const GURL& url,
[email protected]74be069e82010-06-25 00:12:49406 UrlInfo::ResolutionMotivation motivation) {
[email protected]ec86bea2009-12-08 18:35:14407 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
[email protected]c5629c32010-06-23 01:22:43408 DCHECK(url.has_host());
initial.commit09911bf2008-07-26 23:55:29409
[email protected]1933eb202009-02-19 18:23:25410 if (shutdown_)
411 return NULL;
412
[email protected]74be069e82010-06-25 00:12:49413 UrlInfo* info = &results_[url];
[email protected]c5629c32010-06-23 01:22:43414 info->SetUrl(url); // Initialize or DCHECK.
initial.commit09911bf2008-07-26 23:55:29415 // TODO(jar): I need to discard names that have long since expired.
416 // Currently we only add to the domain map :-/
417
[email protected]c5629c32010-06-23 01:22:43418 DCHECK(info->HasUrl(url));
initial.commit09911bf2008-07-26 23:55:29419
[email protected]760d970a2010-05-18 00:39:18420 if (!info->NeedsDnsUpdate()) {
initial.commit09911bf2008-07-26 23:55:29421 info->DLogResultsStats("DNS PrefetchNotUpdated");
[email protected]21dae9b2008-11-06 23:32:53422 return NULL;
initial.commit09911bf2008-07-26 23:55:29423 }
424
[email protected]21dae9b2008-11-06 23:32:53425 info->SetQueuedState(motivation);
[email protected]c5629c32010-06-23 01:22:43426 work_queue_.Push(url, motivation);
[email protected]ec86bea2009-12-08 18:35:14427 StartSomeQueuedResolutions();
[email protected]21dae9b2008-11-06 23:32:53428 return info;
initial.commit09911bf2008-07-26 23:55:29429}
430
[email protected]74be069e82010-06-25 00:12:49431void Predictor::StartSomeQueuedResolutions() {
[email protected]6fad2632009-11-02 05:59:37432 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
[email protected]fd2f8afe2009-06-11 21:53:55433
[email protected]a20bc092009-06-05 01:34:20434 while (!work_queue_.IsEmpty() &&
[email protected]74be069e82010-06-25 00:12:49435 pending_lookups_.size() < max_concurrent_dns_lookups_) {
[email protected]c5629c32010-06-23 01:22:43436 const GURL url(work_queue_.Pop());
[email protected]74be069e82010-06-25 00:12:49437 UrlInfo* info = &results_[url];
[email protected]c5629c32010-06-23 01:22:43438 DCHECK(info->HasUrl(url));
initial.commit09911bf2008-07-26 23:55:29439 info->SetAssignedState();
440
[email protected]ec86bea2009-12-08 18:35:14441 if (CongestionControlPerformed(info)) {
[email protected]a20bc092009-06-05 01:34:20442 DCHECK(work_queue_.IsEmpty());
443 return;
444 }
445
[email protected]c5629c32010-06-23 01:22:43446 LookupRequest* request = new LookupRequest(this, host_resolver_, url);
[email protected]85398532009-06-16 21:32:18447 int status = request->Start();
448 if (status == net::ERR_IO_PENDING) {
[email protected]fd2f8afe2009-06-11 21:53:55449 // Will complete asynchronously.
[email protected]1933eb202009-02-19 18:23:25450 pending_lookups_.insert(request);
451 peak_pending_lookups_ = std::max(peak_pending_lookups_,
452 pending_lookups_.size());
453 } else {
[email protected]221f33362009-06-29 20:46:48454 // Completed synchronously (was already cached by HostResolver), or else
[email protected]85398532009-06-16 21:32:18455 // there was (equivalently) some network error that prevents us from
456 // finding the name. Status net::OK means it was "found."
[email protected]c5629c32010-06-23 01:22:43457 LookupFinished(request, url, status == net::OK);
[email protected]1933eb202009-02-19 18:23:25458 delete request;
459 }
460 }
initial.commit09911bf2008-07-26 23:55:29461}
462
[email protected]74be069e82010-06-25 00:12:49463bool Predictor::CongestionControlPerformed(UrlInfo* info) {
[email protected]ec86bea2009-12-08 18:35:14464 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
[email protected]a20bc092009-06-05 01:34:20465 // Note: queue_duration is ONLY valid after we go to assigned state.
[email protected]74be069e82010-06-25 00:12:49466 if (info->queue_duration() < max_dns_queue_delay_)
[email protected]a20bc092009-06-05 01:34:20467 return false;
468 // We need to discard all entries in our queue, as we're keeping them waiting
469 // too long. By doing this, we'll have a chance to quickly service urgent
470 // resolutions, and not have a bogged down system.
471 while (true) {
472 info->RemoveFromQueue();
473 if (work_queue_.IsEmpty())
474 break;
475 info = &results_[work_queue_.Pop()];
476 info->SetAssignedState();
477 }
478 return true;
479}
480
[email protected]74be069e82010-06-25 00:12:49481void Predictor::OnLookupFinished(LookupRequest* request, const GURL& url,
[email protected]760d970a2010-05-18 00:39:18482 bool found) {
[email protected]6fad2632009-11-02 05:59:37483 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
[email protected]fd2f8afe2009-06-11 21:53:55484
[email protected]c5629c32010-06-23 01:22:43485 LookupFinished(request, url, found);
[email protected]85398532009-06-16 21:32:18486 pending_lookups_.erase(request);
487 delete request;
488
[email protected]ec86bea2009-12-08 18:35:14489 StartSomeQueuedResolutions();
[email protected]85398532009-06-16 21:32:18490}
491
[email protected]74be069e82010-06-25 00:12:49492void Predictor::LookupFinished(LookupRequest* request, const GURL& url,
[email protected]ec86bea2009-12-08 18:35:14493 bool found) {
494 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
[email protected]74be069e82010-06-25 00:12:49495 UrlInfo* info = &results_[url];
[email protected]c5629c32010-06-23 01:22:43496 DCHECK(info->HasUrl(url));
[email protected]a20bc092009-06-05 01:34:20497 if (info->is_marked_to_delete()) {
[email protected]c5629c32010-06-23 01:22:43498 results_.erase(url);
[email protected]a20bc092009-06-05 01:34:20499 } else {
[email protected]1933eb202009-02-19 18:23:25500 if (found)
501 info->SetFoundState();
502 else
503 info->SetNoSuchNameState();
[email protected]7c19b87b02009-01-26 16:19:44504 }
initial.commit09911bf2008-07-26 23:55:29505}
506
[email protected]74be069e82010-06-25 00:12:49507void Predictor::DiscardAllResults() {
[email protected]ec86bea2009-12-08 18:35:14508 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
initial.commit09911bf2008-07-26 23:55:29509 // Delete anything listed so far in this session that shows in about:dns.
510 cache_eviction_map_.clear();
[email protected]74be069e82010-06-25 00:12:49511 dns_cache_hits_.clear();
[email protected]21dae9b2008-11-06 23:32:53512 referrers_.clear();
initial.commit09911bf2008-07-26 23:55:29513
514
515 // Try to delete anything in our work queue.
[email protected]a20bc092009-06-05 01:34:20516 while (!work_queue_.IsEmpty()) {
initial.commit09911bf2008-07-26 23:55:29517 // Emulate processing cycle as though host was not found.
[email protected]c5629c32010-06-23 01:22:43518 GURL url = work_queue_.Pop();
[email protected]74be069e82010-06-25 00:12:49519 UrlInfo* info = &results_[url];
[email protected]c5629c32010-06-23 01:22:43520 DCHECK(info->HasUrl(url));
initial.commit09911bf2008-07-26 23:55:29521 info->SetAssignedState();
522 info->SetNoSuchNameState();
523 }
[email protected]1933eb202009-02-19 18:23:25524 // Now every result_ is either resolved, or is being resolved
525 // (see LookupRequest).
initial.commit09911bf2008-07-26 23:55:29526
527 // Step through result_, recording names of all hosts that can't be erased.
[email protected]1933eb202009-02-19 18:23:25528 // We can't erase anything being worked on.
initial.commit09911bf2008-07-26 23:55:29529 Results assignees;
530 for (Results::iterator it = results_.begin(); results_.end() != it; ++it) {
[email protected]c5629c32010-06-23 01:22:43531 GURL url(it->first);
[email protected]74be069e82010-06-25 00:12:49532 UrlInfo* info = &it->second;
[email protected]c5629c32010-06-23 01:22:43533 DCHECK(info->HasUrl(url));
initial.commit09911bf2008-07-26 23:55:29534 if (info->is_assigned()) {
535 info->SetPendingDeleteState();
[email protected]c5629c32010-06-23 01:22:43536 assignees[url] = *info;
initial.commit09911bf2008-07-26 23:55:29537 }
538 }
[email protected]74be069e82010-06-25 00:12:49539 DCHECK(assignees.size() <= max_concurrent_dns_lookups_);
initial.commit09911bf2008-07-26 23:55:29540 results_.clear();
[email protected]1933eb202009-02-19 18:23:25541 // Put back in the names being worked on.
initial.commit09911bf2008-07-26 23:55:29542 for (Results::iterator it = assignees.begin(); assignees.end() != it; ++it) {
543 DCHECK(it->second.is_marked_to_delete());
544 results_[it->first] = it->second;
545 }
546}
547
[email protected]74be069e82010-06-25 00:12:49548void Predictor::TrimReferrers() {
[email protected]ec86bea2009-12-08 18:35:14549 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
[email protected]c5629c32010-06-23 01:22:43550 std::vector<GURL> urls;
[email protected]03c5e862009-02-17 22:50:14551 for (Referrers::const_iterator it = referrers_.begin();
552 it != referrers_.end(); ++it)
[email protected]c5629c32010-06-23 01:22:43553 urls.push_back(it->first);
554 for (size_t i = 0; i < urls.size(); ++i)
555 if (!referrers_[urls[i]].Trim())
556 referrers_.erase(urls[i]);
[email protected]03c5e862009-02-17 22:50:14557}
558
[email protected]74be069e82010-06-25 00:12:49559void Predictor::SerializeReferrers(ListValue* referral_list) {
[email protected]ec86bea2009-12-08 18:35:14560 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
[email protected]03c5e862009-02-17 22:50:14561 referral_list->Clear();
[email protected]760d970a2010-05-18 00:39:18562 referral_list->Append(new FundamentalValue(DNS_REFERRER_VERSION));
[email protected]03c5e862009-02-17 22:50:14563 for (Referrers::const_iterator it = referrers_.begin();
564 it != referrers_.end(); ++it) {
565 // Serialize the list of subresource names.
566 Value* subresource_list(it->second.Serialize());
567
568 // Create a list for each referer.
[email protected]760d970a2010-05-18 00:39:18569 ListValue* motivator(new ListValue);
[email protected]c5629c32010-06-23 01:22:43570 motivator->Append(new StringValue(it->first.spec()));
[email protected]760d970a2010-05-18 00:39:18571 motivator->Append(subresource_list);
[email protected]03c5e862009-02-17 22:50:14572
[email protected]760d970a2010-05-18 00:39:18573 referral_list->Append(motivator);
[email protected]03c5e862009-02-17 22:50:14574 }
575}
576
[email protected]74be069e82010-06-25 00:12:49577void Predictor::DeserializeReferrers(const ListValue& referral_list) {
[email protected]ec86bea2009-12-08 18:35:14578 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
[email protected]760d970a2010-05-18 00:39:18579 int format_version = -1;
580 if (referral_list.GetSize() > 0 &&
581 referral_list.GetInteger(0, &format_version) &&
582 format_version == DNS_REFERRER_VERSION) {
583 for (size_t i = 1; i < referral_list.GetSize(); ++i) {
584 ListValue* motivator;
585 if (!referral_list.GetList(i, &motivator)) {
586 NOTREACHED();
[email protected]c5629c32010-06-23 01:22:43587 return;
[email protected]760d970a2010-05-18 00:39:18588 }
[email protected]c5629c32010-06-23 01:22:43589 std::string motivating_url_spec;
590 if (!motivator->GetString(0, &motivating_url_spec)) {
[email protected]760d970a2010-05-18 00:39:18591 NOTREACHED();
[email protected]c5629c32010-06-23 01:22:43592 return;
[email protected]760d970a2010-05-18 00:39:18593 }
594
595 Value* subresource_list;
[email protected]c5629c32010-06-23 01:22:43596 if (!motivator->Get(1, &subresource_list)) {
[email protected]760d970a2010-05-18 00:39:18597 NOTREACHED();
[email protected]c5629c32010-06-23 01:22:43598 return;
[email protected]760d970a2010-05-18 00:39:18599 }
[email protected]c5629c32010-06-23 01:22:43600
601 referrers_[GURL(motivating_url_spec)].Deserialize(*subresource_list);
[email protected]760d970a2010-05-18 00:39:18602 }
[email protected]03c5e862009-02-17 22:50:14603 }
604}
605
[email protected]a20bc092009-06-05 01:34:20606
607//------------------------------------------------------------------------------
608
[email protected]74be069e82010-06-25 00:12:49609Predictor::HostNameQueue::HostNameQueue() {
[email protected]a20bc092009-06-05 01:34:20610}
611
[email protected]74be069e82010-06-25 00:12:49612Predictor::HostNameQueue::~HostNameQueue() {
[email protected]a20bc092009-06-05 01:34:20613}
614
[email protected]74be069e82010-06-25 00:12:49615void Predictor::HostNameQueue::Push(const GURL& url,
616 UrlInfo::ResolutionMotivation motivation) {
[email protected]a20bc092009-06-05 01:34:20617 switch (motivation) {
[email protected]74be069e82010-06-25 00:12:49618 case UrlInfo::STATIC_REFERAL_MOTIVATED:
619 case UrlInfo::LEARNED_REFERAL_MOTIVATED:
620 case UrlInfo::MOUSE_OVER_MOTIVATED:
[email protected]c5629c32010-06-23 01:22:43621 rush_queue_.push(url);
[email protected]a20bc092009-06-05 01:34:20622 break;
623
624 default:
[email protected]c5629c32010-06-23 01:22:43625 background_queue_.push(url);
[email protected]a20bc092009-06-05 01:34:20626 break;
627 }
628}
629
[email protected]74be069e82010-06-25 00:12:49630bool Predictor::HostNameQueue::IsEmpty() const {
[email protected]a20bc092009-06-05 01:34:20631 return rush_queue_.empty() && background_queue_.empty();
632}
633
[email protected]74be069e82010-06-25 00:12:49634GURL Predictor::HostNameQueue::Pop() {
[email protected]a20bc092009-06-05 01:34:20635 DCHECK(!IsEmpty());
[email protected]c5629c32010-06-23 01:22:43636 std::queue<GURL> *queue(rush_queue_.empty() ? &background_queue_
637 : &rush_queue_);
638 GURL url(queue->front());
639 queue->pop();
640 return url;
[email protected]a20bc092009-06-05 01:34:20641}
642
initial.commit09911bf2008-07-26 23:55:29643} // namespace chrome_browser_net