diff options
author | rvargas@google.com <rvargas@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-11-11 19:59:23 +0000 |
---|---|---|
committer | rvargas@google.com <rvargas@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-11-11 19:59:23 +0000 |
commit | 983a1da3308b3a4605afdf09c301a01c635004e7 (patch) | |
tree | 706622f65d3d801b15a16170ce8ecf50713109c5 /net/http/http_cache.cc | |
parent | 0d85ea6e9786806964ec2a05a12dd2e0a05bdb0c (diff) | |
download | chromium_src-983a1da3308b3a4605afdf09c301a01c635004e7.zip chromium_src-983a1da3308b3a4605afdf09c301a01c635004e7.tar.gz chromium_src-983a1da3308b3a4605afdf09c301a01c635004e7.tar.bz2 |
Http Cache: Split HttpCache::Transaction to its own set
of files.
No real code change... I'm just moving code around.
BUG=26729
TEST=none
Review URL: http://codereview.chromium.org/387017
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@31693 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/http/http_cache.cc')
-rw-r--r-- | net/http/http_cache.cc | 1583 |
1 files changed, 1 insertions, 1582 deletions
diff --git a/net/http/http_cache.cc b/net/http/http_cache.cc index 8f5db67..1fae4ad 100644 --- a/net/http/http_cache.cc +++ b/net/http/http_cache.cc @@ -5,7 +5,6 @@ #include "net/http/http_cache.h" #include <algorithm> -#include <string> #include "base/compiler_specific.h" @@ -17,23 +16,15 @@ #include "base/pickle.h" #include "base/ref_counted.h" #include "base/string_util.h" -#include "base/time.h" #include "net/base/io_buffer.h" -#include "net/base/load_flags.h" -#include "net/base/load_log.h" #include "net/base/net_errors.h" -#include "net/base/ssl_cert_request_info.h" #include "net/disk_cache/disk_cache.h" +#include "net/http/http_cache_transaction.h" #include "net/http/http_network_layer.h" #include "net/http/http_network_session.h" #include "net/http/http_request_info.h" #include "net/http/http_response_headers.h" #include "net/http/http_response_info.h" -#include "net/http/http_transaction.h" -#include "net/http/http_util.h" -#include "net/http/partial_data.h" - -using base::Time; namespace net { @@ -45,74 +36,6 @@ enum { //----------------------------------------------------------------------------- -struct HeaderNameAndValue { - const char* name; - const char* value; -}; - -// If the request includes one of these request headers, then avoid caching -// to avoid getting confused. -static const HeaderNameAndValue kPassThroughHeaders[] = { - { "if-unmodified-since", NULL }, // causes unexpected 412s - { "if-match", NULL }, // causes unexpected 412s - { "if-range", NULL }, - { NULL, NULL } -}; - -struct ValidationHeaderInfo { - const char* request_header_name; - const char* related_response_header_name; -}; - -static const ValidationHeaderInfo kValidationHeaders[] = { - { "if-modified-since", "last-modified" }, - { "if-none-match", "etag" }, -}; - -// Helper struct to pair a header name with its value, for -// headers used to validate cache entries. -struct ValidationHeaders { - ValidationHeaders() : initialized(false) {} - - std::string values[ARRAYSIZE_UNSAFE(kValidationHeaders)]; - bool initialized; -}; - -// If the request includes one of these request headers, then avoid reusing -// our cached copy if any. -static const HeaderNameAndValue kForceFetchHeaders[] = { - { "cache-control", "no-cache" }, - { "pragma", "no-cache" }, - { NULL, NULL } -}; - -// If the request includes one of these request headers, then force our -// cached copy (if any) to be revalidated before reusing it. -static const HeaderNameAndValue kForceValidateHeaders[] = { - { "cache-control", "max-age=0" }, - { NULL, NULL } -}; - -static bool HeaderMatches(const HttpUtil::HeadersIterator& h, - const HeaderNameAndValue* search) { - for (; search->name; ++search) { - if (!LowerCaseEqualsASCII(h.name_begin(), h.name_end(), search->name)) - continue; - - if (!search->value) - return true; - - HttpUtil::ValuesIterator v(h.values_begin(), h.values_end(), ','); - while (v.GetNext()) { - if (LowerCaseEqualsASCII(v.value_begin(), v.value_end(), search->value)) - return true; - } - } - return false; -} - -//----------------------------------------------------------------------------- - HttpCache::ActiveEntry::ActiveEntry(disk_cache::Entry* e) : disk_entry(e), writer(NULL), @@ -127,1510 +50,6 @@ HttpCache::ActiveEntry::~ActiveEntry() { //----------------------------------------------------------------------------- -class HttpCache::Transaction : public HttpTransaction { - public: - Transaction(HttpCache* cache, bool enable_range_support) - : request_(NULL), - cache_(cache->AsWeakPtr()), - entry_(NULL), - network_trans_(NULL), - callback_(NULL), - mode_(NONE), - reading_(false), - invalid_range_(false), - enable_range_support_(enable_range_support), - truncated_(false), - read_offset_(0), - effective_load_flags_(0), - final_upload_progress_(0), - ALLOW_THIS_IN_INITIALIZER_LIST( - network_info_callback_(this, &Transaction::OnNetworkInfoAvailable)), - ALLOW_THIS_IN_INITIALIZER_LIST( - network_read_callback_(this, &Transaction::OnNetworkReadCompleted)), - ALLOW_THIS_IN_INITIALIZER_LIST( - cache_read_callback_(new CancelableCompletionCallback<Transaction>( - this, &Transaction::OnCacheReadCompleted))), - ALLOW_THIS_IN_INITIALIZER_LIST( - cache_write_callback_(new CancelableCompletionCallback<Transaction>( - this, &Transaction::OnCacheWriteCompleted))), - ALLOW_THIS_IN_INITIALIZER_LIST( - entry_ready_callback_(new CancelableCompletionCallback<Transaction>( - this, &Transaction::OnCacheEntryReady))) { - } - - // Clean up the transaction. - virtual ~Transaction(); - - // HttpTransaction methods: - virtual int Start(const HttpRequestInfo*, CompletionCallback*, LoadLog*); - virtual int RestartIgnoringLastError(CompletionCallback*); - virtual int RestartWithCertificate(X509Certificate* client_cert, - CompletionCallback* callback); - virtual int RestartWithAuth(const std::wstring& username, - const std::wstring& password, - CompletionCallback* callback); - virtual bool IsReadyToRestartForAuth(); - virtual int Read(IOBuffer* buf, int buf_len, CompletionCallback*); - virtual const HttpResponseInfo* GetResponseInfo() const; - virtual LoadState GetLoadState() const; - virtual uint64 GetUploadProgress(void) const; - - // The transaction has the following modes, which apply to how it may access - // its cache entry. - // - // o If the mode of the transaction is NONE, then it is in "pass through" - // mode and all methods just forward to the inner network transaction. - // - // o If the mode of the transaction is only READ, then it may only read from - // the cache entry. - // - // o If the mode of the transaction is only WRITE, then it may only write to - // the cache entry. - // - // o If the mode of the transaction is READ_WRITE, then the transaction may - // optionally modify the cache entry (e.g., possibly corresponding to - // cache validation). - // - // o If the mode of the transaction is UPDATE, then the transaction may - // update existing cache entries, but will never create a new entry or - // respond using the entry read from the cache. - enum Mode { - NONE = 0, - READ_META = 1 << 0, - READ_DATA = 1 << 1, - READ = READ_META | READ_DATA, - WRITE = 1 << 2, - READ_WRITE = READ | WRITE, - UPDATE = READ_META | WRITE, // READ_WRITE & ~READ_DATA - }; - - Mode mode() const { return mode_; } - - const std::string& key() const { return cache_key_; } - - // Associates this transaction with a cache entry. - int AddToEntry(); - - // Called by the HttpCache when the given disk cache entry becomes accessible - // to the transaction. Returns network error code. - int EntryAvailable(ActiveEntry* entry); - - // This transaction is being deleted and we are not done writing to the cache. - // We need to indicate that the response data was truncated. Returns true on - // success. - bool AddTruncatedFlag(); - - private: - // This is a helper function used to trigger a completion callback. It may - // only be called if callback_ is non-null. - void DoCallback(int rv); - - // This will trigger the completion callback if appropriate. - int HandleResult(int rv); - - // Sets request_ and fields derived from it. - void SetRequest(LoadLog* load_log, const HttpRequestInfo* request); - - // Returns true if the request should be handled exclusively by the network - // layer (skipping the cache entirely). - bool ShouldPassThrough(); - - // Called to begin reading from the cache. Returns network error code. - int BeginCacheRead(); - - // Called to begin validating the cache entry. Returns network error code. - int BeginCacheValidation(); - - // Called to begin validating an entry that stores partial content. Returns - // a network error code. - int BeginPartialCacheValidation(); - - // Validates the entry headers against the requested range and continues with - // the validation of the rest of the entry. Returns a network error code. - int ValidateEntryHeadersAndContinue(bool byte_range_requested); - - // Performs the cache validation for the next chunk of data stored by the - // cache. If this chunk is not currently stored, starts the network request - // to fetch it. Returns a network error code. - int ContinuePartialCacheValidation(); - - // Called to start requests which were given an "if-modified-since" or - // "if-none-match" validation header by the caller (NOT when the request was - // conditionalized internally in response to LOAD_VALIDATE_CACHE). - // Returns a network error code. - int BeginExternallyConditionalizedRequest(); - - // Called to begin a network transaction. Returns network error code. - int BeginNetworkRequest(); - - // Called to restart a network transaction after an error. Returns network - // error code. - int RestartNetworkRequest(); - - // Called to restart a network transaction with a client certificate. - // Returns network error code. - int RestartNetworkRequestWithCertificate(X509Certificate* client_cert); - - // Called to restart a network transaction with authentication credentials. - // Returns network error code. - int RestartNetworkRequestWithAuth(const std::wstring& username, - const std::wstring& password); - - // Called to determine if we need to validate the cache entry before using it. - bool RequiresValidation(); - - // Called to make the request conditional (to ask the server if the cached - // copy is valid). Returns true if able to make the request conditional. - bool ConditionalizeRequest(); - - // Makes sure that a 206 response is expected. Returns a network error code. - bool ValidatePartialResponse(const HttpResponseHeaders* headers); - - // Handles a response validation error by bypassing the cache. - void IgnoreRangeRequest(); - - // Reads data from the network. - int ReadFromNetwork(IOBuffer* data, int data_len); - - // Reads data from the cache entry. - int ReadFromEntry(IOBuffer* data, int data_len); - - // Called to populate response_ from the cache entry. - int ReadResponseInfoFromEntry(); - - // Called to write data to the cache entry. If the write fails, then the - // cache entry is destroyed. Future calls to this function will just do - // nothing without side-effect. Returns a network error code. - int WriteToEntry(int index, int offset, IOBuffer* data, int data_len, - CompletionCallback* callback); - - // Called to write response_ to the cache entry. |truncated| indicates if the - // entry should be marked as incomplete. - void WriteResponseInfoToEntry(bool truncated); - - // Called to append response data to the cache entry. Returns a network error - // code. - int AppendResponseDataToEntry(IOBuffer* data, int data_len, - CompletionCallback* callback); - - // Called to truncate response content in the entry. - void TruncateResponseData(); - - // Called when we are done writing to the cache entry. - void DoneWritingToEntry(bool success); - - // Deletes the current partial cache entry (sparse), and optionally removes - // the control object (partial_). - void DoomPartialEntry(bool delete_object); - - // Performs the needed work after receiving data from the network. - int DoNetworkReadCompleted(int result); - - // Performs the needed work after receiving data from the network, when - // working with range requests. - int DoPartialNetworkReadCompleted(int result); - - // Performs the needed work after receiving data from the cache. - int DoCacheReadCompleted(int result); - - // Performs the needed work after receiving data from the cache, when - // working with range requests. - int DoPartialCacheReadCompleted(int result); - - // Performs the needed work after writing data to the cache. - int DoCacheWriteCompleted(int result); - - // Called to signal completion of the network transaction's Start method: - void OnNetworkInfoAvailable(int result); - - // Called to signal completion of the network transaction's Read method: - void OnNetworkReadCompleted(int result); - - // Called to signal completion of the cache's ReadData method: - void OnCacheReadCompleted(int result); - - // Called to signal completion of the cache's WriteData method: - void OnCacheWriteCompleted(int result); - - // Called to signal completion of the cache entry's ReadyForSparseIO method: - void OnCacheEntryReady(int result); - - scoped_refptr<LoadLog> load_log_; - const HttpRequestInfo* request_; - scoped_ptr<HttpRequestInfo> custom_request_; - // If extra_headers specified a "if-modified-since" or "if-none-match", - // |external_validation_| contains the value of those headers. - ValidationHeaders external_validation_; - base::WeakPtr<HttpCache> cache_; - HttpCache::ActiveEntry* entry_; - scoped_ptr<HttpTransaction> network_trans_; - CompletionCallback* callback_; // Consumer's callback. - HttpResponseInfo response_; - HttpResponseInfo auth_response_; - std::string cache_key_; - Mode mode_; - bool reading_; // We are already reading. - bool invalid_range_; // We may bypass the cache for this request. - bool enable_range_support_; - bool truncated_; // We don't have all the response data. - scoped_refptr<IOBuffer> read_buf_; - int read_buf_len_; - int read_offset_; - int effective_load_flags_; - scoped_ptr<PartialData> partial_; // We are dealing with range requests. - uint64 final_upload_progress_; - CompletionCallbackImpl<Transaction> network_info_callback_; - CompletionCallbackImpl<Transaction> network_read_callback_; - scoped_refptr<CancelableCompletionCallback<Transaction> > - cache_read_callback_; - scoped_refptr<CancelableCompletionCallback<Transaction> > - cache_write_callback_; - scoped_refptr<CancelableCompletionCallback<Transaction> > - entry_ready_callback_; -}; - -HttpCache::Transaction::~Transaction() { - if (cache_) { - if (entry_) { - bool cancel_request = reading_ && enable_range_support_; - if (cancel_request) { - if (partial_.get()) { - entry_->disk_entry->CancelSparseIO(); - } else { - cancel_request &= (response_.headers->response_code() == 200); - } - } - - cache_->DoneWithEntry(entry_, this, cancel_request); - } else { - cache_->RemovePendingTransaction(this); - } - } - - // If there is an outstanding callback, mark it as cancelled so running it - // does nothing. - cache_read_callback_->Cancel(); - cache_write_callback_->Cancel(); - - // We could still have a cache read or write in progress, so we just null the - // cache_ pointer to signal that we are dead. See DoCacheReadCompleted. - cache_.reset(); -} - -int HttpCache::Transaction::Start(const HttpRequestInfo* request, - CompletionCallback* callback, - LoadLog* load_log) { - DCHECK(request); - DCHECK(callback); - - // ensure that we only have one asynchronous call at a time. - DCHECK(!callback_); - - if (!cache_) - return ERR_UNEXPECTED; - - SetRequest(load_log, request); - - int rv; - - if (!ShouldPassThrough()) { - cache_key_ = cache_->GenerateCacheKey(request); - - // requested cache access mode - if (effective_load_flags_ & LOAD_ONLY_FROM_CACHE) { - mode_ = READ; - } else if (effective_load_flags_ & LOAD_BYPASS_CACHE) { - mode_ = WRITE; - } else { - mode_ = READ_WRITE; - } - - // Downgrade to UPDATE if the request has been externally conditionalized. - if (external_validation_.initialized) { - if (mode_ & WRITE) { - // Strip off the READ_DATA bit (and maybe add back a READ_META bit - // in case READ was off). - mode_ = UPDATE; - } else { - mode_ = NONE; - } - } - } - - // if must use cache, then we must fail. this can happen for back/forward - // navigations to a page generated via a form post. - if (!(mode_ & READ) && effective_load_flags_ & LOAD_ONLY_FROM_CACHE) - return ERR_CACHE_MISS; - - if (mode_ == NONE) { - if (partial_.get()) - partial_->RestoreHeaders(&custom_request_->extra_headers); - rv = BeginNetworkRequest(); - } else { - rv = AddToEntry(); - } - - // setting this here allows us to check for the existance of a callback_ to - // determine if we are still inside Start. - if (rv == ERR_IO_PENDING) - callback_ = callback; - - return rv; -} - -int HttpCache::Transaction::RestartIgnoringLastError( - CompletionCallback* callback) { - DCHECK(callback); - - // ensure that we only have one asynchronous call at a time. - DCHECK(!callback_); - - if (!cache_) - return ERR_UNEXPECTED; - - int rv = RestartNetworkRequest(); - - if (rv == ERR_IO_PENDING) - callback_ = callback; - - return rv; -} - -int HttpCache::Transaction::RestartWithCertificate( - X509Certificate* client_cert, - CompletionCallback* callback) { - DCHECK(callback); - - // ensure that we only have one asynchronous call at a time. - DCHECK(!callback_); - - if (!cache_) - return ERR_UNEXPECTED; - - int rv = RestartNetworkRequestWithCertificate(client_cert); - - if (rv == ERR_IO_PENDING) - callback_ = callback; - - return rv; -} - -int HttpCache::Transaction::RestartWithAuth( - const std::wstring& username, - const std::wstring& password, - CompletionCallback* callback) { - DCHECK(auth_response_.headers); - DCHECK(callback); - - // Ensure that we only have one asynchronous call at a time. - DCHECK(!callback_); - - if (!cache_) - return ERR_UNEXPECTED; - - // Clear the intermediate response since we are going to start over. - auth_response_ = HttpResponseInfo(); - - int rv = RestartNetworkRequestWithAuth(username, password); - - if (rv == ERR_IO_PENDING) - callback_ = callback; - - return rv; -} - -bool HttpCache::Transaction::IsReadyToRestartForAuth() { - if (!network_trans_.get()) - return false; - return network_trans_->IsReadyToRestartForAuth(); -} - -int HttpCache::Transaction::Read(IOBuffer* buf, int buf_len, - CompletionCallback* callback) { - DCHECK(buf); - DCHECK_GT(buf_len, 0); - DCHECK(callback); - - DCHECK(!callback_); - - if (!cache_) - return ERR_UNEXPECTED; - - // If we have an intermediate auth response at this point, then it means the - // user wishes to read the network response (the error page). If there is a - // previous response in the cache then we should leave it intact. - if (auth_response_.headers && mode_ != NONE) { - DCHECK(mode_ & WRITE); - DoneWritingToEntry(mode_ == READ_WRITE); - mode_ = NONE; - } - - reading_ = true; - int rv; - - switch (mode_) { - case READ_WRITE: - DCHECK(partial_.get()); - if (!network_trans_.get()) { - // We are just reading from the cache, but we may be writing later. - rv = ReadFromEntry(buf, buf_len); - break; - } - case NONE: - case WRITE: - DCHECK(network_trans_.get()); - rv = ReadFromNetwork(buf, buf_len); - break; - case READ: - rv = ReadFromEntry(buf, buf_len); - break; - default: - NOTREACHED(); - rv = ERR_FAILED; - } - - if (rv == ERR_IO_PENDING) { - DCHECK(!callback_); - callback_ = callback; - } - return rv; -} - -const HttpResponseInfo* HttpCache::Transaction::GetResponseInfo() const { - // Null headers means we encountered an error or haven't a response yet - if (auth_response_.headers) - return &auth_response_; - return (response_.headers || response_.ssl_info.cert || - response_.cert_request_info) ? &response_ : NULL; -} - -LoadState HttpCache::Transaction::GetLoadState() const { - if (network_trans_.get()) - return network_trans_->GetLoadState(); - if (entry_ || !request_) - return LOAD_STATE_IDLE; - return LOAD_STATE_WAITING_FOR_CACHE; -} - -uint64 HttpCache::Transaction::GetUploadProgress() const { - if (network_trans_.get()) - return network_trans_->GetUploadProgress(); - return final_upload_progress_; -} - -int HttpCache::Transaction::AddToEntry() { - ActiveEntry* entry = NULL; - - if (!cache_) - return ERR_UNEXPECTED; - - if (mode_ == WRITE) { - cache_->DoomEntry(cache_key_); - } else { - entry = cache_->FindActiveEntry(cache_key_); - if (!entry) { - LoadLog::BeginEvent(load_log_, LoadLog::TYPE_HTTP_CACHE_OPEN_ENTRY); - entry = cache_->OpenEntry(cache_key_); - LoadLog::EndEvent(load_log_, LoadLog::TYPE_HTTP_CACHE_OPEN_ENTRY); - if (!entry) { - if (mode_ == READ_WRITE) { - mode_ = WRITE; - } else if (mode_ == UPDATE) { - // There is no cache entry to update; proceed without caching. - mode_ = NONE; - return BeginNetworkRequest(); - } else { - if (cache_->mode() == PLAYBACK) - DLOG(INFO) << "Playback Cache Miss: " << request_->url; - - // entry does not exist, and not permitted to create a new entry, so - // we must fail. - return HandleResult(ERR_CACHE_MISS); - } - } - } - } - - if (mode_ == WRITE) { - DCHECK(!entry); - LoadLog::BeginEvent(load_log_, LoadLog::TYPE_HTTP_CACHE_CREATE_ENTRY); - entry = cache_->CreateEntry(cache_key_); - LoadLog::EndEvent(load_log_, LoadLog::TYPE_HTTP_CACHE_CREATE_ENTRY); - if (!entry) { - DLOG(WARNING) << "unable to create cache entry"; - mode_ = NONE; - if (partial_.get()) - partial_->RestoreHeaders(&custom_request_->extra_headers); - return BeginNetworkRequest(); - } - } - - - LoadLog::BeginEvent(load_log_, LoadLog::TYPE_HTTP_CACHE_WAITING); - return cache_->AddTransactionToEntry(entry, this); -} - -int HttpCache::Transaction::EntryAvailable(ActiveEntry* entry) { - LoadLog::EndEvent(load_log_, LoadLog::TYPE_HTTP_CACHE_WAITING); - - // We now have access to the cache entry. - // - // o if we are the writer for the transaction, then we can start the network - // transaction. - // - // o if we are a reader for the transaction, then we can start reading the - // cache entry. - // - // o if we can read or write, then we should check if the cache entry needs - // to be validated and then issue a network request if needed or just read - // from the cache if the cache entry is already valid. - // - // o if we are set to UPDATE, then we are handling an externally - // conditionalized request (if-modified-since / if-none-match). We read - // the cache entry, and check if the request headers define a validation - // request. - // - int rv; - entry_ = entry; - switch (mode_) { - case READ: - rv = BeginCacheRead(); - break; - case WRITE: - if (partial_.get()) - partial_->RestoreHeaders(&custom_request_->extra_headers); - rv = BeginNetworkRequest(); - break; - case READ_WRITE: - rv = BeginPartialCacheValidation(); - break; - case UPDATE: - rv = BeginExternallyConditionalizedRequest(); - break; - default: - NOTREACHED(); - rv = ERR_FAILED; - } - return rv; -} - -bool HttpCache::Transaction::AddTruncatedFlag() { - DCHECK(mode_ & WRITE); - - // Don't set the flag for sparse entries. - if (partial_.get()) - return true; - - // Double check that there is something worth keeping. - if (!entry_->disk_entry->GetDataSize(kResponseContentIndex)) - return false; - - truncated_ = true; - WriteResponseInfoToEntry(true); - return true; -} - -void HttpCache::Transaction::DoCallback(int rv) { - DCHECK(rv != ERR_IO_PENDING); - DCHECK(callback_); - - // since Run may result in Read being called, clear callback_ up front. - CompletionCallback* c = callback_; - callback_ = NULL; - c->Run(rv); -} - -int HttpCache::Transaction::HandleResult(int rv) { - DCHECK(rv != ERR_IO_PENDING); - if (callback_) - DoCallback(rv); - return rv; -} - -void HttpCache::Transaction::SetRequest(LoadLog* load_log, - const HttpRequestInfo* request) { - load_log_ = load_log; - request_ = request; - effective_load_flags_ = request_->load_flags; - - switch (cache_->mode()) { - case NORMAL: - break; - case RECORD: - // When in record mode, we want to NEVER load from the cache. - // The reason for this is beacuse we save the Set-Cookie headers - // (intentionally). If we read from the cache, we replay them - // prematurely. - effective_load_flags_ |= LOAD_BYPASS_CACHE; - break; - case PLAYBACK: - // When in playback mode, we want to load exclusively from the cache. - effective_load_flags_ |= LOAD_ONLY_FROM_CACHE; - break; - case DISABLE: - effective_load_flags_ |= LOAD_DISABLE_CACHE; - break; - } - - // Some headers imply load flags. The order here is significant. - // - // LOAD_DISABLE_CACHE : no cache read or write - // LOAD_BYPASS_CACHE : no cache read - // LOAD_VALIDATE_CACHE : no cache read unless validation - // - // The former modes trump latter modes, so if we find a matching header we - // can stop iterating kSpecialHeaders. - // - static const struct { - const HeaderNameAndValue* search; - int load_flag; - } kSpecialHeaders[] = { - { kPassThroughHeaders, LOAD_DISABLE_CACHE }, - { kForceFetchHeaders, LOAD_BYPASS_CACHE }, - { kForceValidateHeaders, LOAD_VALIDATE_CACHE }, - }; - - std::string new_extra_headers; - bool range_found = false; - bool external_validation_error = false; - - // scan request headers to see if we have any that would impact our load flags - HttpUtil::HeadersIterator it(request_->extra_headers.begin(), - request_->extra_headers.end(), - "\r\n"); - while (it.GetNext()) { - if (!LowerCaseEqualsASCII(it.name(), "range")) { - new_extra_headers.append(it.name_begin(), it.values_end()); - new_extra_headers.append("\r\n"); - } else { - if (enable_range_support_) { - range_found = true; - } else { - effective_load_flags_ |= LOAD_DISABLE_CACHE; - continue; - } - } - for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kSpecialHeaders); ++i) { - if (HeaderMatches(it, kSpecialHeaders[i].search)) { - effective_load_flags_ |= kSpecialHeaders[i].load_flag; - break; - } - } - - // Check for conditionalization headers which may correspond with a - // cache validation request. - for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kValidationHeaders); ++i) { - const ValidationHeaderInfo& info = kValidationHeaders[i]; - if (LowerCaseEqualsASCII(it.name_begin(), it.name_end(), - info.request_header_name)) { - if (!external_validation_.values[i].empty() || it.values().empty()) - external_validation_error = true; - external_validation_.values[i] = it.values(); - external_validation_.initialized = true; - break; - } - } - } - - // We don't support ranges and validation headers. - if (range_found && external_validation_.initialized) { - LOG(WARNING) << "Byte ranges AND validation headers found."; - effective_load_flags_ |= LOAD_DISABLE_CACHE; - } - - // If there is more than one validation header, we can't treat this request as - // a cache validation, since we don't know for sure which header the server - // will give us a response for (and they could be contradictory). - if (external_validation_error) { - LOG(WARNING) << "Multiple or malformed validation headers found."; - effective_load_flags_ |= LOAD_DISABLE_CACHE; - } - - if (range_found && !(effective_load_flags_ & LOAD_DISABLE_CACHE)) { - partial_.reset(new PartialData); - if (partial_->Init(request_->extra_headers)) { - // We will be modifying the actual range requested to the server, so - // let's remove the header here. - custom_request_.reset(new HttpRequestInfo(*request_)); - request_ = custom_request_.get(); - custom_request_->extra_headers = new_extra_headers; - partial_->SetHeaders(new_extra_headers); - } else { - // The range is invalid or we cannot handle it properly. - LOG(WARNING) << "Invalid byte range found."; - effective_load_flags_ |= LOAD_DISABLE_CACHE; - partial_.reset(NULL); - } - } -} - -bool HttpCache::Transaction::ShouldPassThrough() { - // We may have a null disk_cache if there is an error we cannot recover from, - // like not enough disk space, or sharing violations. - if (!cache_->disk_cache_.get()) - return true; - - // When using the record/playback modes, we always use the cache - // and we never pass through. - if (cache_->mode() == RECORD || cache_->mode() == PLAYBACK) - return false; - - if (effective_load_flags_ & LOAD_DISABLE_CACHE) - return true; - - if (request_->method == "GET") - return false; - - if (request_->method == "POST" && - request_->upload_data && request_->upload_data->identifier()) - return false; - - // TODO(darin): add support for caching HEAD responses - return true; -} - -int HttpCache::Transaction::BeginCacheRead() { - DCHECK(mode_ == READ); - - // Read response headers. - int rv = ReadResponseInfoFromEntry(); - if (rv != OK) - return HandleResult(rv); - - // We don't support any combination of LOAD_ONLY_FROM_CACHE and byte ranges. - if (response_.headers->response_code() == 206 || partial_.get()) { - NOTREACHED(); - return HandleResult(ERR_CACHE_MISS); - } - - // We don't have the whole resource. - if (truncated_) - return HandleResult(ERR_CACHE_MISS); - - return HandleResult(rv); -} - -int HttpCache::Transaction::BeginCacheValidation() { - DCHECK(mode_ == READ_WRITE); - - if ((effective_load_flags_ & LOAD_PREFERRING_CACHE || - !RequiresValidation()) && !partial_.get()) { - cache_->ConvertWriterToReader(entry_); - mode_ = READ; - } else { - // Make the network request conditional, to see if we may reuse our cached - // response. If we cannot do so, then we just resort to a normal fetch. - // Our mode remains READ_WRITE for a conditional request. We'll switch to - // either READ or WRITE mode once we hear back from the server. - if (!ConditionalizeRequest()) - mode_ = WRITE; - return BeginNetworkRequest(); - } - return HandleResult(OK); -} - -int HttpCache::Transaction::BeginPartialCacheValidation() { - DCHECK(mode_ == READ_WRITE); - - int rv = ReadResponseInfoFromEntry(); - if (rv != OK) { - DCHECK(rv != ERR_IO_PENDING); - return HandleResult(rv); - } - - if (response_.headers->response_code() != 206 && !partial_.get() && - !truncated_) - return BeginCacheValidation(); - - if (!enable_range_support_) - return BeginCacheValidation(); - - bool byte_range_requested = partial_.get() != NULL; - if (byte_range_requested) { - if (OK != entry_->disk_entry->ReadyForSparseIO(entry_ready_callback_)) - return ERR_IO_PENDING; - } else { - // The request is not for a range, but we have stored just ranges. - partial_.reset(new PartialData()); - partial_->SetHeaders(request_->extra_headers); - if (!custom_request_.get()) { - custom_request_.reset(new HttpRequestInfo(*request_)); - request_ = custom_request_.get(); - } - } - - return ValidateEntryHeadersAndContinue(byte_range_requested); -} - -int HttpCache::Transaction::ValidateEntryHeadersAndContinue( - bool byte_range_requested) { - DCHECK(mode_ == READ_WRITE); - - if (!cache_) - return HandleResult(ERR_UNEXPECTED); - - if (!partial_->UpdateFromStoredHeaders(response_.headers, entry_->disk_entry, - truncated_)) { - // The stored data cannot be used. Get rid of it and restart this request. - // We need to also reset the |truncated_| flag as a new entry is created. - DoomPartialEntry(!byte_range_requested); - mode_ = WRITE; - truncated_ = false; - return AddToEntry(); - } - - if (!partial_->IsRequestedRangeOK()) { - // The stored data is fine, but the request may be invalid. - invalid_range_ = true; - } - - return ContinuePartialCacheValidation(); -} - -int HttpCache::Transaction::ContinuePartialCacheValidation() { - DCHECK(mode_ == READ_WRITE); - int rv = partial_->PrepareCacheValidation(entry_->disk_entry, - &custom_request_->extra_headers); - - if (!rv) { - // Don't invoke the callback before telling the cache we're done. - return rv; - } - - if (rv < 0) { - DCHECK(rv != ERR_IO_PENDING); - return HandleResult(rv); - } - - if (reading_ && partial_->IsCurrentRangeCached()) { - rv = ReadFromEntry(read_buf_, read_buf_len_); - - // We are supposed to hanlde errors here. - if (rv < 0 && rv != ERR_IO_PENDING) - HandleResult(rv); - return rv; - } - - return BeginCacheValidation(); -} - -int HttpCache::Transaction::BeginExternallyConditionalizedRequest() { - DCHECK_EQ(UPDATE, mode_); - DCHECK(external_validation_.initialized); - - // Read the cached response. - int rv = ReadResponseInfoFromEntry(); - if (rv != OK) { - DCHECK(rv != ERR_IO_PENDING); - return HandleResult(rv); - } - - for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kValidationHeaders); i++) { - if (external_validation_.values[i].empty()) - continue; - // Retrieve either the cached response's "etag" or "last-modified" header. - std::string validator; - response_.headers->EnumerateHeader( - NULL, - kValidationHeaders[i].related_response_header_name, - &validator); - - if (response_.headers->response_code() != 200 || truncated_ || - validator.empty() || validator != external_validation_.values[i]) { - // The externally conditionalized request is not a validation request - // for our existing cache entry. Proceed with caching disabled. - DoneWritingToEntry(true); - } - } - - return BeginNetworkRequest(); -} - -int HttpCache::Transaction::BeginNetworkRequest() { - DCHECK(mode_ & WRITE || mode_ == NONE); - DCHECK(!network_trans_.get()); - - // Create a network transaction. - int rv = cache_->network_layer_->CreateTransaction(&network_trans_); - if (rv != OK) - return rv; - - rv = network_trans_->Start(request_, &network_info_callback_, load_log_); - if (rv != ERR_IO_PENDING) - OnNetworkInfoAvailable(rv); - return rv; -} - -int HttpCache::Transaction::RestartNetworkRequest() { - DCHECK(mode_ & WRITE || mode_ == NONE); - DCHECK(network_trans_.get()); - - int rv = network_trans_->RestartIgnoringLastError(&network_info_callback_); - if (rv != ERR_IO_PENDING) - OnNetworkInfoAvailable(rv); - return rv; -} - -int HttpCache::Transaction::RestartNetworkRequestWithCertificate( - X509Certificate* client_cert) { - DCHECK(mode_ & WRITE || mode_ == NONE); - DCHECK(network_trans_.get()); - - int rv = network_trans_->RestartWithCertificate(client_cert, - &network_info_callback_); - if (rv != ERR_IO_PENDING) - OnNetworkInfoAvailable(rv); - return rv; -} - -int HttpCache::Transaction::RestartNetworkRequestWithAuth( - const std::wstring& username, - const std::wstring& password) { - DCHECK(mode_ & WRITE || mode_ == NONE); - DCHECK(network_trans_.get()); - - int rv = network_trans_->RestartWithAuth(username, password, - &network_info_callback_); - if (rv != ERR_IO_PENDING) - OnNetworkInfoAvailable(rv); - return rv; -} - -bool HttpCache::Transaction::RequiresValidation() { - // TODO(darin): need to do more work here: - // - make sure we have a matching request method - // - watch out for cached responses that depend on authentication - // In playback mode, nothing requires validation. - if (cache_->mode() == net::HttpCache::PLAYBACK) - return false; - - if (effective_load_flags_ & LOAD_VALIDATE_CACHE) - return true; - - if (response_.headers->response_code() == 206 && !enable_range_support_) - return true; - - if (response_.headers->RequiresValidation( - response_.request_time, response_.response_time, Time::Now())) - return true; - - // Since Vary header computation is fairly expensive, we save it for last. - if (response_.vary_data.is_valid() && - !response_.vary_data.MatchesRequest(*request_, *response_.headers)) - return true; - - return false; -} - -bool HttpCache::Transaction::ConditionalizeRequest() { - DCHECK(response_.headers); - - if (!enable_range_support_ && response_.headers->response_code() != 200) { - // This only makes sense for cached 200 responses. - return false; - } - - // This only makes sense for cached 200 or 206 responses. - if (response_.headers->response_code() != 200 && - response_.headers->response_code() != 206) - return false; - - // Just use the first available ETag and/or Last-Modified header value. - // TODO(darin): Or should we use the last? - - std::string etag_value; - response_.headers->EnumerateHeader(NULL, "etag", &etag_value); - - std::string last_modified_value; - response_.headers->EnumerateHeader(NULL, "last-modified", - &last_modified_value); - - if (etag_value.empty() && last_modified_value.empty()) - return false; - - if (!partial_.get()) { - // Need to customize the request, so this forces us to allocate :( - custom_request_.reset(new HttpRequestInfo(*request_)); - request_ = custom_request_.get(); - } - DCHECK(custom_request_.get()); - - if (!etag_value.empty()) { - if (partial_.get() && !partial_->IsCurrentRangeCached()) { - // We don't want to switch to WRITE mode if we don't have this block of a - // byte-range request because we may have other parts cached. - custom_request_->extra_headers.append("If-Range: "); - } else { - custom_request_->extra_headers.append("If-None-Match: "); - } - custom_request_->extra_headers.append(etag_value); - custom_request_->extra_headers.append("\r\n"); - if (partial_.get() && !partial_->IsCurrentRangeCached()) - return true; - } - - if (!last_modified_value.empty()) { - if (partial_.get() && !partial_->IsCurrentRangeCached()) { - custom_request_->extra_headers.append("If-Range: "); - } else { - custom_request_->extra_headers.append("If-Modified-Since: "); - } - custom_request_->extra_headers.append(last_modified_value); - custom_request_->extra_headers.append("\r\n"); - } - - return true; -} - -// We just received some headers from the server. We may have asked for a range, -// in which case partial_ has an object. This could be the first network request -// we make to fulfill the original request, or we may be already reading (from -// the net and / or the cache). If we are not expecting a certain response, we -// just bypass the cache for this request (but again, maybe we are reading), and -// delete partial_ (so we are not able to "fix" the headers that we return to -// the user). This results in either a weird response for the caller (we don't -// expect it after all), or maybe a range that was not exactly what it was asked -// for. -// -// For example, if the original request is for 30KB and we have the last 20KB, -// we ask the server for the first 10KB. If the resourse has changed, we'll -// end up forwarding the 200 back to the user (so far so good). However, if -// we have instead the first 10KB, we end up sending back a byte range response -// for the first 10KB, because we never asked the server for the last part. It's -// just too complicated to restart the whole request from this point; and of -// course, maybe we already returned the headers. -bool HttpCache::Transaction::ValidatePartialResponse( - const HttpResponseHeaders* headers) { - int response_code = headers->response_code(); - bool partial_content = enable_range_support_ ? response_code == 206 : false; - - if (!entry_) - return false; - - if (invalid_range_) { - // We gave up trying to match this request with the stored data. If the - // server is ok with the request, delete the entry, otherwise just ignore - // this request - if (partial_content || response_code == 200 || response_code == 304) { - DoomPartialEntry(true); - mode_ = NONE; - } else { - IgnoreRangeRequest(); - } - return false; - } - - if (!partial_.get()) { - // We are not expecting 206 but we may have one. - if (partial_content) - IgnoreRangeRequest(); - - return false; - } - - // TODO(rvargas): Do we need to consider other results here?. - bool failure = response_code == 200 || response_code == 416; - - if (partial_->IsCurrentRangeCached()) { - // We asked for "If-None-Match: " so a 206 means a new object. - if (partial_content) - failure = true; - - if (response_code == 304 && partial_->ResponseHeadersOK(headers)) - return false; - } else { - // We asked for "If-Range: " so a 206 means just another range. - if (partial_content && partial_->ResponseHeadersOK(headers)) - return true; - - // 304 is not expected here, but we'll spare the entry. - } - - if (failure) { - // We cannot truncate this entry, it has to be deleted. - DoomPartialEntry(true); - mode_ = NONE; - return false; - } - - IgnoreRangeRequest(); - return false; -} - -void HttpCache::Transaction::IgnoreRangeRequest() { - // We have a problem. We may or may not be reading already (in which case we - // returned the headers), but we'll just pretend that this request is not - // using the cache and see what happens. Most likely this is the first - // response from the server (it's not changing its mind midway, right?). - if (mode_ & WRITE) { - DoneWritingToEntry(mode_ != WRITE); - } else if (mode_ & READ && entry_) { - cache_->DoneReadingFromEntry(entry_, this); - } - - partial_.reset(NULL); - entry_ = NULL; - mode_ = NONE; -} - -int HttpCache::Transaction::ReadFromNetwork(IOBuffer* data, int data_len) { - int rv = network_trans_->Read(data, data_len, &network_read_callback_); - read_buf_ = data; - read_buf_len_ = data_len; - if (rv >= 0) - rv = DoNetworkReadCompleted(rv); - return rv; -} - -int HttpCache::Transaction::ReadFromEntry(IOBuffer* data, int data_len) { - DCHECK(entry_); - int rv; - cache_read_callback_->AddRef(); // Balanced in OnCacheReadCompleted. - if (partial_.get()) { - rv = partial_->CacheRead(entry_->disk_entry, data, data_len, - cache_read_callback_); - } else { - rv = entry_->disk_entry->ReadData(kResponseContentIndex, read_offset_, - data, data_len, cache_read_callback_); - } - read_buf_ = data; - read_buf_len_ = data_len; - if (rv != ERR_IO_PENDING) - cache_read_callback_->Release(); - - if (rv >= 0) - rv = DoCacheReadCompleted(rv); - - return rv; -} - -int HttpCache::Transaction::ReadResponseInfoFromEntry() { - DCHECK(entry_); - - LoadLog::BeginEvent(load_log_, LoadLog::TYPE_HTTP_CACHE_READ_INFO); - bool read_ok = - HttpCache::ReadResponseInfo(entry_->disk_entry, &response_, &truncated_); - LoadLog::EndEvent(load_log_, LoadLog::TYPE_HTTP_CACHE_READ_INFO); - - return read_ok ? OK : ERR_CACHE_READ_FAILURE; -} - -int HttpCache::Transaction::WriteToEntry(int index, int offset, - IOBuffer* data, int data_len, - CompletionCallback* callback) { - if (!entry_) - return data_len; - - int rv = 0; - if (!partial_.get() || !data_len) { - rv = entry_->disk_entry->WriteData(index, offset, data, data_len, callback, - true); - } else { - rv = partial_->CacheWrite(entry_->disk_entry, data, data_len, callback); - } - - if (rv != ERR_IO_PENDING && rv != data_len) { - DLOG(ERROR) << "failed to write response data to cache"; - DoneWritingToEntry(false); - - // We want to ignore errors writing to disk and just keep reading from - // the network. - rv = data_len; - } - return rv; -} - -void HttpCache::Transaction::WriteResponseInfoToEntry(bool truncated) { - if (!entry_) - return; - - // Do not cache no-store content (unless we are record mode). Do not cache - // content with cert errors either. This is to prevent not reporting net - // errors when loading a resource from the cache. When we load a page over - // HTTPS with a cert error we show an SSL blocking page. If the user clicks - // proceed we reload the resource ignoring the errors. The loaded resource - // is then cached. If that resource is subsequently loaded from the cache, - // no net error is reported (even though the cert status contains the actual - // errors) and no SSL blocking page is shown. An alternative would be to - // reverse-map the cert status to a net error and replay the net error. - if ((cache_->mode() != RECORD && - response_.headers->HasHeaderValue("cache-control", "no-store")) || - net::IsCertStatusError(response_.ssl_info.cert_status)) { - DoneWritingToEntry(false); - return; - } - - // When writing headers, we normally only write the non-transient - // headers; when in record mode, record everything. - bool skip_transient_headers = (cache_->mode() != RECORD); - - if (truncated) { - DCHECK_EQ(200, response_.headers->response_code()); - } - - if (!HttpCache::WriteResponseInfo(entry_->disk_entry, &response_, - skip_transient_headers, truncated)) { - DLOG(ERROR) << "failed to write response info to cache"; - DoneWritingToEntry(false); - } -} - -int HttpCache::Transaction::AppendResponseDataToEntry( - IOBuffer* data, int data_len, CompletionCallback* callback) { - if (!entry_ || !data_len) - return data_len; - - int current_size = entry_->disk_entry->GetDataSize(kResponseContentIndex); - return WriteToEntry(kResponseContentIndex, current_size, data, data_len, - callback); -} - -void HttpCache::Transaction::TruncateResponseData() { - if (!entry_) - return; - - // Truncate the stream. - int rv = WriteToEntry(kResponseContentIndex, 0, NULL, 0, NULL); - DCHECK(rv != ERR_IO_PENDING); -} - -void HttpCache::Transaction::DoneWritingToEntry(bool success) { - if (!entry_) - return; - - if (cache_->mode() == RECORD) - DLOG(INFO) << "Recorded: " << request_->method << request_->url - << " status: " << response_.headers->response_code(); - - cache_->DoneWritingToEntry(entry_, success); - entry_ = NULL; - mode_ = NONE; // switch to 'pass through' mode -} - -void HttpCache::Transaction::DoomPartialEntry(bool delete_object) { - cache_->DoneWithEntry(entry_, this, false); - cache_->DoomEntry(cache_key_); - entry_ = NULL; - if (delete_object) - partial_.reset(NULL); -} - -int HttpCache::Transaction::DoNetworkReadCompleted(int result) { - DCHECK(mode_ & WRITE || mode_ == NONE); - - if (!cache_) - return HandleResult(ERR_UNEXPECTED); - - cache_write_callback_->AddRef(); // Balanced in DoCacheWriteCompleted. - - result = AppendResponseDataToEntry(read_buf_, result, cache_write_callback_); - if (result == ERR_IO_PENDING) - return result; - - return DoCacheWriteCompleted(result); -} - -int HttpCache::Transaction::DoPartialNetworkReadCompleted(int result) { - partial_->OnNetworkReadCompleted(result); - - if (result == 0) { // End of file. - if (mode_ == READ_WRITE) { - // We need to move on to the next range. - network_trans_.reset(); - result = ContinuePartialCacheValidation(); - if (result != OK) - // Any error was already handled. - return result; - } - DoneWritingToEntry(true); - } - return HandleResult(result); -} - -int HttpCache::Transaction::DoCacheReadCompleted(int result) { - DCHECK(cache_); - - if (!cache_) - return HandleResult(ERR_UNEXPECTED); - - if (partial_.get()) - return DoPartialCacheReadCompleted(result); - - if (result > 0) { - read_offset_ += result; - } else if (result == 0) { // End of file. - cache_->DoneReadingFromEntry(entry_, this); - entry_ = NULL; - } - return HandleResult(result); -} - -int HttpCache::Transaction::DoPartialCacheReadCompleted(int result) { - partial_->OnCacheReadCompleted(result); - - if (result == 0) { // End of file. - if (partial_.get() && mode_ == READ_WRITE) { - // We need to move on to the next range. - result = ContinuePartialCacheValidation(); - if (result != OK) - // Any error was already handled. - return result; - cache_->ConvertWriterToReader(entry_); - } - cache_->DoneReadingFromEntry(entry_, this); - entry_ = NULL; - } - return HandleResult(result); -} - -int HttpCache::Transaction::DoCacheWriteCompleted(int result) { - DCHECK(cache_); - // Balance the AddRef from DoNetworkReadCompleted. - cache_write_callback_->Release(); - if (!cache_) - return HandleResult(ERR_UNEXPECTED); - - if (result < 0) - return HandleResult(result); - - if (partial_.get()) - return DoPartialNetworkReadCompleted(result); - - if (result == 0) // End of file. - DoneWritingToEntry(true); - - return HandleResult(result); -} - -void HttpCache::Transaction::OnNetworkInfoAvailable(int result) { - DCHECK(result != ERR_IO_PENDING); - - if (!cache_) { - HandleResult(ERR_UNEXPECTED); - return; - } - - if (result == OK) { - const HttpResponseInfo* new_response = network_trans_->GetResponseInfo(); - if (new_response->headers->response_code() == 401 || - new_response->headers->response_code() == 407) { - auth_response_ = *new_response; - } else { - bool partial_content = ValidatePartialResponse(new_response->headers); - if (partial_content && mode_ == READ_WRITE && !truncated_ && - response_.headers->response_code() == 200) { - // We have stored the full entry, but it changed and the server is - // sending a range. We have to delete the old entry. - DoneWritingToEntry(false); - } - - // Are we expecting a response to a conditional query? - if (mode_ == READ_WRITE || mode_ == UPDATE) { - if (new_response->headers->response_code() == 304 || partial_content) { - // Update cached response based on headers in new_response. - // TODO(wtc): should we update cached certificate - // (response_.ssl_info), too? - response_.headers->Update(*new_response->headers); - response_.response_time = new_response->response_time; - response_.request_time = new_response->request_time; - - if (response_.headers->HasHeaderValue("cache-control", "no-store")) { - cache_->DoomEntry(cache_key_); - } else { - // If we are already reading, we already updated the headers for - // this request; doing it again will change Content-Length. - if (!reading_) - WriteResponseInfoToEntry(false); - } - - if (mode_ == UPDATE) { - DCHECK(!partial_content); - // We got a "not modified" response and already updated the - // corresponding cache entry above. - // - // By closing the cached entry now, we make sure that the - // 304 rather than the cached 200 response, is what will be - // returned to the user. - DoneWritingToEntry(true); - } else if (entry_ && !partial_content) { - DCHECK_EQ(READ_WRITE, mode_); - if (!partial_.get() || partial_->IsLastRange()) { - cache_->ConvertWriterToReader(entry_); - mode_ = READ; - } - // We no longer need the network transaction, so destroy it. - final_upload_progress_ = network_trans_->GetUploadProgress(); - network_trans_.reset(); - } - } else { - mode_ = WRITE; - } - } - - if (!(mode_ & READ)) { - // We change the value of Content-Length for partial content. - if (partial_content && partial_.get()) - partial_->FixContentLength(new_response->headers); - - response_ = *new_response; - WriteResponseInfoToEntry(truncated_); - - // Truncate response data. - TruncateResponseData(); - - // If this response is a redirect, then we can stop writing now. (We - // don't need to cache the response body of a redirect.) - if (response_.headers->IsRedirect(NULL)) - DoneWritingToEntry(true); - } - if (reading_ && partial_.get()) { - if (network_trans_.get()) { - result = ReadFromNetwork(read_buf_, read_buf_len_); - } else { - result = ReadFromEntry(read_buf_, read_buf_len_); - } - if (result >= 0 || result == net::ERR_IO_PENDING) - return; - } else if (mode_ != NONE && partial_.get()) { - // We are about to return the headers for a byte-range request to the - // user, so let's fix them. - partial_->FixResponseHeaders(response_.headers); - } - } - } else if (IsCertificateError(result)) { - const HttpResponseInfo* response = network_trans_->GetResponseInfo(); - // If we get a certificate error, then there is a certificate in ssl_info, - // so GetResponseInfo() should never returns NULL here. - DCHECK(response); - response_.ssl_info = response->ssl_info; - } else if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED) { - const HttpResponseInfo* response = network_trans_->GetResponseInfo(); - DCHECK(response); - response_.cert_request_info = response->cert_request_info; - } - HandleResult(result); -} - -void HttpCache::Transaction::OnNetworkReadCompleted(int result) { - DoNetworkReadCompleted(result); -} - -void HttpCache::Transaction::OnCacheReadCompleted(int result) { - cache_read_callback_->Release(); // Balance the AddRef from ReadFromEntry. - DoCacheReadCompleted(result); -} - -void HttpCache::Transaction::OnCacheWriteCompleted(int result) { - DoCacheWriteCompleted(result); -} - -void HttpCache::Transaction::OnCacheEntryReady(int result) { - DCHECK_EQ(OK, result); - ValidateEntryHeadersAndContinue(true); -} - -//----------------------------------------------------------------------------- - HttpCache::HttpCache(HostResolver* host_resolver, ProxyService* proxy_service, SSLConfigService* ssl_config_service, |