Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(153)

Side by Side Diff: components/cronet/stale_host_resolver.cc

Issue 1898873006: Cronet: Use stale DNS cache entries experimentally. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@dns_stale2
Patch Set: rebase, make requested changes Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 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 "components/cronet/stale_host_resolver.h"
6
7 #include "base/callback_helpers.h"
8 #include "base/metrics/histogram_macros.h"
9 #include "base/timer/timer.h"
10 #include "base/values.h"
11 #include "net/base/net_errors.h"
12 #include "net/dns/dns_util.h"
13 #include "net/dns/host_resolver_impl.h"
14
15 #define STALE_HISTOGRAM_ENUM(name, value, max) \
xunjieli 2016/06/13 19:25:04 I am not sure if we want these defines. Can we inl
Julia Tuttle 2016/06/14 23:11:32 Done.
16 UMA_HISTOGRAM_ENUMERATION("DNS.StaleHostResolver." name, value, max)
17
18 #define STALE_HISTOGRAM_TIME(name, time) \
19 UMA_HISTOGRAM_MEDIUM_TIMES("DNS.StaleHostResolver." name, time)
20
21 namespace cronet {
22
23 namespace {
24
25 // Used in histograms; do not modify existing values.
26 enum RequestOutcome {
27 // Served from (valid) cache, hosts file, IP literal, etc.
28 SYNCHRONOUS = 0,
29
30 // Network responded; there was no usable stale data.
31 NETWORK_WITHOUT_STALE = 1,
32
33 // Network responded before stale delay; there was usable stale data.
34 NETWORK_WITH_STALE = 2,
35
36 // Stale data returned; network didn't respond before the stale delay.
37 STALE_BEFORE_NETWORK = 3,
38
39 // Request canceled; there was no usable stale data.
40 CANCELED_WITHOUT_STALE = 4,
41
42 // Request canceled; there was usable stale data.
43 CANCELED_WITH_STALE = 5,
44
45 MAX_REQUEST_OUTCOME
46 };
47
48 void RecordRequestOutcome(RequestOutcome outcome) {
49 STALE_HISTOGRAM_ENUM("RequestOutcome", outcome, MAX_REQUEST_OUTCOME);
50 }
51
52 void RecordAddressListDelta(net::AddressListDeltaType delta) {
53 STALE_HISTOGRAM_ENUM("StaleAddressListDelta", delta, net::MAX_DELTA_TYPE);
54 }
55
56 void RecordTimeDelta(base::TimeTicks network_time, base::TimeTicks stale_time) {
57 if (network_time < stale_time)
58 STALE_HISTOGRAM_TIME("NetworkEarly", stale_time - network_time);
59 else
60 STALE_HISTOGRAM_TIME("NetworkLate", network_time - stale_time);
61 }
62
63 } // namespace
64
65 // A request made by the StaleHostResolver. May return fresh cached data,
66 // network data, or stale cached data.
67 class StaleHostResolver::Request {
68 public:
69 Request();
70
71 // A callback for the caller to decide whether a stale entry is usable or not.
72 typedef base::Callback<bool(const net::HostCache::EntryStaleness&)>
73 StaleEntryUsableCallback;
74
75 // Starts the request. May call |usable_callback| inline if |resolver| returns
76 // stale data to let the caller decide whether the data is usable.
77 //
78 // Returns the result if the request finishes synchronously. Returns
79 // ERR_IO_PENDING and calls |callback| with the result if it finishes
80 // asynchronously, unless the requets is canceled (via |Cancel()|).
xunjieli 2016/06/13 19:25:04 typo in "request"
Julia Tuttle 2016/06/14 23:11:32 Done.
81 //
82 // |addresses| must remain valid until the Request completes (synchronously or
83 // via |callback|) or is canceled (via |Cancel()|).
84 int Start(net::HostResolverImpl* resolver,
85 const RequestInfo& info,
86 net::RequestPriority priority,
87 net::AddressList* addresses,
88 const net::CompletionCallback& callback,
89 const net::BoundNetLog& net_log,
90 const StaleEntryUsableCallback& usable_callback,
91 base::TimeDelta stale_delay);
92
93 // Cancels the request. |resolver| must be the same resolver passed to
94 // |Start()|. Does not call the callback passed to |Start()|.
95 //
96 // |Start()| must have been called and returned ERR_IO_PENDING; the request
97 // must not have been canceled already.
98 void Cancel(net::HostResolverImpl* resolver);
99
100 bool has_network_request() const { return network_handle_ != nullptr; }
101 bool returned_stale_result() const { return callback_.is_null(); }
102
103 private:
104 ~Request();
105
106 static int ResolveStale(net::HostResolverImpl* resolver,
xunjieli 2016/06/13 19:25:04 Need documentation, specifically, what the return
Julia Tuttle 2016/06/14 23:11:32 Done.
107 const net::HostResolver::RequestInfo& info,
108 net::RequestPriority priority,
109 net::AddressList* addresses,
110 const net::CompletionCallback& callback,
111 net::HostResolver::RequestHandle* out_req,
112 int* stale_error,
113 net::AddressList* stale_addresses,
114 net::HostCache::EntryStaleness* stale_info,
115 const net::BoundNetLog& net_log);
116
117 // Callback for the timer to return stale data.
118 void OnStaleDelayElapsed();
119
120 // Callback for the underlying network request.
121 void OnNetworkRequestComplete(int error);
122
123 // Populates |*addresses_| from |addresses| if and only if rv is OK, clears
124 // |addresses_| to make sure the request doesn't return twice, and then
125 // returns |rv|.
126 int HandleResult(int rv, const net::AddressList& addresses);
127
128 void RecordSynchronousRequest();
129 void RecordNetworkRequest(int error);
130 void RecordCanceledRequest();
131
132 net::AddressList* addresses_;
133 net::CompletionCallback callback_;
134
135 int stale_error_;
136 net::AddressList stale_addresses_;
137 base::OneShotTimer stale_timer_;
138
139 net::AddressList network_addresses_;
140 net::HostResolver::RequestHandle network_handle_;
xunjieli 2016/06/13 19:25:03 need documentation on these member variables.
Julia Tuttle 2016/06/14 23:11:32 Done.
141 };
142
143 StaleHostResolver::Request::Request()
144 : addresses_(nullptr), network_handle_(nullptr) {}
145
146 int StaleHostResolver::Request::Start(
147 net::HostResolverImpl* resolver,
148 const RequestInfo& info,
149 net::RequestPriority priority,
150 net::AddressList* addresses,
151 const net::CompletionCallback& callback,
152 const net::BoundNetLog& net_log,
153 const StaleEntryUsableCallback& usable_callback,
154 base::TimeDelta stale_delay) {
155 DCHECK(!callback.is_null());
156 DCHECK(!usable_callback.is_null());
157
158 callback_ = callback;
159 addresses_ = addresses;
160
161 // Request never deletes itself until the network request returns (if one is
162 // started), so it's safe to use Unretained here.
163 net::CompletionCallback network_callback =
164 base::Bind(&StaleHostResolver::Request::OnNetworkRequestComplete,
165 base::Unretained(this));
166 net::HostCache::EntryStaleness stale_info;
167 int rv = ResolveStale(resolver, info, priority, &network_addresses_,
168 network_callback, &network_handle_, &stale_error_,
169 &stale_addresses_, &stale_info, net_log);
170 if (rv != net::ERR_IO_PENDING) {
171 DCHECK(!network_handle_);
172 rv = HandleResult(rv, network_addresses_);
173 RecordSynchronousRequest();
174 delete this;
175 return rv;
176 }
177
178 if (stale_error_ != net::ERR_DNS_CACHE_MISS &&
179 usable_callback.Run(stale_info)) {
180 // |stale_timer_| is deleted when the Request is deleted, so it's safe to
181 // use Unretained here.
182 base::Callback<void()> stale_callback =
183 base::Bind(&StaleHostResolver::Request::OnStaleDelayElapsed,
184 base::Unretained(this));
185 stale_timer_.Start(FROM_HERE, stale_delay, stale_callback);
186 } else {
187 stale_addresses_.clear();
188 }
189
190 return net::ERR_IO_PENDING;
191 }
192
193 void StaleHostResolver::Request::Cancel(net::HostResolverImpl* resolver) {
194 DCHECK(has_network_request());
195 DCHECK(!returned_stale_result());
196
197 resolver->CancelRequest(network_handle_);
198 network_handle_ = nullptr;
199
200 RecordCanceledRequest();
201 delete this;
202 }
203
204 StaleHostResolver::Request::~Request() {
205 DCHECK(!has_network_request());
206 }
207
208 // static
209 int StaleHostResolver::Request::ResolveStale(
210 net::HostResolverImpl* resolver,
211 const net::HostResolver::RequestInfo& info,
212 net::RequestPriority priority,
213 net::AddressList* addresses,
214 const net::CompletionCallback& callback,
215 net::HostResolver::RequestHandle* out_req,
216 int* stale_error,
217 net::AddressList* stale_addresses,
218 net::HostCache::EntryStaleness* stale_info,
219 const net::BoundNetLog& net_log) {
220 net::AddressList cache_addresses;
221 int cache_rv = resolver->ResolveStaleFromCache(info, &cache_addresses,
222 stale_info, net_log);
223 // If it's a fresh cache hit (or literal), return it synchronously.
224 if (cache_rv != net::ERR_DNS_CACHE_MISS && !stale_info->is_stale()) {
225 *stale_error = net::ERR_UNEXPECTED;
226 *addresses = cache_addresses;
227 return cache_rv;
228 }
229
230 // If it's a stale cache hit, fill in |*stale_addresses| but run the network
231 // request anyway.
232 if (cache_rv != net::ERR_DNS_CACHE_MISS)
233 *stale_addresses = cache_addresses;
234
235 *stale_error = cache_rv;
236
237 // Don't check the cache again.
238 net::HostResolver::RequestInfo no_cache_info(info);
239 no_cache_info.set_allow_cached_response(false);
240 return resolver->Resolve(no_cache_info, priority, addresses, callback,
241 out_req, net_log);
242 }
243
244 void StaleHostResolver::Request::OnStaleDelayElapsed() {
245 DCHECK(has_network_request());
246 DCHECK(!returned_stale_result());
247
248 base::ResetAndReturn(&callback_)
249 .Run(HandleResult(stale_error_, stale_addresses_));
250
251 // Don't delete |this|; let the underlying request continue to backfill the
252 // host cache.
253 }
254
255 void StaleHostResolver::Request::OnNetworkRequestComplete(int error) {
256 DCHECK(has_network_request());
257
258 network_handle_ = nullptr;
259
260 if (!returned_stale_result())
261 callback_.Run(HandleResult(error, network_addresses_));
262
263 RecordNetworkRequest(error);
264 delete this;
265 }
266
267 int StaleHostResolver::Request::HandleResult(
268 int rv,
269 const net::AddressList& addresses) {
270 DCHECK(addresses_);
271
272 if (rv == net::OK)
273 *addresses_ = addresses;
274 addresses_ = nullptr;
275 return rv;
276 }
277
278 void StaleHostResolver::Request::RecordSynchronousRequest() {
279 RecordRequestOutcome(SYNCHRONOUS);
280 }
281
282 void StaleHostResolver::Request::RecordNetworkRequest(int error) {
283 if (stale_timer_.IsRunning() || returned_stale_result())
284 RecordTimeDelta(base::TimeTicks::Now(), stale_timer_.desired_run_time());
285
286 if (returned_stale_result() && stale_error_ == net::OK && error == net::OK) {
287 RecordAddressListDelta(
288 FindAddressListDeltaType(stale_addresses_, network_addresses_));
289 }
290
291 if (returned_stale_result())
292 RecordRequestOutcome(STALE_BEFORE_NETWORK);
293 else if (stale_timer_.IsRunning())
294 RecordRequestOutcome(NETWORK_WITH_STALE);
295 else
296 RecordRequestOutcome(NETWORK_WITHOUT_STALE);
297 }
298
299 void StaleHostResolver::Request::RecordCanceledRequest() {
300 if (stale_timer_.IsRunning())
301 RecordRequestOutcome(CANCELED_WITH_STALE);
302 else
303 RecordRequestOutcome(CANCELED_WITHOUT_STALE);
304 }
305
306 namespace {
307
308 bool StaleEntryIsUsable(const StaleHostResolver::StaleOptions& options,
309 const net::HostCache::EntryStaleness& entry) {
310 if (options.max_expired_time != base::TimeDelta() &&
311 entry.expired_by > options.max_expired_time) {
312 return false;
313 }
314 if (options.max_stale_uses > 0 && entry.stale_hits > options.max_stale_uses)
315 return false;
316 if (!options.allow_other_network && entry.network_changes > 0)
317 return false;
318 return true;
319 }
320
321 } // namespace
xunjieli 2016/06/13 19:25:03 Can we have the anonymous namespaces at the top of
Julia Tuttle 2016/06/14 23:11:32 Done.
322
323 StaleHostResolver::StaleHostResolver(
324 std::unique_ptr<net::HostResolverImpl> inner_resolver,
325 const StaleOptions& stale_options)
326 : resolver_(std::move(inner_resolver)), options_(stale_options) {
xunjieli 2016/06/13 19:25:04 nit: Can we rename |resolver_| to |inner_resolver_
Julia Tuttle 2016/06/14 23:11:32 Done.
327 DCHECK_GE(0, stale_options.max_expired_time.InMicroseconds());
328 DCHECK_GE(0, stale_options.max_stale_uses);
329 }
330
331 StaleHostResolver::~StaleHostResolver() {
332 for (auto* request : pending_requests_)
333 request->Cancel(resolver_.get());
334 pending_requests_.clear();
335 }
336
337 int StaleHostResolver::Resolve(const RequestInfo& info,
338 net::RequestPriority priority,
339 net::AddressList* addresses,
340 const net::CompletionCallback& callback,
341 RequestHandle* out_req,
342 const net::BoundNetLog& net_log) {
343 StaleHostResolver::Request::StaleEntryUsableCallback usable_callback =
344 base::Bind(&StaleEntryIsUsable, options_);
345
346 Request* request = new Request();
347 pending_requests_.insert(request);
xunjieli 2016/06/13 19:25:04 If request->Start() finishes synchronously, can we
Julia Tuttle 2016/06/14 23:11:32 Done.
348 // If Request is not complete when the StaleHostResolver is being destroyed,
349 // the destructor will Cancel it, preventing this callback from being called,
350 // so it's safe to use Unretained here.
351 net::CompletionCallback wrapped_callback =
352 base::Bind(&StaleHostResolver::OnRequestComplete, base::Unretained(this),
353 request, callback);
354
355 int rv = request->Start(resolver_.get(), info, priority, addresses,
356 wrapped_callback, net_log, usable_callback,
357 options_.delay);
358 if (rv == net::ERR_IO_PENDING && out_req)
359 *out_req = reinterpret_cast<RequestHandle>(request);
360 return rv;
361 }
362
363 void StaleHostResolver::CancelRequest(RequestHandle req_handle) {
364 Request* request = reinterpret_cast<Request*>(req_handle);
365 DCHECK(request);
366 DCHECK_EQ(1u, pending_requests_.count(request));
367
368 request->Cancel(resolver_.get());
369 pending_requests_.erase(request);
370 }
371
372 int StaleHostResolver::ResolveFromCache(const RequestInfo& info,
373 net::AddressList* addresses,
374 const net::BoundNetLog& net_log) {
375 return resolver_->ResolveFromCache(info, addresses, net_log);
376 }
377
378 void StaleHostResolver::SetDnsClientEnabled(bool enabled) {
379 resolver_->SetDnsClientEnabled(enabled);
380 }
381
382 net::HostCache* StaleHostResolver::GetHostCache() {
383 return resolver_->GetHostCache();
384 }
385
386 std::unique_ptr<base::Value> StaleHostResolver::GetDnsConfigAsValue() const {
387 return resolver_->GetDnsConfigAsValue();
388 }
389
390 void StaleHostResolver::OnRequestComplete(
391 StaleHostResolver::Request* request,
392 const net::CompletionCallback& outer_callback,
393 int error) {
394 DCHECK(request);
395 DCHECK_NE(net::ERR_IO_PENDING, error);
396
397 DCHECK_EQ(1u, pending_requests_.count(request));
398 pending_requests_.erase(request);
xunjieli 2016/06/13 19:25:04 Instead of deleting |request| in OnNetworkRequestC
Julia Tuttle 2016/06/14 23:11:32 Done.
399
400 outer_callback.Run(error);
401 }
402
403 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698