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.
5 #include "net/http/http_cache_transaction.h"
7 #include "build/build_config.h"
15 #include "base/bind.h"
16 #include "base/compiler_specific.h"
17 #include "base/memory/ref_counted.h"
18 #include "base/metrics/field_trial.h"
19 #include "base/metrics/histogram.h"
20 #include "base/string_util.h"
21 #include "base/time.h"
22 #include "net/base/cert_status_flags.h"
23 #include "net/base/completion_callback.h"
24 #include "net/base/io_buffer.h"
25 #include "net/base/load_flags.h"
26 #include "net/base/net_errors.h"
27 #include "net/base/net_log.h"
28 #include "net/base/ssl_cert_request_info.h"
29 #include "net/base/ssl_config_service.h"
30 #include "net/disk_cache/disk_cache.h"
31 #include "net/http/http_network_session.h"
32 #include "net/http/http_request_info.h"
33 #include "net/http/http_response_headers.h"
34 #include "net/http/http_transaction_delegate.h"
35 #include "net/http/http_transaction.h"
36 #include "net/http/http_util.h"
37 #include "net/http/partial_data.h"
40 using base::TimeDelta
;
41 using base::TimeTicks
;
45 // The cutoff for tagging small transactions in histograms; this size was chosen
46 // to cover resources likely to be received in a single TCP window. With an
47 // initial CWND of 10, and an MTU of 1500 bytes, with TCP and HTTP framing
48 // overhead this is a size relatively likely to take only one RTT.
49 const int kSmallResourceMaxBytes
= 14 * 1024;
55 struct HeaderNameAndValue
{
60 // If the request includes one of these request headers, then avoid caching
61 // to avoid getting confused.
62 static const HeaderNameAndValue kPassThroughHeaders
[] = {
63 { "if-unmodified-since", NULL
}, // causes unexpected 412s
64 { "if-match", NULL
}, // causes unexpected 412s
69 struct ValidationHeaderInfo
{
70 const char* request_header_name
;
71 const char* related_response_header_name
;
74 static const ValidationHeaderInfo kValidationHeaders
[] = {
75 { "if-modified-since", "last-modified" },
76 { "if-none-match", "etag" },
79 // If the request includes one of these request headers, then avoid reusing
80 // our cached copy if any.
81 static const HeaderNameAndValue kForceFetchHeaders
[] = {
82 { "cache-control", "no-cache" },
83 { "pragma", "no-cache" },
87 // If the request includes one of these request headers, then force our
88 // cached copy (if any) to be revalidated before reusing it.
89 static const HeaderNameAndValue kForceValidateHeaders
[] = {
90 { "cache-control", "max-age=0" },
94 static bool HeaderMatches(const HttpRequestHeaders
& headers
,
95 const HeaderNameAndValue
* search
) {
96 for (; search
->name
; ++search
) {
97 std::string header_value
;
98 if (!headers
.GetHeader(search
->name
, &header_value
))
104 HttpUtil::ValuesIterator
v(header_value
.begin(), header_value
.end(), ',');
105 while (v
.GetNext()) {
106 if (LowerCaseEqualsASCII(v
.value_begin(), v
.value_end(), search
->value
))
113 //-----------------------------------------------------------------------------
115 HttpCache::Transaction::Transaction(
117 HttpTransactionDelegate
* transaction_delegate
)
118 : next_state_(STATE_NONE
),
120 cache_(cache
->AsWeakPtr()),
123 network_trans_(NULL
),
126 target_state_(STATE_NONE
),
128 invalid_range_(false),
131 range_requested_(false),
132 handling_206_(false),
133 cache_pending_(false),
134 done_reading_(false),
136 effective_load_flags_(0),
138 final_upload_progress_(0),
139 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)),
140 ALLOW_THIS_IN_INITIALIZER_LIST(io_callback_(
141 base::Bind(&Transaction::OnIOComplete
,
142 weak_factory_
.GetWeakPtr()))),
143 transaction_pattern_(PATTERN_UNDEFINED
),
144 bytes_read_from_cache_(0),
145 bytes_read_from_network_(0),
146 transaction_delegate_(transaction_delegate
) {
147 COMPILE_ASSERT(HttpCache::Transaction::kNumValidationHeaders
==
148 arraysize(kValidationHeaders
),
149 Invalid_number_of_validation_headers
);
152 HttpCache::Transaction::~Transaction() {
153 // We may have to issue another IO, but we should never invoke the callback_
159 bool cancel_request
= reading_
;
160 if (cancel_request
) {
161 if (partial_
.get()) {
162 entry_
->disk_entry
->CancelSparseIO();
164 cancel_request
&= (response_
.headers
->response_code() == 200);
168 cache_
->DoneWithEntry(entry_
, this, cancel_request
);
169 } else if (cache_pending_
) {
170 cache_
->RemovePendingTransaction(this);
174 // Cancel any outstanding callbacks before we drop our reference to the
175 // HttpCache. This probably isn't strictly necessary, but might as well.
176 weak_factory_
.InvalidateWeakPtrs();
178 // We could still have a cache read or write in progress, so we just null the
179 // cache_ pointer to signal that we are dead. See DoCacheReadCompleted.
183 int HttpCache::Transaction::WriteMetadata(IOBuffer
* buf
, int buf_len
,
184 const CompletionCallback
& callback
) {
186 DCHECK_GT(buf_len
, 0);
187 DCHECK(!callback
.is_null());
188 if (!cache_
|| !entry_
)
189 return ERR_UNEXPECTED
;
191 // We don't need to track this operation for anything.
192 // It could be possible to check if there is something already written and
193 // avoid writing again (it should be the same, right?), but let's allow the
194 // caller to "update" the contents with something new.
195 return entry_
->disk_entry
->WriteData(kMetadataIndex
, 0, buf
, buf_len
,
199 bool HttpCache::Transaction::AddTruncatedFlag() {
200 DCHECK(mode_
& WRITE
|| mode_
== NONE
);
202 // Don't set the flag for sparse entries.
203 if (partial_
.get() && !truncated_
)
206 if (!CanResume(true))
209 // We may have received the whole resource already.
214 target_state_
= STATE_NONE
;
215 next_state_
= STATE_CACHE_WRITE_TRUNCATED_RESPONSE
;
220 LoadState
HttpCache::Transaction::GetWriterLoadState() const {
221 if (network_trans_
.get())
222 return network_trans_
->GetLoadState();
223 if (entry_
|| !request_
)
224 return LOAD_STATE_IDLE
;
225 return LOAD_STATE_WAITING_FOR_CACHE
;
228 const BoundNetLog
& HttpCache::Transaction::net_log() const {
232 int HttpCache::Transaction::Start(const HttpRequestInfo
* request
,
233 const CompletionCallback
& callback
,
234 const BoundNetLog
& net_log
) {
236 DCHECK(!callback
.is_null());
238 // Ensure that we only have one asynchronous call at a time.
239 DCHECK(callback_
.is_null());
241 DCHECK(!network_trans_
.get());
245 return ERR_UNEXPECTED
;
247 SetRequest(net_log
, request
);
249 // We have to wait until the backend is initialized so we start the SM.
250 next_state_
= STATE_GET_BACKEND
;
253 // Setting this here allows us to check for the existence of a callback_ to
254 // determine if we are still inside Start.
255 if (rv
== ERR_IO_PENDING
)
256 callback_
= callback
;
261 int HttpCache::Transaction::RestartIgnoringLastError(
262 const CompletionCallback
& callback
) {
263 DCHECK(!callback
.is_null());
265 // Ensure that we only have one asynchronous call at a time.
266 DCHECK(callback_
.is_null());
269 return ERR_UNEXPECTED
;
271 int rv
= RestartNetworkRequest();
273 if (rv
== ERR_IO_PENDING
)
274 callback_
= callback
;
279 int HttpCache::Transaction::RestartWithCertificate(
280 X509Certificate
* client_cert
,
281 const CompletionCallback
& callback
) {
282 DCHECK(!callback
.is_null());
284 // Ensure that we only have one asynchronous call at a time.
285 DCHECK(callback_
.is_null());
288 return ERR_UNEXPECTED
;
290 int rv
= RestartNetworkRequestWithCertificate(client_cert
);
292 if (rv
== ERR_IO_PENDING
)
293 callback_
= callback
;
298 int HttpCache::Transaction::RestartWithAuth(
299 const AuthCredentials
& credentials
,
300 const CompletionCallback
& callback
) {
301 DCHECK(auth_response_
.headers
);
302 DCHECK(!callback
.is_null());
304 // Ensure that we only have one asynchronous call at a time.
305 DCHECK(callback_
.is_null());
308 return ERR_UNEXPECTED
;
310 // Clear the intermediate response since we are going to start over.
311 auth_response_
= HttpResponseInfo();
313 int rv
= RestartNetworkRequestWithAuth(credentials
);
315 if (rv
== ERR_IO_PENDING
)
316 callback_
= callback
;
321 bool HttpCache::Transaction::IsReadyToRestartForAuth() {
322 if (!network_trans_
.get())
324 return network_trans_
->IsReadyToRestartForAuth();
327 int HttpCache::Transaction::Read(IOBuffer
* buf
, int buf_len
,
328 const CompletionCallback
& callback
) {
330 DCHECK_GT(buf_len
, 0);
331 DCHECK(!callback
.is_null());
333 DCHECK(callback_
.is_null());
336 return ERR_UNEXPECTED
;
338 // If we have an intermediate auth response at this point, then it means the
339 // user wishes to read the network response (the error page). If there is a
340 // previous response in the cache then we should leave it intact.
341 if (auth_response_
.headers
&& mode_
!= NONE
) {
342 UpdateTransactionPattern(PATTERN_NOT_COVERED
);
343 DCHECK(mode_
& WRITE
);
344 DoneWritingToEntry(mode_
== READ_WRITE
);
353 DCHECK(partial_
.get());
354 if (!network_trans_
.get()) {
355 // We are just reading from the cache, but we may be writing later.
356 rv
= ReadFromEntry(buf
, buf_len
);
361 DCHECK(network_trans_
.get());
362 rv
= ReadFromNetwork(buf
, buf_len
);
365 rv
= ReadFromEntry(buf
, buf_len
);
372 if (rv
== ERR_IO_PENDING
) {
373 DCHECK(callback_
.is_null());
374 callback_
= callback
;
379 void HttpCache::Transaction::StopCaching() {
380 // We really don't know where we are now. Hopefully there is no operation in
381 // progress, but nothing really prevents this method to be called after we
382 // returned ERR_IO_PENDING. We cannot attempt to truncate the entry at this
383 // point because we need the state machine for that (and even if we are really
384 // free, that would be an asynchronous operation). In other words, keep the
385 // entry how it is (it will be marked as truncated at destruction), and let
386 // the next piece of code that executes know that we are now reading directly
388 if (cache_
&& entry_
&& (mode_
& WRITE
) && network_trans_
.get() &&
389 !is_sparse_
&& !range_requested_
)
393 void HttpCache::Transaction::DoneReading() {
394 if (cache_
&& entry_
) {
396 DCHECK_NE(mode_
, UPDATE
);
398 DoneWritingToEntry(true);
402 const HttpResponseInfo
* HttpCache::Transaction::GetResponseInfo() const {
403 // Null headers means we encountered an error or haven't a response yet
404 if (auth_response_
.headers
)
405 return &auth_response_
;
406 return (response_
.headers
|| response_
.ssl_info
.cert
||
407 response_
.cert_request_info
) ? &response_
: NULL
;
410 LoadState
HttpCache::Transaction::GetLoadState() const {
411 LoadState state
= GetWriterLoadState();
412 if (state
!= LOAD_STATE_WAITING_FOR_CACHE
)
416 return cache_
->GetLoadStateForPendingTransaction(this);
418 return LOAD_STATE_IDLE
;
421 uint64
HttpCache::Transaction::GetUploadProgress() const {
422 if (network_trans_
.get())
423 return network_trans_
->GetUploadProgress();
424 return final_upload_progress_
;
427 //-----------------------------------------------------------------------------
429 void HttpCache::Transaction::DoCallback(int rv
) {
430 DCHECK(rv
!= ERR_IO_PENDING
);
431 DCHECK(!callback_
.is_null());
433 // Since Run may result in Read being called, clear callback_ up front.
434 CompletionCallback c
= callback_
;
439 int HttpCache::Transaction::HandleResult(int rv
) {
440 DCHECK(rv
!= ERR_IO_PENDING
);
441 if (!callback_
.is_null())
447 // A few common patterns: (Foo* means Foo -> FooComplete)
451 // GetBackend* -> InitEntry -> OpenEntry* -> CreateEntry* -> AddToEntry* ->
452 // SendRequest* -> SuccessfulSendRequest -> OverwriteCachedResponse ->
453 // CacheWriteResponse* -> TruncateCachedData* -> TruncateCachedMetadata* ->
454 // PartialHeadersReceived
457 // NetworkRead* -> CacheWriteData*
459 // Cached entry, no validation:
461 // GetBackend* -> InitEntry -> OpenEntry* -> AddToEntry* -> CacheReadResponse*
462 // -> BeginPartialCacheValidation() -> BeginCacheValidation()
467 // Cached entry, validation (304):
469 // GetBackend* -> InitEntry -> OpenEntry* -> AddToEntry* -> CacheReadResponse*
470 // -> BeginPartialCacheValidation() -> BeginCacheValidation() ->
471 // SendRequest* -> SuccessfulSendRequest -> UpdateCachedResponse ->
472 // CacheWriteResponse* -> UpdateCachedResponseComplete ->
473 // OverwriteCachedResponse -> PartialHeadersReceived
478 // Cached entry, validation and replace (200):
480 // GetBackend* -> InitEntry -> OpenEntry* -> AddToEntry* -> CacheReadResponse*
481 // -> BeginPartialCacheValidation() -> BeginCacheValidation() ->
482 // SendRequest* -> SuccessfulSendRequest -> OverwriteCachedResponse ->
483 // CacheWriteResponse* -> DoTruncateCachedData* -> TruncateCachedMetadata* ->
484 // PartialHeadersReceived
487 // NetworkRead* -> CacheWriteData*
489 // Sparse entry, partially cached, byte range request:
491 // GetBackend* -> InitEntry -> OpenEntry* -> AddToEntry* -> CacheReadResponse*
492 // -> BeginPartialCacheValidation() -> CacheQueryData* ->
493 // ValidateEntryHeadersAndContinue() -> StartPartialCacheValidation ->
494 // CompletePartialCacheValidation -> BeginCacheValidation() -> SendRequest* ->
495 // SuccessfulSendRequest -> UpdateCachedResponse -> CacheWriteResponse* ->
496 // UpdateCachedResponseComplete -> OverwriteCachedResponse ->
497 // PartialHeadersReceived
500 // NetworkRead* -> CacheWriteData*
503 // NetworkRead* -> CacheWriteData* -> StartPartialCacheValidation ->
504 // CompletePartialCacheValidation -> CacheReadData* ->
507 // CacheReadData* -> StartPartialCacheValidation ->
508 // CompletePartialCacheValidation -> BeginCacheValidation() -> SendRequest* ->
509 // SuccessfulSendRequest -> UpdateCachedResponse* -> OverwriteCachedResponse
510 // -> PartialHeadersReceived -> NetworkRead* -> CacheWriteData*
512 int HttpCache::Transaction::DoLoop(int result
) {
513 DCHECK(next_state_
!= STATE_NONE
);
517 State state
= next_state_
;
518 next_state_
= STATE_NONE
;
520 case STATE_GET_BACKEND
:
524 case STATE_GET_BACKEND_COMPLETE
:
525 rv
= DoGetBackendComplete(rv
);
527 case STATE_SEND_REQUEST
:
529 rv
= DoSendRequest();
531 case STATE_SEND_REQUEST_COMPLETE
:
532 rv
= DoSendRequestComplete(rv
);
534 case STATE_SUCCESSFUL_SEND_REQUEST
:
536 rv
= DoSuccessfulSendRequest();
538 case STATE_NETWORK_READ
:
540 rv
= DoNetworkRead();
542 case STATE_NETWORK_READ_COMPLETE
:
543 rv
= DoNetworkReadComplete(rv
);
545 case STATE_INIT_ENTRY
:
549 case STATE_OPEN_ENTRY
:
553 case STATE_OPEN_ENTRY_COMPLETE
:
554 rv
= DoOpenEntryComplete(rv
);
556 case STATE_CREATE_ENTRY
:
558 rv
= DoCreateEntry();
560 case STATE_CREATE_ENTRY_COMPLETE
:
561 rv
= DoCreateEntryComplete(rv
);
563 case STATE_DOOM_ENTRY
:
567 case STATE_DOOM_ENTRY_COMPLETE
:
568 rv
= DoDoomEntryComplete(rv
);
570 case STATE_ADD_TO_ENTRY
:
574 case STATE_ADD_TO_ENTRY_COMPLETE
:
575 rv
= DoAddToEntryComplete(rv
);
577 case STATE_START_PARTIAL_CACHE_VALIDATION
:
579 rv
= DoStartPartialCacheValidation();
581 case STATE_COMPLETE_PARTIAL_CACHE_VALIDATION
:
582 rv
= DoCompletePartialCacheValidation(rv
);
584 case STATE_UPDATE_CACHED_RESPONSE
:
586 rv
= DoUpdateCachedResponse();
588 case STATE_UPDATE_CACHED_RESPONSE_COMPLETE
:
589 rv
= DoUpdateCachedResponseComplete(rv
);
591 case STATE_OVERWRITE_CACHED_RESPONSE
:
593 rv
= DoOverwriteCachedResponse();
595 case STATE_TRUNCATE_CACHED_DATA
:
597 rv
= DoTruncateCachedData();
599 case STATE_TRUNCATE_CACHED_DATA_COMPLETE
:
600 rv
= DoTruncateCachedDataComplete(rv
);
602 case STATE_TRUNCATE_CACHED_METADATA
:
604 rv
= DoTruncateCachedMetadata();
606 case STATE_TRUNCATE_CACHED_METADATA_COMPLETE
:
607 rv
= DoTruncateCachedMetadataComplete(rv
);
609 case STATE_PARTIAL_HEADERS_RECEIVED
:
611 rv
= DoPartialHeadersReceived();
613 case STATE_CACHE_READ_RESPONSE
:
615 rv
= DoCacheReadResponse();
617 case STATE_CACHE_READ_RESPONSE_COMPLETE
:
618 rv
= DoCacheReadResponseComplete(rv
);
620 case STATE_CACHE_WRITE_RESPONSE
:
622 rv
= DoCacheWriteResponse();
624 case STATE_CACHE_WRITE_TRUNCATED_RESPONSE
:
626 rv
= DoCacheWriteTruncatedResponse();
628 case STATE_CACHE_WRITE_RESPONSE_COMPLETE
:
629 rv
= DoCacheWriteResponseComplete(rv
);
631 case STATE_CACHE_READ_METADATA
:
633 rv
= DoCacheReadMetadata();
635 case STATE_CACHE_READ_METADATA_COMPLETE
:
636 rv
= DoCacheReadMetadataComplete(rv
);
638 case STATE_CACHE_QUERY_DATA
:
640 rv
= DoCacheQueryData();
642 case STATE_CACHE_QUERY_DATA_COMPLETE
:
643 rv
= DoCacheQueryDataComplete(rv
);
645 case STATE_CACHE_READ_DATA
:
647 rv
= DoCacheReadData();
649 case STATE_CACHE_READ_DATA_COMPLETE
:
650 rv
= DoCacheReadDataComplete(rv
);
652 case STATE_CACHE_WRITE_DATA
:
653 rv
= DoCacheWriteData(rv
);
655 case STATE_CACHE_WRITE_DATA_COMPLETE
:
656 rv
= DoCacheWriteDataComplete(rv
);
659 NOTREACHED() << "bad state";
663 } while (rv
!= ERR_IO_PENDING
&& next_state_
!= STATE_NONE
);
665 if (rv
!= ERR_IO_PENDING
)
671 int HttpCache::Transaction::DoGetBackend() {
672 cache_pending_
= true;
673 next_state_
= STATE_GET_BACKEND_COMPLETE
;
674 net_log_
.BeginEvent(NetLog::TYPE_HTTP_CACHE_GET_BACKEND
);
675 ReportCacheActionStart();
676 return cache_
->GetBackendForTransaction(this);
679 int HttpCache::Transaction::DoGetBackendComplete(int result
) {
680 DCHECK(result
== OK
|| result
== ERR_FAILED
);
681 ReportCacheActionFinish();
682 net_log_
.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_GET_BACKEND
,
684 cache_pending_
= false;
686 if (!ShouldPassThrough()) {
687 cache_key_
= cache_
->GenerateCacheKey(request_
);
689 // Requested cache access mode.
690 if (effective_load_flags_
& LOAD_ONLY_FROM_CACHE
) {
692 } else if (effective_load_flags_
& LOAD_BYPASS_CACHE
) {
698 // Downgrade to UPDATE if the request has been externally conditionalized.
699 if (external_validation_
.initialized
) {
701 // Strip off the READ_DATA bit (and maybe add back a READ_META bit
702 // in case READ was off).
710 // Use PUT and DELETE only to invalidate existing stored entries.
711 if ((request_
->method
== "PUT" || request_
->method
== "DELETE") &&
712 mode_
!= READ_WRITE
&& mode_
!= WRITE
) {
716 // If must use cache, then we must fail. This can happen for back/forward
717 // navigations to a page generated via a form post.
718 if (!(mode_
& READ
) && effective_load_flags_
& LOAD_ONLY_FROM_CACHE
)
719 return ERR_CACHE_MISS
;
722 if (partial_
.get()) {
723 partial_
->RestoreHeaders(&custom_request_
->extra_headers
);
726 next_state_
= STATE_SEND_REQUEST
;
728 next_state_
= STATE_INIT_ENTRY
;
731 // This is only set if we have something to do with the response.
732 range_requested_
= (partial_
.get() != NULL
);
737 int HttpCache::Transaction::DoSendRequest() {
738 DCHECK(mode_
& WRITE
|| mode_
== NONE
);
739 DCHECK(!network_trans_
.get());
741 send_request_since_
= TimeTicks::Now();
743 // Create a network transaction.
744 int rv
= cache_
->network_layer_
->CreateTransaction(&network_trans_
, NULL
);
748 next_state_
= STATE_SEND_REQUEST_COMPLETE
;
749 rv
= network_trans_
->Start(request_
, io_callback_
, net_log_
);
753 int HttpCache::Transaction::DoSendRequestComplete(int result
) {
755 return ERR_UNEXPECTED
;
758 next_state_
= STATE_SUCCESSFUL_SEND_REQUEST
;
762 // Do not record requests that have network errors or restarts.
763 UpdateTransactionPattern(PATTERN_NOT_COVERED
);
764 if (IsCertificateError(result
)) {
765 const HttpResponseInfo
* response
= network_trans_
->GetResponseInfo();
766 // If we get a certificate error, then there is a certificate in ssl_info,
767 // so GetResponseInfo() should never return NULL here.
769 response_
.ssl_info
= response
->ssl_info
;
770 } else if (result
== ERR_SSL_CLIENT_AUTH_CERT_NEEDED
) {
771 const HttpResponseInfo
* response
= network_trans_
->GetResponseInfo();
773 response_
.cert_request_info
= response
->cert_request_info
;
778 // We received the response headers and there is no error.
779 int HttpCache::Transaction::DoSuccessfulSendRequest() {
780 DCHECK(!new_response_
);
781 const HttpResponseInfo
* new_response
= network_trans_
->GetResponseInfo();
782 if (new_response
->headers
->response_code() == 401 ||
783 new_response
->headers
->response_code() == 407) {
784 auth_response_
= *new_response
;
788 new_response_
= new_response
;
789 if (!ValidatePartialResponse() && !auth_response_
.headers
) {
790 // Something went wrong with this request and we have to restart it.
791 // If we have an authentication response, we are exposed to weird things
792 // hapenning if the user cancels the authentication before we receive
794 UpdateTransactionPattern(PATTERN_NOT_COVERED
);
795 response_
= HttpResponseInfo();
796 network_trans_
.reset();
797 new_response_
= NULL
;
798 next_state_
= STATE_SEND_REQUEST
;
801 bytes_read_from_network_
+= new_response_
->headers
->raw_headers().size();
802 if (handling_206_
&& mode_
== READ_WRITE
&& !truncated_
&& !is_sparse_
) {
803 // We have stored the full entry, but it changed and the server is
804 // sending a range. We have to delete the old entry.
805 UpdateTransactionPattern(PATTERN_NOT_COVERED
);
806 DoneWritingToEntry(false);
808 if (new_response_
->headers
->response_code() == 416) {
809 DCHECK_EQ(NONE
, mode_
);
810 response_
= *new_response_
;
815 UpdateTransactionPattern(PATTERN_ENTRY_NOT_CACHED
);
817 if (mode_
== WRITE
&&
818 (request_
->method
== "PUT" || request_
->method
== "DELETE")) {
819 if (new_response
->headers
->response_code() == 200) {
820 int ret
= cache_
->DoomEntry(cache_key_
, NULL
);
826 // Are we expecting a response to a conditional query?
827 if (mode_
== READ_WRITE
|| mode_
== UPDATE
) {
828 if (new_response
->headers
->response_code() == 304 || handling_206_
) {
829 UpdateTransactionPattern(PATTERN_ENTRY_VALIDATED
);
830 next_state_
= STATE_UPDATE_CACHED_RESPONSE
;
833 UpdateTransactionPattern(PATTERN_ENTRY_UPDATED
);
837 next_state_
= STATE_OVERWRITE_CACHED_RESPONSE
;
841 int HttpCache::Transaction::DoNetworkRead() {
842 next_state_
= STATE_NETWORK_READ_COMPLETE
;
843 return network_trans_
->Read(read_buf_
, io_buf_len_
, io_callback_
);
846 int HttpCache::Transaction::DoNetworkReadComplete(int result
) {
847 DCHECK(mode_
& WRITE
|| mode_
== NONE
);
850 return ERR_UNEXPECTED
;
853 bytes_read_from_network_
+= result
;
855 // If there is an error or we aren't saving the data, we are done; just wait
856 // until the destructor runs to see if we can keep the data.
857 if (mode_
== NONE
|| result
< 0)
860 next_state_
= STATE_CACHE_WRITE_DATA
;
864 int HttpCache::Transaction::DoInitEntry() {
868 return ERR_UNEXPECTED
;
870 if (mode_
== WRITE
) {
871 next_state_
= STATE_DOOM_ENTRY
;
875 next_state_
= STATE_OPEN_ENTRY
;
879 int HttpCache::Transaction::DoOpenEntry() {
881 next_state_
= STATE_OPEN_ENTRY_COMPLETE
;
882 cache_pending_
= true;
883 net_log_
.BeginEvent(NetLog::TYPE_HTTP_CACHE_OPEN_ENTRY
);
884 first_cache_access_since_
= TimeTicks::Now();
885 ReportCacheActionStart();
886 return cache_
->OpenEntry(cache_key_
, &new_entry_
, this);
889 int HttpCache::Transaction::DoOpenEntryComplete(int result
) {
890 // It is important that we go to STATE_ADD_TO_ENTRY whenever the result is
891 // OK, otherwise the cache will end up with an active entry without any
892 // transaction attached.
893 ReportCacheActionFinish();
894 net_log_
.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_OPEN_ENTRY
, result
);
895 cache_pending_
= false;
897 next_state_
= STATE_ADD_TO_ENTRY
;
901 if (result
== ERR_CACHE_RACE
) {
902 next_state_
= STATE_INIT_ENTRY
;
906 if (request_
->method
== "PUT" || request_
->method
== "DELETE") {
907 DCHECK(mode_
== READ_WRITE
|| mode_
== WRITE
);
909 next_state_
= STATE_SEND_REQUEST
;
913 if (mode_
== READ_WRITE
) {
915 next_state_
= STATE_CREATE_ENTRY
;
918 if (mode_
== UPDATE
) {
919 // There is no cache entry to update; proceed without caching.
921 next_state_
= STATE_SEND_REQUEST
;
924 if (cache_
->mode() == PLAYBACK
)
925 DVLOG(1) << "Playback Cache Miss: " << request_
->url
;
927 // The entry does not exist, and we are not permitted to create a new entry,
929 return ERR_CACHE_MISS
;
932 int HttpCache::Transaction::DoCreateEntry() {
934 next_state_
= STATE_CREATE_ENTRY_COMPLETE
;
935 cache_pending_
= true;
936 net_log_
.BeginEvent(NetLog::TYPE_HTTP_CACHE_CREATE_ENTRY
);
937 ReportCacheActionStart();
938 return cache_
->CreateEntry(cache_key_
, &new_entry_
, this);
941 int HttpCache::Transaction::DoCreateEntryComplete(int result
) {
942 // It is important that we go to STATE_ADD_TO_ENTRY whenever the result is
943 // OK, otherwise the cache will end up with an active entry without any
944 // transaction attached.
945 ReportCacheActionFinish();
946 net_log_
.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_CREATE_ENTRY
,
948 cache_pending_
= false;
949 next_state_
= STATE_ADD_TO_ENTRY
;
951 if (result
== ERR_CACHE_RACE
) {
952 next_state_
= STATE_INIT_ENTRY
;
957 // We have a race here: Maybe we failed to open the entry and decided to
958 // create one, but by the time we called create, another transaction already
959 // created the entry. If we want to eliminate this issue, we need an atomic
960 // OpenOrCreate() method exposed by the disk cache.
961 DLOG(WARNING
) << "Unable to create cache entry";
964 partial_
->RestoreHeaders(&custom_request_
->extra_headers
);
965 next_state_
= STATE_SEND_REQUEST
;
970 int HttpCache::Transaction::DoDoomEntry() {
971 next_state_
= STATE_DOOM_ENTRY_COMPLETE
;
972 cache_pending_
= true;
973 if (first_cache_access_since_
.is_null())
974 first_cache_access_since_
= TimeTicks::Now();
975 net_log_
.BeginEvent(NetLog::TYPE_HTTP_CACHE_DOOM_ENTRY
);
976 ReportCacheActionStart();
977 return cache_
->DoomEntry(cache_key_
, this);
980 int HttpCache::Transaction::DoDoomEntryComplete(int result
) {
981 ReportCacheActionFinish();
982 net_log_
.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_DOOM_ENTRY
, result
);
983 next_state_
= STATE_CREATE_ENTRY
;
984 cache_pending_
= false;
985 if (result
== ERR_CACHE_RACE
)
986 next_state_
= STATE_INIT_ENTRY
;
990 int HttpCache::Transaction::DoAddToEntry() {
992 cache_pending_
= true;
993 next_state_
= STATE_ADD_TO_ENTRY_COMPLETE
;
994 net_log_
.BeginEvent(NetLog::TYPE_HTTP_CACHE_ADD_TO_ENTRY
);
995 DCHECK(entry_lock_waiting_since_
.is_null());
996 entry_lock_waiting_since_
= TimeTicks::Now();
997 return cache_
->AddTransactionToEntry(new_entry_
, this);
1000 int HttpCache::Transaction::DoAddToEntryComplete(int result
) {
1001 net_log_
.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_ADD_TO_ENTRY
,
1004 const TimeDelta entry_lock_wait
=
1005 TimeTicks::Now() - entry_lock_waiting_since_
;
1006 UMA_HISTOGRAM_TIMES("HttpCache.EntryLockWait", entry_lock_wait
);
1007 static const bool prefetching_fieldtrial
=
1008 base::FieldTrialList::TrialExists("Prefetch");
1009 if (prefetching_fieldtrial
) {
1010 UMA_HISTOGRAM_TIMES(
1011 base::FieldTrial::MakeName("HttpCache.EntryLockWait", "Prefetch"),
1014 static const bool prerendering_fieldtrial
=
1015 base::FieldTrialList::TrialExists("Prerender");
1016 if (prerendering_fieldtrial
) {
1017 UMA_HISTOGRAM_TIMES(
1018 base::FieldTrial::MakeName("HttpCache.EntryLockWait", "Prerender"),
1022 entry_lock_waiting_since_
= TimeTicks();
1024 cache_pending_
= false;
1026 if (result
== ERR_CACHE_RACE
) {
1028 next_state_
= STATE_INIT_ENTRY
;
1033 // If there is a failure, the cache should have taken care of new_entry_.
1039 entry_
= new_entry_
;
1042 if (mode_
== WRITE
) {
1044 partial_
->RestoreHeaders(&custom_request_
->extra_headers
);
1045 next_state_
= STATE_SEND_REQUEST
;
1047 // We have to read the headers from the cached entry.
1048 DCHECK(mode_
& READ_META
);
1049 next_state_
= STATE_CACHE_READ_RESPONSE
;
1054 // We may end up here multiple times for a given request.
1055 int HttpCache::Transaction::DoStartPartialCacheValidation() {
1059 next_state_
= STATE_COMPLETE_PARTIAL_CACHE_VALIDATION
;
1060 return partial_
->ShouldValidateCache(entry_
->disk_entry
, io_callback_
);
1063 int HttpCache::Transaction::DoCompletePartialCacheValidation(int result
) {
1065 // This is the end of the request.
1066 if (mode_
& WRITE
) {
1067 DoneWritingToEntry(true);
1069 cache_
->DoneReadingFromEntry(entry_
, this);
1078 partial_
->PrepareCacheValidation(entry_
->disk_entry
,
1079 &custom_request_
->extra_headers
);
1081 if (reading_
&& partial_
->IsCurrentRangeCached()) {
1082 next_state_
= STATE_CACHE_READ_DATA
;
1086 return BeginCacheValidation();
1089 // We received 304 or 206 and we want to update the cached response headers.
1090 int HttpCache::Transaction::DoUpdateCachedResponse() {
1091 next_state_
= STATE_UPDATE_CACHED_RESPONSE_COMPLETE
;
1093 // Update cached response based on headers in new_response.
1094 // TODO(wtc): should we update cached certificate (response_.ssl_info), too?
1095 response_
.headers
->Update(*new_response_
->headers
);
1096 response_
.response_time
= new_response_
->response_time
;
1097 response_
.request_time
= new_response_
->request_time
;
1099 if (response_
.headers
->HasHeaderValue("cache-control", "no-store")) {
1100 if (!entry_
->doomed
) {
1101 int ret
= cache_
->DoomEntry(cache_key_
, NULL
);
1105 // If we are already reading, we already updated the headers for this
1106 // request; doing it again will change Content-Length.
1108 target_state_
= STATE_UPDATE_CACHED_RESPONSE_COMPLETE
;
1109 next_state_
= STATE_CACHE_WRITE_RESPONSE
;
1116 int HttpCache::Transaction::DoUpdateCachedResponseComplete(int result
) {
1117 if (mode_
== UPDATE
) {
1118 DCHECK(!handling_206_
);
1119 // We got a "not modified" response and already updated the corresponding
1120 // cache entry above.
1122 // By closing the cached entry now, we make sure that the 304 rather than
1123 // the cached 200 response, is what will be returned to the user.
1124 DoneWritingToEntry(true);
1125 } else if (entry_
&& !handling_206_
) {
1126 DCHECK_EQ(READ_WRITE
, mode_
);
1127 if (!partial_
.get() || partial_
->IsLastRange()) {
1128 cache_
->ConvertWriterToReader(entry_
);
1131 // We no longer need the network transaction, so destroy it.
1132 final_upload_progress_
= network_trans_
->GetUploadProgress();
1133 network_trans_
.reset();
1134 } else if (entry_
&& handling_206_
&& truncated_
&&
1135 partial_
->initial_validation()) {
1136 // We just finished the validation of a truncated entry, and the server
1137 // is willing to resume the operation. Now we go back and start serving
1138 // the first part to the user.
1139 network_trans_
.reset();
1140 new_response_
= NULL
;
1141 next_state_
= STATE_START_PARTIAL_CACHE_VALIDATION
;
1142 partial_
->SetRangeToStartDownload();
1145 next_state_
= STATE_OVERWRITE_CACHED_RESPONSE
;
1149 int HttpCache::Transaction::DoOverwriteCachedResponse() {
1151 next_state_
= STATE_PARTIAL_HEADERS_RECEIVED
;
1155 // We change the value of Content-Length for partial content.
1156 if (handling_206_
&& partial_
.get())
1157 partial_
->FixContentLength(new_response_
->headers
);
1159 response_
= *new_response_
;
1161 if (handling_206_
&& !CanResume(false)) {
1162 // There is no point in storing this resource because it will never be used.
1163 DoneWritingToEntry(false);
1165 partial_
->FixResponseHeaders(response_
.headers
, true);
1166 next_state_
= STATE_PARTIAL_HEADERS_RECEIVED
;
1170 target_state_
= STATE_TRUNCATE_CACHED_DATA
;
1171 next_state_
= truncated_
? STATE_CACHE_WRITE_TRUNCATED_RESPONSE
:
1172 STATE_CACHE_WRITE_RESPONSE
;
1176 int HttpCache::Transaction::DoTruncateCachedData() {
1177 next_state_
= STATE_TRUNCATE_CACHED_DATA_COMPLETE
;
1180 if (net_log_
.IsLoggingAllEvents())
1181 net_log_
.BeginEvent(NetLog::TYPE_HTTP_CACHE_WRITE_DATA
);
1182 ReportCacheActionStart();
1183 // Truncate the stream.
1184 return WriteToEntry(kResponseContentIndex
, 0, NULL
, 0, io_callback_
);
1187 int HttpCache::Transaction::DoTruncateCachedDataComplete(int result
) {
1189 ReportCacheActionFinish();
1190 if (net_log_
.IsLoggingAllEvents()) {
1191 net_log_
.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_WRITE_DATA
,
1196 next_state_
= STATE_TRUNCATE_CACHED_METADATA
;
1200 int HttpCache::Transaction::DoTruncateCachedMetadata() {
1201 next_state_
= STATE_TRUNCATE_CACHED_METADATA_COMPLETE
;
1205 if (net_log_
.IsLoggingAllEvents())
1206 net_log_
.BeginEvent(NetLog::TYPE_HTTP_CACHE_WRITE_INFO
);
1207 ReportCacheActionStart();
1208 return WriteToEntry(kMetadataIndex
, 0, NULL
, 0, io_callback_
);
1211 int HttpCache::Transaction::DoTruncateCachedMetadataComplete(int result
) {
1213 ReportCacheActionFinish();
1214 if (net_log_
.IsLoggingAllEvents()) {
1215 net_log_
.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_WRITE_INFO
,
1220 // If this response is a redirect, then we can stop writing now. (We don't
1221 // need to cache the response body of a redirect.)
1222 if (response_
.headers
->IsRedirect(NULL
))
1223 DoneWritingToEntry(true);
1224 next_state_
= STATE_PARTIAL_HEADERS_RECEIVED
;
1228 int HttpCache::Transaction::DoPartialHeadersReceived() {
1229 new_response_
= NULL
;
1230 if (entry_
&& !partial_
.get() &&
1231 entry_
->disk_entry
->GetDataSize(kMetadataIndex
))
1232 next_state_
= STATE_CACHE_READ_METADATA
;
1234 if (!partial_
.get())
1238 if (network_trans_
.get()) {
1239 next_state_
= STATE_NETWORK_READ
;
1241 next_state_
= STATE_CACHE_READ_DATA
;
1243 } else if (mode_
!= NONE
) {
1244 // We are about to return the headers for a byte-range request to the user,
1245 // so let's fix them.
1246 partial_
->FixResponseHeaders(response_
.headers
, true);
1251 int HttpCache::Transaction::DoCacheReadResponse() {
1253 next_state_
= STATE_CACHE_READ_RESPONSE_COMPLETE
;
1255 io_buf_len_
= entry_
->disk_entry
->GetDataSize(kResponseInfoIndex
);
1256 read_buf_
= new IOBuffer(io_buf_len_
);
1258 net_log_
.BeginEvent(NetLog::TYPE_HTTP_CACHE_READ_INFO
);
1259 ReportCacheActionStart();
1260 return entry_
->disk_entry
->ReadData(kResponseInfoIndex
, 0, read_buf_
,
1261 io_buf_len_
, io_callback_
);
1264 int HttpCache::Transaction::DoCacheReadResponseComplete(int result
) {
1265 ReportCacheActionFinish();
1266 net_log_
.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_READ_INFO
, result
);
1267 if (result
!= io_buf_len_
||
1268 !HttpCache::ParseResponseInfo(read_buf_
->data(), io_buf_len_
,
1269 &response_
, &truncated_
)) {
1270 return OnCacheReadError(result
, true);
1272 bytes_read_from_cache_
+= result
;
1274 // Some resources may have slipped in as truncated when they're not.
1275 int current_size
= entry_
->disk_entry
->GetDataSize(kResponseContentIndex
);
1276 if (response_
.headers
->GetContentLength() == current_size
)
1279 // We now have access to the cache entry.
1281 // o if we are a reader for the transaction, then we can start reading the
1284 // o if we can read or write, then we should check if the cache entry needs
1285 // to be validated and then issue a network request if needed or just read
1286 // from the cache if the cache entry is already valid.
1288 // o if we are set to UPDATE, then we are handling an externally
1289 // conditionalized request (if-modified-since / if-none-match). We check
1290 // if the request headers define a validation request.
1294 UpdateTransactionPattern(PATTERN_ENTRY_USED
);
1295 result
= BeginCacheRead();
1298 result
= BeginPartialCacheValidation();
1301 result
= BeginExternallyConditionalizedRequest();
1306 result
= ERR_FAILED
;
1311 int HttpCache::Transaction::DoCacheWriteResponse() {
1313 if (net_log_
.IsLoggingAllEvents())
1314 net_log_
.BeginEvent(NetLog::TYPE_HTTP_CACHE_WRITE_INFO
);
1315 ReportCacheActionStart();
1317 return WriteResponseInfoToEntry(false);
1320 int HttpCache::Transaction::DoCacheWriteTruncatedResponse() {
1322 if (net_log_
.IsLoggingAllEvents() && entry_
)
1323 net_log_
.BeginEvent(NetLog::TYPE_HTTP_CACHE_WRITE_INFO
);
1324 ReportCacheActionStart();
1326 return WriteResponseInfoToEntry(true);
1329 int HttpCache::Transaction::DoCacheWriteResponseComplete(int result
) {
1330 next_state_
= target_state_
;
1331 target_state_
= STATE_NONE
;
1334 ReportCacheActionFinish();
1335 if (net_log_
.IsLoggingAllEvents()) {
1336 net_log_
.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_WRITE_INFO
,
1340 // Balance the AddRef from WriteResponseInfoToEntry.
1341 if (result
!= io_buf_len_
) {
1342 DLOG(ERROR
) << "failed to write response info to cache";
1343 DoneWritingToEntry(false);
1348 int HttpCache::Transaction::DoCacheReadMetadata() {
1350 DCHECK(!response_
.metadata
);
1351 next_state_
= STATE_CACHE_READ_METADATA_COMPLETE
;
1353 response_
.metadata
=
1354 new IOBufferWithSize(entry_
->disk_entry
->GetDataSize(kMetadataIndex
));
1356 net_log_
.BeginEvent(NetLog::TYPE_HTTP_CACHE_READ_INFO
);
1357 ReportCacheActionStart();
1358 return entry_
->disk_entry
->ReadData(kMetadataIndex
, 0, response_
.metadata
,
1359 response_
.metadata
->size(),
1363 int HttpCache::Transaction::DoCacheReadMetadataComplete(int result
) {
1364 ReportCacheActionFinish();
1365 net_log_
.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_READ_INFO
, result
);
1366 if (result
!= response_
.metadata
->size())
1367 return OnCacheReadError(result
, false);
1371 int HttpCache::Transaction::DoCacheQueryData() {
1372 next_state_
= STATE_CACHE_QUERY_DATA_COMPLETE
;
1374 // Balanced in DoCacheQueryDataComplete.
1375 return entry_
->disk_entry
->ReadyForSparseIO(io_callback_
);
1378 int HttpCache::Transaction::DoCacheQueryDataComplete(int result
) {
1379 DCHECK_EQ(OK
, result
);
1381 return ERR_UNEXPECTED
;
1383 return ValidateEntryHeadersAndContinue();
1386 int HttpCache::Transaction::DoCacheReadData() {
1388 next_state_
= STATE_CACHE_READ_DATA_COMPLETE
;
1390 if (net_log_
.IsLoggingAllEvents())
1391 net_log_
.BeginEvent(NetLog::TYPE_HTTP_CACHE_READ_DATA
);
1392 ReportCacheActionStart();
1393 if (partial_
.get()) {
1394 return partial_
->CacheRead(entry_
->disk_entry
, read_buf_
, io_buf_len_
,
1398 return entry_
->disk_entry
->ReadData(kResponseContentIndex
, read_offset_
,
1399 read_buf_
, io_buf_len_
, io_callback_
);
1402 int HttpCache::Transaction::DoCacheReadDataComplete(int result
) {
1403 ReportCacheActionFinish();
1404 if (net_log_
.IsLoggingAllEvents()) {
1405 net_log_
.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_READ_DATA
,
1410 return ERR_UNEXPECTED
;
1412 if (partial_
.get()) {
1413 // Partial requests are confusing to report in histograms because they may
1414 // have multiple underlying requests.
1415 UpdateTransactionPattern(PATTERN_NOT_COVERED
);
1416 return DoPartialCacheReadCompleted(result
);
1420 read_offset_
+= result
;
1421 bytes_read_from_cache_
+= result
;
1422 } else if (result
== 0) { // End of file.
1424 cache_
->DoneReadingFromEntry(entry_
, this);
1427 return OnCacheReadError(result
, false);
1432 int HttpCache::Transaction::DoCacheWriteData(int num_bytes
) {
1433 next_state_
= STATE_CACHE_WRITE_DATA_COMPLETE
;
1434 write_len_
= num_bytes
;
1436 if (net_log_
.IsLoggingAllEvents())
1437 net_log_
.BeginEvent(NetLog::TYPE_HTTP_CACHE_WRITE_DATA
);
1438 ReportCacheActionStart();
1441 return AppendResponseDataToEntry(read_buf_
, num_bytes
, io_callback_
);
1444 int HttpCache::Transaction::DoCacheWriteDataComplete(int result
) {
1446 ReportCacheActionFinish();
1447 if (net_log_
.IsLoggingAllEvents()) {
1448 net_log_
.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_WRITE_DATA
,
1452 // Balance the AddRef from DoCacheWriteData.
1454 return ERR_UNEXPECTED
;
1456 if (result
!= write_len_
) {
1457 DLOG(ERROR
) << "failed to write response data to cache";
1458 DoneWritingToEntry(false);
1460 // We want to ignore errors writing to disk and just keep reading from
1462 result
= write_len_
;
1463 } else if (!done_reading_
&& entry_
) {
1464 int current_size
= entry_
->disk_entry
->GetDataSize(kResponseContentIndex
);
1465 int64 body_size
= response_
.headers
->GetContentLength();
1466 if (body_size
>= 0 && body_size
<= current_size
)
1467 done_reading_
= true;
1470 if (partial_
.get()) {
1471 // This may be the last request.
1472 if (!(result
== 0 && !truncated_
&&
1473 (partial_
->IsLastRange() || mode_
== WRITE
)))
1474 return DoPartialNetworkReadCompleted(result
);
1478 // End of file. This may be the result of a connection problem so see if we
1479 // have to keep the entry around to be flagged as truncated later on.
1480 if (done_reading_
|| !entry_
|| partial_
.get() ||
1481 response_
.headers
->GetContentLength() <= 0)
1482 DoneWritingToEntry(true);
1488 //-----------------------------------------------------------------------------
1490 void HttpCache::Transaction::SetRequest(const BoundNetLog
& net_log
,
1491 const HttpRequestInfo
* request
) {
1494 effective_load_flags_
= request_
->load_flags
;
1496 switch (cache_
->mode()) {
1500 // When in record mode, we want to NEVER load from the cache.
1501 // The reason for this is beacuse we save the Set-Cookie headers
1502 // (intentionally). If we read from the cache, we replay them
1504 effective_load_flags_
|= LOAD_BYPASS_CACHE
;
1507 // When in playback mode, we want to load exclusively from the cache.
1508 effective_load_flags_
|= LOAD_ONLY_FROM_CACHE
;
1511 effective_load_flags_
|= LOAD_DISABLE_CACHE
;
1515 // Some headers imply load flags. The order here is significant.
1517 // LOAD_DISABLE_CACHE : no cache read or write
1518 // LOAD_BYPASS_CACHE : no cache read
1519 // LOAD_VALIDATE_CACHE : no cache read unless validation
1521 // The former modes trump latter modes, so if we find a matching header we
1522 // can stop iterating kSpecialHeaders.
1524 static const struct {
1525 const HeaderNameAndValue
* search
;
1527 } kSpecialHeaders
[] = {
1528 { kPassThroughHeaders
, LOAD_DISABLE_CACHE
},
1529 { kForceFetchHeaders
, LOAD_BYPASS_CACHE
},
1530 { kForceValidateHeaders
, LOAD_VALIDATE_CACHE
},
1533 bool range_found
= false;
1534 bool external_validation_error
= false;
1536 if (request_
->extra_headers
.HasHeader(HttpRequestHeaders::kRange
))
1539 for (size_t i
= 0; i
< ARRAYSIZE_UNSAFE(kSpecialHeaders
); ++i
) {
1540 if (HeaderMatches(request_
->extra_headers
, kSpecialHeaders
[i
].search
)) {
1541 effective_load_flags_
|= kSpecialHeaders
[i
].load_flag
;
1546 // Check for conditionalization headers which may correspond with a
1547 // cache validation request.
1548 for (size_t i
= 0; i
< arraysize(kValidationHeaders
); ++i
) {
1549 const ValidationHeaderInfo
& info
= kValidationHeaders
[i
];
1550 std::string validation_value
;
1551 if (request_
->extra_headers
.GetHeader(
1552 info
.request_header_name
, &validation_value
)) {
1553 if (!external_validation_
.values
[i
].empty() ||
1554 validation_value
.empty())
1555 external_validation_error
= true;
1556 external_validation_
.values
[i
] = validation_value
;
1557 external_validation_
.initialized
= true;
1562 // We don't support ranges and validation headers.
1563 if (range_found
&& external_validation_
.initialized
) {
1564 LOG(WARNING
) << "Byte ranges AND validation headers found.";
1565 effective_load_flags_
|= LOAD_DISABLE_CACHE
;
1568 // If there is more than one validation header, we can't treat this request as
1569 // a cache validation, since we don't know for sure which header the server
1570 // will give us a response for (and they could be contradictory).
1571 if (external_validation_error
) {
1572 LOG(WARNING
) << "Multiple or malformed validation headers found.";
1573 effective_load_flags_
|= LOAD_DISABLE_CACHE
;
1576 if (range_found
&& !(effective_load_flags_
& LOAD_DISABLE_CACHE
)) {
1577 UpdateTransactionPattern(PATTERN_NOT_COVERED
);
1578 partial_
.reset(new PartialData
);
1579 if (request_
->method
== "GET" && partial_
->Init(request_
->extra_headers
)) {
1580 // We will be modifying the actual range requested to the server, so
1581 // let's remove the header here.
1582 custom_request_
.reset(new HttpRequestInfo(*request_
));
1583 custom_request_
->extra_headers
.RemoveHeader(HttpRequestHeaders::kRange
);
1584 request_
= custom_request_
.get();
1585 partial_
->SetHeaders(custom_request_
->extra_headers
);
1587 // The range is invalid or we cannot handle it properly.
1588 VLOG(1) << "Invalid byte range found.";
1589 effective_load_flags_
|= LOAD_DISABLE_CACHE
;
1590 partial_
.reset(NULL
);
1595 bool HttpCache::Transaction::ShouldPassThrough() {
1596 // We may have a null disk_cache if there is an error we cannot recover from,
1597 // like not enough disk space, or sharing violations.
1598 if (!cache_
->disk_cache_
.get())
1601 // When using the record/playback modes, we always use the cache
1602 // and we never pass through.
1603 if (cache_
->mode() == RECORD
|| cache_
->mode() == PLAYBACK
)
1606 if (effective_load_flags_
& LOAD_DISABLE_CACHE
)
1609 if (request_
->method
== "GET")
1612 if (request_
->method
== "POST" &&
1613 request_
->upload_data
&& request_
->upload_data
->identifier())
1616 if (request_
->method
== "PUT" && request_
->upload_data
)
1619 if (request_
->method
== "DELETE")
1622 // TODO(darin): add support for caching HEAD responses
1626 int HttpCache::Transaction::BeginCacheRead() {
1627 // We don't support any combination of LOAD_ONLY_FROM_CACHE and byte ranges.
1628 if (response_
.headers
->response_code() == 206 || partial_
.get()) {
1630 return ERR_CACHE_MISS
;
1633 // We don't have the whole resource.
1635 return ERR_CACHE_MISS
;
1637 if (entry_
->disk_entry
->GetDataSize(kMetadataIndex
))
1638 next_state_
= STATE_CACHE_READ_METADATA
;
1643 int HttpCache::Transaction::BeginCacheValidation() {
1644 DCHECK(mode_
== READ_WRITE
);
1646 bool skip_validation
= effective_load_flags_
& LOAD_PREFERRING_CACHE
||
1647 !RequiresValidation();
1650 // Truncated entries can cause partial gets, so we shouldn't record this
1651 // load in histograms.
1652 UpdateTransactionPattern(PATTERN_NOT_COVERED
);
1653 skip_validation
= !partial_
->initial_validation();
1656 if (partial_
.get() && (is_sparse_
|| truncated_
) &&
1657 (!partial_
->IsCurrentRangeCached() || invalid_range_
)) {
1658 // Force revalidation for sparse or truncated entries. Note that we don't
1659 // want to ignore the regular validation logic just because a byte range was
1660 // part of the request.
1661 skip_validation
= false;
1664 if (skip_validation
) {
1665 UpdateTransactionPattern(PATTERN_ENTRY_USED
);
1666 if (partial_
.get()) {
1667 if (truncated_
|| is_sparse_
|| !invalid_range_
) {
1668 // We are going to return the saved response headers to the caller, so
1669 // we may need to adjust them first.
1670 next_state_
= STATE_PARTIAL_HEADERS_RECEIVED
;
1676 cache_
->ConvertWriterToReader(entry_
);
1679 if (entry_
->disk_entry
->GetDataSize(kMetadataIndex
))
1680 next_state_
= STATE_CACHE_READ_METADATA
;
1682 // Make the network request conditional, to see if we may reuse our cached
1683 // response. If we cannot do so, then we just resort to a normal fetch.
1684 // Our mode remains READ_WRITE for a conditional request. We'll switch to
1685 // either READ or WRITE mode once we hear back from the server.
1686 if (!ConditionalizeRequest()) {
1687 UpdateTransactionPattern(PATTERN_NOT_COVERED
);
1689 return DoRestartPartialRequest();
1691 DCHECK_NE(206, response_
.headers
->response_code());
1694 next_state_
= STATE_SEND_REQUEST
;
1699 int HttpCache::Transaction::BeginPartialCacheValidation() {
1700 DCHECK(mode_
== READ_WRITE
);
1702 if (response_
.headers
->response_code() != 206 && !partial_
.get() &&
1704 return BeginCacheValidation();
1706 // Partial requests should not be recorded in histograms.
1707 UpdateTransactionPattern(PATTERN_NOT_COVERED
);
1708 if (range_requested_
) {
1709 next_state_
= STATE_CACHE_QUERY_DATA
;
1712 // The request is not for a range, but we have stored just ranges.
1713 partial_
.reset(new PartialData());
1714 partial_
->SetHeaders(request_
->extra_headers
);
1715 if (!custom_request_
.get()) {
1716 custom_request_
.reset(new HttpRequestInfo(*request_
));
1717 request_
= custom_request_
.get();
1720 return ValidateEntryHeadersAndContinue();
1723 // This should only be called once per request.
1724 int HttpCache::Transaction::ValidateEntryHeadersAndContinue() {
1725 DCHECK(mode_
== READ_WRITE
);
1727 if (!partial_
->UpdateFromStoredHeaders(response_
.headers
, entry_
->disk_entry
,
1729 return DoRestartPartialRequest();
1732 if (response_
.headers
->response_code() == 206)
1735 if (!partial_
->IsRequestedRangeOK()) {
1736 // The stored data is fine, but the request may be invalid.
1737 invalid_range_
= true;
1740 next_state_
= STATE_START_PARTIAL_CACHE_VALIDATION
;
1744 int HttpCache::Transaction::BeginExternallyConditionalizedRequest() {
1745 DCHECK_EQ(UPDATE
, mode_
);
1746 DCHECK(external_validation_
.initialized
);
1748 for (size_t i
= 0; i
< arraysize(kValidationHeaders
); i
++) {
1749 if (external_validation_
.values
[i
].empty())
1751 // Retrieve either the cached response's "etag" or "last-modified" header.
1752 std::string validator
;
1753 response_
.headers
->EnumerateHeader(
1755 kValidationHeaders
[i
].related_response_header_name
,
1758 if (response_
.headers
->response_code() != 200 || truncated_
||
1759 validator
.empty() || validator
!= external_validation_
.values
[i
]) {
1760 // The externally conditionalized request is not a validation request
1761 // for our existing cache entry. Proceed with caching disabled.
1762 UpdateTransactionPattern(PATTERN_NOT_COVERED
);
1763 DoneWritingToEntry(true);
1767 next_state_
= STATE_SEND_REQUEST
;
1771 int HttpCache::Transaction::RestartNetworkRequest() {
1772 DCHECK(mode_
& WRITE
|| mode_
== NONE
);
1773 DCHECK(network_trans_
.get());
1774 DCHECK_EQ(STATE_NONE
, next_state_
);
1776 next_state_
= STATE_SEND_REQUEST_COMPLETE
;
1777 int rv
= network_trans_
->RestartIgnoringLastError(io_callback_
);
1778 if (rv
!= ERR_IO_PENDING
)
1783 int HttpCache::Transaction::RestartNetworkRequestWithCertificate(
1784 X509Certificate
* client_cert
) {
1785 DCHECK(mode_
& WRITE
|| mode_
== NONE
);
1786 DCHECK(network_trans_
.get());
1787 DCHECK_EQ(STATE_NONE
, next_state_
);
1789 next_state_
= STATE_SEND_REQUEST_COMPLETE
;
1790 int rv
= network_trans_
->RestartWithCertificate(client_cert
, io_callback_
);
1791 if (rv
!= ERR_IO_PENDING
)
1796 int HttpCache::Transaction::RestartNetworkRequestWithAuth(
1797 const AuthCredentials
& credentials
) {
1798 DCHECK(mode_
& WRITE
|| mode_
== NONE
);
1799 DCHECK(network_trans_
.get());
1800 DCHECK_EQ(STATE_NONE
, next_state_
);
1802 next_state_
= STATE_SEND_REQUEST_COMPLETE
;
1803 int rv
= network_trans_
->RestartWithAuth(credentials
, io_callback_
);
1804 if (rv
!= ERR_IO_PENDING
)
1809 bool HttpCache::Transaction::RequiresValidation() {
1810 // TODO(darin): need to do more work here:
1811 // - make sure we have a matching request method
1812 // - watch out for cached responses that depend on authentication
1813 // In playback mode, nothing requires validation.
1814 if (cache_
->mode() == net::HttpCache::PLAYBACK
)
1817 if (effective_load_flags_
& LOAD_VALIDATE_CACHE
)
1820 if (request_
->method
== "PUT" || request_
->method
== "DELETE")
1823 if (response_
.headers
->RequiresValidation(
1824 response_
.request_time
, response_
.response_time
, Time::Now()))
1827 // Since Vary header computation is fairly expensive, we save it for last.
1828 if (response_
.vary_data
.is_valid() &&
1829 !response_
.vary_data
.MatchesRequest(*request_
, *response_
.headers
))
1835 bool HttpCache::Transaction::ConditionalizeRequest() {
1836 DCHECK(response_
.headers
);
1838 if (request_
->method
== "PUT" || request_
->method
== "DELETE")
1841 // This only makes sense for cached 200 or 206 responses.
1842 if (response_
.headers
->response_code() != 200 &&
1843 response_
.headers
->response_code() != 206)
1846 // We should have handled this case before.
1847 DCHECK(response_
.headers
->response_code() != 206 ||
1848 response_
.headers
->HasStrongValidators());
1850 // Just use the first available ETag and/or Last-Modified header value.
1851 // TODO(darin): Or should we use the last?
1853 std::string etag_value
;
1854 response_
.headers
->EnumerateHeader(NULL
, "etag", &etag_value
);
1856 std::string last_modified_value
;
1857 response_
.headers
->EnumerateHeader(NULL
, "last-modified",
1858 &last_modified_value
);
1860 if (response_
.headers
->GetHttpVersion() < HttpVersion(1, 1))
1863 if (etag_value
.empty() && last_modified_value
.empty())
1866 if (!partial_
.get()) {
1867 // Need to customize the request, so this forces us to allocate :(
1868 custom_request_
.reset(new HttpRequestInfo(*request_
));
1869 request_
= custom_request_
.get();
1871 DCHECK(custom_request_
.get());
1873 bool use_if_range
= partial_
.get() && !partial_
->IsCurrentRangeCached() &&
1876 if (!etag_value
.empty()) {
1878 // We don't want to switch to WRITE mode if we don't have this block of a
1879 // byte-range request because we may have other parts cached.
1880 custom_request_
->extra_headers
.SetHeader(
1881 HttpRequestHeaders::kIfRange
, etag_value
);
1883 custom_request_
->extra_headers
.SetHeader(
1884 HttpRequestHeaders::kIfNoneMatch
, etag_value
);
1886 // For byte-range requests, make sure that we use only one way to validate
1888 if (partial_
.get() && !partial_
->IsCurrentRangeCached())
1892 if (!last_modified_value
.empty()) {
1894 custom_request_
->extra_headers
.SetHeader(
1895 HttpRequestHeaders::kIfRange
, last_modified_value
);
1897 custom_request_
->extra_headers
.SetHeader(
1898 HttpRequestHeaders::kIfModifiedSince
, last_modified_value
);
1905 // We just received some headers from the server. We may have asked for a range,
1906 // in which case partial_ has an object. This could be the first network request
1907 // we make to fulfill the original request, or we may be already reading (from
1908 // the net and / or the cache). If we are not expecting a certain response, we
1909 // just bypass the cache for this request (but again, maybe we are reading), and
1910 // delete partial_ (so we are not able to "fix" the headers that we return to
1911 // the user). This results in either a weird response for the caller (we don't
1912 // expect it after all), or maybe a range that was not exactly what it was asked
1915 // If the server is simply telling us that the resource has changed, we delete
1916 // the cached entry and restart the request as the caller intended (by returning
1917 // false from this method). However, we may not be able to do that at any point,
1918 // for instance if we already returned the headers to the user.
1920 // WARNING: Whenever this code returns false, it has to make sure that the next
1921 // time it is called it will return true so that we don't keep retrying the
1923 bool HttpCache::Transaction::ValidatePartialResponse() {
1924 const HttpResponseHeaders
* headers
= new_response_
->headers
;
1925 int response_code
= headers
->response_code();
1926 bool partial_response
= (response_code
== 206);
1927 handling_206_
= false;
1929 if (!entry_
|| request_
->method
!= "GET")
1932 if (invalid_range_
) {
1933 // We gave up trying to match this request with the stored data. If the
1934 // server is ok with the request, delete the entry, otherwise just ignore
1937 if (partial_response
|| response_code
== 200) {
1938 DoomPartialEntry(true);
1941 if (response_code
== 304)
1943 IgnoreRangeRequest();
1948 if (!partial_
.get()) {
1949 // We are not expecting 206 but we may have one.
1950 if (partial_response
)
1951 IgnoreRangeRequest();
1956 // TODO(rvargas): Do we need to consider other results here?.
1957 bool failure
= response_code
== 200 || response_code
== 416;
1959 if (partial_
->IsCurrentRangeCached()) {
1960 // We asked for "If-None-Match: " so a 206 means a new object.
1961 if (partial_response
)
1964 if (response_code
== 304 && partial_
->ResponseHeadersOK(headers
))
1967 // We asked for "If-Range: " so a 206 means just another range.
1968 if (partial_response
&& partial_
->ResponseHeadersOK(headers
)) {
1969 handling_206_
= true;
1973 if (!reading_
&& !is_sparse_
&& !partial_response
) {
1974 // See if we can ignore the fact that we issued a byte range request.
1975 // If the server sends 200, just store it. If it sends an error, redirect
1976 // or something else, we may store the response as long as we didn't have
1977 // anything already stored.
1978 if (response_code
== 200 ||
1979 (!truncated_
&& response_code
!= 304 && response_code
!= 416)) {
1980 // The server is sending something else, and we can save it.
1981 DCHECK((truncated_
&& !partial_
->IsLastRange()) || range_requested_
);
1988 // 304 is not expected here, but we'll spare the entry (unless it was
1995 // We cannot truncate this entry, it has to be deleted.
1996 UpdateTransactionPattern(PATTERN_NOT_COVERED
);
1997 DoomPartialEntry(false);
1999 if (!reading_
&& !partial_
->IsLastRange()) {
2000 // We'll attempt to issue another network request, this time without us
2001 // messing up the headers.
2002 partial_
->RestoreHeaders(&custom_request_
->extra_headers
);
2007 LOG(WARNING
) << "Failed to revalidate partial entry";
2012 IgnoreRangeRequest();
2016 void HttpCache::Transaction::IgnoreRangeRequest() {
2017 // We have a problem. We may or may not be reading already (in which case we
2018 // returned the headers), but we'll just pretend that this request is not
2019 // using the cache and see what happens. Most likely this is the first
2020 // response from the server (it's not changing its mind midway, right?).
2021 UpdateTransactionPattern(PATTERN_NOT_COVERED
);
2022 if (mode_
& WRITE
) {
2023 DoneWritingToEntry(mode_
!= WRITE
);
2024 } else if (mode_
& READ
&& entry_
) {
2025 cache_
->DoneReadingFromEntry(entry_
, this);
2028 partial_
.reset(NULL
);
2033 void HttpCache::Transaction::FailRangeRequest() {
2034 response_
= *new_response_
;
2035 partial_
->FixResponseHeaders(response_
.headers
, false);
2038 int HttpCache::Transaction::ReadFromNetwork(IOBuffer
* data
, int data_len
) {
2040 io_buf_len_
= data_len
;
2041 next_state_
= STATE_NETWORK_READ
;
2045 int HttpCache::Transaction::ReadFromEntry(IOBuffer
* data
, int data_len
) {
2047 io_buf_len_
= data_len
;
2048 next_state_
= STATE_CACHE_READ_DATA
;
2052 int HttpCache::Transaction::WriteToEntry(int index
, int offset
,
2053 IOBuffer
* data
, int data_len
,
2054 const CompletionCallback
& callback
) {
2059 if (!partial_
.get() || !data_len
) {
2060 rv
= entry_
->disk_entry
->WriteData(index
, offset
, data
, data_len
, callback
,
2063 rv
= partial_
->CacheWrite(entry_
->disk_entry
, data
, data_len
, callback
);
2068 int HttpCache::Transaction::WriteResponseInfoToEntry(bool truncated
) {
2069 next_state_
= STATE_CACHE_WRITE_RESPONSE_COMPLETE
;
2073 // Do not cache no-store content (unless we are record mode). Do not cache
2074 // content with cert errors either. This is to prevent not reporting net
2075 // errors when loading a resource from the cache. When we load a page over
2076 // HTTPS with a cert error we show an SSL blocking page. If the user clicks
2077 // proceed we reload the resource ignoring the errors. The loaded resource
2078 // is then cached. If that resource is subsequently loaded from the cache,
2079 // no net error is reported (even though the cert status contains the actual
2080 // errors) and no SSL blocking page is shown. An alternative would be to
2081 // reverse-map the cert status to a net error and replay the net error.
2082 if ((cache_
->mode() != RECORD
&&
2083 response_
.headers
->HasHeaderValue("cache-control", "no-store")) ||
2084 net::IsCertStatusError(response_
.ssl_info
.cert_status
)) {
2085 DoneWritingToEntry(false);
2089 // When writing headers, we normally only write the non-transient
2090 // headers; when in record mode, record everything.
2091 bool skip_transient_headers
= (cache_
->mode() != RECORD
);
2094 DCHECK_EQ(200, response_
.headers
->response_code());
2097 scoped_refptr
<PickledIOBuffer
> data(new PickledIOBuffer());
2098 response_
.Persist(data
->pickle(), skip_transient_headers
, truncated
);
2101 io_buf_len_
= data
->pickle()->size();
2102 return entry_
->disk_entry
->WriteData(kResponseInfoIndex
, 0, data
,
2103 io_buf_len_
, io_callback_
, true);
2106 int HttpCache::Transaction::AppendResponseDataToEntry(
2107 IOBuffer
* data
, int data_len
, const CompletionCallback
& callback
) {
2108 if (!entry_
|| !data_len
)
2111 int current_size
= entry_
->disk_entry
->GetDataSize(kResponseContentIndex
);
2112 return WriteToEntry(kResponseContentIndex
, current_size
, data
, data_len
,
2116 void HttpCache::Transaction::DoneWritingToEntry(bool success
) {
2122 cache_
->DoneWritingToEntry(entry_
, success
);
2124 mode_
= NONE
; // switch to 'pass through' mode
2127 int HttpCache::Transaction::OnCacheReadError(int result
, bool restart
) {
2128 DLOG(ERROR
) << "ReadData failed: " << result
;
2130 // Avoid using this entry in the future.
2132 cache_
->DoomActiveEntry(cache_key_
);
2136 DCHECK(!network_trans_
.get());
2137 cache_
->DoneWithEntry(entry_
, this, false);
2141 next_state_
= STATE_GET_BACKEND
;
2145 return ERR_CACHE_READ_FAILURE
;
2148 void HttpCache::Transaction::DoomPartialEntry(bool delete_object
) {
2149 DVLOG(2) << "DoomPartialEntry";
2150 int rv
= cache_
->DoomEntry(cache_key_
, NULL
);
2152 cache_
->DoneWithEntry(entry_
, this, false);
2156 partial_
.reset(NULL
);
2159 int HttpCache::Transaction::DoPartialNetworkReadCompleted(int result
) {
2160 partial_
->OnNetworkReadCompleted(result
);
2163 // We need to move on to the next range.
2164 network_trans_
.reset();
2165 next_state_
= STATE_START_PARTIAL_CACHE_VALIDATION
;
2170 int HttpCache::Transaction::DoPartialCacheReadCompleted(int result
) {
2171 partial_
->OnCacheReadCompleted(result
);
2173 if (result
== 0 && mode_
== READ_WRITE
) {
2174 // We need to move on to the next range.
2175 next_state_
= STATE_START_PARTIAL_CACHE_VALIDATION
;
2176 } else if (result
< 0) {
2177 return OnCacheReadError(result
, false);
2182 int HttpCache::Transaction::DoRestartPartialRequest() {
2183 // The stored data cannot be used. Get rid of it and restart this request.
2184 // We need to also reset the |truncated_| flag as a new entry is created.
2185 DoomPartialEntry(!range_requested_
);
2188 next_state_
= STATE_INIT_ENTRY
;
2192 // Histogram data from the end of 2010 show the following distribution of
2193 // response headers:
2195 // Content-Length............... 87%
2196 // Date......................... 98%
2197 // Last-Modified................ 49%
2198 // Etag......................... 19%
2199 // Accept-Ranges: bytes......... 25%
2200 // Accept-Ranges: none.......... 0.4%
2201 // Strong Validator............. 50%
2202 // Strong Validator + ranges.... 24%
2203 // Strong Validator + CL........ 49%
2205 bool HttpCache::Transaction::CanResume(bool has_data
) {
2206 // Double check that there is something worth keeping.
2207 if (has_data
&& !entry_
->disk_entry
->GetDataSize(kResponseContentIndex
))
2210 if (request_
->method
!= "GET")
2213 if (response_
.headers
->GetContentLength() <= 0 ||
2214 response_
.headers
->HasHeaderValue("Accept-Ranges", "none") ||
2215 !response_
.headers
->HasStrongValidators())
2221 void HttpCache::Transaction::OnIOComplete(int result
) {
2225 void HttpCache::Transaction::ReportCacheActionStart() {
2226 if (transaction_delegate_
)
2227 transaction_delegate_
->OnCacheActionStart();
2230 void HttpCache::Transaction::ReportCacheActionFinish() {
2231 if (transaction_delegate_
)
2232 transaction_delegate_
->OnCacheActionFinish();
2235 void HttpCache::Transaction::UpdateTransactionPattern(
2236 TransactionPattern new_transaction_pattern
) {
2237 if (transaction_pattern_
== PATTERN_NOT_COVERED
)
2239 DCHECK(transaction_pattern_
== PATTERN_UNDEFINED
||
2240 new_transaction_pattern
== PATTERN_NOT_COVERED
);
2241 transaction_pattern_
= new_transaction_pattern
;
2244 void HttpCache::Transaction::RecordHistograms() {
2245 DCHECK_NE(PATTERN_UNDEFINED
, transaction_pattern_
);
2246 if (!cache_
|| !cache_
->GetCurrentBackend() ||
2247 cache_
->GetCurrentBackend()->GetCacheType() != DISK_CACHE
||
2248 cache_
->mode() != NORMAL
|| request_
->method
!= "GET") {
2251 UMA_HISTOGRAM_BOOLEAN("HttpCache.HasPattern",
2252 transaction_pattern_
!= PATTERN_NOT_COVERED
);
2253 if (transaction_pattern_
== PATTERN_NOT_COVERED
)
2255 DCHECK(!range_requested_
);
2256 DCHECK(!first_cache_access_since_
.is_null());
2258 TimeDelta total_time
= base::TimeTicks::Now() - first_cache_access_since_
;
2260 UMA_HISTOGRAM_TIMES("HttpCache.AccessToDone", total_time
);
2262 bool did_send_request
= !send_request_since_
.is_null();
2264 (did_send_request
&& (transaction_pattern_
== PATTERN_ENTRY_NOT_CACHED
||
2265 transaction_pattern_
== PATTERN_ENTRY_VALIDATED
||
2266 transaction_pattern_
== PATTERN_ENTRY_UPDATED
)) ||
2267 (!did_send_request
&& transaction_pattern_
== PATTERN_ENTRY_USED
));
2270 if (transaction_pattern_
== PATTERN_ENTRY_NOT_CACHED
||
2271 transaction_pattern_
== PATTERN_ENTRY_UPDATED
) {
2272 resource_size
= bytes_read_from_network_
;
2274 DCHECK(transaction_pattern_
== PATTERN_ENTRY_VALIDATED
||
2275 transaction_pattern_
== PATTERN_ENTRY_USED
);
2276 resource_size
= bytes_read_from_cache_
;
2279 bool is_small_resource
= resource_size
< kSmallResourceMaxBytes
;
2280 if (is_small_resource
)
2281 UMA_HISTOGRAM_TIMES("HttpCache.AccessToDone.SmallResource", total_time
);
2283 if (!did_send_request
) {
2284 DCHECK(transaction_pattern_
== PATTERN_ENTRY_USED
);
2285 UMA_HISTOGRAM_TIMES("HttpCache.AccessToDone.Used", total_time
);
2286 if (is_small_resource
) {
2287 UMA_HISTOGRAM_TIMES(
2288 "HttpCache.AccessToDone.Used.SmallResource", total_time
);
2293 TimeDelta before_send_time
= send_request_since_
- first_cache_access_since_
;
2294 int before_send_percent
=
2295 total_time
.ToInternalValue() == 0 ? 0
2296 : before_send_time
* 100 / total_time
;
2297 DCHECK_LE(0, before_send_percent
);
2298 DCHECK_GE(100, before_send_percent
);
2300 UMA_HISTOGRAM_TIMES("HttpCache.AccessToDone.SentRequest", total_time
);
2301 UMA_HISTOGRAM_TIMES("HttpCache.BeforeSend", before_send_time
);
2302 UMA_HISTOGRAM_PERCENTAGE("HttpCache.PercentBeforeSend", before_send_percent
);
2303 if (is_small_resource
) {
2304 UMA_HISTOGRAM_TIMES(
2305 "HttpCache.AccessToDone.SentRequest.SmallResource", total_time
);
2306 UMA_HISTOGRAM_TIMES("HttpCache.BeforeSend.SmallResource", before_send_time
);
2307 UMA_HISTOGRAM_PERCENTAGE(
2308 "HttpCache.PercentBeforeSend.SmallResource", before_send_percent
);
2311 // TODO(gavinp): Remove or minimize these histograms, particularly the ones
2312 // below this comment after we have received initial data.
2313 switch (transaction_pattern_
) {
2314 case PATTERN_ENTRY_NOT_CACHED
: {
2315 UMA_HISTOGRAM_TIMES("HttpCache.BeforeSend.NotCached", before_send_time
);
2316 UMA_HISTOGRAM_PERCENTAGE(
2317 "HttpCache.PercentBeforeSend.NotCached", before_send_percent
);
2318 if (is_small_resource
) {
2319 UMA_HISTOGRAM_TIMES(
2320 "HttpCache.BeforeSend.NotCached.SmallResource", before_send_time
);
2321 UMA_HISTOGRAM_PERCENTAGE(
2322 "HttpCache.PercentBeforeSend.NotCached.SmallResource",
2323 before_send_percent
);
2327 case PATTERN_ENTRY_VALIDATED
: {
2328 UMA_HISTOGRAM_TIMES("HttpCache.BeforeSend.Validated", before_send_time
);
2329 UMA_HISTOGRAM_PERCENTAGE(
2330 "HttpCache.PercentBeforeSend.Validated", before_send_percent
);
2331 if (is_small_resource
) {
2332 UMA_HISTOGRAM_TIMES(
2333 "HttpCache.BeforeSend.Validated.SmallResource", before_send_time
);
2334 UMA_HISTOGRAM_PERCENTAGE(
2335 "HttpCache.PercentBeforeSend.Validated.SmallResource",
2336 before_send_percent
);
2340 case PATTERN_ENTRY_UPDATED
: {
2341 UMA_HISTOGRAM_TIMES("HttpCache.BeforeSend.Updated", before_send_time
);
2342 UMA_HISTOGRAM_PERCENTAGE(
2343 "HttpCache.PercentBeforeSend.Updated", before_send_percent
);
2344 if (is_small_resource
) {
2345 UMA_HISTOGRAM_TIMES(
2346 "HttpCache.BeforeSend.Updated.SmallResource", before_send_time
);
2347 UMA_HISTOGRAM_PERCENTAGE(
2348 "HttpCache.PercentBeforeSend.Updated.SmallResource",
2349 before_send_percent
);