[Android] Remove the webview_core static library.
[chromium-blink-merge.git] / net / http / http_cache_transaction.cc
bloba52309b69552081b74a8300b3c045d9b86b1bd53
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"
9 #if defined(OS_POSIX)
10 #include <unistd.h>
11 #endif
13 #include <string>
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"
39 using base::Time;
40 using base::TimeDelta;
41 using base::TimeTicks;
43 namespace {
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;
51 } // namespace
53 namespace net {
55 struct HeaderNameAndValue {
56 const char* name;
57 const char* value;
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
65 { "if-range", NULL },
66 { NULL, NULL }
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" },
84 { NULL, NULL }
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" },
91 { NULL, NULL }
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))
99 continue;
101 if (!search->value)
102 return true;
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))
107 return true;
110 return false;
113 //-----------------------------------------------------------------------------
115 HttpCache::Transaction::Transaction(
116 HttpCache* cache,
117 HttpTransactionDelegate* transaction_delegate)
118 : next_state_(STATE_NONE),
119 request_(NULL),
120 cache_(cache->AsWeakPtr()),
121 entry_(NULL),
122 new_entry_(NULL),
123 network_trans_(NULL),
124 new_response_(NULL),
125 mode_(NONE),
126 target_state_(STATE_NONE),
127 reading_(false),
128 invalid_range_(false),
129 truncated_(false),
130 is_sparse_(false),
131 range_requested_(false),
132 handling_206_(false),
133 cache_pending_(false),
134 done_reading_(false),
135 read_offset_(0),
136 effective_load_flags_(0),
137 write_len_(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_
154 // after this point.
155 callback_.Reset();
157 if (cache_) {
158 if (entry_) {
159 bool cancel_request = reading_;
160 if (cancel_request) {
161 if (partial_.get()) {
162 entry_->disk_entry->CancelSparseIO();
163 } else {
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.
180 cache_.reset();
183 int HttpCache::Transaction::WriteMetadata(IOBuffer* buf, int buf_len,
184 const CompletionCallback& callback) {
185 DCHECK(buf);
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,
196 callback, true);
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_)
204 return true;
206 if (!CanResume(true))
207 return false;
209 // We may have received the whole resource already.
210 if (done_reading_)
211 return true;
213 truncated_ = true;
214 target_state_ = STATE_NONE;
215 next_state_ = STATE_CACHE_WRITE_TRUNCATED_RESPONSE;
216 DoLoop(OK);
217 return true;
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 {
229 return net_log_;
232 int HttpCache::Transaction::Start(const HttpRequestInfo* request,
233 const CompletionCallback& callback,
234 const BoundNetLog& net_log) {
235 DCHECK(request);
236 DCHECK(!callback.is_null());
238 // Ensure that we only have one asynchronous call at a time.
239 DCHECK(callback_.is_null());
240 DCHECK(!reading_);
241 DCHECK(!network_trans_.get());
242 DCHECK(!entry_);
244 if (!cache_)
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;
251 int rv = DoLoop(OK);
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;
258 return rv;
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());
268 if (!cache_)
269 return ERR_UNEXPECTED;
271 int rv = RestartNetworkRequest();
273 if (rv == ERR_IO_PENDING)
274 callback_ = callback;
276 return rv;
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());
287 if (!cache_)
288 return ERR_UNEXPECTED;
290 int rv = RestartNetworkRequestWithCertificate(client_cert);
292 if (rv == ERR_IO_PENDING)
293 callback_ = callback;
295 return rv;
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());
307 if (!cache_)
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;
318 return rv;
321 bool HttpCache::Transaction::IsReadyToRestartForAuth() {
322 if (!network_trans_.get())
323 return false;
324 return network_trans_->IsReadyToRestartForAuth();
327 int HttpCache::Transaction::Read(IOBuffer* buf, int buf_len,
328 const CompletionCallback& callback) {
329 DCHECK(buf);
330 DCHECK_GT(buf_len, 0);
331 DCHECK(!callback.is_null());
333 DCHECK(callback_.is_null());
335 if (!cache_)
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);
345 mode_ = NONE;
348 reading_ = true;
349 int rv;
351 switch (mode_) {
352 case 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);
357 break;
359 case NONE:
360 case WRITE:
361 DCHECK(network_trans_.get());
362 rv = ReadFromNetwork(buf, buf_len);
363 break;
364 case READ:
365 rv = ReadFromEntry(buf, buf_len);
366 break;
367 default:
368 NOTREACHED();
369 rv = ERR_FAILED;
372 if (rv == ERR_IO_PENDING) {
373 DCHECK(callback_.is_null());
374 callback_ = callback;
376 return rv;
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
387 // from the net.
388 if (cache_ && entry_ && (mode_ & WRITE) && network_trans_.get() &&
389 !is_sparse_ && !range_requested_)
390 mode_ = NONE;
393 void HttpCache::Transaction::DoneReading() {
394 if (cache_ && entry_) {
395 DCHECK(reading_);
396 DCHECK_NE(mode_, UPDATE);
397 if (mode_ & WRITE)
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)
413 return state;
415 if (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_;
435 callback_.Reset();
436 c.Run(rv);
439 int HttpCache::Transaction::HandleResult(int rv) {
440 DCHECK(rv != ERR_IO_PENDING);
441 if (!callback_.is_null())
442 DoCallback(rv);
444 return rv;
447 // A few common patterns: (Foo* means Foo -> FooComplete)
449 // Not-cached entry:
450 // Start():
451 // GetBackend* -> InitEntry -> OpenEntry* -> CreateEntry* -> AddToEntry* ->
452 // SendRequest* -> SuccessfulSendRequest -> OverwriteCachedResponse ->
453 // CacheWriteResponse* -> TruncateCachedData* -> TruncateCachedMetadata* ->
454 // PartialHeadersReceived
456 // Read():
457 // NetworkRead* -> CacheWriteData*
459 // Cached entry, no validation:
460 // Start():
461 // GetBackend* -> InitEntry -> OpenEntry* -> AddToEntry* -> CacheReadResponse*
462 // -> BeginPartialCacheValidation() -> BeginCacheValidation()
464 // Read():
465 // CacheReadData*
467 // Cached entry, validation (304):
468 // Start():
469 // GetBackend* -> InitEntry -> OpenEntry* -> AddToEntry* -> CacheReadResponse*
470 // -> BeginPartialCacheValidation() -> BeginCacheValidation() ->
471 // SendRequest* -> SuccessfulSendRequest -> UpdateCachedResponse ->
472 // CacheWriteResponse* -> UpdateCachedResponseComplete ->
473 // OverwriteCachedResponse -> PartialHeadersReceived
475 // Read():
476 // CacheReadData*
478 // Cached entry, validation and replace (200):
479 // Start():
480 // GetBackend* -> InitEntry -> OpenEntry* -> AddToEntry* -> CacheReadResponse*
481 // -> BeginPartialCacheValidation() -> BeginCacheValidation() ->
482 // SendRequest* -> SuccessfulSendRequest -> OverwriteCachedResponse ->
483 // CacheWriteResponse* -> DoTruncateCachedData* -> TruncateCachedMetadata* ->
484 // PartialHeadersReceived
486 // Read():
487 // NetworkRead* -> CacheWriteData*
489 // Sparse entry, partially cached, byte range request:
490 // Start():
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
499 // Read() 1:
500 // NetworkRead* -> CacheWriteData*
502 // Read() 2:
503 // NetworkRead* -> CacheWriteData* -> StartPartialCacheValidation ->
504 // CompletePartialCacheValidation -> CacheReadData* ->
506 // Read() 3:
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);
515 int rv = result;
516 do {
517 State state = next_state_;
518 next_state_ = STATE_NONE;
519 switch (state) {
520 case STATE_GET_BACKEND:
521 DCHECK_EQ(OK, rv);
522 rv = DoGetBackend();
523 break;
524 case STATE_GET_BACKEND_COMPLETE:
525 rv = DoGetBackendComplete(rv);
526 break;
527 case STATE_SEND_REQUEST:
528 DCHECK_EQ(OK, rv);
529 rv = DoSendRequest();
530 break;
531 case STATE_SEND_REQUEST_COMPLETE:
532 rv = DoSendRequestComplete(rv);
533 break;
534 case STATE_SUCCESSFUL_SEND_REQUEST:
535 DCHECK_EQ(OK, rv);
536 rv = DoSuccessfulSendRequest();
537 break;
538 case STATE_NETWORK_READ:
539 DCHECK_EQ(OK, rv);
540 rv = DoNetworkRead();
541 break;
542 case STATE_NETWORK_READ_COMPLETE:
543 rv = DoNetworkReadComplete(rv);
544 break;
545 case STATE_INIT_ENTRY:
546 DCHECK_EQ(OK, rv);
547 rv = DoInitEntry();
548 break;
549 case STATE_OPEN_ENTRY:
550 DCHECK_EQ(OK, rv);
551 rv = DoOpenEntry();
552 break;
553 case STATE_OPEN_ENTRY_COMPLETE:
554 rv = DoOpenEntryComplete(rv);
555 break;
556 case STATE_CREATE_ENTRY:
557 DCHECK_EQ(OK, rv);
558 rv = DoCreateEntry();
559 break;
560 case STATE_CREATE_ENTRY_COMPLETE:
561 rv = DoCreateEntryComplete(rv);
562 break;
563 case STATE_DOOM_ENTRY:
564 DCHECK_EQ(OK, rv);
565 rv = DoDoomEntry();
566 break;
567 case STATE_DOOM_ENTRY_COMPLETE:
568 rv = DoDoomEntryComplete(rv);
569 break;
570 case STATE_ADD_TO_ENTRY:
571 DCHECK_EQ(OK, rv);
572 rv = DoAddToEntry();
573 break;
574 case STATE_ADD_TO_ENTRY_COMPLETE:
575 rv = DoAddToEntryComplete(rv);
576 break;
577 case STATE_START_PARTIAL_CACHE_VALIDATION:
578 DCHECK_EQ(OK, rv);
579 rv = DoStartPartialCacheValidation();
580 break;
581 case STATE_COMPLETE_PARTIAL_CACHE_VALIDATION:
582 rv = DoCompletePartialCacheValidation(rv);
583 break;
584 case STATE_UPDATE_CACHED_RESPONSE:
585 DCHECK_EQ(OK, rv);
586 rv = DoUpdateCachedResponse();
587 break;
588 case STATE_UPDATE_CACHED_RESPONSE_COMPLETE:
589 rv = DoUpdateCachedResponseComplete(rv);
590 break;
591 case STATE_OVERWRITE_CACHED_RESPONSE:
592 DCHECK_EQ(OK, rv);
593 rv = DoOverwriteCachedResponse();
594 break;
595 case STATE_TRUNCATE_CACHED_DATA:
596 DCHECK_EQ(OK, rv);
597 rv = DoTruncateCachedData();
598 break;
599 case STATE_TRUNCATE_CACHED_DATA_COMPLETE:
600 rv = DoTruncateCachedDataComplete(rv);
601 break;
602 case STATE_TRUNCATE_CACHED_METADATA:
603 DCHECK_EQ(OK, rv);
604 rv = DoTruncateCachedMetadata();
605 break;
606 case STATE_TRUNCATE_CACHED_METADATA_COMPLETE:
607 rv = DoTruncateCachedMetadataComplete(rv);
608 break;
609 case STATE_PARTIAL_HEADERS_RECEIVED:
610 DCHECK_EQ(OK, rv);
611 rv = DoPartialHeadersReceived();
612 break;
613 case STATE_CACHE_READ_RESPONSE:
614 DCHECK_EQ(OK, rv);
615 rv = DoCacheReadResponse();
616 break;
617 case STATE_CACHE_READ_RESPONSE_COMPLETE:
618 rv = DoCacheReadResponseComplete(rv);
619 break;
620 case STATE_CACHE_WRITE_RESPONSE:
621 DCHECK_EQ(OK, rv);
622 rv = DoCacheWriteResponse();
623 break;
624 case STATE_CACHE_WRITE_TRUNCATED_RESPONSE:
625 DCHECK_EQ(OK, rv);
626 rv = DoCacheWriteTruncatedResponse();
627 break;
628 case STATE_CACHE_WRITE_RESPONSE_COMPLETE:
629 rv = DoCacheWriteResponseComplete(rv);
630 break;
631 case STATE_CACHE_READ_METADATA:
632 DCHECK_EQ(OK, rv);
633 rv = DoCacheReadMetadata();
634 break;
635 case STATE_CACHE_READ_METADATA_COMPLETE:
636 rv = DoCacheReadMetadataComplete(rv);
637 break;
638 case STATE_CACHE_QUERY_DATA:
639 DCHECK_EQ(OK, rv);
640 rv = DoCacheQueryData();
641 break;
642 case STATE_CACHE_QUERY_DATA_COMPLETE:
643 rv = DoCacheQueryDataComplete(rv);
644 break;
645 case STATE_CACHE_READ_DATA:
646 DCHECK_EQ(OK, rv);
647 rv = DoCacheReadData();
648 break;
649 case STATE_CACHE_READ_DATA_COMPLETE:
650 rv = DoCacheReadDataComplete(rv);
651 break;
652 case STATE_CACHE_WRITE_DATA:
653 rv = DoCacheWriteData(rv);
654 break;
655 case STATE_CACHE_WRITE_DATA_COMPLETE:
656 rv = DoCacheWriteDataComplete(rv);
657 break;
658 default:
659 NOTREACHED() << "bad state";
660 rv = ERR_FAILED;
661 break;
663 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
665 if (rv != ERR_IO_PENDING)
666 HandleResult(rv);
668 return rv;
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,
683 result);
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) {
691 mode_ = READ;
692 } else if (effective_load_flags_ & LOAD_BYPASS_CACHE) {
693 mode_ = WRITE;
694 } else {
695 mode_ = READ_WRITE;
698 // Downgrade to UPDATE if the request has been externally conditionalized.
699 if (external_validation_.initialized) {
700 if (mode_ & WRITE) {
701 // Strip off the READ_DATA bit (and maybe add back a READ_META bit
702 // in case READ was off).
703 mode_ = UPDATE;
704 } else {
705 mode_ = NONE;
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) {
713 mode_ = NONE;
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;
721 if (mode_ == NONE) {
722 if (partial_.get()) {
723 partial_->RestoreHeaders(&custom_request_->extra_headers);
724 partial_.reset();
726 next_state_ = STATE_SEND_REQUEST;
727 } else {
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);
734 return OK;
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);
745 if (rv != OK)
746 return rv;
748 next_state_ = STATE_SEND_REQUEST_COMPLETE;
749 rv = network_trans_->Start(request_, io_callback_, net_log_);
750 return rv;
753 int HttpCache::Transaction::DoSendRequestComplete(int result) {
754 if (!cache_)
755 return ERR_UNEXPECTED;
757 if (result == OK) {
758 next_state_ = STATE_SUCCESSFUL_SEND_REQUEST;
759 return OK;
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.
768 DCHECK(response);
769 response_.ssl_info = response->ssl_info;
770 } else if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED) {
771 const HttpResponseInfo* response = network_trans_->GetResponseInfo();
772 DCHECK(response);
773 response_.cert_request_info = response->cert_request_info;
775 return result;
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;
785 return OK;
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
793 // the new response.
794 UpdateTransactionPattern(PATTERN_NOT_COVERED);
795 response_ = HttpResponseInfo();
796 network_trans_.reset();
797 new_response_ = NULL;
798 next_state_ = STATE_SEND_REQUEST;
799 return OK;
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_;
811 return OK;
814 if (mode_ == WRITE)
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);
821 DCHECK_EQ(OK, ret);
823 mode_ = NONE;
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;
831 return OK;
833 UpdateTransactionPattern(PATTERN_ENTRY_UPDATED);
834 mode_ = WRITE;
837 next_state_ = STATE_OVERWRITE_CACHED_RESPONSE;
838 return OK;
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);
849 if (!cache_)
850 return ERR_UNEXPECTED;
852 if (result > 0)
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)
858 return result;
860 next_state_ = STATE_CACHE_WRITE_DATA;
861 return result;
864 int HttpCache::Transaction::DoInitEntry() {
865 DCHECK(!new_entry_);
867 if (!cache_)
868 return ERR_UNEXPECTED;
870 if (mode_ == WRITE) {
871 next_state_ = STATE_DOOM_ENTRY;
872 return OK;
875 next_state_ = STATE_OPEN_ENTRY;
876 return OK;
879 int HttpCache::Transaction::DoOpenEntry() {
880 DCHECK(!new_entry_);
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;
896 if (result == OK) {
897 next_state_ = STATE_ADD_TO_ENTRY;
898 return OK;
901 if (result == ERR_CACHE_RACE) {
902 next_state_ = STATE_INIT_ENTRY;
903 return OK;
906 if (request_->method == "PUT" || request_->method == "DELETE") {
907 DCHECK(mode_ == READ_WRITE || mode_ == WRITE);
908 mode_ = NONE;
909 next_state_ = STATE_SEND_REQUEST;
910 return OK;
913 if (mode_ == READ_WRITE) {
914 mode_ = WRITE;
915 next_state_ = STATE_CREATE_ENTRY;
916 return OK;
918 if (mode_ == UPDATE) {
919 // There is no cache entry to update; proceed without caching.
920 mode_ = NONE;
921 next_state_ = STATE_SEND_REQUEST;
922 return OK;
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,
928 // so we must fail.
929 return ERR_CACHE_MISS;
932 int HttpCache::Transaction::DoCreateEntry() {
933 DCHECK(!new_entry_);
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,
947 result);
948 cache_pending_ = false;
949 next_state_ = STATE_ADD_TO_ENTRY;
951 if (result == ERR_CACHE_RACE) {
952 next_state_ = STATE_INIT_ENTRY;
953 return OK;
956 if (result != OK) {
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";
962 mode_ = NONE;
963 if (partial_.get())
964 partial_->RestoreHeaders(&custom_request_->extra_headers);
965 next_state_ = STATE_SEND_REQUEST;
967 return OK;
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;
987 return OK;
990 int HttpCache::Transaction::DoAddToEntry() {
991 DCHECK(new_entry_);
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,
1002 result);
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"),
1012 entry_lock_wait);
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"),
1019 entry_lock_wait);
1022 entry_lock_waiting_since_ = TimeTicks();
1023 DCHECK(new_entry_);
1024 cache_pending_ = false;
1026 if (result == ERR_CACHE_RACE) {
1027 new_entry_ = NULL;
1028 next_state_ = STATE_INIT_ENTRY;
1029 return OK;
1032 if (result != OK) {
1033 // If there is a failure, the cache should have taken care of new_entry_.
1034 NOTREACHED();
1035 new_entry_ = NULL;
1036 return result;
1039 entry_ = new_entry_;
1040 new_entry_ = NULL;
1042 if (mode_ == WRITE) {
1043 if (partial_.get())
1044 partial_->RestoreHeaders(&custom_request_->extra_headers);
1045 next_state_ = STATE_SEND_REQUEST;
1046 } else {
1047 // We have to read the headers from the cached entry.
1048 DCHECK(mode_ & READ_META);
1049 next_state_ = STATE_CACHE_READ_RESPONSE;
1051 return OK;
1054 // We may end up here multiple times for a given request.
1055 int HttpCache::Transaction::DoStartPartialCacheValidation() {
1056 if (mode_ == NONE)
1057 return OK;
1059 next_state_ = STATE_COMPLETE_PARTIAL_CACHE_VALIDATION;
1060 return partial_->ShouldValidateCache(entry_->disk_entry, io_callback_);
1063 int HttpCache::Transaction::DoCompletePartialCacheValidation(int result) {
1064 if (!result) {
1065 // This is the end of the request.
1066 if (mode_ & WRITE) {
1067 DoneWritingToEntry(true);
1068 } else {
1069 cache_->DoneReadingFromEntry(entry_, this);
1070 entry_ = NULL;
1072 return result;
1075 if (result < 0)
1076 return result;
1078 partial_->PrepareCacheValidation(entry_->disk_entry,
1079 &custom_request_->extra_headers);
1081 if (reading_ && partial_->IsCurrentRangeCached()) {
1082 next_state_ = STATE_CACHE_READ_DATA;
1083 return OK;
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;
1092 int rv = OK;
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);
1102 DCHECK_EQ(OK, ret);
1104 } else {
1105 // If we are already reading, we already updated the headers for this
1106 // request; doing it again will change Content-Length.
1107 if (!reading_) {
1108 target_state_ = STATE_UPDATE_CACHED_RESPONSE_COMPLETE;
1109 next_state_ = STATE_CACHE_WRITE_RESPONSE;
1110 rv = OK;
1113 return rv;
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_);
1129 mode_ = READ;
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();
1143 return OK;
1145 next_state_ = STATE_OVERWRITE_CACHED_RESPONSE;
1146 return OK;
1149 int HttpCache::Transaction::DoOverwriteCachedResponse() {
1150 if (mode_ & READ) {
1151 next_state_ = STATE_PARTIAL_HEADERS_RECEIVED;
1152 return OK;
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);
1164 if (partial_.get())
1165 partial_->FixResponseHeaders(response_.headers, true);
1166 next_state_ = STATE_PARTIAL_HEADERS_RECEIVED;
1167 return OK;
1170 target_state_ = STATE_TRUNCATE_CACHED_DATA;
1171 next_state_ = truncated_ ? STATE_CACHE_WRITE_TRUNCATED_RESPONSE :
1172 STATE_CACHE_WRITE_RESPONSE;
1173 return OK;
1176 int HttpCache::Transaction::DoTruncateCachedData() {
1177 next_state_ = STATE_TRUNCATE_CACHED_DATA_COMPLETE;
1178 if (!entry_)
1179 return OK;
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) {
1188 if (entry_) {
1189 ReportCacheActionFinish();
1190 if (net_log_.IsLoggingAllEvents()) {
1191 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_WRITE_DATA,
1192 result);
1196 next_state_ = STATE_TRUNCATE_CACHED_METADATA;
1197 return OK;
1200 int HttpCache::Transaction::DoTruncateCachedMetadata() {
1201 next_state_ = STATE_TRUNCATE_CACHED_METADATA_COMPLETE;
1202 if (!entry_)
1203 return OK;
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) {
1212 if (entry_) {
1213 ReportCacheActionFinish();
1214 if (net_log_.IsLoggingAllEvents()) {
1215 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_WRITE_INFO,
1216 result);
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;
1225 return OK;
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())
1235 return OK;
1237 if (reading_) {
1238 if (network_trans_.get()) {
1239 next_state_ = STATE_NETWORK_READ;
1240 } else {
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);
1248 return OK;
1251 int HttpCache::Transaction::DoCacheReadResponse() {
1252 DCHECK(entry_);
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)
1277 truncated_ = false;
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
1282 // cache entry.
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.
1292 switch (mode_) {
1293 case READ:
1294 UpdateTransactionPattern(PATTERN_ENTRY_USED);
1295 result = BeginCacheRead();
1296 break;
1297 case READ_WRITE:
1298 result = BeginPartialCacheValidation();
1299 break;
1300 case UPDATE:
1301 result = BeginExternallyConditionalizedRequest();
1302 break;
1303 case WRITE:
1304 default:
1305 NOTREACHED();
1306 result = ERR_FAILED;
1308 return result;
1311 int HttpCache::Transaction::DoCacheWriteResponse() {
1312 if (entry_) {
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() {
1321 if (entry_) {
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;
1332 if (!entry_)
1333 return OK;
1334 ReportCacheActionFinish();
1335 if (net_log_.IsLoggingAllEvents()) {
1336 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_WRITE_INFO,
1337 result);
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);
1345 return OK;
1348 int HttpCache::Transaction::DoCacheReadMetadata() {
1349 DCHECK(entry_);
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(),
1360 io_callback_);
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);
1368 return OK;
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);
1380 if (!cache_)
1381 return ERR_UNEXPECTED;
1383 return ValidateEntryHeadersAndContinue();
1386 int HttpCache::Transaction::DoCacheReadData() {
1387 DCHECK(entry_);
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_,
1395 io_callback_);
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,
1406 result);
1409 if (!cache_)
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);
1419 if (result > 0) {
1420 read_offset_ += result;
1421 bytes_read_from_cache_ += result;
1422 } else if (result == 0) { // End of file.
1423 RecordHistograms();
1424 cache_->DoneReadingFromEntry(entry_, this);
1425 entry_ = NULL;
1426 } else {
1427 return OnCacheReadError(result, false);
1429 return result;
1432 int HttpCache::Transaction::DoCacheWriteData(int num_bytes) {
1433 next_state_ = STATE_CACHE_WRITE_DATA_COMPLETE;
1434 write_len_ = num_bytes;
1435 if (entry_) {
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) {
1445 if (entry_) {
1446 ReportCacheActionFinish();
1447 if (net_log_.IsLoggingAllEvents()) {
1448 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_WRITE_DATA,
1449 result);
1452 // Balance the AddRef from DoCacheWriteData.
1453 if (!cache_)
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
1461 // the network.
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);
1477 if (result == 0) {
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);
1485 return result;
1488 //-----------------------------------------------------------------------------
1490 void HttpCache::Transaction::SetRequest(const BoundNetLog& net_log,
1491 const HttpRequestInfo* request) {
1492 net_log_ = net_log;
1493 request_ = request;
1494 effective_load_flags_ = request_->load_flags;
1496 switch (cache_->mode()) {
1497 case NORMAL:
1498 break;
1499 case RECORD:
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
1503 // prematurely.
1504 effective_load_flags_ |= LOAD_BYPASS_CACHE;
1505 break;
1506 case PLAYBACK:
1507 // When in playback mode, we want to load exclusively from the cache.
1508 effective_load_flags_ |= LOAD_ONLY_FROM_CACHE;
1509 break;
1510 case DISABLE:
1511 effective_load_flags_ |= LOAD_DISABLE_CACHE;
1512 break;
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;
1526 int load_flag;
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))
1537 range_found = true;
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;
1542 break;
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;
1558 break;
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);
1586 } else {
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())
1599 return true;
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)
1604 return false;
1606 if (effective_load_flags_ & LOAD_DISABLE_CACHE)
1607 return true;
1609 if (request_->method == "GET")
1610 return false;
1612 if (request_->method == "POST" &&
1613 request_->upload_data && request_->upload_data->identifier())
1614 return false;
1616 if (request_->method == "PUT" && request_->upload_data)
1617 return false;
1619 if (request_->method == "DELETE")
1620 return false;
1622 // TODO(darin): add support for caching HEAD responses
1623 return true;
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()) {
1629 NOTREACHED();
1630 return ERR_CACHE_MISS;
1633 // We don't have the whole resource.
1634 if (truncated_)
1635 return ERR_CACHE_MISS;
1637 if (entry_->disk_entry->GetDataSize(kMetadataIndex))
1638 next_state_ = STATE_CACHE_READ_METADATA;
1640 return OK;
1643 int HttpCache::Transaction::BeginCacheValidation() {
1644 DCHECK(mode_ == READ_WRITE);
1646 bool skip_validation = effective_load_flags_ & LOAD_PREFERRING_CACHE ||
1647 !RequiresValidation();
1649 if (truncated_) {
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;
1671 return OK;
1672 } else {
1673 partial_.reset();
1676 cache_->ConvertWriterToReader(entry_);
1677 mode_ = READ;
1679 if (entry_->disk_entry->GetDataSize(kMetadataIndex))
1680 next_state_ = STATE_CACHE_READ_METADATA;
1681 } else {
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);
1688 if (partial_.get())
1689 return DoRestartPartialRequest();
1691 DCHECK_NE(206, response_.headers->response_code());
1692 mode_ = WRITE;
1694 next_state_ = STATE_SEND_REQUEST;
1696 return OK;
1699 int HttpCache::Transaction::BeginPartialCacheValidation() {
1700 DCHECK(mode_ == READ_WRITE);
1702 if (response_.headers->response_code() != 206 && !partial_.get() &&
1703 !truncated_)
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;
1710 return OK;
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,
1728 truncated_)) {
1729 return DoRestartPartialRequest();
1732 if (response_.headers->response_code() == 206)
1733 is_sparse_ = true;
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;
1741 return OK;
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())
1750 continue;
1751 // Retrieve either the cached response's "etag" or "last-modified" header.
1752 std::string validator;
1753 response_.headers->EnumerateHeader(
1754 NULL,
1755 kValidationHeaders[i].related_response_header_name,
1756 &validator);
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;
1768 return OK;
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)
1779 return DoLoop(rv);
1780 return rv;
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)
1792 return DoLoop(rv);
1793 return rv;
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)
1805 return DoLoop(rv);
1806 return rv;
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)
1815 return false;
1817 if (effective_load_flags_ & LOAD_VALIDATE_CACHE)
1818 return true;
1820 if (request_->method == "PUT" || request_->method == "DELETE")
1821 return true;
1823 if (response_.headers->RequiresValidation(
1824 response_.request_time, response_.response_time, Time::Now()))
1825 return true;
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))
1830 return true;
1832 return false;
1835 bool HttpCache::Transaction::ConditionalizeRequest() {
1836 DCHECK(response_.headers);
1838 if (request_->method == "PUT" || request_->method == "DELETE")
1839 return false;
1841 // This only makes sense for cached 200 or 206 responses.
1842 if (response_.headers->response_code() != 200 &&
1843 response_.headers->response_code() != 206)
1844 return false;
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))
1861 etag_value.clear();
1863 if (etag_value.empty() && last_modified_value.empty())
1864 return false;
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() &&
1874 !invalid_range_;
1876 if (!etag_value.empty()) {
1877 if (use_if_range) {
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);
1882 } else {
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
1887 // the request.
1888 if (partial_.get() && !partial_->IsCurrentRangeCached())
1889 return true;
1892 if (!last_modified_value.empty()) {
1893 if (use_if_range) {
1894 custom_request_->extra_headers.SetHeader(
1895 HttpRequestHeaders::kIfRange, last_modified_value);
1896 } else {
1897 custom_request_->extra_headers.SetHeader(
1898 HttpRequestHeaders::kIfModifiedSince, last_modified_value);
1902 return true;
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
1913 // for.
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
1922 // request.
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")
1930 return true;
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
1935 // this request
1936 DCHECK(!reading_);
1937 if (partial_response || response_code == 200) {
1938 DoomPartialEntry(true);
1939 mode_ = NONE;
1940 } else {
1941 if (response_code == 304)
1942 FailRangeRequest();
1943 IgnoreRangeRequest();
1945 return true;
1948 if (!partial_.get()) {
1949 // We are not expecting 206 but we may have one.
1950 if (partial_response)
1951 IgnoreRangeRequest();
1953 return true;
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)
1962 failure = true;
1964 if (response_code == 304 && partial_->ResponseHeadersOK(headers))
1965 return true;
1966 } else {
1967 // We asked for "If-Range: " so a 206 means just another range.
1968 if (partial_response && partial_->ResponseHeadersOK(headers)) {
1969 handling_206_ = true;
1970 return 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_);
1982 partial_.reset();
1983 truncated_ = false;
1984 return true;
1988 // 304 is not expected here, but we'll spare the entry (unless it was
1989 // truncated).
1990 if (truncated_)
1991 failure = true;
1994 if (failure) {
1995 // We cannot truncate this entry, it has to be deleted.
1996 UpdateTransactionPattern(PATTERN_NOT_COVERED);
1997 DoomPartialEntry(false);
1998 mode_ = NONE;
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);
2003 partial_.reset();
2004 truncated_ = false;
2005 return false;
2007 LOG(WARNING) << "Failed to revalidate partial entry";
2008 partial_.reset();
2009 return true;
2012 IgnoreRangeRequest();
2013 return true;
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);
2029 entry_ = NULL;
2030 mode_ = NONE;
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) {
2039 read_buf_ = data;
2040 io_buf_len_ = data_len;
2041 next_state_ = STATE_NETWORK_READ;
2042 return DoLoop(OK);
2045 int HttpCache::Transaction::ReadFromEntry(IOBuffer* data, int data_len) {
2046 read_buf_ = data;
2047 io_buf_len_ = data_len;
2048 next_state_ = STATE_CACHE_READ_DATA;
2049 return DoLoop(OK);
2052 int HttpCache::Transaction::WriteToEntry(int index, int offset,
2053 IOBuffer* data, int data_len,
2054 const CompletionCallback& callback) {
2055 if (!entry_)
2056 return data_len;
2058 int rv = 0;
2059 if (!partial_.get() || !data_len) {
2060 rv = entry_->disk_entry->WriteData(index, offset, data, data_len, callback,
2061 true);
2062 } else {
2063 rv = partial_->CacheWrite(entry_->disk_entry, data, data_len, callback);
2065 return rv;
2068 int HttpCache::Transaction::WriteResponseInfoToEntry(bool truncated) {
2069 next_state_ = STATE_CACHE_WRITE_RESPONSE_COMPLETE;
2070 if (!entry_)
2071 return OK;
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);
2086 return OK;
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);
2093 if (truncated) {
2094 DCHECK_EQ(200, response_.headers->response_code());
2097 scoped_refptr<PickledIOBuffer> data(new PickledIOBuffer());
2098 response_.Persist(data->pickle(), skip_transient_headers, truncated);
2099 data->Done();
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)
2109 return data_len;
2111 int current_size = entry_->disk_entry->GetDataSize(kResponseContentIndex);
2112 return WriteToEntry(kResponseContentIndex, current_size, data, data_len,
2113 callback);
2116 void HttpCache::Transaction::DoneWritingToEntry(bool success) {
2117 if (!entry_)
2118 return;
2120 RecordHistograms();
2122 cache_->DoneWritingToEntry(entry_, success);
2123 entry_ = NULL;
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.
2131 if (cache_)
2132 cache_->DoomActiveEntry(cache_key_);
2134 if (restart) {
2135 DCHECK(!reading_);
2136 DCHECK(!network_trans_.get());
2137 cache_->DoneWithEntry(entry_, this, false);
2138 entry_ = NULL;
2139 is_sparse_ = false;
2140 partial_.reset();
2141 next_state_ = STATE_GET_BACKEND;
2142 return OK;
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);
2151 DCHECK_EQ(OK, rv);
2152 cache_->DoneWithEntry(entry_, this, false);
2153 entry_ = NULL;
2154 is_sparse_ = false;
2155 if (delete_object)
2156 partial_.reset(NULL);
2159 int HttpCache::Transaction::DoPartialNetworkReadCompleted(int result) {
2160 partial_->OnNetworkReadCompleted(result);
2162 if (result == 0) {
2163 // We need to move on to the next range.
2164 network_trans_.reset();
2165 next_state_ = STATE_START_PARTIAL_CACHE_VALIDATION;
2167 return result;
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);
2179 return result;
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_);
2186 mode_ = WRITE;
2187 truncated_ = false;
2188 next_state_ = STATE_INIT_ENTRY;
2189 return OK;
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))
2208 return false;
2210 if (request_->method != "GET")
2211 return false;
2213 if (response_.headers->GetContentLength() <= 0 ||
2214 response_.headers->HasHeaderValue("Accept-Ranges", "none") ||
2215 !response_.headers->HasStrongValidators())
2216 return false;
2218 return true;
2221 void HttpCache::Transaction::OnIOComplete(int result) {
2222 DoLoop(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)
2238 return;
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") {
2249 return;
2251 UMA_HISTOGRAM_BOOLEAN("HttpCache.HasPattern",
2252 transaction_pattern_ != PATTERN_NOT_COVERED);
2253 if (transaction_pattern_ == PATTERN_NOT_COVERED)
2254 return;
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();
2263 DCHECK(
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));
2269 int resource_size;
2270 if (transaction_pattern_ == PATTERN_ENTRY_NOT_CACHED ||
2271 transaction_pattern_ == PATTERN_ENTRY_UPDATED) {
2272 resource_size = bytes_read_from_network_;
2273 } else {
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);
2290 return;
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);
2325 break;
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);
2338 break;
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);
2351 break;
2353 default:
2354 NOTREACHED();
2358 } // namespace net