OLD | NEW |
| (Empty) |
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 "net/http/http_cache_transaction.h" | |
6 | |
7 #include "build/build_config.h" | |
8 | |
9 #if defined(OS_POSIX) | |
10 #include <unistd.h> | |
11 #endif | |
12 | |
13 #include <algorithm> | |
14 #include <string> | |
15 | |
16 #include "base/bind.h" | |
17 #include "base/compiler_specific.h" | |
18 #include "base/format_macros.h" | |
19 #include "base/memory/ref_counted.h" | |
20 #include "base/memory/scoped_ptr.h" | |
21 #include "base/metrics/field_trial.h" | |
22 #include "base/metrics/histogram.h" | |
23 #include "base/metrics/sparse_histogram.h" | |
24 #include "base/profiler/scoped_tracker.h" | |
25 #include "base/rand_util.h" | |
26 #include "base/strings/string_number_conversions.h" | |
27 #include "base/strings/string_piece.h" | |
28 #include "base/strings/string_util.h" | |
29 #include "base/strings/stringprintf.h" | |
30 #include "base/time/clock.h" | |
31 #include "base/time/time.h" | |
32 #include "base/values.h" | |
33 #include "net/base/completion_callback.h" | |
34 #include "net/base/io_buffer.h" | |
35 #include "net/base/load_flags.h" | |
36 #include "net/base/load_timing_info.h" | |
37 #include "net/base/net_errors.h" | |
38 #include "net/base/net_log.h" | |
39 #include "net/base/upload_data_stream.h" | |
40 #include "net/cert/cert_status_flags.h" | |
41 #include "net/disk_cache/disk_cache.h" | |
42 #include "net/http/disk_based_cert_cache.h" | |
43 #include "net/http/http_network_session.h" | |
44 #include "net/http/http_request_info.h" | |
45 #include "net/http/http_response_headers.h" | |
46 #include "net/http/http_transaction.h" | |
47 #include "net/http/http_util.h" | |
48 #include "net/http/partial_data.h" | |
49 #include "net/ssl/ssl_cert_request_info.h" | |
50 #include "net/ssl/ssl_config_service.h" | |
51 | |
52 using base::Time; | |
53 using base::TimeDelta; | |
54 using base::TimeTicks; | |
55 | |
56 namespace { | |
57 | |
58 // TODO(ricea): Move this to HttpResponseHeaders once it is standardised. | |
59 static const char kFreshnessHeader[] = "Resource-Freshness"; | |
60 | |
61 // Stores data relevant to the statistics of writing and reading entire | |
62 // certificate chains using DiskBasedCertCache. |num_pending_ops| is the number | |
63 // of certificates in the chain that have pending operations in the | |
64 // DiskBasedCertCache. |start_time| is the time that the read and write | |
65 // commands began being issued to the DiskBasedCertCache. | |
66 // TODO(brandonsalmon): Remove this when it is no longer necessary to | |
67 // collect data. | |
68 class SharedChainData : public base::RefCounted<SharedChainData> { | |
69 public: | |
70 SharedChainData(int num_ops, TimeTicks start) | |
71 : num_pending_ops(num_ops), start_time(start) {} | |
72 | |
73 int num_pending_ops; | |
74 TimeTicks start_time; | |
75 | |
76 private: | |
77 friend class base::RefCounted<SharedChainData>; | |
78 ~SharedChainData() {} | |
79 DISALLOW_COPY_AND_ASSIGN(SharedChainData); | |
80 }; | |
81 | |
82 // Used to obtain a cache entry key for an OSCertHandle. | |
83 // TODO(brandonsalmon): Remove this when cache keys are stored | |
84 // and no longer have to be recomputed to retrieve the OSCertHandle | |
85 // from the disk. | |
86 std::string GetCacheKeyForCert(net::X509Certificate::OSCertHandle cert_handle) { | |
87 net::SHA1HashValue fingerprint = | |
88 net::X509Certificate::CalculateFingerprint(cert_handle); | |
89 | |
90 return "cert:" + | |
91 base::HexEncode(fingerprint.data, arraysize(fingerprint.data)); | |
92 } | |
93 | |
94 // |dist_from_root| indicates the position of the read certificate in the | |
95 // certificate chain, 0 indicating it is the root. |is_leaf| indicates | |
96 // whether or not the read certificate was the leaf of the chain. | |
97 // |shared_chain_data| contains data shared by each certificate in | |
98 // the chain. | |
99 void OnCertReadIOComplete( | |
100 int dist_from_root, | |
101 bool is_leaf, | |
102 const scoped_refptr<SharedChainData>& shared_chain_data, | |
103 net::X509Certificate::OSCertHandle cert_handle) { | |
104 // If |num_pending_ops| is one, this was the last pending read operation | |
105 // for this chain of certificates. The total time used to read the chain | |
106 // can be calculated by subtracting the starting time from Now(). | |
107 shared_chain_data->num_pending_ops--; | |
108 if (!shared_chain_data->num_pending_ops) { | |
109 const TimeDelta read_chain_wait = | |
110 TimeTicks::Now() - shared_chain_data->start_time; | |
111 UMA_HISTOGRAM_CUSTOM_TIMES("DiskBasedCertCache.ChainReadTime", | |
112 read_chain_wait, | |
113 base::TimeDelta::FromMilliseconds(1), | |
114 base::TimeDelta::FromMinutes(10), | |
115 50); | |
116 } | |
117 | |
118 bool success = (cert_handle != NULL); | |
119 if (is_leaf) | |
120 UMA_HISTOGRAM_BOOLEAN("DiskBasedCertCache.CertIoReadSuccessLeaf", success); | |
121 | |
122 if (success) | |
123 UMA_HISTOGRAM_CUSTOM_COUNTS( | |
124 "DiskBasedCertCache.CertIoReadSuccess", dist_from_root, 0, 10, 7); | |
125 else | |
126 UMA_HISTOGRAM_CUSTOM_COUNTS( | |
127 "DiskBasedCertCache.CertIoReadFailure", dist_from_root, 0, 10, 7); | |
128 } | |
129 | |
130 // |dist_from_root| indicates the position of the written certificate in the | |
131 // certificate chain, 0 indicating it is the root. |is_leaf| indicates | |
132 // whether or not the written certificate was the leaf of the chain. | |
133 // |shared_chain_data| contains data shared by each certificate in | |
134 // the chain. | |
135 void OnCertWriteIOComplete( | |
136 int dist_from_root, | |
137 bool is_leaf, | |
138 const scoped_refptr<SharedChainData>& shared_chain_data, | |
139 const std::string& key) { | |
140 // If |num_pending_ops| is one, this was the last pending write operation | |
141 // for this chain of certificates. The total time used to write the chain | |
142 // can be calculated by subtracting the starting time from Now(). | |
143 shared_chain_data->num_pending_ops--; | |
144 if (!shared_chain_data->num_pending_ops) { | |
145 const TimeDelta write_chain_wait = | |
146 TimeTicks::Now() - shared_chain_data->start_time; | |
147 UMA_HISTOGRAM_CUSTOM_TIMES("DiskBasedCertCache.ChainWriteTime", | |
148 write_chain_wait, | |
149 base::TimeDelta::FromMilliseconds(1), | |
150 base::TimeDelta::FromMinutes(10), | |
151 50); | |
152 } | |
153 | |
154 bool success = !key.empty(); | |
155 if (is_leaf) | |
156 UMA_HISTOGRAM_BOOLEAN("DiskBasedCertCache.CertIoWriteSuccessLeaf", success); | |
157 | |
158 if (success) | |
159 UMA_HISTOGRAM_CUSTOM_COUNTS( | |
160 "DiskBasedCertCache.CertIoWriteSuccess", dist_from_root, 0, 10, 7); | |
161 else | |
162 UMA_HISTOGRAM_CUSTOM_COUNTS( | |
163 "DiskBasedCertCache.CertIoWriteFailure", dist_from_root, 0, 10, 7); | |
164 } | |
165 | |
166 // From http://tools.ietf.org/html/draft-ietf-httpbis-p6-cache-21#section-6 | |
167 // a "non-error response" is one with a 2xx (Successful) or 3xx | |
168 // (Redirection) status code. | |
169 bool NonErrorResponse(int status_code) { | |
170 int status_code_range = status_code / 100; | |
171 return status_code_range == 2 || status_code_range == 3; | |
172 } | |
173 | |
174 // Error codes that will be considered indicative of a page being offline/ | |
175 // unreachable for LOAD_FROM_CACHE_IF_OFFLINE. | |
176 bool IsOfflineError(int error) { | |
177 return (error == net::ERR_NAME_NOT_RESOLVED || | |
178 error == net::ERR_INTERNET_DISCONNECTED || | |
179 error == net::ERR_ADDRESS_UNREACHABLE || | |
180 error == net::ERR_CONNECTION_TIMED_OUT); | |
181 } | |
182 | |
183 // Enum for UMA, indicating the status (with regard to offline mode) of | |
184 // a particular request. | |
185 enum RequestOfflineStatus { | |
186 // A cache transaction hit in cache (data was present and not stale) | |
187 // and returned it. | |
188 OFFLINE_STATUS_FRESH_CACHE, | |
189 | |
190 // A network request was required for a cache entry, and it succeeded. | |
191 OFFLINE_STATUS_NETWORK_SUCCEEDED, | |
192 | |
193 // A network request was required for a cache entry, and it failed with | |
194 // a non-offline error. | |
195 OFFLINE_STATUS_NETWORK_FAILED, | |
196 | |
197 // A network request was required for a cache entry, it failed with an | |
198 // offline error, and we could serve stale data if | |
199 // LOAD_FROM_CACHE_IF_OFFLINE was set. | |
200 OFFLINE_STATUS_DATA_AVAILABLE_OFFLINE, | |
201 | |
202 // A network request was required for a cache entry, it failed with | |
203 // an offline error, and there was no servable data in cache (even | |
204 // stale data). | |
205 OFFLINE_STATUS_DATA_UNAVAILABLE_OFFLINE, | |
206 | |
207 OFFLINE_STATUS_MAX_ENTRIES | |
208 }; | |
209 | |
210 void RecordOfflineStatus(int load_flags, RequestOfflineStatus status) { | |
211 // Restrict to main frame to keep statistics close to | |
212 // "would have shown them something useful if offline mode was enabled". | |
213 if (load_flags & net::LOAD_MAIN_FRAME) { | |
214 UMA_HISTOGRAM_ENUMERATION("HttpCache.OfflineStatus", status, | |
215 OFFLINE_STATUS_MAX_ENTRIES); | |
216 } | |
217 } | |
218 | |
219 void RecordNoStoreHeaderHistogram(int load_flags, | |
220 const net::HttpResponseInfo* response) { | |
221 if (load_flags & net::LOAD_MAIN_FRAME) { | |
222 UMA_HISTOGRAM_BOOLEAN( | |
223 "Net.MainFrameNoStore", | |
224 response->headers->HasHeaderValue("cache-control", "no-store")); | |
225 } | |
226 } | |
227 | |
228 base::Value* NetLogAsyncRevalidationInfoCallback( | |
229 const net::NetLog::Source& source, | |
230 const net::HttpRequestInfo* request, | |
231 net::NetLog::LogLevel log_level) { | |
232 base::DictionaryValue* dict = new base::DictionaryValue(); | |
233 source.AddToEventParameters(dict); | |
234 | |
235 dict->SetString("url", request->url.possibly_invalid_spec()); | |
236 dict->SetString("method", request->method); | |
237 return dict; | |
238 } | |
239 | |
240 enum ExternallyConditionalizedType { | |
241 EXTERNALLY_CONDITIONALIZED_CACHE_REQUIRES_VALIDATION, | |
242 EXTERNALLY_CONDITIONALIZED_CACHE_USABLE, | |
243 EXTERNALLY_CONDITIONALIZED_MISMATCHED_VALIDATORS, | |
244 EXTERNALLY_CONDITIONALIZED_MAX | |
245 }; | |
246 | |
247 } // namespace | |
248 | |
249 namespace net { | |
250 | |
251 struct HeaderNameAndValue { | |
252 const char* name; | |
253 const char* value; | |
254 }; | |
255 | |
256 // If the request includes one of these request headers, then avoid caching | |
257 // to avoid getting confused. | |
258 static const HeaderNameAndValue kPassThroughHeaders[] = { | |
259 { "if-unmodified-since", NULL }, // causes unexpected 412s | |
260 { "if-match", NULL }, // causes unexpected 412s | |
261 { "if-range", NULL }, | |
262 { NULL, NULL } | |
263 }; | |
264 | |
265 struct ValidationHeaderInfo { | |
266 const char* request_header_name; | |
267 const char* related_response_header_name; | |
268 }; | |
269 | |
270 static const ValidationHeaderInfo kValidationHeaders[] = { | |
271 { "if-modified-since", "last-modified" }, | |
272 { "if-none-match", "etag" }, | |
273 }; | |
274 | |
275 // If the request includes one of these request headers, then avoid reusing | |
276 // our cached copy if any. | |
277 static const HeaderNameAndValue kForceFetchHeaders[] = { | |
278 { "cache-control", "no-cache" }, | |
279 { "pragma", "no-cache" }, | |
280 { NULL, NULL } | |
281 }; | |
282 | |
283 // If the request includes one of these request headers, then force our | |
284 // cached copy (if any) to be revalidated before reusing it. | |
285 static const HeaderNameAndValue kForceValidateHeaders[] = { | |
286 { "cache-control", "max-age=0" }, | |
287 { NULL, NULL } | |
288 }; | |
289 | |
290 static bool HeaderMatches(const HttpRequestHeaders& headers, | |
291 const HeaderNameAndValue* search) { | |
292 for (; search->name; ++search) { | |
293 std::string header_value; | |
294 if (!headers.GetHeader(search->name, &header_value)) | |
295 continue; | |
296 | |
297 if (!search->value) | |
298 return true; | |
299 | |
300 HttpUtil::ValuesIterator v(header_value.begin(), header_value.end(), ','); | |
301 while (v.GetNext()) { | |
302 if (LowerCaseEqualsASCII(v.value_begin(), v.value_end(), search->value)) | |
303 return true; | |
304 } | |
305 } | |
306 return false; | |
307 } | |
308 | |
309 //----------------------------------------------------------------------------- | |
310 | |
311 HttpCache::Transaction::Transaction(RequestPriority priority, HttpCache* cache) | |
312 : next_state_(STATE_NONE), | |
313 request_(NULL), | |
314 priority_(priority), | |
315 cache_(cache->GetWeakPtr()), | |
316 entry_(NULL), | |
317 new_entry_(NULL), | |
318 new_response_(NULL), | |
319 mode_(NONE), | |
320 target_state_(STATE_NONE), | |
321 reading_(false), | |
322 invalid_range_(false), | |
323 truncated_(false), | |
324 is_sparse_(false), | |
325 range_requested_(false), | |
326 handling_206_(false), | |
327 cache_pending_(false), | |
328 done_reading_(false), | |
329 vary_mismatch_(false), | |
330 couldnt_conditionalize_request_(false), | |
331 bypass_lock_for_test_(false), | |
332 fail_conditionalization_for_test_(false), | |
333 io_buf_len_(0), | |
334 read_offset_(0), | |
335 effective_load_flags_(0), | |
336 write_len_(0), | |
337 transaction_pattern_(PATTERN_UNDEFINED), | |
338 total_received_bytes_(0), | |
339 websocket_handshake_stream_base_create_helper_(NULL), | |
340 weak_factory_(this) { | |
341 static_assert(HttpCache::Transaction::kNumValidationHeaders == | |
342 arraysize(kValidationHeaders), | |
343 "invalid number of validation headers"); | |
344 | |
345 io_callback_ = base::Bind(&Transaction::OnIOComplete, | |
346 weak_factory_.GetWeakPtr()); | |
347 } | |
348 | |
349 HttpCache::Transaction::~Transaction() { | |
350 // We may have to issue another IO, but we should never invoke the callback_ | |
351 // after this point. | |
352 callback_.Reset(); | |
353 | |
354 if (cache_) { | |
355 if (entry_) { | |
356 bool cancel_request = reading_ && response_.headers.get(); | |
357 if (cancel_request) { | |
358 if (partial_) { | |
359 entry_->disk_entry->CancelSparseIO(); | |
360 } else { | |
361 cancel_request &= (response_.headers->response_code() == 200); | |
362 } | |
363 } | |
364 | |
365 cache_->DoneWithEntry(entry_, this, cancel_request); | |
366 } else if (cache_pending_) { | |
367 cache_->RemovePendingTransaction(this); | |
368 } | |
369 } | |
370 } | |
371 | |
372 int HttpCache::Transaction::WriteMetadata(IOBuffer* buf, int buf_len, | |
373 const CompletionCallback& callback) { | |
374 DCHECK(buf); | |
375 DCHECK_GT(buf_len, 0); | |
376 DCHECK(!callback.is_null()); | |
377 if (!cache_.get() || !entry_) | |
378 return ERR_UNEXPECTED; | |
379 | |
380 // We don't need to track this operation for anything. | |
381 // It could be possible to check if there is something already written and | |
382 // avoid writing again (it should be the same, right?), but let's allow the | |
383 // caller to "update" the contents with something new. | |
384 return entry_->disk_entry->WriteData(kMetadataIndex, 0, buf, buf_len, | |
385 callback, true); | |
386 } | |
387 | |
388 bool HttpCache::Transaction::AddTruncatedFlag() { | |
389 DCHECK(mode_ & WRITE || mode_ == NONE); | |
390 | |
391 // Don't set the flag for sparse entries. | |
392 if (partial_.get() && !truncated_) | |
393 return true; | |
394 | |
395 if (!CanResume(true)) | |
396 return false; | |
397 | |
398 // We may have received the whole resource already. | |
399 if (done_reading_) | |
400 return true; | |
401 | |
402 truncated_ = true; | |
403 target_state_ = STATE_NONE; | |
404 next_state_ = STATE_CACHE_WRITE_TRUNCATED_RESPONSE; | |
405 DoLoop(OK); | |
406 return true; | |
407 } | |
408 | |
409 LoadState HttpCache::Transaction::GetWriterLoadState() const { | |
410 if (network_trans_.get()) | |
411 return network_trans_->GetLoadState(); | |
412 if (entry_ || !request_) | |
413 return LOAD_STATE_IDLE; | |
414 return LOAD_STATE_WAITING_FOR_CACHE; | |
415 } | |
416 | |
417 const BoundNetLog& HttpCache::Transaction::net_log() const { | |
418 return net_log_; | |
419 } | |
420 | |
421 int HttpCache::Transaction::Start(const HttpRequestInfo* request, | |
422 const CompletionCallback& callback, | |
423 const BoundNetLog& net_log) { | |
424 DCHECK(request); | |
425 DCHECK(!callback.is_null()); | |
426 | |
427 // Ensure that we only have one asynchronous call at a time. | |
428 DCHECK(callback_.is_null()); | |
429 DCHECK(!reading_); | |
430 DCHECK(!network_trans_.get()); | |
431 DCHECK(!entry_); | |
432 | |
433 if (!cache_.get()) | |
434 return ERR_UNEXPECTED; | |
435 | |
436 SetRequest(net_log, request); | |
437 | |
438 // We have to wait until the backend is initialized so we start the SM. | |
439 next_state_ = STATE_GET_BACKEND; | |
440 int rv = DoLoop(OK); | |
441 | |
442 // Setting this here allows us to check for the existence of a callback_ to | |
443 // determine if we are still inside Start. | |
444 if (rv == ERR_IO_PENDING) { | |
445 callback_ = tracked_objects::ScopedTracker::TrackCallback( | |
446 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
447 "422516 HttpCache::Transaction::Start"), | |
448 callback); | |
449 } | |
450 | |
451 return rv; | |
452 } | |
453 | |
454 int HttpCache::Transaction::RestartIgnoringLastError( | |
455 const CompletionCallback& callback) { | |
456 DCHECK(!callback.is_null()); | |
457 | |
458 // Ensure that we only have one asynchronous call at a time. | |
459 DCHECK(callback_.is_null()); | |
460 | |
461 if (!cache_.get()) | |
462 return ERR_UNEXPECTED; | |
463 | |
464 int rv = RestartNetworkRequest(); | |
465 | |
466 if (rv == ERR_IO_PENDING) { | |
467 callback_ = tracked_objects::ScopedTracker::TrackCallback( | |
468 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
469 "422516 HttpCache::Transaction::RestartIgnoringLastError"), | |
470 callback); | |
471 } | |
472 | |
473 return rv; | |
474 } | |
475 | |
476 int HttpCache::Transaction::RestartWithCertificate( | |
477 X509Certificate* client_cert, | |
478 const CompletionCallback& callback) { | |
479 DCHECK(!callback.is_null()); | |
480 | |
481 // Ensure that we only have one asynchronous call at a time. | |
482 DCHECK(callback_.is_null()); | |
483 | |
484 if (!cache_.get()) | |
485 return ERR_UNEXPECTED; | |
486 | |
487 int rv = RestartNetworkRequestWithCertificate(client_cert); | |
488 | |
489 if (rv == ERR_IO_PENDING) { | |
490 callback_ = tracked_objects::ScopedTracker::TrackCallback( | |
491 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
492 "422516 HttpCache::Transaction::RestartWithCertificate"), | |
493 callback); | |
494 } | |
495 | |
496 return rv; | |
497 } | |
498 | |
499 int HttpCache::Transaction::RestartWithAuth( | |
500 const AuthCredentials& credentials, | |
501 const CompletionCallback& callback) { | |
502 DCHECK(auth_response_.headers.get()); | |
503 DCHECK(!callback.is_null()); | |
504 | |
505 // Ensure that we only have one asynchronous call at a time. | |
506 DCHECK(callback_.is_null()); | |
507 | |
508 if (!cache_.get()) | |
509 return ERR_UNEXPECTED; | |
510 | |
511 // Clear the intermediate response since we are going to start over. | |
512 auth_response_ = HttpResponseInfo(); | |
513 | |
514 int rv = RestartNetworkRequestWithAuth(credentials); | |
515 | |
516 if (rv == ERR_IO_PENDING) { | |
517 callback_ = tracked_objects::ScopedTracker::TrackCallback( | |
518 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
519 "422516 HttpCache::Transaction::RestartWithAuth"), | |
520 callback); | |
521 } | |
522 | |
523 return rv; | |
524 } | |
525 | |
526 bool HttpCache::Transaction::IsReadyToRestartForAuth() { | |
527 if (!network_trans_.get()) | |
528 return false; | |
529 return network_trans_->IsReadyToRestartForAuth(); | |
530 } | |
531 | |
532 int HttpCache::Transaction::Read(IOBuffer* buf, int buf_len, | |
533 const CompletionCallback& callback) { | |
534 DCHECK(buf); | |
535 DCHECK_GT(buf_len, 0); | |
536 DCHECK(!callback.is_null()); | |
537 | |
538 DCHECK(callback_.is_null()); | |
539 | |
540 if (!cache_.get()) | |
541 return ERR_UNEXPECTED; | |
542 | |
543 // If we have an intermediate auth response at this point, then it means the | |
544 // user wishes to read the network response (the error page). If there is a | |
545 // previous response in the cache then we should leave it intact. | |
546 if (auth_response_.headers.get() && mode_ != NONE) { | |
547 UpdateTransactionPattern(PATTERN_NOT_COVERED); | |
548 DCHECK(mode_ & WRITE); | |
549 DoneWritingToEntry(mode_ == READ_WRITE); | |
550 mode_ = NONE; | |
551 } | |
552 | |
553 reading_ = true; | |
554 int rv; | |
555 | |
556 switch (mode_) { | |
557 case READ_WRITE: | |
558 DCHECK(partial_.get()); | |
559 if (!network_trans_.get()) { | |
560 // We are just reading from the cache, but we may be writing later. | |
561 rv = ReadFromEntry(buf, buf_len); | |
562 break; | |
563 } | |
564 case NONE: | |
565 case WRITE: | |
566 DCHECK(network_trans_.get()); | |
567 rv = ReadFromNetwork(buf, buf_len); | |
568 break; | |
569 case READ: | |
570 rv = ReadFromEntry(buf, buf_len); | |
571 break; | |
572 default: | |
573 NOTREACHED(); | |
574 rv = ERR_FAILED; | |
575 } | |
576 | |
577 if (rv == ERR_IO_PENDING) { | |
578 DCHECK(callback_.is_null()); | |
579 callback_ = tracked_objects::ScopedTracker::TrackCallback( | |
580 FROM_HERE_WITH_EXPLICIT_FUNCTION("422516 HttpCache::Transaction::Read"), | |
581 callback); | |
582 } | |
583 return rv; | |
584 } | |
585 | |
586 void HttpCache::Transaction::StopCaching() { | |
587 // We really don't know where we are now. Hopefully there is no operation in | |
588 // progress, but nothing really prevents this method to be called after we | |
589 // returned ERR_IO_PENDING. We cannot attempt to truncate the entry at this | |
590 // point because we need the state machine for that (and even if we are really | |
591 // free, that would be an asynchronous operation). In other words, keep the | |
592 // entry how it is (it will be marked as truncated at destruction), and let | |
593 // the next piece of code that executes know that we are now reading directly | |
594 // from the net. | |
595 // TODO(mmenke): This doesn't release the lock on the cache entry, so a | |
596 // future request for the resource will be blocked on this one. | |
597 // Fix this. | |
598 if (cache_.get() && entry_ && (mode_ & WRITE) && network_trans_.get() && | |
599 !is_sparse_ && !range_requested_) { | |
600 mode_ = NONE; | |
601 } | |
602 } | |
603 | |
604 bool HttpCache::Transaction::GetFullRequestHeaders( | |
605 HttpRequestHeaders* headers) const { | |
606 if (network_trans_) | |
607 return network_trans_->GetFullRequestHeaders(headers); | |
608 | |
609 // TODO(ttuttle): Read headers from cache. | |
610 return false; | |
611 } | |
612 | |
613 int64 HttpCache::Transaction::GetTotalReceivedBytes() const { | |
614 int64 total_received_bytes = total_received_bytes_; | |
615 if (network_trans_) | |
616 total_received_bytes += network_trans_->GetTotalReceivedBytes(); | |
617 return total_received_bytes; | |
618 } | |
619 | |
620 void HttpCache::Transaction::DoneReading() { | |
621 if (cache_.get() && entry_) { | |
622 DCHECK_NE(mode_, UPDATE); | |
623 if (mode_ & WRITE) { | |
624 DoneWritingToEntry(true); | |
625 } else if (mode_ & READ) { | |
626 // It is necessary to check mode_ & READ because it is possible | |
627 // for mode_ to be NONE and entry_ non-NULL with a write entry | |
628 // if StopCaching was called. | |
629 cache_->DoneReadingFromEntry(entry_, this); | |
630 entry_ = NULL; | |
631 } | |
632 } | |
633 } | |
634 | |
635 const HttpResponseInfo* HttpCache::Transaction::GetResponseInfo() const { | |
636 // Null headers means we encountered an error or haven't a response yet | |
637 if (auth_response_.headers.get()) | |
638 return &auth_response_; | |
639 return (response_.headers.get() || response_.ssl_info.cert.get() || | |
640 response_.cert_request_info.get()) | |
641 ? &response_ | |
642 : NULL; | |
643 } | |
644 | |
645 LoadState HttpCache::Transaction::GetLoadState() const { | |
646 LoadState state = GetWriterLoadState(); | |
647 if (state != LOAD_STATE_WAITING_FOR_CACHE) | |
648 return state; | |
649 | |
650 if (cache_.get()) | |
651 return cache_->GetLoadStateForPendingTransaction(this); | |
652 | |
653 return LOAD_STATE_IDLE; | |
654 } | |
655 | |
656 UploadProgress HttpCache::Transaction::GetUploadProgress() const { | |
657 if (network_trans_.get()) | |
658 return network_trans_->GetUploadProgress(); | |
659 return final_upload_progress_; | |
660 } | |
661 | |
662 void HttpCache::Transaction::SetQuicServerInfo( | |
663 QuicServerInfo* quic_server_info) {} | |
664 | |
665 bool HttpCache::Transaction::GetLoadTimingInfo( | |
666 LoadTimingInfo* load_timing_info) const { | |
667 if (network_trans_) | |
668 return network_trans_->GetLoadTimingInfo(load_timing_info); | |
669 | |
670 if (old_network_trans_load_timing_) { | |
671 *load_timing_info = *old_network_trans_load_timing_; | |
672 return true; | |
673 } | |
674 | |
675 if (first_cache_access_since_.is_null()) | |
676 return false; | |
677 | |
678 // If the cache entry was opened, return that time. | |
679 load_timing_info->send_start = first_cache_access_since_; | |
680 // This time doesn't make much sense when reading from the cache, so just use | |
681 // the same time as send_start. | |
682 load_timing_info->send_end = first_cache_access_since_; | |
683 return true; | |
684 } | |
685 | |
686 void HttpCache::Transaction::SetPriority(RequestPriority priority) { | |
687 priority_ = priority; | |
688 if (network_trans_) | |
689 network_trans_->SetPriority(priority_); | |
690 } | |
691 | |
692 void HttpCache::Transaction::SetWebSocketHandshakeStreamCreateHelper( | |
693 WebSocketHandshakeStreamBase::CreateHelper* create_helper) { | |
694 websocket_handshake_stream_base_create_helper_ = create_helper; | |
695 if (network_trans_) | |
696 network_trans_->SetWebSocketHandshakeStreamCreateHelper(create_helper); | |
697 } | |
698 | |
699 void HttpCache::Transaction::SetBeforeNetworkStartCallback( | |
700 const BeforeNetworkStartCallback& callback) { | |
701 DCHECK(!network_trans_); | |
702 before_network_start_callback_ = callback; | |
703 } | |
704 | |
705 void HttpCache::Transaction::SetBeforeProxyHeadersSentCallback( | |
706 const BeforeProxyHeadersSentCallback& callback) { | |
707 DCHECK(!network_trans_); | |
708 before_proxy_headers_sent_callback_ = callback; | |
709 } | |
710 | |
711 int HttpCache::Transaction::ResumeNetworkStart() { | |
712 if (network_trans_) | |
713 return network_trans_->ResumeNetworkStart(); | |
714 return ERR_UNEXPECTED; | |
715 } | |
716 | |
717 //----------------------------------------------------------------------------- | |
718 | |
719 void HttpCache::Transaction::DoCallback(int rv) { | |
720 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
721 tracked_objects::ScopedTracker tracking_profile( | |
722 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
723 "422516 HttpCache::Transaction::DoCallback")); | |
724 | |
725 DCHECK(rv != ERR_IO_PENDING); | |
726 DCHECK(!callback_.is_null()); | |
727 | |
728 read_buf_ = NULL; // Release the buffer before invoking the callback. | |
729 | |
730 // Since Run may result in Read being called, clear callback_ up front. | |
731 CompletionCallback c = callback_; | |
732 callback_.Reset(); | |
733 c.Run(rv); | |
734 } | |
735 | |
736 int HttpCache::Transaction::HandleResult(int rv) { | |
737 DCHECK(rv != ERR_IO_PENDING); | |
738 if (!callback_.is_null()) | |
739 DoCallback(rv); | |
740 | |
741 return rv; | |
742 } | |
743 | |
744 // A few common patterns: (Foo* means Foo -> FooComplete) | |
745 // | |
746 // 1. Not-cached entry: | |
747 // Start(): | |
748 // GetBackend* -> InitEntry -> OpenEntry* -> CreateEntry* -> AddToEntry* -> | |
749 // SendRequest* -> SuccessfulSendRequest -> OverwriteCachedResponse -> | |
750 // CacheWriteResponse* -> TruncateCachedData* -> TruncateCachedMetadata* -> | |
751 // PartialHeadersReceived | |
752 // | |
753 // Read(): | |
754 // NetworkRead* -> CacheWriteData* | |
755 // | |
756 // 2. Cached entry, no validation: | |
757 // Start(): | |
758 // GetBackend* -> InitEntry -> OpenEntry* -> AddToEntry* -> CacheReadResponse* | |
759 // -> CacheDispatchValidation -> BeginPartialCacheValidation() -> | |
760 // BeginCacheValidation() -> SetupEntryForRead() | |
761 // | |
762 // Read(): | |
763 // CacheReadData* | |
764 // | |
765 // 3. Cached entry, validation (304): | |
766 // Start(): | |
767 // GetBackend* -> InitEntry -> OpenEntry* -> AddToEntry* -> CacheReadResponse* | |
768 // -> CacheDispatchValidation -> BeginPartialCacheValidation() -> | |
769 // BeginCacheValidation() -> SendRequest* -> SuccessfulSendRequest -> | |
770 // UpdateCachedResponse -> CacheWriteResponse* -> UpdateCachedResponseComplete | |
771 // -> OverwriteCachedResponse -> PartialHeadersReceived | |
772 // | |
773 // Read(): | |
774 // CacheReadData* | |
775 // | |
776 // 4. Cached entry, validation and replace (200): | |
777 // Start(): | |
778 // GetBackend* -> InitEntry -> OpenEntry* -> AddToEntry* -> CacheReadResponse* | |
779 // -> CacheDispatchValidation -> BeginPartialCacheValidation() -> | |
780 // BeginCacheValidation() -> SendRequest* -> SuccessfulSendRequest -> | |
781 // OverwriteCachedResponse -> CacheWriteResponse* -> DoTruncateCachedData* -> | |
782 // TruncateCachedMetadata* -> PartialHeadersReceived | |
783 // | |
784 // Read(): | |
785 // NetworkRead* -> CacheWriteData* | |
786 // | |
787 // 5. Sparse entry, partially cached, byte range request: | |
788 // Start(): | |
789 // GetBackend* -> InitEntry -> OpenEntry* -> AddToEntry* -> CacheReadResponse* | |
790 // -> CacheDispatchValidation -> BeginPartialCacheValidation() -> | |
791 // CacheQueryData* -> ValidateEntryHeadersAndContinue() -> | |
792 // StartPartialCacheValidation -> CompletePartialCacheValidation -> | |
793 // BeginCacheValidation() -> SendRequest* -> SuccessfulSendRequest -> | |
794 // UpdateCachedResponse -> CacheWriteResponse* -> UpdateCachedResponseComplete | |
795 // -> OverwriteCachedResponse -> PartialHeadersReceived | |
796 // | |
797 // Read() 1: | |
798 // NetworkRead* -> CacheWriteData* | |
799 // | |
800 // Read() 2: | |
801 // NetworkRead* -> CacheWriteData* -> StartPartialCacheValidation -> | |
802 // CompletePartialCacheValidation -> CacheReadData* -> | |
803 // | |
804 // Read() 3: | |
805 // CacheReadData* -> StartPartialCacheValidation -> | |
806 // CompletePartialCacheValidation -> BeginCacheValidation() -> SendRequest* -> | |
807 // SuccessfulSendRequest -> UpdateCachedResponse* -> OverwriteCachedResponse | |
808 // -> PartialHeadersReceived -> NetworkRead* -> CacheWriteData* | |
809 // | |
810 // 6. HEAD. Not-cached entry: | |
811 // Pass through. Don't save a HEAD by itself. | |
812 // Start(): | |
813 // GetBackend* -> InitEntry -> OpenEntry* -> SendRequest* | |
814 // | |
815 // 7. HEAD. Cached entry, no validation: | |
816 // Start(): | |
817 // The same flow as for a GET request (example #2) | |
818 // | |
819 // Read(): | |
820 // CacheReadData (returns 0) | |
821 // | |
822 // 8. HEAD. Cached entry, validation (304): | |
823 // The request updates the stored headers. | |
824 // Start(): Same as for a GET request (example #3) | |
825 // | |
826 // Read(): | |
827 // CacheReadData (returns 0) | |
828 // | |
829 // 9. HEAD. Cached entry, validation and replace (200): | |
830 // Pass through. The request dooms the old entry, as a HEAD won't be stored by | |
831 // itself. | |
832 // Start(): | |
833 // GetBackend* -> InitEntry -> OpenEntry* -> AddToEntry* -> CacheReadResponse* | |
834 // -> CacheDispatchValidation -> BeginPartialCacheValidation() -> | |
835 // BeginCacheValidation() -> SendRequest* -> SuccessfulSendRequest -> | |
836 // OverwriteCachedResponse | |
837 // | |
838 // 10. HEAD. Sparse entry, partially cached: | |
839 // Serve the request from the cache, as long as it doesn't require | |
840 // revalidation. Ignore missing ranges when deciding to revalidate. If the | |
841 // entry requires revalidation, ignore the whole request and go to full pass | |
842 // through (the result of the HEAD request will NOT update the entry). | |
843 // | |
844 // Start(): Basically the same as example 7, as we never create a partial_ | |
845 // object for this request. | |
846 // | |
847 // 11. Prefetch, not-cached entry: | |
848 // The same as example 1. The "unused_since_prefetch" bit is stored as true in | |
849 // UpdateCachedResponse. | |
850 // | |
851 // 12. Prefetch, cached entry: | |
852 // Like examples 2-4, only CacheToggleUnusedSincePrefetch* is inserted between | |
853 // CacheReadResponse* and CacheDispatchValidation if the unused_since_prefetch | |
854 // bit is unset. | |
855 // | |
856 // 13. Cached entry less than 5 minutes old, unused_since_prefetch is true: | |
857 // Skip validation, similar to example 2. | |
858 // GetBackend* -> InitEntry -> OpenEntry* -> AddToEntry* -> CacheReadResponse* | |
859 // -> CacheToggleUnusedSincePrefetch* -> CacheDispatchValidation -> | |
860 // BeginPartialCacheValidation() -> BeginCacheValidation() -> | |
861 // SetupEntryForRead() | |
862 // | |
863 // Read(): | |
864 // CacheReadData* | |
865 // | |
866 // 14. Cached entry more than 5 minutes old, unused_since_prefetch is true: | |
867 // Like examples 2-4, only CacheToggleUnusedSincePrefetch* is inserted between | |
868 // CacheReadResponse* and CacheDispatchValidation. | |
869 int HttpCache::Transaction::DoLoop(int result) { | |
870 DCHECK(next_state_ != STATE_NONE); | |
871 | |
872 int rv = result; | |
873 do { | |
874 State state = next_state_; | |
875 next_state_ = STATE_NONE; | |
876 switch (state) { | |
877 case STATE_GET_BACKEND: | |
878 DCHECK_EQ(OK, rv); | |
879 rv = DoGetBackend(); | |
880 break; | |
881 case STATE_GET_BACKEND_COMPLETE: | |
882 rv = DoGetBackendComplete(rv); | |
883 break; | |
884 case STATE_SEND_REQUEST: | |
885 DCHECK_EQ(OK, rv); | |
886 rv = DoSendRequest(); | |
887 break; | |
888 case STATE_SEND_REQUEST_COMPLETE: | |
889 rv = DoSendRequestComplete(rv); | |
890 break; | |
891 case STATE_SUCCESSFUL_SEND_REQUEST: | |
892 DCHECK_EQ(OK, rv); | |
893 rv = DoSuccessfulSendRequest(); | |
894 break; | |
895 case STATE_NETWORK_READ: | |
896 DCHECK_EQ(OK, rv); | |
897 rv = DoNetworkRead(); | |
898 break; | |
899 case STATE_NETWORK_READ_COMPLETE: | |
900 rv = DoNetworkReadComplete(rv); | |
901 break; | |
902 case STATE_INIT_ENTRY: | |
903 DCHECK_EQ(OK, rv); | |
904 rv = DoInitEntry(); | |
905 break; | |
906 case STATE_OPEN_ENTRY: | |
907 DCHECK_EQ(OK, rv); | |
908 rv = DoOpenEntry(); | |
909 break; | |
910 case STATE_OPEN_ENTRY_COMPLETE: | |
911 rv = DoOpenEntryComplete(rv); | |
912 break; | |
913 case STATE_CREATE_ENTRY: | |
914 DCHECK_EQ(OK, rv); | |
915 rv = DoCreateEntry(); | |
916 break; | |
917 case STATE_CREATE_ENTRY_COMPLETE: | |
918 rv = DoCreateEntryComplete(rv); | |
919 break; | |
920 case STATE_DOOM_ENTRY: | |
921 DCHECK_EQ(OK, rv); | |
922 rv = DoDoomEntry(); | |
923 break; | |
924 case STATE_DOOM_ENTRY_COMPLETE: | |
925 rv = DoDoomEntryComplete(rv); | |
926 break; | |
927 case STATE_ADD_TO_ENTRY: | |
928 DCHECK_EQ(OK, rv); | |
929 rv = DoAddToEntry(); | |
930 break; | |
931 case STATE_ADD_TO_ENTRY_COMPLETE: | |
932 rv = DoAddToEntryComplete(rv); | |
933 break; | |
934 case STATE_START_PARTIAL_CACHE_VALIDATION: | |
935 DCHECK_EQ(OK, rv); | |
936 rv = DoStartPartialCacheValidation(); | |
937 break; | |
938 case STATE_COMPLETE_PARTIAL_CACHE_VALIDATION: | |
939 rv = DoCompletePartialCacheValidation(rv); | |
940 break; | |
941 case STATE_UPDATE_CACHED_RESPONSE: | |
942 DCHECK_EQ(OK, rv); | |
943 rv = DoUpdateCachedResponse(); | |
944 break; | |
945 case STATE_UPDATE_CACHED_RESPONSE_COMPLETE: | |
946 rv = DoUpdateCachedResponseComplete(rv); | |
947 break; | |
948 case STATE_OVERWRITE_CACHED_RESPONSE: | |
949 DCHECK_EQ(OK, rv); | |
950 rv = DoOverwriteCachedResponse(); | |
951 break; | |
952 case STATE_TRUNCATE_CACHED_DATA: | |
953 DCHECK_EQ(OK, rv); | |
954 rv = DoTruncateCachedData(); | |
955 break; | |
956 case STATE_TRUNCATE_CACHED_DATA_COMPLETE: | |
957 rv = DoTruncateCachedDataComplete(rv); | |
958 break; | |
959 case STATE_TRUNCATE_CACHED_METADATA: | |
960 DCHECK_EQ(OK, rv); | |
961 rv = DoTruncateCachedMetadata(); | |
962 break; | |
963 case STATE_TRUNCATE_CACHED_METADATA_COMPLETE: | |
964 rv = DoTruncateCachedMetadataComplete(rv); | |
965 break; | |
966 case STATE_PARTIAL_HEADERS_RECEIVED: | |
967 DCHECK_EQ(OK, rv); | |
968 rv = DoPartialHeadersReceived(); | |
969 break; | |
970 case STATE_CACHE_READ_RESPONSE: | |
971 DCHECK_EQ(OK, rv); | |
972 rv = DoCacheReadResponse(); | |
973 break; | |
974 case STATE_CACHE_READ_RESPONSE_COMPLETE: | |
975 rv = DoCacheReadResponseComplete(rv); | |
976 break; | |
977 case STATE_CACHE_DISPATCH_VALIDATION: | |
978 DCHECK_EQ(OK, rv); | |
979 rv = DoCacheDispatchValidation(); | |
980 break; | |
981 case STATE_TOGGLE_UNUSED_SINCE_PREFETCH: | |
982 DCHECK_EQ(OK, rv); | |
983 rv = DoCacheToggleUnusedSincePrefetch(); | |
984 break; | |
985 case STATE_TOGGLE_UNUSED_SINCE_PREFETCH_COMPLETE: | |
986 rv = DoCacheToggleUnusedSincePrefetchComplete(rv); | |
987 break; | |
988 case STATE_CACHE_WRITE_RESPONSE: | |
989 DCHECK_EQ(OK, rv); | |
990 rv = DoCacheWriteResponse(); | |
991 break; | |
992 case STATE_CACHE_WRITE_TRUNCATED_RESPONSE: | |
993 DCHECK_EQ(OK, rv); | |
994 rv = DoCacheWriteTruncatedResponse(); | |
995 break; | |
996 case STATE_CACHE_WRITE_RESPONSE_COMPLETE: | |
997 rv = DoCacheWriteResponseComplete(rv); | |
998 break; | |
999 case STATE_CACHE_READ_METADATA: | |
1000 DCHECK_EQ(OK, rv); | |
1001 rv = DoCacheReadMetadata(); | |
1002 break; | |
1003 case STATE_CACHE_READ_METADATA_COMPLETE: | |
1004 rv = DoCacheReadMetadataComplete(rv); | |
1005 break; | |
1006 case STATE_CACHE_QUERY_DATA: | |
1007 DCHECK_EQ(OK, rv); | |
1008 rv = DoCacheQueryData(); | |
1009 break; | |
1010 case STATE_CACHE_QUERY_DATA_COMPLETE: | |
1011 rv = DoCacheQueryDataComplete(rv); | |
1012 break; | |
1013 case STATE_CACHE_READ_DATA: | |
1014 DCHECK_EQ(OK, rv); | |
1015 rv = DoCacheReadData(); | |
1016 break; | |
1017 case STATE_CACHE_READ_DATA_COMPLETE: | |
1018 rv = DoCacheReadDataComplete(rv); | |
1019 break; | |
1020 case STATE_CACHE_WRITE_DATA: | |
1021 rv = DoCacheWriteData(rv); | |
1022 break; | |
1023 case STATE_CACHE_WRITE_DATA_COMPLETE: | |
1024 rv = DoCacheWriteDataComplete(rv); | |
1025 break; | |
1026 default: | |
1027 NOTREACHED() << "bad state"; | |
1028 rv = ERR_FAILED; | |
1029 break; | |
1030 } | |
1031 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE); | |
1032 | |
1033 if (rv != ERR_IO_PENDING) | |
1034 HandleResult(rv); | |
1035 | |
1036 return rv; | |
1037 } | |
1038 | |
1039 int HttpCache::Transaction::DoGetBackend() { | |
1040 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
1041 tracked_objects::ScopedTracker tracking_profile( | |
1042 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
1043 "422516 HttpCache::Transaction::DoGetBackend")); | |
1044 | |
1045 cache_pending_ = true; | |
1046 next_state_ = STATE_GET_BACKEND_COMPLETE; | |
1047 net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_GET_BACKEND); | |
1048 return cache_->GetBackendForTransaction(this); | |
1049 } | |
1050 | |
1051 int HttpCache::Transaction::DoGetBackendComplete(int result) { | |
1052 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
1053 tracked_objects::ScopedTracker tracking_profile( | |
1054 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
1055 "422516 HttpCache::Transaction::DoGetBackendComplete")); | |
1056 | |
1057 DCHECK(result == OK || result == ERR_FAILED); | |
1058 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_GET_BACKEND, | |
1059 result); | |
1060 cache_pending_ = false; | |
1061 | |
1062 if (!ShouldPassThrough()) { | |
1063 cache_key_ = cache_->GenerateCacheKey(request_); | |
1064 | |
1065 // Requested cache access mode. | |
1066 if (effective_load_flags_ & LOAD_ONLY_FROM_CACHE) { | |
1067 mode_ = READ; | |
1068 } else if (effective_load_flags_ & LOAD_BYPASS_CACHE) { | |
1069 mode_ = WRITE; | |
1070 } else { | |
1071 mode_ = READ_WRITE; | |
1072 } | |
1073 | |
1074 // Downgrade to UPDATE if the request has been externally conditionalized. | |
1075 if (external_validation_.initialized) { | |
1076 if (mode_ & WRITE) { | |
1077 // Strip off the READ_DATA bit (and maybe add back a READ_META bit | |
1078 // in case READ was off). | |
1079 mode_ = UPDATE; | |
1080 } else { | |
1081 mode_ = NONE; | |
1082 } | |
1083 } | |
1084 } | |
1085 | |
1086 // Use PUT and DELETE only to invalidate existing stored entries. | |
1087 if ((request_->method == "PUT" || request_->method == "DELETE") && | |
1088 mode_ != READ_WRITE && mode_ != WRITE) { | |
1089 mode_ = NONE; | |
1090 } | |
1091 | |
1092 // Note that if mode_ == UPDATE (which is tied to external_validation_), the | |
1093 // transaction behaves the same for GET and HEAD requests at this point: if it | |
1094 // was not modified, the entry is updated and a response is not returned from | |
1095 // the cache. If we receive 200, it doesn't matter if there was a validation | |
1096 // header or not. | |
1097 if (request_->method == "HEAD" && mode_ == WRITE) | |
1098 mode_ = NONE; | |
1099 | |
1100 // If must use cache, then we must fail. This can happen for back/forward | |
1101 // navigations to a page generated via a form post. | |
1102 if (!(mode_ & READ) && effective_load_flags_ & LOAD_ONLY_FROM_CACHE) | |
1103 return ERR_CACHE_MISS; | |
1104 | |
1105 if (mode_ == NONE) { | |
1106 if (partial_.get()) { | |
1107 partial_->RestoreHeaders(&custom_request_->extra_headers); | |
1108 partial_.reset(); | |
1109 } | |
1110 next_state_ = STATE_SEND_REQUEST; | |
1111 } else { | |
1112 next_state_ = STATE_INIT_ENTRY; | |
1113 } | |
1114 | |
1115 // This is only set if we have something to do with the response. | |
1116 range_requested_ = (partial_.get() != NULL); | |
1117 | |
1118 return OK; | |
1119 } | |
1120 | |
1121 int HttpCache::Transaction::DoSendRequest() { | |
1122 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
1123 tracked_objects::ScopedTracker tracking_profile( | |
1124 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
1125 "422516 HttpCache::Transaction::DoSendRequest")); | |
1126 | |
1127 DCHECK(mode_ & WRITE || mode_ == NONE); | |
1128 DCHECK(!network_trans_.get()); | |
1129 | |
1130 send_request_since_ = TimeTicks::Now(); | |
1131 | |
1132 // Create a network transaction. | |
1133 int rv = cache_->network_layer_->CreateTransaction(priority_, | |
1134 &network_trans_); | |
1135 if (rv != OK) | |
1136 return rv; | |
1137 network_trans_->SetBeforeNetworkStartCallback(before_network_start_callback_); | |
1138 network_trans_->SetBeforeProxyHeadersSentCallback( | |
1139 before_proxy_headers_sent_callback_); | |
1140 | |
1141 // Old load timing information, if any, is now obsolete. | |
1142 old_network_trans_load_timing_.reset(); | |
1143 | |
1144 if (websocket_handshake_stream_base_create_helper_) | |
1145 network_trans_->SetWebSocketHandshakeStreamCreateHelper( | |
1146 websocket_handshake_stream_base_create_helper_); | |
1147 | |
1148 next_state_ = STATE_SEND_REQUEST_COMPLETE; | |
1149 rv = network_trans_->Start(request_, io_callback_, net_log_); | |
1150 return rv; | |
1151 } | |
1152 | |
1153 int HttpCache::Transaction::DoSendRequestComplete(int result) { | |
1154 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
1155 tracked_objects::ScopedTracker tracking_profile( | |
1156 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
1157 "422516 HttpCache::Transaction::DoSendRequestComplete")); | |
1158 | |
1159 if (!cache_.get()) | |
1160 return ERR_UNEXPECTED; | |
1161 | |
1162 // If requested, and we have a readable cache entry, and we have | |
1163 // an error indicating that we're offline as opposed to in contact | |
1164 // with a bad server, read from cache anyway. | |
1165 if (IsOfflineError(result)) { | |
1166 if (mode_ == READ_WRITE && entry_ && !partial_) { | |
1167 RecordOfflineStatus(effective_load_flags_, | |
1168 OFFLINE_STATUS_DATA_AVAILABLE_OFFLINE); | |
1169 if (effective_load_flags_ & LOAD_FROM_CACHE_IF_OFFLINE) { | |
1170 UpdateTransactionPattern(PATTERN_NOT_COVERED); | |
1171 response_.server_data_unavailable = true; | |
1172 return SetupEntryForRead(); | |
1173 } | |
1174 } else { | |
1175 RecordOfflineStatus(effective_load_flags_, | |
1176 OFFLINE_STATUS_DATA_UNAVAILABLE_OFFLINE); | |
1177 } | |
1178 } else { | |
1179 RecordOfflineStatus(effective_load_flags_, | |
1180 (result == OK ? OFFLINE_STATUS_NETWORK_SUCCEEDED : | |
1181 OFFLINE_STATUS_NETWORK_FAILED)); | |
1182 } | |
1183 | |
1184 // If we tried to conditionalize the request and failed, we know | |
1185 // we won't be reading from the cache after this point. | |
1186 if (couldnt_conditionalize_request_) | |
1187 mode_ = WRITE; | |
1188 | |
1189 if (result == OK) { | |
1190 next_state_ = STATE_SUCCESSFUL_SEND_REQUEST; | |
1191 return OK; | |
1192 } | |
1193 | |
1194 // Do not record requests that have network errors or restarts. | |
1195 UpdateTransactionPattern(PATTERN_NOT_COVERED); | |
1196 if (IsCertificateError(result)) { | |
1197 const HttpResponseInfo* response = network_trans_->GetResponseInfo(); | |
1198 // If we get a certificate error, then there is a certificate in ssl_info, | |
1199 // so GetResponseInfo() should never return NULL here. | |
1200 DCHECK(response); | |
1201 response_.ssl_info = response->ssl_info; | |
1202 } else if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED) { | |
1203 const HttpResponseInfo* response = network_trans_->GetResponseInfo(); | |
1204 DCHECK(response); | |
1205 response_.cert_request_info = response->cert_request_info; | |
1206 } else if (response_.was_cached) { | |
1207 DoneWritingToEntry(true); | |
1208 } | |
1209 return result; | |
1210 } | |
1211 | |
1212 // We received the response headers and there is no error. | |
1213 int HttpCache::Transaction::DoSuccessfulSendRequest() { | |
1214 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
1215 tracked_objects::ScopedTracker tracking_profile( | |
1216 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
1217 "422516 HttpCache::Transaction::DoSuccessfulSendRequest")); | |
1218 | |
1219 DCHECK(!new_response_); | |
1220 const HttpResponseInfo* new_response = network_trans_->GetResponseInfo(); | |
1221 bool authentication_failure = false; | |
1222 | |
1223 if (new_response->headers->response_code() == 401 || | |
1224 new_response->headers->response_code() == 407) { | |
1225 auth_response_ = *new_response; | |
1226 if (!reading_) | |
1227 return OK; | |
1228 | |
1229 // We initiated a second request the caller doesn't know about. We should be | |
1230 // able to authenticate this request because we should have authenticated | |
1231 // this URL moments ago. | |
1232 if (IsReadyToRestartForAuth()) { | |
1233 DCHECK(!response_.auth_challenge.get()); | |
1234 next_state_ = STATE_SEND_REQUEST_COMPLETE; | |
1235 // In theory we should check to see if there are new cookies, but there | |
1236 // is no way to do that from here. | |
1237 return network_trans_->RestartWithAuth(AuthCredentials(), io_callback_); | |
1238 } | |
1239 | |
1240 // We have to perform cleanup at this point so that at least the next | |
1241 // request can succeed. | |
1242 authentication_failure = true; | |
1243 if (entry_) | |
1244 DoomPartialEntry(false); | |
1245 mode_ = NONE; | |
1246 partial_.reset(); | |
1247 } | |
1248 | |
1249 new_response_ = new_response; | |
1250 if (authentication_failure || | |
1251 (!ValidatePartialResponse() && !auth_response_.headers.get())) { | |
1252 // Something went wrong with this request and we have to restart it. | |
1253 // If we have an authentication response, we are exposed to weird things | |
1254 // hapenning if the user cancels the authentication before we receive | |
1255 // the new response. | |
1256 net_log_.AddEvent(NetLog::TYPE_HTTP_CACHE_RE_SEND_PARTIAL_REQUEST); | |
1257 UpdateTransactionPattern(PATTERN_NOT_COVERED); | |
1258 response_ = HttpResponseInfo(); | |
1259 ResetNetworkTransaction(); | |
1260 new_response_ = NULL; | |
1261 next_state_ = STATE_SEND_REQUEST; | |
1262 return OK; | |
1263 } | |
1264 | |
1265 if (handling_206_ && mode_ == READ_WRITE && !truncated_ && !is_sparse_) { | |
1266 // We have stored the full entry, but it changed and the server is | |
1267 // sending a range. We have to delete the old entry. | |
1268 UpdateTransactionPattern(PATTERN_NOT_COVERED); | |
1269 DoneWritingToEntry(false); | |
1270 } | |
1271 | |
1272 if (mode_ == WRITE && | |
1273 transaction_pattern_ != PATTERN_ENTRY_CANT_CONDITIONALIZE) { | |
1274 UpdateTransactionPattern(PATTERN_ENTRY_NOT_CACHED); | |
1275 } | |
1276 | |
1277 // Invalidate any cached GET with a successful PUT or DELETE. | |
1278 if (mode_ == WRITE && | |
1279 (request_->method == "PUT" || request_->method == "DELETE")) { | |
1280 if (NonErrorResponse(new_response->headers->response_code())) { | |
1281 int ret = cache_->DoomEntry(cache_key_, NULL); | |
1282 DCHECK_EQ(OK, ret); | |
1283 } | |
1284 cache_->DoneWritingToEntry(entry_, true); | |
1285 entry_ = NULL; | |
1286 mode_ = NONE; | |
1287 } | |
1288 | |
1289 // Invalidate any cached GET with a successful POST. | |
1290 if (!(effective_load_flags_ & LOAD_DISABLE_CACHE) && | |
1291 request_->method == "POST" && | |
1292 NonErrorResponse(new_response->headers->response_code())) { | |
1293 cache_->DoomMainEntryForUrl(request_->url); | |
1294 } | |
1295 | |
1296 RecordNoStoreHeaderHistogram(request_->load_flags, new_response); | |
1297 | |
1298 if (new_response_->headers->response_code() == 416 && | |
1299 (request_->method == "GET" || request_->method == "POST")) { | |
1300 // If there is an active entry it may be destroyed with this transaction. | |
1301 response_ = *new_response_; | |
1302 return OK; | |
1303 } | |
1304 | |
1305 // Are we expecting a response to a conditional query? | |
1306 if (mode_ == READ_WRITE || mode_ == UPDATE) { | |
1307 if (new_response->headers->response_code() == 304 || handling_206_) { | |
1308 UpdateTransactionPattern(PATTERN_ENTRY_VALIDATED); | |
1309 next_state_ = STATE_UPDATE_CACHED_RESPONSE; | |
1310 return OK; | |
1311 } | |
1312 UpdateTransactionPattern(PATTERN_ENTRY_UPDATED); | |
1313 mode_ = WRITE; | |
1314 } | |
1315 | |
1316 next_state_ = STATE_OVERWRITE_CACHED_RESPONSE; | |
1317 return OK; | |
1318 } | |
1319 | |
1320 int HttpCache::Transaction::DoNetworkRead() { | |
1321 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
1322 tracked_objects::ScopedTracker tracking_profile( | |
1323 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
1324 "422516 HttpCache::Transaction::DoNetworkRead")); | |
1325 | |
1326 next_state_ = STATE_NETWORK_READ_COMPLETE; | |
1327 return network_trans_->Read(read_buf_.get(), io_buf_len_, io_callback_); | |
1328 } | |
1329 | |
1330 int HttpCache::Transaction::DoNetworkReadComplete(int result) { | |
1331 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
1332 tracked_objects::ScopedTracker tracking_profile( | |
1333 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
1334 "422516 HttpCache::Transaction::DoNetworkReadComplete")); | |
1335 | |
1336 DCHECK(mode_ & WRITE || mode_ == NONE); | |
1337 | |
1338 if (!cache_.get()) | |
1339 return ERR_UNEXPECTED; | |
1340 | |
1341 // If there is an error or we aren't saving the data, we are done; just wait | |
1342 // until the destructor runs to see if we can keep the data. | |
1343 if (mode_ == NONE || result < 0) | |
1344 return result; | |
1345 | |
1346 next_state_ = STATE_CACHE_WRITE_DATA; | |
1347 return result; | |
1348 } | |
1349 | |
1350 int HttpCache::Transaction::DoInitEntry() { | |
1351 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
1352 tracked_objects::ScopedTracker tracking_profile( | |
1353 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
1354 "422516 HttpCache::Transaction::DoInitEntry")); | |
1355 | |
1356 DCHECK(!new_entry_); | |
1357 | |
1358 if (!cache_.get()) | |
1359 return ERR_UNEXPECTED; | |
1360 | |
1361 if (mode_ == WRITE) { | |
1362 next_state_ = STATE_DOOM_ENTRY; | |
1363 return OK; | |
1364 } | |
1365 | |
1366 next_state_ = STATE_OPEN_ENTRY; | |
1367 return OK; | |
1368 } | |
1369 | |
1370 int HttpCache::Transaction::DoOpenEntry() { | |
1371 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
1372 tracked_objects::ScopedTracker tracking_profile( | |
1373 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
1374 "422516 HttpCache::Transaction::DoOpenEntry")); | |
1375 | |
1376 DCHECK(!new_entry_); | |
1377 next_state_ = STATE_OPEN_ENTRY_COMPLETE; | |
1378 cache_pending_ = true; | |
1379 net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_OPEN_ENTRY); | |
1380 first_cache_access_since_ = TimeTicks::Now(); | |
1381 return cache_->OpenEntry(cache_key_, &new_entry_, this); | |
1382 } | |
1383 | |
1384 int HttpCache::Transaction::DoOpenEntryComplete(int result) { | |
1385 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
1386 tracked_objects::ScopedTracker tracking_profile( | |
1387 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
1388 "422516 HttpCache::Transaction::DoOpenEntryComplete")); | |
1389 | |
1390 // It is important that we go to STATE_ADD_TO_ENTRY whenever the result is | |
1391 // OK, otherwise the cache will end up with an active entry without any | |
1392 // transaction attached. | |
1393 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_OPEN_ENTRY, result); | |
1394 cache_pending_ = false; | |
1395 if (result == OK) { | |
1396 next_state_ = STATE_ADD_TO_ENTRY; | |
1397 return OK; | |
1398 } | |
1399 | |
1400 if (result == ERR_CACHE_RACE) { | |
1401 next_state_ = STATE_INIT_ENTRY; | |
1402 return OK; | |
1403 } | |
1404 | |
1405 if (request_->method == "PUT" || request_->method == "DELETE" || | |
1406 (request_->method == "HEAD" && mode_ == READ_WRITE)) { | |
1407 DCHECK(mode_ == READ_WRITE || mode_ == WRITE || request_->method == "HEAD"); | |
1408 mode_ = NONE; | |
1409 next_state_ = STATE_SEND_REQUEST; | |
1410 return OK; | |
1411 } | |
1412 | |
1413 if (mode_ == READ_WRITE) { | |
1414 mode_ = WRITE; | |
1415 next_state_ = STATE_CREATE_ENTRY; | |
1416 return OK; | |
1417 } | |
1418 if (mode_ == UPDATE) { | |
1419 // There is no cache entry to update; proceed without caching. | |
1420 mode_ = NONE; | |
1421 next_state_ = STATE_SEND_REQUEST; | |
1422 return OK; | |
1423 } | |
1424 if (cache_->mode() == PLAYBACK) | |
1425 DVLOG(1) << "Playback Cache Miss: " << request_->url; | |
1426 | |
1427 // The entry does not exist, and we are not permitted to create a new entry, | |
1428 // so we must fail. | |
1429 return ERR_CACHE_MISS; | |
1430 } | |
1431 | |
1432 int HttpCache::Transaction::DoCreateEntry() { | |
1433 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
1434 tracked_objects::ScopedTracker tracking_profile( | |
1435 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
1436 "422516 HttpCache::Transaction::DoCreateEntry")); | |
1437 | |
1438 DCHECK(!new_entry_); | |
1439 next_state_ = STATE_CREATE_ENTRY_COMPLETE; | |
1440 cache_pending_ = true; | |
1441 net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_CREATE_ENTRY); | |
1442 return cache_->CreateEntry(cache_key_, &new_entry_, this); | |
1443 } | |
1444 | |
1445 int HttpCache::Transaction::DoCreateEntryComplete(int result) { | |
1446 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
1447 tracked_objects::ScopedTracker tracking_profile( | |
1448 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
1449 "422516 HttpCache::Transaction::DoCreateEntryComplete")); | |
1450 | |
1451 // It is important that we go to STATE_ADD_TO_ENTRY whenever the result is | |
1452 // OK, otherwise the cache will end up with an active entry without any | |
1453 // transaction attached. | |
1454 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_CREATE_ENTRY, | |
1455 result); | |
1456 cache_pending_ = false; | |
1457 next_state_ = STATE_ADD_TO_ENTRY; | |
1458 | |
1459 if (result == ERR_CACHE_RACE) { | |
1460 next_state_ = STATE_INIT_ENTRY; | |
1461 return OK; | |
1462 } | |
1463 | |
1464 if (result != OK) { | |
1465 // We have a race here: Maybe we failed to open the entry and decided to | |
1466 // create one, but by the time we called create, another transaction already | |
1467 // created the entry. If we want to eliminate this issue, we need an atomic | |
1468 // OpenOrCreate() method exposed by the disk cache. | |
1469 DLOG(WARNING) << "Unable to create cache entry"; | |
1470 mode_ = NONE; | |
1471 if (partial_.get()) | |
1472 partial_->RestoreHeaders(&custom_request_->extra_headers); | |
1473 next_state_ = STATE_SEND_REQUEST; | |
1474 } | |
1475 return OK; | |
1476 } | |
1477 | |
1478 int HttpCache::Transaction::DoDoomEntry() { | |
1479 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
1480 tracked_objects::ScopedTracker tracking_profile( | |
1481 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
1482 "422516 HttpCache::Transaction::DoDoomEntry")); | |
1483 | |
1484 next_state_ = STATE_DOOM_ENTRY_COMPLETE; | |
1485 cache_pending_ = true; | |
1486 if (first_cache_access_since_.is_null()) | |
1487 first_cache_access_since_ = TimeTicks::Now(); | |
1488 net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_DOOM_ENTRY); | |
1489 return cache_->DoomEntry(cache_key_, this); | |
1490 } | |
1491 | |
1492 int HttpCache::Transaction::DoDoomEntryComplete(int result) { | |
1493 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
1494 tracked_objects::ScopedTracker tracking_profile( | |
1495 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
1496 "422516 HttpCache::Transaction::DoDoomEntryComplete")); | |
1497 | |
1498 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_DOOM_ENTRY, result); | |
1499 next_state_ = STATE_CREATE_ENTRY; | |
1500 cache_pending_ = false; | |
1501 if (result == ERR_CACHE_RACE) | |
1502 next_state_ = STATE_INIT_ENTRY; | |
1503 return OK; | |
1504 } | |
1505 | |
1506 int HttpCache::Transaction::DoAddToEntry() { | |
1507 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
1508 tracked_objects::ScopedTracker tracking_profile( | |
1509 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
1510 "422516 HttpCache::Transaction::DoAddToEntry")); | |
1511 | |
1512 DCHECK(new_entry_); | |
1513 cache_pending_ = true; | |
1514 next_state_ = STATE_ADD_TO_ENTRY_COMPLETE; | |
1515 net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_ADD_TO_ENTRY); | |
1516 DCHECK(entry_lock_waiting_since_.is_null()); | |
1517 entry_lock_waiting_since_ = TimeTicks::Now(); | |
1518 int rv = cache_->AddTransactionToEntry(new_entry_, this); | |
1519 if (rv == ERR_IO_PENDING) { | |
1520 if (bypass_lock_for_test_) { | |
1521 OnAddToEntryTimeout(entry_lock_waiting_since_); | |
1522 } else { | |
1523 int timeout_milliseconds = 20 * 1000; | |
1524 if (partial_ && new_entry_->writer && | |
1525 new_entry_->writer->range_requested_) { | |
1526 // Quickly timeout and bypass the cache if we're a range request and | |
1527 // we're blocked by the reader/writer lock. Doing so eliminates a long | |
1528 // running issue, http://crbug.com/31014, where two of the same media | |
1529 // resources could not be played back simultaneously due to one locking | |
1530 // the cache entry until the entire video was downloaded. | |
1531 // | |
1532 // Bypassing the cache is not ideal, as we are now ignoring the cache | |
1533 // entirely for all range requests to a resource beyond the first. This | |
1534 // is however a much more succinct solution than the alternatives, which | |
1535 // would require somewhat significant changes to the http caching logic. | |
1536 // | |
1537 // Allow some timeout slack for the entry addition to complete in case | |
1538 // the writer lock is imminently released; we want to avoid skipping | |
1539 // the cache if at all possible. See http://crbug.com/408765 | |
1540 timeout_milliseconds = 25; | |
1541 } | |
1542 base::MessageLoop::current()->PostDelayedTask( | |
1543 FROM_HERE, | |
1544 base::Bind(&HttpCache::Transaction::OnAddToEntryTimeout, | |
1545 weak_factory_.GetWeakPtr(), entry_lock_waiting_since_), | |
1546 TimeDelta::FromMilliseconds(timeout_milliseconds)); | |
1547 } | |
1548 } | |
1549 return rv; | |
1550 } | |
1551 | |
1552 int HttpCache::Transaction::DoAddToEntryComplete(int result) { | |
1553 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
1554 tracked_objects::ScopedTracker tracking_profile( | |
1555 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
1556 "422516 HttpCache::Transaction::DoAddToEntryComplete")); | |
1557 | |
1558 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_ADD_TO_ENTRY, | |
1559 result); | |
1560 const TimeDelta entry_lock_wait = | |
1561 TimeTicks::Now() - entry_lock_waiting_since_; | |
1562 UMA_HISTOGRAM_TIMES("HttpCache.EntryLockWait", entry_lock_wait); | |
1563 | |
1564 entry_lock_waiting_since_ = TimeTicks(); | |
1565 DCHECK(new_entry_); | |
1566 cache_pending_ = false; | |
1567 | |
1568 if (result == OK) | |
1569 entry_ = new_entry_; | |
1570 | |
1571 // If there is a failure, the cache should have taken care of new_entry_. | |
1572 new_entry_ = NULL; | |
1573 | |
1574 if (result == ERR_CACHE_RACE) { | |
1575 next_state_ = STATE_INIT_ENTRY; | |
1576 return OK; | |
1577 } | |
1578 | |
1579 if (result == ERR_CACHE_LOCK_TIMEOUT) { | |
1580 // The cache is busy, bypass it for this transaction. | |
1581 mode_ = NONE; | |
1582 next_state_ = STATE_SEND_REQUEST; | |
1583 if (partial_) { | |
1584 partial_->RestoreHeaders(&custom_request_->extra_headers); | |
1585 partial_.reset(); | |
1586 } | |
1587 return OK; | |
1588 } | |
1589 | |
1590 if (result != OK) { | |
1591 NOTREACHED(); | |
1592 return result; | |
1593 } | |
1594 | |
1595 if (mode_ == WRITE) { | |
1596 if (partial_.get()) | |
1597 partial_->RestoreHeaders(&custom_request_->extra_headers); | |
1598 next_state_ = STATE_SEND_REQUEST; | |
1599 } else { | |
1600 // We have to read the headers from the cached entry. | |
1601 DCHECK(mode_ & READ_META); | |
1602 next_state_ = STATE_CACHE_READ_RESPONSE; | |
1603 } | |
1604 return OK; | |
1605 } | |
1606 | |
1607 // We may end up here multiple times for a given request. | |
1608 int HttpCache::Transaction::DoStartPartialCacheValidation() { | |
1609 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
1610 tracked_objects::ScopedTracker tracking_profile( | |
1611 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
1612 "422516 HttpCache::Transaction::DoStartPartialCacheValidation")); | |
1613 | |
1614 if (mode_ == NONE) | |
1615 return OK; | |
1616 | |
1617 next_state_ = STATE_COMPLETE_PARTIAL_CACHE_VALIDATION; | |
1618 return partial_->ShouldValidateCache(entry_->disk_entry, io_callback_); | |
1619 } | |
1620 | |
1621 int HttpCache::Transaction::DoCompletePartialCacheValidation(int result) { | |
1622 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
1623 tracked_objects::ScopedTracker tracking_profile( | |
1624 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
1625 "422516 HttpCache::Transaction::DoCompletePartialCacheValidation")); | |
1626 | |
1627 if (!result) { | |
1628 // This is the end of the request. | |
1629 if (mode_ & WRITE) { | |
1630 DoneWritingToEntry(true); | |
1631 } else { | |
1632 cache_->DoneReadingFromEntry(entry_, this); | |
1633 entry_ = NULL; | |
1634 } | |
1635 return result; | |
1636 } | |
1637 | |
1638 if (result < 0) | |
1639 return result; | |
1640 | |
1641 partial_->PrepareCacheValidation(entry_->disk_entry, | |
1642 &custom_request_->extra_headers); | |
1643 | |
1644 if (reading_ && partial_->IsCurrentRangeCached()) { | |
1645 next_state_ = STATE_CACHE_READ_DATA; | |
1646 return OK; | |
1647 } | |
1648 | |
1649 return BeginCacheValidation(); | |
1650 } | |
1651 | |
1652 // We received 304 or 206 and we want to update the cached response headers. | |
1653 int HttpCache::Transaction::DoUpdateCachedResponse() { | |
1654 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
1655 tracked_objects::ScopedTracker tracking_profile( | |
1656 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
1657 "422516 HttpCache::Transaction::DoUpdateCachedResponse")); | |
1658 | |
1659 next_state_ = STATE_UPDATE_CACHED_RESPONSE_COMPLETE; | |
1660 int rv = OK; | |
1661 // Update cached response based on headers in new_response. | |
1662 // TODO(wtc): should we update cached certificate (response_.ssl_info), too? | |
1663 response_.headers->Update(*new_response_->headers.get()); | |
1664 response_.response_time = new_response_->response_time; | |
1665 response_.request_time = new_response_->request_time; | |
1666 response_.network_accessed = new_response_->network_accessed; | |
1667 response_.unused_since_prefetch = new_response_->unused_since_prefetch; | |
1668 | |
1669 if (response_.headers->HasHeaderValue("cache-control", "no-store")) { | |
1670 if (!entry_->doomed) { | |
1671 int ret = cache_->DoomEntry(cache_key_, NULL); | |
1672 DCHECK_EQ(OK, ret); | |
1673 } | |
1674 } else { | |
1675 // If we are already reading, we already updated the headers for this | |
1676 // request; doing it again will change Content-Length. | |
1677 if (!reading_) { | |
1678 target_state_ = STATE_UPDATE_CACHED_RESPONSE_COMPLETE; | |
1679 next_state_ = STATE_CACHE_WRITE_RESPONSE; | |
1680 rv = OK; | |
1681 } | |
1682 } | |
1683 return rv; | |
1684 } | |
1685 | |
1686 int HttpCache::Transaction::DoUpdateCachedResponseComplete(int result) { | |
1687 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
1688 tracked_objects::ScopedTracker tracking_profile( | |
1689 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
1690 "422516 HttpCache::Transaction::DoUpdateCachedResponseComplete")); | |
1691 | |
1692 if (mode_ == UPDATE) { | |
1693 DCHECK(!handling_206_); | |
1694 // We got a "not modified" response and already updated the corresponding | |
1695 // cache entry above. | |
1696 // | |
1697 // By closing the cached entry now, we make sure that the 304 rather than | |
1698 // the cached 200 response, is what will be returned to the user. | |
1699 DoneWritingToEntry(true); | |
1700 } else if (entry_ && !handling_206_) { | |
1701 DCHECK_EQ(READ_WRITE, mode_); | |
1702 if (!partial_.get() || partial_->IsLastRange()) { | |
1703 cache_->ConvertWriterToReader(entry_); | |
1704 mode_ = READ; | |
1705 } | |
1706 // We no longer need the network transaction, so destroy it. | |
1707 final_upload_progress_ = network_trans_->GetUploadProgress(); | |
1708 ResetNetworkTransaction(); | |
1709 } else if (entry_ && handling_206_ && truncated_ && | |
1710 partial_->initial_validation()) { | |
1711 // We just finished the validation of a truncated entry, and the server | |
1712 // is willing to resume the operation. Now we go back and start serving | |
1713 // the first part to the user. | |
1714 ResetNetworkTransaction(); | |
1715 new_response_ = NULL; | |
1716 next_state_ = STATE_START_PARTIAL_CACHE_VALIDATION; | |
1717 partial_->SetRangeToStartDownload(); | |
1718 return OK; | |
1719 } | |
1720 next_state_ = STATE_OVERWRITE_CACHED_RESPONSE; | |
1721 return OK; | |
1722 } | |
1723 | |
1724 int HttpCache::Transaction::DoOverwriteCachedResponse() { | |
1725 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
1726 tracked_objects::ScopedTracker tracking_profile( | |
1727 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
1728 "422516 HttpCache::Transaction::DoOverwriteCachedResponse")); | |
1729 | |
1730 if (mode_ & READ) { | |
1731 next_state_ = STATE_PARTIAL_HEADERS_RECEIVED; | |
1732 return OK; | |
1733 } | |
1734 | |
1735 // We change the value of Content-Length for partial content. | |
1736 if (handling_206_ && partial_.get()) | |
1737 partial_->FixContentLength(new_response_->headers.get()); | |
1738 | |
1739 response_ = *new_response_; | |
1740 | |
1741 if (request_->method == "HEAD") { | |
1742 // This response is replacing the cached one. | |
1743 DoneWritingToEntry(false); | |
1744 mode_ = NONE; | |
1745 new_response_ = NULL; | |
1746 return OK; | |
1747 } | |
1748 | |
1749 if (handling_206_ && !CanResume(false)) { | |
1750 // There is no point in storing this resource because it will never be used. | |
1751 // This may change if we support LOAD_ONLY_FROM_CACHE with sparse entries. | |
1752 DoneWritingToEntry(false); | |
1753 if (partial_.get()) | |
1754 partial_->FixResponseHeaders(response_.headers.get(), true); | |
1755 next_state_ = STATE_PARTIAL_HEADERS_RECEIVED; | |
1756 return OK; | |
1757 } | |
1758 | |
1759 target_state_ = STATE_TRUNCATE_CACHED_DATA; | |
1760 next_state_ = truncated_ ? STATE_CACHE_WRITE_TRUNCATED_RESPONSE : | |
1761 STATE_CACHE_WRITE_RESPONSE; | |
1762 return OK; | |
1763 } | |
1764 | |
1765 int HttpCache::Transaction::DoTruncateCachedData() { | |
1766 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
1767 tracked_objects::ScopedTracker tracking_profile( | |
1768 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
1769 "422516 HttpCache::Transaction::DoTruncateCachedData")); | |
1770 | |
1771 next_state_ = STATE_TRUNCATE_CACHED_DATA_COMPLETE; | |
1772 if (!entry_) | |
1773 return OK; | |
1774 if (net_log_.IsLogging()) | |
1775 net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_WRITE_DATA); | |
1776 // Truncate the stream. | |
1777 return WriteToEntry(kResponseContentIndex, 0, NULL, 0, io_callback_); | |
1778 } | |
1779 | |
1780 int HttpCache::Transaction::DoTruncateCachedDataComplete(int result) { | |
1781 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
1782 tracked_objects::ScopedTracker tracking_profile( | |
1783 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
1784 "422516 HttpCache::Transaction::DoTruncateCachedDataComplete")); | |
1785 | |
1786 if (entry_) { | |
1787 if (net_log_.IsLogging()) { | |
1788 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_WRITE_DATA, | |
1789 result); | |
1790 } | |
1791 } | |
1792 | |
1793 next_state_ = STATE_TRUNCATE_CACHED_METADATA; | |
1794 return OK; | |
1795 } | |
1796 | |
1797 int HttpCache::Transaction::DoTruncateCachedMetadata() { | |
1798 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
1799 tracked_objects::ScopedTracker tracking_profile( | |
1800 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
1801 "422516 HttpCache::Transaction::DoTruncateCachedMetadata")); | |
1802 | |
1803 next_state_ = STATE_TRUNCATE_CACHED_METADATA_COMPLETE; | |
1804 if (!entry_) | |
1805 return OK; | |
1806 | |
1807 if (net_log_.IsLogging()) | |
1808 net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_WRITE_INFO); | |
1809 return WriteToEntry(kMetadataIndex, 0, NULL, 0, io_callback_); | |
1810 } | |
1811 | |
1812 int HttpCache::Transaction::DoTruncateCachedMetadataComplete(int result) { | |
1813 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
1814 tracked_objects::ScopedTracker tracking_profile( | |
1815 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
1816 "422516 HttpCache::Transaction::DoTruncateCachedMetadataComplete")); | |
1817 | |
1818 if (entry_) { | |
1819 if (net_log_.IsLogging()) { | |
1820 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_WRITE_INFO, | |
1821 result); | |
1822 } | |
1823 } | |
1824 | |
1825 next_state_ = STATE_PARTIAL_HEADERS_RECEIVED; | |
1826 return OK; | |
1827 } | |
1828 | |
1829 int HttpCache::Transaction::DoPartialHeadersReceived() { | |
1830 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
1831 tracked_objects::ScopedTracker tracking_profile( | |
1832 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
1833 "422516 HttpCache::Transaction::DoPartialHeadersReceived")); | |
1834 | |
1835 new_response_ = NULL; | |
1836 if (entry_ && !partial_.get() && | |
1837 entry_->disk_entry->GetDataSize(kMetadataIndex)) | |
1838 next_state_ = STATE_CACHE_READ_METADATA; | |
1839 | |
1840 if (!partial_.get()) | |
1841 return OK; | |
1842 | |
1843 if (reading_) { | |
1844 if (network_trans_.get()) { | |
1845 next_state_ = STATE_NETWORK_READ; | |
1846 } else { | |
1847 next_state_ = STATE_CACHE_READ_DATA; | |
1848 } | |
1849 } else if (mode_ != NONE) { | |
1850 // We are about to return the headers for a byte-range request to the user, | |
1851 // so let's fix them. | |
1852 partial_->FixResponseHeaders(response_.headers.get(), true); | |
1853 } | |
1854 return OK; | |
1855 } | |
1856 | |
1857 int HttpCache::Transaction::DoCacheReadResponse() { | |
1858 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
1859 tracked_objects::ScopedTracker tracking_profile( | |
1860 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
1861 "422516 HttpCache::Transaction::DoCacheReadResponse")); | |
1862 | |
1863 DCHECK(entry_); | |
1864 next_state_ = STATE_CACHE_READ_RESPONSE_COMPLETE; | |
1865 | |
1866 io_buf_len_ = entry_->disk_entry->GetDataSize(kResponseInfoIndex); | |
1867 read_buf_ = new IOBuffer(io_buf_len_); | |
1868 | |
1869 net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_READ_INFO); | |
1870 return entry_->disk_entry->ReadData(kResponseInfoIndex, 0, read_buf_.get(), | |
1871 io_buf_len_, io_callback_); | |
1872 } | |
1873 | |
1874 int HttpCache::Transaction::DoCacheReadResponseComplete(int result) { | |
1875 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
1876 tracked_objects::ScopedTracker tracking_profile( | |
1877 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
1878 "422516 HttpCache::Transaction::DoCacheReadResponseComplete")); | |
1879 | |
1880 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_READ_INFO, result); | |
1881 if (result != io_buf_len_ || | |
1882 !HttpCache::ParseResponseInfo(read_buf_->data(), io_buf_len_, | |
1883 &response_, &truncated_)) { | |
1884 return OnCacheReadError(result, true); | |
1885 } | |
1886 | |
1887 // cert_cache() will be null if the CertCacheTrial field trial is disabled. | |
1888 if (cache_->cert_cache() && response_.ssl_info.is_valid()) | |
1889 ReadCertChain(); | |
1890 | |
1891 // Some resources may have slipped in as truncated when they're not. | |
1892 int current_size = entry_->disk_entry->GetDataSize(kResponseContentIndex); | |
1893 if (response_.headers->GetContentLength() == current_size) | |
1894 truncated_ = false; | |
1895 | |
1896 if ((response_.unused_since_prefetch && | |
1897 !(request_->load_flags & LOAD_PREFETCH)) || | |
1898 (!response_.unused_since_prefetch && | |
1899 (request_->load_flags & LOAD_PREFETCH))) { | |
1900 // Either this is the first use of an entry since it was prefetched or | |
1901 // this is a prefetch. The value of response.unused_since_prefetch is valid | |
1902 // for this transaction but the bit needs to be flipped in storage. | |
1903 next_state_ = STATE_TOGGLE_UNUSED_SINCE_PREFETCH; | |
1904 return OK; | |
1905 } | |
1906 | |
1907 next_state_ = STATE_CACHE_DISPATCH_VALIDATION; | |
1908 return OK; | |
1909 } | |
1910 | |
1911 int HttpCache::Transaction::DoCacheDispatchValidation() { | |
1912 // We now have access to the cache entry. | |
1913 // | |
1914 // o if we are a reader for the transaction, then we can start reading the | |
1915 // cache entry. | |
1916 // | |
1917 // o if we can read or write, then we should check if the cache entry needs | |
1918 // to be validated and then issue a network request if needed or just read | |
1919 // from the cache if the cache entry is already valid. | |
1920 // | |
1921 // o if we are set to UPDATE, then we are handling an externally | |
1922 // conditionalized request (if-modified-since / if-none-match). We check | |
1923 // if the request headers define a validation request. | |
1924 // | |
1925 int result = ERR_FAILED; | |
1926 switch (mode_) { | |
1927 case READ: | |
1928 UpdateTransactionPattern(PATTERN_ENTRY_USED); | |
1929 result = BeginCacheRead(); | |
1930 break; | |
1931 case READ_WRITE: | |
1932 result = BeginPartialCacheValidation(); | |
1933 break; | |
1934 case UPDATE: | |
1935 result = BeginExternallyConditionalizedRequest(); | |
1936 break; | |
1937 case WRITE: | |
1938 default: | |
1939 NOTREACHED(); | |
1940 } | |
1941 return result; | |
1942 } | |
1943 | |
1944 int HttpCache::Transaction::DoCacheToggleUnusedSincePrefetch() { | |
1945 // Write back the toggled value for the next use of this entry. | |
1946 response_.unused_since_prefetch = !response_.unused_since_prefetch; | |
1947 | |
1948 // TODO(jkarlin): If DoUpdateCachedResponse is also called for this | |
1949 // transaction then metadata will be written to cache twice. If prefetching | |
1950 // becomes more common, consider combining the writes. | |
1951 target_state_ = STATE_TOGGLE_UNUSED_SINCE_PREFETCH_COMPLETE; | |
1952 next_state_ = STATE_CACHE_WRITE_RESPONSE; | |
1953 return OK; | |
1954 } | |
1955 | |
1956 int HttpCache::Transaction::DoCacheToggleUnusedSincePrefetchComplete( | |
1957 int result) { | |
1958 // Restore the original value for this transaction. | |
1959 response_.unused_since_prefetch = !response_.unused_since_prefetch; | |
1960 next_state_ = STATE_CACHE_DISPATCH_VALIDATION; | |
1961 return OK; | |
1962 } | |
1963 | |
1964 int HttpCache::Transaction::DoCacheWriteResponse() { | |
1965 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
1966 tracked_objects::ScopedTracker tracking_profile( | |
1967 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
1968 "422516 HttpCache::Transaction::DoCacheWriteResponse")); | |
1969 | |
1970 if (entry_) { | |
1971 if (net_log_.IsLogging()) | |
1972 net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_WRITE_INFO); | |
1973 } | |
1974 return WriteResponseInfoToEntry(false); | |
1975 } | |
1976 | |
1977 int HttpCache::Transaction::DoCacheWriteTruncatedResponse() { | |
1978 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
1979 tracked_objects::ScopedTracker tracking_profile( | |
1980 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
1981 "422516 HttpCache::Transaction::DoCacheWriteTruncatedResponse")); | |
1982 | |
1983 if (entry_) { | |
1984 if (net_log_.IsLogging()) | |
1985 net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_WRITE_INFO); | |
1986 } | |
1987 return WriteResponseInfoToEntry(true); | |
1988 } | |
1989 | |
1990 int HttpCache::Transaction::DoCacheWriteResponseComplete(int result) { | |
1991 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
1992 tracked_objects::ScopedTracker tracking_profile( | |
1993 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
1994 "422516 HttpCache::Transaction::DoCacheWriteResponseComplete")); | |
1995 | |
1996 next_state_ = target_state_; | |
1997 target_state_ = STATE_NONE; | |
1998 if (!entry_) | |
1999 return OK; | |
2000 if (net_log_.IsLogging()) { | |
2001 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_WRITE_INFO, | |
2002 result); | |
2003 } | |
2004 | |
2005 // Balance the AddRef from WriteResponseInfoToEntry. | |
2006 if (result != io_buf_len_) { | |
2007 DLOG(ERROR) << "failed to write response info to cache"; | |
2008 DoneWritingToEntry(false); | |
2009 } | |
2010 return OK; | |
2011 } | |
2012 | |
2013 int HttpCache::Transaction::DoCacheReadMetadata() { | |
2014 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
2015 tracked_objects::ScopedTracker tracking_profile( | |
2016 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
2017 "422516 HttpCache::Transaction::DoCacheReadMetadata")); | |
2018 | |
2019 DCHECK(entry_); | |
2020 DCHECK(!response_.metadata.get()); | |
2021 next_state_ = STATE_CACHE_READ_METADATA_COMPLETE; | |
2022 | |
2023 response_.metadata = | |
2024 new IOBufferWithSize(entry_->disk_entry->GetDataSize(kMetadataIndex)); | |
2025 | |
2026 net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_READ_INFO); | |
2027 return entry_->disk_entry->ReadData(kMetadataIndex, 0, | |
2028 response_.metadata.get(), | |
2029 response_.metadata->size(), | |
2030 io_callback_); | |
2031 } | |
2032 | |
2033 int HttpCache::Transaction::DoCacheReadMetadataComplete(int result) { | |
2034 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
2035 tracked_objects::ScopedTracker tracking_profile( | |
2036 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
2037 "422516 HttpCache::Transaction::DoCacheReadMetadataComplete")); | |
2038 | |
2039 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_READ_INFO, result); | |
2040 if (result != response_.metadata->size()) | |
2041 return OnCacheReadError(result, false); | |
2042 return OK; | |
2043 } | |
2044 | |
2045 int HttpCache::Transaction::DoCacheQueryData() { | |
2046 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
2047 tracked_objects::ScopedTracker tracking_profile( | |
2048 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
2049 "422516 HttpCache::Transaction::DoCacheQueryData")); | |
2050 | |
2051 next_state_ = STATE_CACHE_QUERY_DATA_COMPLETE; | |
2052 return entry_->disk_entry->ReadyForSparseIO(io_callback_); | |
2053 } | |
2054 | |
2055 int HttpCache::Transaction::DoCacheQueryDataComplete(int result) { | |
2056 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
2057 tracked_objects::ScopedTracker tracking_profile( | |
2058 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
2059 "422516 HttpCache::Transaction::DoCacheQueryDataComplete")); | |
2060 | |
2061 DCHECK_EQ(OK, result); | |
2062 if (!cache_.get()) | |
2063 return ERR_UNEXPECTED; | |
2064 | |
2065 return ValidateEntryHeadersAndContinue(); | |
2066 } | |
2067 | |
2068 int HttpCache::Transaction::DoCacheReadData() { | |
2069 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
2070 tracked_objects::ScopedTracker tracking_profile( | |
2071 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
2072 "422516 HttpCache::Transaction::DoCacheReadData")); | |
2073 | |
2074 DCHECK(entry_); | |
2075 next_state_ = STATE_CACHE_READ_DATA_COMPLETE; | |
2076 | |
2077 if (net_log_.IsLogging()) | |
2078 net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_READ_DATA); | |
2079 if (partial_.get()) { | |
2080 return partial_->CacheRead(entry_->disk_entry, read_buf_.get(), io_buf_len_, | |
2081 io_callback_); | |
2082 } | |
2083 | |
2084 return entry_->disk_entry->ReadData(kResponseContentIndex, read_offset_, | |
2085 read_buf_.get(), io_buf_len_, | |
2086 io_callback_); | |
2087 } | |
2088 | |
2089 int HttpCache::Transaction::DoCacheReadDataComplete(int result) { | |
2090 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
2091 tracked_objects::ScopedTracker tracking_profile( | |
2092 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
2093 "422516 HttpCache::Transaction::DoCacheReadDataComplete")); | |
2094 | |
2095 if (net_log_.IsLogging()) { | |
2096 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_READ_DATA, | |
2097 result); | |
2098 } | |
2099 | |
2100 if (!cache_.get()) | |
2101 return ERR_UNEXPECTED; | |
2102 | |
2103 if (partial_.get()) { | |
2104 // Partial requests are confusing to report in histograms because they may | |
2105 // have multiple underlying requests. | |
2106 UpdateTransactionPattern(PATTERN_NOT_COVERED); | |
2107 return DoPartialCacheReadCompleted(result); | |
2108 } | |
2109 | |
2110 if (result > 0) { | |
2111 read_offset_ += result; | |
2112 } else if (result == 0) { // End of file. | |
2113 RecordHistograms(); | |
2114 cache_->DoneReadingFromEntry(entry_, this); | |
2115 entry_ = NULL; | |
2116 } else { | |
2117 return OnCacheReadError(result, false); | |
2118 } | |
2119 return result; | |
2120 } | |
2121 | |
2122 int HttpCache::Transaction::DoCacheWriteData(int num_bytes) { | |
2123 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
2124 tracked_objects::ScopedTracker tracking_profile( | |
2125 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
2126 "422516 HttpCache::Transaction::DoCacheWriteData")); | |
2127 | |
2128 next_state_ = STATE_CACHE_WRITE_DATA_COMPLETE; | |
2129 write_len_ = num_bytes; | |
2130 if (entry_) { | |
2131 if (net_log_.IsLogging()) | |
2132 net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_WRITE_DATA); | |
2133 } | |
2134 | |
2135 return AppendResponseDataToEntry(read_buf_.get(), num_bytes, io_callback_); | |
2136 } | |
2137 | |
2138 int HttpCache::Transaction::DoCacheWriteDataComplete(int result) { | |
2139 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
2140 tracked_objects::ScopedTracker tracking_profile( | |
2141 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
2142 "422516 HttpCache::Transaction::DoCacheWriteDataComplete")); | |
2143 | |
2144 if (entry_) { | |
2145 if (net_log_.IsLogging()) { | |
2146 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_WRITE_DATA, | |
2147 result); | |
2148 } | |
2149 } | |
2150 // Balance the AddRef from DoCacheWriteData. | |
2151 if (!cache_.get()) | |
2152 return ERR_UNEXPECTED; | |
2153 | |
2154 if (result != write_len_) { | |
2155 DLOG(ERROR) << "failed to write response data to cache"; | |
2156 DoneWritingToEntry(false); | |
2157 | |
2158 // We want to ignore errors writing to disk and just keep reading from | |
2159 // the network. | |
2160 result = write_len_; | |
2161 } else if (!done_reading_ && entry_) { | |
2162 int current_size = entry_->disk_entry->GetDataSize(kResponseContentIndex); | |
2163 int64 body_size = response_.headers->GetContentLength(); | |
2164 if (body_size >= 0 && body_size <= current_size) | |
2165 done_reading_ = true; | |
2166 } | |
2167 | |
2168 if (partial_.get()) { | |
2169 // This may be the last request. | |
2170 if (!(result == 0 && !truncated_ && | |
2171 (partial_->IsLastRange() || mode_ == WRITE))) | |
2172 return DoPartialNetworkReadCompleted(result); | |
2173 } | |
2174 | |
2175 if (result == 0) { | |
2176 // End of file. This may be the result of a connection problem so see if we | |
2177 // have to keep the entry around to be flagged as truncated later on. | |
2178 if (done_reading_ || !entry_ || partial_.get() || | |
2179 response_.headers->GetContentLength() <= 0) | |
2180 DoneWritingToEntry(true); | |
2181 } | |
2182 | |
2183 return result; | |
2184 } | |
2185 | |
2186 //----------------------------------------------------------------------------- | |
2187 | |
2188 void HttpCache::Transaction::ReadCertChain() { | |
2189 std::string key = | |
2190 GetCacheKeyForCert(response_.ssl_info.cert->os_cert_handle()); | |
2191 const X509Certificate::OSCertHandles& intermediates = | |
2192 response_.ssl_info.cert->GetIntermediateCertificates(); | |
2193 int dist_from_root = intermediates.size(); | |
2194 | |
2195 scoped_refptr<SharedChainData> shared_chain_data( | |
2196 new SharedChainData(intermediates.size() + 1, TimeTicks::Now())); | |
2197 cache_->cert_cache()->GetCertificate(key, | |
2198 base::Bind(&OnCertReadIOComplete, | |
2199 dist_from_root, | |
2200 true /* is leaf */, | |
2201 shared_chain_data)); | |
2202 | |
2203 for (X509Certificate::OSCertHandles::const_iterator it = | |
2204 intermediates.begin(); | |
2205 it != intermediates.end(); | |
2206 ++it) { | |
2207 --dist_from_root; | |
2208 key = GetCacheKeyForCert(*it); | |
2209 cache_->cert_cache()->GetCertificate(key, | |
2210 base::Bind(&OnCertReadIOComplete, | |
2211 dist_from_root, | |
2212 false /* is not leaf */, | |
2213 shared_chain_data)); | |
2214 } | |
2215 DCHECK_EQ(0, dist_from_root); | |
2216 } | |
2217 | |
2218 void HttpCache::Transaction::WriteCertChain() { | |
2219 const X509Certificate::OSCertHandles& intermediates = | |
2220 response_.ssl_info.cert->GetIntermediateCertificates(); | |
2221 int dist_from_root = intermediates.size(); | |
2222 | |
2223 scoped_refptr<SharedChainData> shared_chain_data( | |
2224 new SharedChainData(intermediates.size() + 1, TimeTicks::Now())); | |
2225 cache_->cert_cache()->SetCertificate( | |
2226 response_.ssl_info.cert->os_cert_handle(), | |
2227 base::Bind(&OnCertWriteIOComplete, | |
2228 dist_from_root, | |
2229 true /* is leaf */, | |
2230 shared_chain_data)); | |
2231 for (X509Certificate::OSCertHandles::const_iterator it = | |
2232 intermediates.begin(); | |
2233 it != intermediates.end(); | |
2234 ++it) { | |
2235 --dist_from_root; | |
2236 cache_->cert_cache()->SetCertificate(*it, | |
2237 base::Bind(&OnCertWriteIOComplete, | |
2238 dist_from_root, | |
2239 false /* is not leaf */, | |
2240 shared_chain_data)); | |
2241 } | |
2242 DCHECK_EQ(0, dist_from_root); | |
2243 } | |
2244 | |
2245 void HttpCache::Transaction::SetRequest(const BoundNetLog& net_log, | |
2246 const HttpRequestInfo* request) { | |
2247 net_log_ = net_log; | |
2248 request_ = request; | |
2249 effective_load_flags_ = request_->load_flags; | |
2250 | |
2251 switch (cache_->mode()) { | |
2252 case NORMAL: | |
2253 break; | |
2254 case RECORD: | |
2255 // When in record mode, we want to NEVER load from the cache. | |
2256 // The reason for this is because we save the Set-Cookie headers | |
2257 // (intentionally). If we read from the cache, we replay them | |
2258 // prematurely. | |
2259 effective_load_flags_ |= LOAD_BYPASS_CACHE; | |
2260 break; | |
2261 case PLAYBACK: | |
2262 // When in playback mode, we want to load exclusively from the cache. | |
2263 effective_load_flags_ |= LOAD_ONLY_FROM_CACHE; | |
2264 break; | |
2265 case DISABLE: | |
2266 effective_load_flags_ |= LOAD_DISABLE_CACHE; | |
2267 break; | |
2268 } | |
2269 | |
2270 // Some headers imply load flags. The order here is significant. | |
2271 // | |
2272 // LOAD_DISABLE_CACHE : no cache read or write | |
2273 // LOAD_BYPASS_CACHE : no cache read | |
2274 // LOAD_VALIDATE_CACHE : no cache read unless validation | |
2275 // | |
2276 // The former modes trump latter modes, so if we find a matching header we | |
2277 // can stop iterating kSpecialHeaders. | |
2278 // | |
2279 static const struct { | |
2280 const HeaderNameAndValue* search; | |
2281 int load_flag; | |
2282 } kSpecialHeaders[] = { | |
2283 { kPassThroughHeaders, LOAD_DISABLE_CACHE }, | |
2284 { kForceFetchHeaders, LOAD_BYPASS_CACHE }, | |
2285 { kForceValidateHeaders, LOAD_VALIDATE_CACHE }, | |
2286 }; | |
2287 | |
2288 bool range_found = false; | |
2289 bool external_validation_error = false; | |
2290 bool special_headers = false; | |
2291 | |
2292 if (request_->extra_headers.HasHeader(HttpRequestHeaders::kRange)) | |
2293 range_found = true; | |
2294 | |
2295 for (size_t i = 0; i < arraysize(kSpecialHeaders); ++i) { | |
2296 if (HeaderMatches(request_->extra_headers, kSpecialHeaders[i].search)) { | |
2297 effective_load_flags_ |= kSpecialHeaders[i].load_flag; | |
2298 special_headers = true; | |
2299 break; | |
2300 } | |
2301 } | |
2302 | |
2303 // Check for conditionalization headers which may correspond with a | |
2304 // cache validation request. | |
2305 for (size_t i = 0; i < arraysize(kValidationHeaders); ++i) { | |
2306 const ValidationHeaderInfo& info = kValidationHeaders[i]; | |
2307 std::string validation_value; | |
2308 if (request_->extra_headers.GetHeader( | |
2309 info.request_header_name, &validation_value)) { | |
2310 if (!external_validation_.values[i].empty() || | |
2311 validation_value.empty()) { | |
2312 external_validation_error = true; | |
2313 } | |
2314 external_validation_.values[i] = validation_value; | |
2315 external_validation_.initialized = true; | |
2316 } | |
2317 } | |
2318 | |
2319 if (range_found || special_headers || external_validation_.initialized) { | |
2320 // Log the headers before request_ is modified. | |
2321 std::string empty; | |
2322 net_log_.AddEvent( | |
2323 NetLog::TYPE_HTTP_CACHE_CALLER_REQUEST_HEADERS, | |
2324 base::Bind(&HttpRequestHeaders::NetLogCallback, | |
2325 base::Unretained(&request_->extra_headers), &empty)); | |
2326 } | |
2327 | |
2328 // We don't support ranges and validation headers. | |
2329 if (range_found && external_validation_.initialized) { | |
2330 LOG(WARNING) << "Byte ranges AND validation headers found."; | |
2331 effective_load_flags_ |= LOAD_DISABLE_CACHE; | |
2332 } | |
2333 | |
2334 // If there is more than one validation header, we can't treat this request as | |
2335 // a cache validation, since we don't know for sure which header the server | |
2336 // will give us a response for (and they could be contradictory). | |
2337 if (external_validation_error) { | |
2338 LOG(WARNING) << "Multiple or malformed validation headers found."; | |
2339 effective_load_flags_ |= LOAD_DISABLE_CACHE; | |
2340 } | |
2341 | |
2342 if (range_found && !(effective_load_flags_ & LOAD_DISABLE_CACHE)) { | |
2343 UpdateTransactionPattern(PATTERN_NOT_COVERED); | |
2344 partial_.reset(new PartialData); | |
2345 if (request_->method == "GET" && partial_->Init(request_->extra_headers)) { | |
2346 // We will be modifying the actual range requested to the server, so | |
2347 // let's remove the header here. | |
2348 custom_request_.reset(new HttpRequestInfo(*request_)); | |
2349 custom_request_->extra_headers.RemoveHeader(HttpRequestHeaders::kRange); | |
2350 request_ = custom_request_.get(); | |
2351 partial_->SetHeaders(custom_request_->extra_headers); | |
2352 } else { | |
2353 // The range is invalid or we cannot handle it properly. | |
2354 VLOG(1) << "Invalid byte range found."; | |
2355 effective_load_flags_ |= LOAD_DISABLE_CACHE; | |
2356 partial_.reset(NULL); | |
2357 } | |
2358 } | |
2359 } | |
2360 | |
2361 bool HttpCache::Transaction::ShouldPassThrough() { | |
2362 // We may have a null disk_cache if there is an error we cannot recover from, | |
2363 // like not enough disk space, or sharing violations. | |
2364 if (!cache_->disk_cache_.get()) | |
2365 return true; | |
2366 | |
2367 // When using the record/playback modes, we always use the cache | |
2368 // and we never pass through. | |
2369 if (cache_->mode() == RECORD || cache_->mode() == PLAYBACK) | |
2370 return false; | |
2371 | |
2372 if (effective_load_flags_ & LOAD_DISABLE_CACHE) | |
2373 return true; | |
2374 | |
2375 if (request_->method == "GET" || request_->method == "HEAD") | |
2376 return false; | |
2377 | |
2378 if (request_->method == "POST" && request_->upload_data_stream && | |
2379 request_->upload_data_stream->identifier()) { | |
2380 return false; | |
2381 } | |
2382 | |
2383 if (request_->method == "PUT" && request_->upload_data_stream) | |
2384 return false; | |
2385 | |
2386 if (request_->method == "DELETE") | |
2387 return false; | |
2388 | |
2389 return true; | |
2390 } | |
2391 | |
2392 int HttpCache::Transaction::BeginCacheRead() { | |
2393 // We don't support any combination of LOAD_ONLY_FROM_CACHE and byte ranges. | |
2394 if (response_.headers->response_code() == 206 || partial_.get()) { | |
2395 NOTREACHED(); | |
2396 return ERR_CACHE_MISS; | |
2397 } | |
2398 | |
2399 if (request_->method == "HEAD") | |
2400 FixHeadersForHead(); | |
2401 | |
2402 // We don't have the whole resource. | |
2403 if (truncated_) | |
2404 return ERR_CACHE_MISS; | |
2405 | |
2406 if (entry_->disk_entry->GetDataSize(kMetadataIndex)) | |
2407 next_state_ = STATE_CACHE_READ_METADATA; | |
2408 | |
2409 return OK; | |
2410 } | |
2411 | |
2412 int HttpCache::Transaction::BeginCacheValidation() { | |
2413 DCHECK(mode_ == READ_WRITE); | |
2414 | |
2415 ValidationType required_validation = RequiresValidation(); | |
2416 | |
2417 bool skip_validation = (required_validation == VALIDATION_NONE); | |
2418 | |
2419 if (required_validation == VALIDATION_ASYNCHRONOUS && | |
2420 !(request_->method == "GET" && (truncated_ || partial_)) && cache_ && | |
2421 cache_->use_stale_while_revalidate()) { | |
2422 TriggerAsyncValidation(); | |
2423 skip_validation = true; | |
2424 } | |
2425 | |
2426 if (request_->method == "HEAD" && | |
2427 (truncated_ || response_.headers->response_code() == 206)) { | |
2428 DCHECK(!partial_); | |
2429 if (skip_validation) | |
2430 return SetupEntryForRead(); | |
2431 | |
2432 // Bail out! | |
2433 next_state_ = STATE_SEND_REQUEST; | |
2434 mode_ = NONE; | |
2435 return OK; | |
2436 } | |
2437 | |
2438 if (truncated_) { | |
2439 // Truncated entries can cause partial gets, so we shouldn't record this | |
2440 // load in histograms. | |
2441 UpdateTransactionPattern(PATTERN_NOT_COVERED); | |
2442 skip_validation = !partial_->initial_validation(); | |
2443 } | |
2444 | |
2445 if (partial_.get() && (is_sparse_ || truncated_) && | |
2446 (!partial_->IsCurrentRangeCached() || invalid_range_)) { | |
2447 // Force revalidation for sparse or truncated entries. Note that we don't | |
2448 // want to ignore the regular validation logic just because a byte range was | |
2449 // part of the request. | |
2450 skip_validation = false; | |
2451 } | |
2452 | |
2453 if (skip_validation) { | |
2454 // TODO(ricea): Is this pattern okay for asynchronous revalidations? | |
2455 UpdateTransactionPattern(PATTERN_ENTRY_USED); | |
2456 RecordOfflineStatus(effective_load_flags_, OFFLINE_STATUS_FRESH_CACHE); | |
2457 return SetupEntryForRead(); | |
2458 } else { | |
2459 // Make the network request conditional, to see if we may reuse our cached | |
2460 // response. If we cannot do so, then we just resort to a normal fetch. | |
2461 // Our mode remains READ_WRITE for a conditional request. Even if the | |
2462 // conditionalization fails, we don't switch to WRITE mode until we | |
2463 // know we won't be falling back to using the cache entry in the | |
2464 // LOAD_FROM_CACHE_IF_OFFLINE case. | |
2465 if (!ConditionalizeRequest()) { | |
2466 couldnt_conditionalize_request_ = true; | |
2467 UpdateTransactionPattern(PATTERN_ENTRY_CANT_CONDITIONALIZE); | |
2468 if (partial_.get()) | |
2469 return DoRestartPartialRequest(); | |
2470 | |
2471 DCHECK_NE(206, response_.headers->response_code()); | |
2472 } | |
2473 next_state_ = STATE_SEND_REQUEST; | |
2474 } | |
2475 return OK; | |
2476 } | |
2477 | |
2478 int HttpCache::Transaction::BeginPartialCacheValidation() { | |
2479 DCHECK(mode_ == READ_WRITE); | |
2480 | |
2481 if (response_.headers->response_code() != 206 && !partial_.get() && | |
2482 !truncated_) { | |
2483 return BeginCacheValidation(); | |
2484 } | |
2485 | |
2486 // Partial requests should not be recorded in histograms. | |
2487 UpdateTransactionPattern(PATTERN_NOT_COVERED); | |
2488 if (range_requested_) { | |
2489 next_state_ = STATE_CACHE_QUERY_DATA; | |
2490 return OK; | |
2491 } | |
2492 | |
2493 // The request is not for a range, but we have stored just ranges. | |
2494 | |
2495 if (request_->method == "HEAD") | |
2496 return BeginCacheValidation(); | |
2497 | |
2498 partial_.reset(new PartialData()); | |
2499 partial_->SetHeaders(request_->extra_headers); | |
2500 if (!custom_request_.get()) { | |
2501 custom_request_.reset(new HttpRequestInfo(*request_)); | |
2502 request_ = custom_request_.get(); | |
2503 } | |
2504 | |
2505 return ValidateEntryHeadersAndContinue(); | |
2506 } | |
2507 | |
2508 // This should only be called once per request. | |
2509 int HttpCache::Transaction::ValidateEntryHeadersAndContinue() { | |
2510 DCHECK(mode_ == READ_WRITE); | |
2511 | |
2512 if (!partial_->UpdateFromStoredHeaders( | |
2513 response_.headers.get(), entry_->disk_entry, truncated_)) { | |
2514 return DoRestartPartialRequest(); | |
2515 } | |
2516 | |
2517 if (response_.headers->response_code() == 206) | |
2518 is_sparse_ = true; | |
2519 | |
2520 if (!partial_->IsRequestedRangeOK()) { | |
2521 // The stored data is fine, but the request may be invalid. | |
2522 invalid_range_ = true; | |
2523 } | |
2524 | |
2525 next_state_ = STATE_START_PARTIAL_CACHE_VALIDATION; | |
2526 return OK; | |
2527 } | |
2528 | |
2529 int HttpCache::Transaction::BeginExternallyConditionalizedRequest() { | |
2530 DCHECK_EQ(UPDATE, mode_); | |
2531 DCHECK(external_validation_.initialized); | |
2532 | |
2533 for (size_t i = 0; i < arraysize(kValidationHeaders); i++) { | |
2534 if (external_validation_.values[i].empty()) | |
2535 continue; | |
2536 // Retrieve either the cached response's "etag" or "last-modified" header. | |
2537 std::string validator; | |
2538 response_.headers->EnumerateHeader( | |
2539 NULL, | |
2540 kValidationHeaders[i].related_response_header_name, | |
2541 &validator); | |
2542 | |
2543 if (response_.headers->response_code() != 200 || truncated_ || | |
2544 validator.empty() || validator != external_validation_.values[i]) { | |
2545 // The externally conditionalized request is not a validation request | |
2546 // for our existing cache entry. Proceed with caching disabled. | |
2547 UpdateTransactionPattern(PATTERN_NOT_COVERED); | |
2548 DoneWritingToEntry(true); | |
2549 } | |
2550 } | |
2551 | |
2552 // TODO(ricea): This calculation is expensive to perform just to collect | |
2553 // statistics. Either remove it or use the result, depending on the result of | |
2554 // the experiment. | |
2555 ExternallyConditionalizedType type = | |
2556 EXTERNALLY_CONDITIONALIZED_CACHE_USABLE; | |
2557 if (mode_ == NONE) | |
2558 type = EXTERNALLY_CONDITIONALIZED_MISMATCHED_VALIDATORS; | |
2559 else if (RequiresValidation()) | |
2560 type = EXTERNALLY_CONDITIONALIZED_CACHE_REQUIRES_VALIDATION; | |
2561 | |
2562 // TODO(ricea): Add CACHE_USABLE_STALE once stale-while-revalidate CL landed. | |
2563 // TODO(ricea): Either remove this histogram or make it permanent by M40. | |
2564 UMA_HISTOGRAM_ENUMERATION("HttpCache.ExternallyConditionalized", | |
2565 type, | |
2566 EXTERNALLY_CONDITIONALIZED_MAX); | |
2567 | |
2568 next_state_ = STATE_SEND_REQUEST; | |
2569 return OK; | |
2570 } | |
2571 | |
2572 int HttpCache::Transaction::RestartNetworkRequest() { | |
2573 DCHECK(mode_ & WRITE || mode_ == NONE); | |
2574 DCHECK(network_trans_.get()); | |
2575 DCHECK_EQ(STATE_NONE, next_state_); | |
2576 | |
2577 next_state_ = STATE_SEND_REQUEST_COMPLETE; | |
2578 int rv = network_trans_->RestartIgnoringLastError(io_callback_); | |
2579 if (rv != ERR_IO_PENDING) | |
2580 return DoLoop(rv); | |
2581 return rv; | |
2582 } | |
2583 | |
2584 int HttpCache::Transaction::RestartNetworkRequestWithCertificate( | |
2585 X509Certificate* client_cert) { | |
2586 DCHECK(mode_ & WRITE || mode_ == NONE); | |
2587 DCHECK(network_trans_.get()); | |
2588 DCHECK_EQ(STATE_NONE, next_state_); | |
2589 | |
2590 next_state_ = STATE_SEND_REQUEST_COMPLETE; | |
2591 int rv = network_trans_->RestartWithCertificate(client_cert, io_callback_); | |
2592 if (rv != ERR_IO_PENDING) | |
2593 return DoLoop(rv); | |
2594 return rv; | |
2595 } | |
2596 | |
2597 int HttpCache::Transaction::RestartNetworkRequestWithAuth( | |
2598 const AuthCredentials& credentials) { | |
2599 DCHECK(mode_ & WRITE || mode_ == NONE); | |
2600 DCHECK(network_trans_.get()); | |
2601 DCHECK_EQ(STATE_NONE, next_state_); | |
2602 | |
2603 next_state_ = STATE_SEND_REQUEST_COMPLETE; | |
2604 int rv = network_trans_->RestartWithAuth(credentials, io_callback_); | |
2605 if (rv != ERR_IO_PENDING) | |
2606 return DoLoop(rv); | |
2607 return rv; | |
2608 } | |
2609 | |
2610 ValidationType HttpCache::Transaction::RequiresValidation() { | |
2611 // TODO(darin): need to do more work here: | |
2612 // - make sure we have a matching request method | |
2613 // - watch out for cached responses that depend on authentication | |
2614 | |
2615 // In playback mode, nothing requires validation. | |
2616 if (cache_->mode() == net::HttpCache::PLAYBACK) | |
2617 return VALIDATION_NONE; | |
2618 | |
2619 if (response_.vary_data.is_valid() && | |
2620 !response_.vary_data.MatchesRequest(*request_, | |
2621 *response_.headers.get())) { | |
2622 vary_mismatch_ = true; | |
2623 return VALIDATION_SYNCHRONOUS; | |
2624 } | |
2625 | |
2626 if (effective_load_flags_ & LOAD_PREFERRING_CACHE) | |
2627 return VALIDATION_NONE; | |
2628 | |
2629 if (response_.unused_since_prefetch && | |
2630 !(effective_load_flags_ & LOAD_PREFETCH) && | |
2631 response_.headers->GetCurrentAge( | |
2632 response_.request_time, response_.response_time, | |
2633 cache_->clock_->Now()) < TimeDelta::FromMinutes(kPrefetchReuseMins)) { | |
2634 // The first use of a resource after prefetch within a short window skips | |
2635 // validation. | |
2636 return VALIDATION_NONE; | |
2637 } | |
2638 | |
2639 if (effective_load_flags_ & (LOAD_VALIDATE_CACHE | LOAD_ASYNC_REVALIDATION)) | |
2640 return VALIDATION_SYNCHRONOUS; | |
2641 | |
2642 if (request_->method == "PUT" || request_->method == "DELETE") | |
2643 return VALIDATION_SYNCHRONOUS; | |
2644 | |
2645 ValidationType validation_required_by_headers = | |
2646 response_.headers->RequiresValidation(response_.request_time, | |
2647 response_.response_time, | |
2648 cache_->clock_->Now()); | |
2649 | |
2650 if (validation_required_by_headers == VALIDATION_ASYNCHRONOUS) { | |
2651 // Asynchronous revalidation is only supported for GET and HEAD methods. | |
2652 if (request_->method != "GET" && request_->method != "HEAD") | |
2653 return VALIDATION_SYNCHRONOUS; | |
2654 } | |
2655 | |
2656 return validation_required_by_headers; | |
2657 } | |
2658 | |
2659 bool HttpCache::Transaction::ConditionalizeRequest() { | |
2660 DCHECK(response_.headers.get()); | |
2661 | |
2662 if (request_->method == "PUT" || request_->method == "DELETE") | |
2663 return false; | |
2664 | |
2665 // This only makes sense for cached 200 or 206 responses. | |
2666 if (response_.headers->response_code() != 200 && | |
2667 response_.headers->response_code() != 206) { | |
2668 return false; | |
2669 } | |
2670 | |
2671 if (fail_conditionalization_for_test_) | |
2672 return false; | |
2673 | |
2674 DCHECK(response_.headers->response_code() != 206 || | |
2675 response_.headers->HasStrongValidators()); | |
2676 | |
2677 // Just use the first available ETag and/or Last-Modified header value. | |
2678 // TODO(darin): Or should we use the last? | |
2679 | |
2680 std::string etag_value; | |
2681 if (response_.headers->GetHttpVersion() >= HttpVersion(1, 1)) | |
2682 response_.headers->EnumerateHeader(NULL, "etag", &etag_value); | |
2683 | |
2684 std::string last_modified_value; | |
2685 if (!vary_mismatch_) { | |
2686 response_.headers->EnumerateHeader(NULL, "last-modified", | |
2687 &last_modified_value); | |
2688 } | |
2689 | |
2690 if (etag_value.empty() && last_modified_value.empty()) | |
2691 return false; | |
2692 | |
2693 if (!partial_.get()) { | |
2694 // Need to customize the request, so this forces us to allocate :( | |
2695 custom_request_.reset(new HttpRequestInfo(*request_)); | |
2696 request_ = custom_request_.get(); | |
2697 } | |
2698 DCHECK(custom_request_.get()); | |
2699 | |
2700 bool use_if_range = partial_.get() && !partial_->IsCurrentRangeCached() && | |
2701 !invalid_range_; | |
2702 | |
2703 if (!use_if_range) { | |
2704 // stale-while-revalidate is not useful when we only have a partial response | |
2705 // cached, so don't set the header in that case. | |
2706 HttpResponseHeaders::FreshnessLifetimes lifetimes = | |
2707 response_.headers->GetFreshnessLifetimes(response_.response_time); | |
2708 if (lifetimes.staleness > TimeDelta()) { | |
2709 TimeDelta current_age = response_.headers->GetCurrentAge( | |
2710 response_.request_time, response_.response_time, | |
2711 cache_->clock_->Now()); | |
2712 | |
2713 custom_request_->extra_headers.SetHeader( | |
2714 kFreshnessHeader, | |
2715 base::StringPrintf("max-age=%" PRId64 | |
2716 ",stale-while-revalidate=%" PRId64 ",age=%" PRId64, | |
2717 lifetimes.freshness.InSeconds(), | |
2718 lifetimes.staleness.InSeconds(), | |
2719 current_age.InSeconds())); | |
2720 } | |
2721 } | |
2722 | |
2723 if (!etag_value.empty()) { | |
2724 if (use_if_range) { | |
2725 // We don't want to switch to WRITE mode if we don't have this block of a | |
2726 // byte-range request because we may have other parts cached. | |
2727 custom_request_->extra_headers.SetHeader( | |
2728 HttpRequestHeaders::kIfRange, etag_value); | |
2729 } else { | |
2730 custom_request_->extra_headers.SetHeader( | |
2731 HttpRequestHeaders::kIfNoneMatch, etag_value); | |
2732 } | |
2733 // For byte-range requests, make sure that we use only one way to validate | |
2734 // the request. | |
2735 if (partial_.get() && !partial_->IsCurrentRangeCached()) | |
2736 return true; | |
2737 } | |
2738 | |
2739 if (!last_modified_value.empty()) { | |
2740 if (use_if_range) { | |
2741 custom_request_->extra_headers.SetHeader( | |
2742 HttpRequestHeaders::kIfRange, last_modified_value); | |
2743 } else { | |
2744 custom_request_->extra_headers.SetHeader( | |
2745 HttpRequestHeaders::kIfModifiedSince, last_modified_value); | |
2746 } | |
2747 } | |
2748 | |
2749 return true; | |
2750 } | |
2751 | |
2752 // We just received some headers from the server. We may have asked for a range, | |
2753 // in which case partial_ has an object. This could be the first network request | |
2754 // we make to fulfill the original request, or we may be already reading (from | |
2755 // the net and / or the cache). If we are not expecting a certain response, we | |
2756 // just bypass the cache for this request (but again, maybe we are reading), and | |
2757 // delete partial_ (so we are not able to "fix" the headers that we return to | |
2758 // the user). This results in either a weird response for the caller (we don't | |
2759 // expect it after all), or maybe a range that was not exactly what it was asked | |
2760 // for. | |
2761 // | |
2762 // If the server is simply telling us that the resource has changed, we delete | |
2763 // the cached entry and restart the request as the caller intended (by returning | |
2764 // false from this method). However, we may not be able to do that at any point, | |
2765 // for instance if we already returned the headers to the user. | |
2766 // | |
2767 // WARNING: Whenever this code returns false, it has to make sure that the next | |
2768 // time it is called it will return true so that we don't keep retrying the | |
2769 // request. | |
2770 bool HttpCache::Transaction::ValidatePartialResponse() { | |
2771 const HttpResponseHeaders* headers = new_response_->headers.get(); | |
2772 int response_code = headers->response_code(); | |
2773 bool partial_response = (response_code == 206); | |
2774 handling_206_ = false; | |
2775 | |
2776 if (!entry_ || request_->method != "GET") | |
2777 return true; | |
2778 | |
2779 if (invalid_range_) { | |
2780 // We gave up trying to match this request with the stored data. If the | |
2781 // server is ok with the request, delete the entry, otherwise just ignore | |
2782 // this request | |
2783 DCHECK(!reading_); | |
2784 if (partial_response || response_code == 200) { | |
2785 DoomPartialEntry(true); | |
2786 mode_ = NONE; | |
2787 } else { | |
2788 if (response_code == 304) | |
2789 FailRangeRequest(); | |
2790 IgnoreRangeRequest(); | |
2791 } | |
2792 return true; | |
2793 } | |
2794 | |
2795 if (!partial_.get()) { | |
2796 // We are not expecting 206 but we may have one. | |
2797 if (partial_response) | |
2798 IgnoreRangeRequest(); | |
2799 | |
2800 return true; | |
2801 } | |
2802 | |
2803 // TODO(rvargas): Do we need to consider other results here?. | |
2804 bool failure = response_code == 200 || response_code == 416; | |
2805 | |
2806 if (partial_->IsCurrentRangeCached()) { | |
2807 // We asked for "If-None-Match: " so a 206 means a new object. | |
2808 if (partial_response) | |
2809 failure = true; | |
2810 | |
2811 if (response_code == 304 && partial_->ResponseHeadersOK(headers)) | |
2812 return true; | |
2813 } else { | |
2814 // We asked for "If-Range: " so a 206 means just another range. | |
2815 if (partial_response) { | |
2816 if (partial_->ResponseHeadersOK(headers)) { | |
2817 handling_206_ = true; | |
2818 return true; | |
2819 } else { | |
2820 failure = true; | |
2821 } | |
2822 } | |
2823 | |
2824 if (!reading_ && !is_sparse_ && !partial_response) { | |
2825 // See if we can ignore the fact that we issued a byte range request. | |
2826 // If the server sends 200, just store it. If it sends an error, redirect | |
2827 // or something else, we may store the response as long as we didn't have | |
2828 // anything already stored. | |
2829 if (response_code == 200 || | |
2830 (!truncated_ && response_code != 304 && response_code != 416)) { | |
2831 // The server is sending something else, and we can save it. | |
2832 DCHECK((truncated_ && !partial_->IsLastRange()) || range_requested_); | |
2833 partial_.reset(); | |
2834 truncated_ = false; | |
2835 return true; | |
2836 } | |
2837 } | |
2838 | |
2839 // 304 is not expected here, but we'll spare the entry (unless it was | |
2840 // truncated). | |
2841 if (truncated_) | |
2842 failure = true; | |
2843 } | |
2844 | |
2845 if (failure) { | |
2846 // We cannot truncate this entry, it has to be deleted. | |
2847 UpdateTransactionPattern(PATTERN_NOT_COVERED); | |
2848 mode_ = NONE; | |
2849 if (is_sparse_ || truncated_) { | |
2850 // There was something cached to start with, either sparsed data (206), or | |
2851 // a truncated 200, which means that we probably modified the request, | |
2852 // adding a byte range or modifying the range requested by the caller. | |
2853 if (!reading_ && !partial_->IsLastRange()) { | |
2854 // We have not returned anything to the caller yet so it should be safe | |
2855 // to issue another network request, this time without us messing up the | |
2856 // headers. | |
2857 ResetPartialState(true); | |
2858 return false; | |
2859 } | |
2860 LOG(WARNING) << "Failed to revalidate partial entry"; | |
2861 } | |
2862 DoomPartialEntry(true); | |
2863 return true; | |
2864 } | |
2865 | |
2866 IgnoreRangeRequest(); | |
2867 return true; | |
2868 } | |
2869 | |
2870 void HttpCache::Transaction::IgnoreRangeRequest() { | |
2871 // We have a problem. We may or may not be reading already (in which case we | |
2872 // returned the headers), but we'll just pretend that this request is not | |
2873 // using the cache and see what happens. Most likely this is the first | |
2874 // response from the server (it's not changing its mind midway, right?). | |
2875 UpdateTransactionPattern(PATTERN_NOT_COVERED); | |
2876 if (mode_ & WRITE) | |
2877 DoneWritingToEntry(mode_ != WRITE); | |
2878 else if (mode_ & READ && entry_) | |
2879 cache_->DoneReadingFromEntry(entry_, this); | |
2880 | |
2881 partial_.reset(NULL); | |
2882 entry_ = NULL; | |
2883 mode_ = NONE; | |
2884 } | |
2885 | |
2886 void HttpCache::Transaction::FixHeadersForHead() { | |
2887 if (response_.headers->response_code() == 206) { | |
2888 response_.headers->RemoveHeader("Content-Range"); | |
2889 response_.headers->ReplaceStatusLine("HTTP/1.1 200 OK"); | |
2890 } | |
2891 } | |
2892 | |
2893 void HttpCache::Transaction::TriggerAsyncValidation() { | |
2894 DCHECK(!request_->upload_data_stream); | |
2895 BoundNetLog async_revalidation_net_log( | |
2896 BoundNetLog::Make(net_log_.net_log(), NetLog::SOURCE_ASYNC_REVALIDATION)); | |
2897 net_log_.AddEvent( | |
2898 NetLog::TYPE_HTTP_CACHE_VALIDATE_RESOURCE_ASYNC, | |
2899 async_revalidation_net_log.source().ToEventParametersCallback()); | |
2900 async_revalidation_net_log.BeginEvent( | |
2901 NetLog::TYPE_ASYNC_REVALIDATION, | |
2902 base::Bind( | |
2903 &NetLogAsyncRevalidationInfoCallback, net_log_.source(), request_)); | |
2904 base::MessageLoop::current()->PostTask( | |
2905 FROM_HERE, | |
2906 base::Bind(&HttpCache::PerformAsyncValidation, | |
2907 cache_, // cache_ is a weak pointer. | |
2908 *request_, | |
2909 async_revalidation_net_log)); | |
2910 } | |
2911 | |
2912 void HttpCache::Transaction::FailRangeRequest() { | |
2913 response_ = *new_response_; | |
2914 partial_->FixResponseHeaders(response_.headers.get(), false); | |
2915 } | |
2916 | |
2917 int HttpCache::Transaction::SetupEntryForRead() { | |
2918 if (network_trans_) | |
2919 ResetNetworkTransaction(); | |
2920 if (partial_.get()) { | |
2921 if (truncated_ || is_sparse_ || !invalid_range_) { | |
2922 // We are going to return the saved response headers to the caller, so | |
2923 // we may need to adjust them first. | |
2924 next_state_ = STATE_PARTIAL_HEADERS_RECEIVED; | |
2925 return OK; | |
2926 } else { | |
2927 partial_.reset(); | |
2928 } | |
2929 } | |
2930 cache_->ConvertWriterToReader(entry_); | |
2931 mode_ = READ; | |
2932 | |
2933 if (request_->method == "HEAD") | |
2934 FixHeadersForHead(); | |
2935 | |
2936 if (entry_->disk_entry->GetDataSize(kMetadataIndex)) | |
2937 next_state_ = STATE_CACHE_READ_METADATA; | |
2938 return OK; | |
2939 } | |
2940 | |
2941 | |
2942 int HttpCache::Transaction::ReadFromNetwork(IOBuffer* data, int data_len) { | |
2943 read_buf_ = data; | |
2944 io_buf_len_ = data_len; | |
2945 next_state_ = STATE_NETWORK_READ; | |
2946 return DoLoop(OK); | |
2947 } | |
2948 | |
2949 int HttpCache::Transaction::ReadFromEntry(IOBuffer* data, int data_len) { | |
2950 if (request_->method == "HEAD") | |
2951 return 0; | |
2952 | |
2953 read_buf_ = data; | |
2954 io_buf_len_ = data_len; | |
2955 next_state_ = STATE_CACHE_READ_DATA; | |
2956 return DoLoop(OK); | |
2957 } | |
2958 | |
2959 int HttpCache::Transaction::WriteToEntry(int index, int offset, | |
2960 IOBuffer* data, int data_len, | |
2961 const CompletionCallback& callback) { | |
2962 if (!entry_) | |
2963 return data_len; | |
2964 | |
2965 int rv = 0; | |
2966 if (!partial_.get() || !data_len) { | |
2967 rv = entry_->disk_entry->WriteData(index, offset, data, data_len, callback, | |
2968 true); | |
2969 } else { | |
2970 rv = partial_->CacheWrite(entry_->disk_entry, data, data_len, callback); | |
2971 } | |
2972 return rv; | |
2973 } | |
2974 | |
2975 int HttpCache::Transaction::WriteResponseInfoToEntry(bool truncated) { | |
2976 next_state_ = STATE_CACHE_WRITE_RESPONSE_COMPLETE; | |
2977 if (!entry_) | |
2978 return OK; | |
2979 | |
2980 // Do not cache no-store content (unless we are record mode). Do not cache | |
2981 // content with cert errors either. This is to prevent not reporting net | |
2982 // errors when loading a resource from the cache. When we load a page over | |
2983 // HTTPS with a cert error we show an SSL blocking page. If the user clicks | |
2984 // proceed we reload the resource ignoring the errors. The loaded resource | |
2985 // is then cached. If that resource is subsequently loaded from the cache, | |
2986 // no net error is reported (even though the cert status contains the actual | |
2987 // errors) and no SSL blocking page is shown. An alternative would be to | |
2988 // reverse-map the cert status to a net error and replay the net error. | |
2989 if ((cache_->mode() != RECORD && | |
2990 response_.headers->HasHeaderValue("cache-control", "no-store")) || | |
2991 net::IsCertStatusError(response_.ssl_info.cert_status)) { | |
2992 DoneWritingToEntry(false); | |
2993 if (net_log_.IsLogging()) | |
2994 net_log_.EndEvent(NetLog::TYPE_HTTP_CACHE_WRITE_INFO); | |
2995 return OK; | |
2996 } | |
2997 | |
2998 // cert_cache() will be null if the CertCacheTrial field trial is disabled. | |
2999 if (cache_->cert_cache() && response_.ssl_info.is_valid()) | |
3000 WriteCertChain(); | |
3001 | |
3002 // When writing headers, we normally only write the non-transient | |
3003 // headers; when in record mode, record everything. | |
3004 bool skip_transient_headers = (cache_->mode() != RECORD); | |
3005 | |
3006 if (truncated) | |
3007 DCHECK_EQ(200, response_.headers->response_code()); | |
3008 | |
3009 scoped_refptr<PickledIOBuffer> data(new PickledIOBuffer()); | |
3010 response_.Persist(data->pickle(), skip_transient_headers, truncated); | |
3011 data->Done(); | |
3012 | |
3013 io_buf_len_ = data->pickle()->size(); | |
3014 return entry_->disk_entry->WriteData(kResponseInfoIndex, 0, data.get(), | |
3015 io_buf_len_, io_callback_, true); | |
3016 } | |
3017 | |
3018 int HttpCache::Transaction::AppendResponseDataToEntry( | |
3019 IOBuffer* data, int data_len, const CompletionCallback& callback) { | |
3020 if (!entry_ || !data_len) | |
3021 return data_len; | |
3022 | |
3023 int current_size = entry_->disk_entry->GetDataSize(kResponseContentIndex); | |
3024 return WriteToEntry(kResponseContentIndex, current_size, data, data_len, | |
3025 callback); | |
3026 } | |
3027 | |
3028 void HttpCache::Transaction::DoneWritingToEntry(bool success) { | |
3029 if (!entry_) | |
3030 return; | |
3031 | |
3032 RecordHistograms(); | |
3033 | |
3034 cache_->DoneWritingToEntry(entry_, success); | |
3035 entry_ = NULL; | |
3036 mode_ = NONE; // switch to 'pass through' mode | |
3037 } | |
3038 | |
3039 int HttpCache::Transaction::OnCacheReadError(int result, bool restart) { | |
3040 DLOG(ERROR) << "ReadData failed: " << result; | |
3041 const int result_for_histogram = std::max(0, -result); | |
3042 if (restart) { | |
3043 UMA_HISTOGRAM_SPARSE_SLOWLY("HttpCache.ReadErrorRestartable", | |
3044 result_for_histogram); | |
3045 } else { | |
3046 UMA_HISTOGRAM_SPARSE_SLOWLY("HttpCache.ReadErrorNonRestartable", | |
3047 result_for_histogram); | |
3048 } | |
3049 | |
3050 // Avoid using this entry in the future. | |
3051 if (cache_.get()) | |
3052 cache_->DoomActiveEntry(cache_key_); | |
3053 | |
3054 if (restart) { | |
3055 DCHECK(!reading_); | |
3056 DCHECK(!network_trans_.get()); | |
3057 cache_->DoneWithEntry(entry_, this, false); | |
3058 entry_ = NULL; | |
3059 is_sparse_ = false; | |
3060 partial_.reset(); | |
3061 next_state_ = STATE_GET_BACKEND; | |
3062 return OK; | |
3063 } | |
3064 | |
3065 return ERR_CACHE_READ_FAILURE; | |
3066 } | |
3067 | |
3068 void HttpCache::Transaction::OnAddToEntryTimeout(base::TimeTicks start_time) { | |
3069 if (entry_lock_waiting_since_ != start_time) | |
3070 return; | |
3071 | |
3072 DCHECK_EQ(next_state_, STATE_ADD_TO_ENTRY_COMPLETE); | |
3073 | |
3074 if (!cache_) | |
3075 return; | |
3076 | |
3077 cache_->RemovePendingTransaction(this); | |
3078 OnIOComplete(ERR_CACHE_LOCK_TIMEOUT); | |
3079 } | |
3080 | |
3081 void HttpCache::Transaction::DoomPartialEntry(bool delete_object) { | |
3082 DVLOG(2) << "DoomPartialEntry"; | |
3083 int rv = cache_->DoomEntry(cache_key_, NULL); | |
3084 DCHECK_EQ(OK, rv); | |
3085 cache_->DoneWithEntry(entry_, this, false); | |
3086 entry_ = NULL; | |
3087 is_sparse_ = false; | |
3088 truncated_ = false; | |
3089 if (delete_object) | |
3090 partial_.reset(NULL); | |
3091 } | |
3092 | |
3093 int HttpCache::Transaction::DoPartialNetworkReadCompleted(int result) { | |
3094 partial_->OnNetworkReadCompleted(result); | |
3095 | |
3096 if (result == 0) { | |
3097 // We need to move on to the next range. | |
3098 ResetNetworkTransaction(); | |
3099 next_state_ = STATE_START_PARTIAL_CACHE_VALIDATION; | |
3100 } | |
3101 return result; | |
3102 } | |
3103 | |
3104 int HttpCache::Transaction::DoPartialCacheReadCompleted(int result) { | |
3105 partial_->OnCacheReadCompleted(result); | |
3106 | |
3107 if (result == 0 && mode_ == READ_WRITE) { | |
3108 // We need to move on to the next range. | |
3109 next_state_ = STATE_START_PARTIAL_CACHE_VALIDATION; | |
3110 } else if (result < 0) { | |
3111 return OnCacheReadError(result, false); | |
3112 } | |
3113 return result; | |
3114 } | |
3115 | |
3116 int HttpCache::Transaction::DoRestartPartialRequest() { | |
3117 // The stored data cannot be used. Get rid of it and restart this request. | |
3118 net_log_.AddEvent(NetLog::TYPE_HTTP_CACHE_RESTART_PARTIAL_REQUEST); | |
3119 | |
3120 // WRITE + Doom + STATE_INIT_ENTRY == STATE_CREATE_ENTRY (without an attempt | |
3121 // to Doom the entry again). | |
3122 mode_ = WRITE; | |
3123 ResetPartialState(!range_requested_); | |
3124 next_state_ = STATE_CREATE_ENTRY; | |
3125 return OK; | |
3126 } | |
3127 | |
3128 void HttpCache::Transaction::ResetPartialState(bool delete_object) { | |
3129 partial_->RestoreHeaders(&custom_request_->extra_headers); | |
3130 DoomPartialEntry(delete_object); | |
3131 | |
3132 if (!delete_object) { | |
3133 // The simplest way to re-initialize partial_ is to create a new object. | |
3134 partial_.reset(new PartialData()); | |
3135 if (partial_->Init(request_->extra_headers)) | |
3136 partial_->SetHeaders(custom_request_->extra_headers); | |
3137 else | |
3138 partial_.reset(); | |
3139 } | |
3140 } | |
3141 | |
3142 void HttpCache::Transaction::ResetNetworkTransaction() { | |
3143 DCHECK(!old_network_trans_load_timing_); | |
3144 DCHECK(network_trans_); | |
3145 LoadTimingInfo load_timing; | |
3146 if (network_trans_->GetLoadTimingInfo(&load_timing)) | |
3147 old_network_trans_load_timing_.reset(new LoadTimingInfo(load_timing)); | |
3148 total_received_bytes_ += network_trans_->GetTotalReceivedBytes(); | |
3149 network_trans_.reset(); | |
3150 } | |
3151 | |
3152 // Histogram data from the end of 2010 show the following distribution of | |
3153 // response headers: | |
3154 // | |
3155 // Content-Length............... 87% | |
3156 // Date......................... 98% | |
3157 // Last-Modified................ 49% | |
3158 // Etag......................... 19% | |
3159 // Accept-Ranges: bytes......... 25% | |
3160 // Accept-Ranges: none.......... 0.4% | |
3161 // Strong Validator............. 50% | |
3162 // Strong Validator + ranges.... 24% | |
3163 // Strong Validator + CL........ 49% | |
3164 // | |
3165 bool HttpCache::Transaction::CanResume(bool has_data) { | |
3166 // Double check that there is something worth keeping. | |
3167 if (has_data && !entry_->disk_entry->GetDataSize(kResponseContentIndex)) | |
3168 return false; | |
3169 | |
3170 if (request_->method != "GET") | |
3171 return false; | |
3172 | |
3173 // Note that if this is a 206, content-length was already fixed after calling | |
3174 // PartialData::ResponseHeadersOK(). | |
3175 if (response_.headers->GetContentLength() <= 0 || | |
3176 response_.headers->HasHeaderValue("Accept-Ranges", "none") || | |
3177 !response_.headers->HasStrongValidators()) { | |
3178 return false; | |
3179 } | |
3180 | |
3181 return true; | |
3182 } | |
3183 | |
3184 void HttpCache::Transaction::UpdateTransactionPattern( | |
3185 TransactionPattern new_transaction_pattern) { | |
3186 if (transaction_pattern_ == PATTERN_NOT_COVERED) | |
3187 return; | |
3188 DCHECK(transaction_pattern_ == PATTERN_UNDEFINED || | |
3189 new_transaction_pattern == PATTERN_NOT_COVERED); | |
3190 transaction_pattern_ = new_transaction_pattern; | |
3191 } | |
3192 | |
3193 void HttpCache::Transaction::RecordHistograms() { | |
3194 DCHECK_NE(PATTERN_UNDEFINED, transaction_pattern_); | |
3195 if (!cache_.get() || !cache_->GetCurrentBackend() || | |
3196 cache_->GetCurrentBackend()->GetCacheType() != DISK_CACHE || | |
3197 cache_->mode() != NORMAL || request_->method != "GET") { | |
3198 return; | |
3199 } | |
3200 UMA_HISTOGRAM_ENUMERATION( | |
3201 "HttpCache.Pattern", transaction_pattern_, PATTERN_MAX); | |
3202 if (transaction_pattern_ == PATTERN_NOT_COVERED) | |
3203 return; | |
3204 DCHECK(!range_requested_); | |
3205 DCHECK(!first_cache_access_since_.is_null()); | |
3206 | |
3207 TimeDelta total_time = base::TimeTicks::Now() - first_cache_access_since_; | |
3208 | |
3209 UMA_HISTOGRAM_TIMES("HttpCache.AccessToDone", total_time); | |
3210 | |
3211 bool did_send_request = !send_request_since_.is_null(); | |
3212 DCHECK( | |
3213 (did_send_request && | |
3214 (transaction_pattern_ == PATTERN_ENTRY_NOT_CACHED || | |
3215 transaction_pattern_ == PATTERN_ENTRY_VALIDATED || | |
3216 transaction_pattern_ == PATTERN_ENTRY_UPDATED || | |
3217 transaction_pattern_ == PATTERN_ENTRY_CANT_CONDITIONALIZE)) || | |
3218 (!did_send_request && transaction_pattern_ == PATTERN_ENTRY_USED)); | |
3219 | |
3220 if (!did_send_request) { | |
3221 DCHECK(transaction_pattern_ == PATTERN_ENTRY_USED); | |
3222 UMA_HISTOGRAM_TIMES("HttpCache.AccessToDone.Used", total_time); | |
3223 return; | |
3224 } | |
3225 | |
3226 TimeDelta before_send_time = send_request_since_ - first_cache_access_since_; | |
3227 int64 before_send_percent = (total_time.ToInternalValue() == 0) ? | |
3228 0 : before_send_time * 100 / total_time; | |
3229 DCHECK_GE(before_send_percent, 0); | |
3230 DCHECK_LE(before_send_percent, 100); | |
3231 base::HistogramBase::Sample before_send_sample = | |
3232 static_cast<base::HistogramBase::Sample>(before_send_percent); | |
3233 | |
3234 UMA_HISTOGRAM_TIMES("HttpCache.AccessToDone.SentRequest", total_time); | |
3235 UMA_HISTOGRAM_TIMES("HttpCache.BeforeSend", before_send_time); | |
3236 UMA_HISTOGRAM_PERCENTAGE("HttpCache.PercentBeforeSend", before_send_sample); | |
3237 | |
3238 // TODO(gavinp): Remove or minimize these histograms, particularly the ones | |
3239 // below this comment after we have received initial data. | |
3240 switch (transaction_pattern_) { | |
3241 case PATTERN_ENTRY_CANT_CONDITIONALIZE: { | |
3242 UMA_HISTOGRAM_TIMES("HttpCache.BeforeSend.CantConditionalize", | |
3243 before_send_time); | |
3244 UMA_HISTOGRAM_PERCENTAGE("HttpCache.PercentBeforeSend.CantConditionalize", | |
3245 before_send_sample); | |
3246 break; | |
3247 } | |
3248 case PATTERN_ENTRY_NOT_CACHED: { | |
3249 UMA_HISTOGRAM_TIMES("HttpCache.BeforeSend.NotCached", before_send_time); | |
3250 UMA_HISTOGRAM_PERCENTAGE("HttpCache.PercentBeforeSend.NotCached", | |
3251 before_send_sample); | |
3252 break; | |
3253 } | |
3254 case PATTERN_ENTRY_VALIDATED: { | |
3255 UMA_HISTOGRAM_TIMES("HttpCache.BeforeSend.Validated", before_send_time); | |
3256 UMA_HISTOGRAM_PERCENTAGE("HttpCache.PercentBeforeSend.Validated", | |
3257 before_send_sample); | |
3258 break; | |
3259 } | |
3260 case PATTERN_ENTRY_UPDATED: { | |
3261 UMA_HISTOGRAM_TIMES("HttpCache.BeforeSend.Updated", before_send_time); | |
3262 UMA_HISTOGRAM_PERCENTAGE("HttpCache.PercentBeforeSend.Updated", | |
3263 before_send_sample); | |
3264 break; | |
3265 } | |
3266 default: | |
3267 NOTREACHED(); | |
3268 } | |
3269 } | |
3270 | |
3271 void HttpCache::Transaction::OnIOComplete(int result) { | |
3272 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
3273 tracked_objects::ScopedTracker tracking_profile( | |
3274 FROM_HERE_WITH_EXPLICIT_FUNCTION("422516 Transaction::OnIOComplete")); | |
3275 | |
3276 DoLoop(result); | |
3277 } | |
3278 | |
3279 } // namespace net | |
OLD | NEW |