Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "webkit/appcache/appcache_update_job.h" | 5 #include "webkit/appcache/appcache_update_job.h" |
| 6 | 6 |
| 7 #include "base/compiler_specific.h" | 7 #include "base/compiler_specific.h" |
| 8 #include "base/message_loop.h" | 8 #include "base/message_loop.h" |
| 9 #include "base/string_util.h" | 9 #include "base/string_util.h" |
| 10 #include "net/base/io_buffer.h" | 10 #include "net/base/io_buffer.h" |
| 11 #include "net/base/load_flags.h" | 11 #include "net/base/load_flags.h" |
| 12 #include "net/base/net_errors.h" | 12 #include "net/base/net_errors.h" |
| 13 #include "net/http/http_request_headers.h" | 13 #include "net/http/http_request_headers.h" |
| 14 #include "webkit/appcache/appcache_group.h" | 14 #include "webkit/appcache/appcache_group.h" |
| 15 #include "webkit/appcache/appcache_policy.h" | 15 #include "webkit/appcache/appcache_policy.h" |
| 16 #include "webkit/appcache/appcache_response.h" | |
| 17 | 16 |
| 18 namespace appcache { | 17 namespace appcache { |
| 19 | 18 |
| 20 static const int kBufferSize = 32768; | 19 static const int kBufferSize = 32768; |
| 21 static const size_t kMaxConcurrentUrlFetches = 2; | 20 static const size_t kMaxConcurrentUrlFetches = 2; |
| 22 static const int kMax503Retries = 3; | 21 static const int kMax503Retries = 3; |
| 23 | 22 |
| 24 // Extra info associated with requests for use during response processing. | 23 // Extra info associated with requests for use during response processing. |
| 25 // This info is deleted when the URLRequest is deleted. | 24 // This info is deleted when the URLRequest is deleted. |
| 26 class UpdateJobInfo : public URLRequest::UserData { | 25 class UpdateJobInfo : public URLRequest::UserData { |
| 27 public: | 26 public: |
| 28 enum RequestType { | 27 enum RequestType { |
| 29 MANIFEST_FETCH, | 28 MANIFEST_FETCH, |
| 30 URL_FETCH, | 29 URL_FETCH, |
| 31 MASTER_ENTRY_FETCH, | 30 MASTER_ENTRY_FETCH, |
| 32 MANIFEST_REFETCH, | 31 MANIFEST_REFETCH, |
| 33 }; | 32 }; |
| 34 | 33 |
| 35 explicit UpdateJobInfo(RequestType request_type) | 34 explicit UpdateJobInfo(RequestType request_type) |
| 36 : type_(request_type), | 35 : type_(request_type), |
| 37 buffer_(new net::IOBuffer(kBufferSize)), | 36 buffer_(new net::IOBuffer(kBufferSize)), |
| 38 retry_503_attempts_(0), | 37 retry_503_attempts_(0), |
| 39 update_job_(NULL), | 38 update_job_(NULL), |
| 40 request_(NULL), | 39 request_(NULL), |
| 41 ALLOW_THIS_IN_INITIALIZER_LIST(write_callback_( | 40 ALLOW_THIS_IN_INITIALIZER_LIST(write_callback_( |
| 42 this, &UpdateJobInfo::OnWriteComplete)) { | 41 this, &UpdateJobInfo::OnWriteComplete)) { |
| 43 } | 42 } |
| 44 | 43 |
| 45 void SetUpResponseWriter(AppCacheResponseWriter* writer, | 44 void SetUpResponseWriter(AppCacheResponseWriter* writer, |
| 46 AppCacheUpdateJob* update, | 45 AppCacheUpdateJob* update, |
| 47 URLRequest* request) { | 46 URLRequest* request) { |
| 48 DCHECK(!response_writer_.get()); | 47 DCHECK(!response_writer_.get()); |
| 49 response_writer_.reset(writer); | 48 response_writer_.reset(writer); |
| 50 update_job_ = update; | 49 update_job_ = update; |
| 51 request_ = request; | 50 request_ = request; |
| 52 } | 51 } |
| 53 | 52 |
| 54 void OnWriteComplete(int result) { | 53 void OnWriteComplete(int result) { |
| 55 // A completed write may delete the URL request and this object. | 54 // A completed write may delete the URL request and this object. |
| 56 update_job_->OnWriteResponseComplete(result, request_, this); | 55 update_job_->OnWriteResponseComplete(result, request_, this); |
| 57 } | 56 } |
| 58 | 57 |
| 59 RequestType type_; | 58 RequestType type_; |
| 60 scoped_refptr<net::IOBuffer> buffer_; | 59 scoped_refptr<net::IOBuffer> buffer_; |
| 61 int retry_503_attempts_; | 60 int retry_503_attempts_; |
| 62 | 61 |
| 62 // The entry from the newest cache for this url, used for 304 responses. | |
| 63 AppCacheEntry existing_entry_; | |
| 64 | |
| 63 // Info needed to write responses to storage and process callbacks. | 65 // Info needed to write responses to storage and process callbacks. |
| 64 scoped_ptr<AppCacheResponseWriter> response_writer_; | 66 scoped_ptr<AppCacheResponseWriter> response_writer_; |
| 65 AppCacheUpdateJob* update_job_; | 67 AppCacheUpdateJob* update_job_; |
| 66 URLRequest* request_; | 68 URLRequest* request_; |
| 67 net::CompletionCallbackImpl<UpdateJobInfo> write_callback_; | 69 net::CompletionCallbackImpl<UpdateJobInfo> write_callback_; |
| 68 }; | 70 }; |
| 69 | 71 |
| 70 // Helper class for collecting hosts per frontend when sending notifications | 72 // Helper class for collecting hosts per frontend when sending notifications |
| 71 // so that only one notification is sent for all hosts using the same frontend. | 73 // so that only one notification is sent for all hosts using the same frontend. |
| 72 class HostNotifier { | 74 class HostNotifier { |
| (...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 152 DCHECK(!inprogress_cache_); | 154 DCHECK(!inprogress_cache_); |
| 153 DCHECK(pending_master_entries_.empty()); | 155 DCHECK(pending_master_entries_.empty()); |
| 154 DCHECK(master_entry_fetches_.empty()); | 156 DCHECK(master_entry_fetches_.empty()); |
| 155 | 157 |
| 156 if (group_) | 158 if (group_) |
| 157 group_->SetUpdateStatus(AppCacheGroup::IDLE); | 159 group_->SetUpdateStatus(AppCacheGroup::IDLE); |
| 158 | 160 |
| 159 policy_callback_->Cancel(); | 161 policy_callback_->Cancel(); |
| 160 } | 162 } |
| 161 | 163 |
| 164 UpdateJobInfo* AppCacheUpdateJob::GetUpdateJobInfo(URLRequest* request) { | |
| 165 return static_cast<UpdateJobInfo*>(request->GetUserData(this)); | |
| 166 } | |
| 167 | |
| 162 void AppCacheUpdateJob::StartUpdate(AppCacheHost* host, | 168 void AppCacheUpdateJob::StartUpdate(AppCacheHost* host, |
| 163 const GURL& new_master_resource) { | 169 const GURL& new_master_resource) { |
| 164 DCHECK(group_->update_job() == this); | 170 DCHECK(group_->update_job() == this); |
| 165 DCHECK(!group_->is_obsolete()); | 171 DCHECK(!group_->is_obsolete()); |
| 166 | 172 |
| 167 bool is_new_pending_master_entry = false; | 173 bool is_new_pending_master_entry = false; |
| 168 if (!new_master_resource.is_empty()) { | 174 if (!new_master_resource.is_empty()) { |
| 169 DCHECK(new_master_resource == host->pending_master_entry_url()); | 175 DCHECK(new_master_resource == host->pending_master_entry_url()); |
| 170 DCHECK(!new_master_resource.has_ref()); | 176 DCHECK(!new_master_resource.has_ref()); |
| 171 DCHECK(new_master_resource.GetOrigin() == manifest_url_.GetOrigin()); | 177 DCHECK(new_master_resource.GetOrigin() == manifest_url_.GetOrigin()); |
| (...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 278 | 284 |
| 279 // Add any necessary Http headers before sending fetch request. | 285 // Add any necessary Http headers before sending fetch request. |
| 280 if (is_first_fetch) { | 286 if (is_first_fetch) { |
| 281 AppCacheEntry* entry = (update_type_ == UPGRADE_ATTEMPT) ? | 287 AppCacheEntry* entry = (update_type_ == UPGRADE_ATTEMPT) ? |
| 282 group_->newest_complete_cache()->GetEntry(manifest_url_) : NULL; | 288 group_->newest_complete_cache()->GetEntry(manifest_url_) : NULL; |
| 283 if (entry) { | 289 if (entry) { |
| 284 // Asynchronously load response info for manifest from newest cache. | 290 // Asynchronously load response info for manifest from newest cache. |
| 285 service_->storage()->LoadResponseInfo(manifest_url_, | 291 service_->storage()->LoadResponseInfo(manifest_url_, |
| 286 entry->response_id(), this); | 292 entry->response_id(), this); |
| 287 } else { | 293 } else { |
| 288 AddHttpHeadersAndFetch(manifest_url_request_, NULL); | 294 manifest_url_request_->Start(); |
| 289 } | 295 } |
| 290 } else { | 296 } else { |
| 291 DCHECK(internal_state_ == REFETCH_MANIFEST); | 297 DCHECK(internal_state_ == REFETCH_MANIFEST); |
| 292 DCHECK(manifest_response_info_.get()); | 298 DCHECK(manifest_response_info_.get()); |
| 293 AddHttpHeadersAndFetch(manifest_url_request_, | 299 AddConditionalHeaders(manifest_url_request_, |
| 294 manifest_response_info_.get()); | 300 manifest_response_info_.get()); |
| 301 manifest_url_request_->Start(); | |
| 295 } | 302 } |
| 296 } | 303 } |
| 297 | 304 |
| 298 void AppCacheUpdateJob::AddHttpHeadersAndFetch( | 305 void AppCacheUpdateJob::AddConditionalHeaders( |
| 299 URLRequest* request, const net::HttpResponseInfo* info) { | 306 URLRequest* request, const net::HttpResponseInfo* info) { |
| 300 DCHECK(request); | 307 DCHECK(request && info); |
| 301 if (info) { | 308 net::HttpRequestHeaders extra_headers; |
| 302 net::HttpRequestHeaders extra_headers; | |
| 303 | 309 |
| 304 // Add If-Modified-Since header if response info has Last-Modified header. | 310 // Add If-Modified-Since header if response info has Last-Modified header. |
| 305 const std::string last_modified = "Last-Modified"; | 311 const std::string last_modified = "Last-Modified"; |
| 306 std::string last_modified_value; | 312 std::string last_modified_value; |
| 307 info->headers->EnumerateHeader(NULL, last_modified, &last_modified_value); | 313 info->headers->EnumerateHeader(NULL, last_modified, &last_modified_value); |
| 308 if (!last_modified_value.empty()) { | 314 if (!last_modified_value.empty()) { |
| 309 extra_headers.SetHeader(net::HttpRequestHeaders::kIfModifiedSince, | 315 extra_headers.SetHeader(net::HttpRequestHeaders::kIfModifiedSince, |
| 310 last_modified_value); | 316 last_modified_value); |
| 311 } | 317 } |
| 312 | 318 |
| 313 // Add If-None-Match header if resposne info has ETag header. | 319 // Add If-None-Match header if resposne info has ETag header. |
| 314 const std::string etag = "ETag"; | 320 const std::string etag = "ETag"; |
| 315 std::string etag_value; | 321 std::string etag_value; |
| 316 info->headers->EnumerateHeader(NULL, etag, &etag_value); | 322 info->headers->EnumerateHeader(NULL, etag, &etag_value); |
| 317 if (!etag_value.empty()) { | 323 if (!etag_value.empty()) { |
| 318 extra_headers.SetHeader(net::HttpRequestHeaders::kIfNoneMatch, | 324 extra_headers.SetHeader(net::HttpRequestHeaders::kIfNoneMatch, |
| 319 etag_value); | 325 etag_value); |
| 320 } | |
| 321 | |
| 322 if (!extra_headers.IsEmpty()) | |
| 323 request->SetExtraRequestHeaders(extra_headers); | |
| 324 } | 326 } |
| 325 request->Start(); | 327 if (!extra_headers.IsEmpty()) |
| 328 request->SetExtraRequestHeaders(extra_headers); | |
| 326 } | 329 } |
| 327 | 330 |
| 328 void AppCacheUpdateJob::OnResponseStarted(URLRequest *request) { | 331 void AppCacheUpdateJob::OnResponseStarted(URLRequest *request) { |
| 329 if (request->status().is_success() && | 332 if (request->status().is_success() && |
| 330 (request->GetResponseCode() / 100) == 2) { | 333 (request->GetResponseCode() / 100) == 2) { |
| 331 // Write response info to storage for URL fetches. Wait for async write | 334 // Write response info to storage for URL fetches. Wait for async write |
| 332 // completion before reading any response data. | 335 // completion before reading any response data. |
| 333 UpdateJobInfo* info = | 336 UpdateJobInfo* info = GetUpdateJobInfo(request); |
| 334 static_cast<UpdateJobInfo*>(request->GetUserData(this)); | |
| 335 if (info->type_ == UpdateJobInfo::URL_FETCH || | 337 if (info->type_ == UpdateJobInfo::URL_FETCH || |
| 336 info->type_ == UpdateJobInfo::MASTER_ENTRY_FETCH) { | 338 info->type_ == UpdateJobInfo::MASTER_ENTRY_FETCH) { |
| 337 info->SetUpResponseWriter( | 339 info->SetUpResponseWriter( |
| 338 service_->storage()->CreateResponseWriter(manifest_url_), | 340 service_->storage()->CreateResponseWriter(manifest_url_), |
| 339 this, request); | 341 this, request); |
| 340 stored_response_ids_.push_back(info->response_writer_->response_id()); | 342 stored_response_ids_.push_back(info->response_writer_->response_id()); |
| 341 scoped_refptr<HttpResponseInfoIOBuffer> io_buffer = | 343 scoped_refptr<HttpResponseInfoIOBuffer> io_buffer = |
| 342 new HttpResponseInfoIOBuffer( | 344 new HttpResponseInfoIOBuffer( |
| 343 new net::HttpResponseInfo(request->response_info())); | 345 new net::HttpResponseInfo(request->response_info())); |
| 344 info->response_writer_->WriteInfo(io_buffer, &info->write_callback_); | 346 info->response_writer_->WriteInfo(io_buffer, &info->write_callback_); |
| 345 } else { | 347 } else { |
| 346 ReadResponseData(request); | 348 ReadResponseData(request); |
| 347 } | 349 } |
| 348 } else { | 350 } else { |
| 349 OnResponseCompleted(request); | 351 OnResponseCompleted(request); |
| 350 } | 352 } |
| 351 } | 353 } |
| 352 | 354 |
| 353 void AppCacheUpdateJob::ReadResponseData(URLRequest* request) { | 355 void AppCacheUpdateJob::ReadResponseData(URLRequest* request) { |
| 354 if (internal_state_ == CACHE_FAILURE || internal_state_ == CANCELLED || | 356 if (internal_state_ == CACHE_FAILURE || internal_state_ == CANCELLED || |
| 355 internal_state_ == COMPLETED) { | 357 internal_state_ == COMPLETED) { |
| 356 return; | 358 return; |
| 357 } | 359 } |
| 358 | 360 |
| 359 int bytes_read = 0; | 361 int bytes_read = 0; |
| 360 UpdateJobInfo* info = | 362 UpdateJobInfo* info = GetUpdateJobInfo(request); |
| 361 static_cast<UpdateJobInfo*>(request->GetUserData(this)); | |
| 362 request->Read(info->buffer_, kBufferSize, &bytes_read); | 363 request->Read(info->buffer_, kBufferSize, &bytes_read); |
| 363 OnReadCompleted(request, bytes_read); | 364 OnReadCompleted(request, bytes_read); |
| 364 } | 365 } |
| 365 | 366 |
| 366 void AppCacheUpdateJob::OnReadCompleted(URLRequest* request, int bytes_read) { | 367 void AppCacheUpdateJob::OnReadCompleted(URLRequest* request, int bytes_read) { |
| 367 bool data_consumed = true; | 368 bool data_consumed = true; |
| 368 if (request->status().is_success() && bytes_read > 0) { | 369 if (request->status().is_success() && bytes_read > 0) { |
| 369 UpdateJobInfo* info = | 370 UpdateJobInfo* info = GetUpdateJobInfo(request); |
| 370 static_cast<UpdateJobInfo*>(request->GetUserData(this)); | |
| 371 | |
| 372 data_consumed = ConsumeResponseData(request, info, bytes_read); | 371 data_consumed = ConsumeResponseData(request, info, bytes_read); |
| 373 if (data_consumed) { | 372 if (data_consumed) { |
| 374 bytes_read = 0; | 373 bytes_read = 0; |
| 375 while (request->Read(info->buffer_, kBufferSize, &bytes_read)) { | 374 while (request->Read(info->buffer_, kBufferSize, &bytes_read)) { |
| 376 if (bytes_read > 0) { | 375 if (bytes_read > 0) { |
| 377 data_consumed = ConsumeResponseData(request, info, bytes_read); | 376 data_consumed = ConsumeResponseData(request, info, bytes_read); |
| 378 if (!data_consumed) | 377 if (!data_consumed) |
| 379 break; // wait for async data processing, then read more | 378 break; // wait for async data processing, then read more |
| 380 } else { | 379 } else { |
| 381 break; | 380 break; |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 432 } | 431 } |
| 433 | 432 |
| 434 void AppCacheUpdateJob::OnResponseCompleted(URLRequest* request) { | 433 void AppCacheUpdateJob::OnResponseCompleted(URLRequest* request) { |
| 435 // Retry for 503s where retry-after is 0. | 434 // Retry for 503s where retry-after is 0. |
| 436 if (request->status().is_success() && | 435 if (request->status().is_success() && |
| 437 request->GetResponseCode() == 503 && | 436 request->GetResponseCode() == 503 && |
| 438 RetryRequest(request)) { | 437 RetryRequest(request)) { |
| 439 return; | 438 return; |
| 440 } | 439 } |
| 441 | 440 |
| 442 UpdateJobInfo* info = | 441 UpdateJobInfo* info = GetUpdateJobInfo(request); |
| 443 static_cast<UpdateJobInfo*>(request->GetUserData(this)); | |
| 444 switch (info->type_) { | 442 switch (info->type_) { |
| 445 case UpdateJobInfo::MANIFEST_FETCH: | 443 case UpdateJobInfo::MANIFEST_FETCH: |
| 446 HandleManifestFetchCompleted(request); | 444 HandleManifestFetchCompleted(request); |
| 447 break; | 445 break; |
| 448 case UpdateJobInfo::URL_FETCH: | 446 case UpdateJobInfo::URL_FETCH: |
| 449 HandleUrlFetchCompleted(request); | 447 HandleUrlFetchCompleted(request); |
| 450 break; | 448 break; |
| 451 case UpdateJobInfo::MASTER_ENTRY_FETCH: | 449 case UpdateJobInfo::MASTER_ENTRY_FETCH: |
| 452 HandleMasterEntryFetchCompleted(request); | 450 HandleMasterEntryFetchCompleted(request); |
| 453 break; | 451 break; |
| 454 case UpdateJobInfo::MANIFEST_REFETCH: | 452 case UpdateJobInfo::MANIFEST_REFETCH: |
| 455 HandleManifestRefetchCompleted(request); | 453 HandleManifestRefetchCompleted(request); |
| 456 break; | 454 break; |
| 457 default: | 455 default: |
| 458 NOTREACHED(); | 456 NOTREACHED(); |
| 459 } | 457 } |
| 460 | 458 |
| 461 delete request; | 459 delete request; |
| 462 } | 460 } |
| 463 | 461 |
| 464 bool AppCacheUpdateJob::RetryRequest(URLRequest* request) { | 462 bool AppCacheUpdateJob::RetryRequest(URLRequest* request) { |
| 465 UpdateJobInfo* info = | 463 UpdateJobInfo* info = GetUpdateJobInfo(request); |
| 466 static_cast<UpdateJobInfo*>(request->GetUserData(this)); | |
| 467 if (info->retry_503_attempts_ >= kMax503Retries) { | 464 if (info->retry_503_attempts_ >= kMax503Retries) { |
| 468 return false; | 465 return false; |
| 469 } | 466 } |
| 470 | 467 |
| 471 if (!request->response_headers()->HasHeaderValue("retry-after", "0")) | 468 if (!request->response_headers()->HasHeaderValue("retry-after", "0")) |
| 472 return false; | 469 return false; |
| 473 | 470 |
| 474 const GURL& url = request->original_url(); | 471 const GURL& url = request->original_url(); |
| 475 URLRequest* retry = new URLRequest(url, this); | 472 URLRequest* retry = new URLRequest(url, this); |
| 476 UpdateJobInfo* retry_info = new UpdateJobInfo(info->type_); | 473 UpdateJobInfo* retry_info = new UpdateJobInfo(info->type_); |
| 477 retry_info->retry_503_attempts_ = info->retry_503_attempts_ + 1; | 474 retry_info->retry_503_attempts_ = info->retry_503_attempts_ + 1; |
| 475 retry_info->existing_entry_ = info->existing_entry_; | |
| 478 retry->SetUserData(this, retry_info); | 476 retry->SetUserData(this, retry_info); |
| 479 retry->set_context(request->context()); | 477 retry->set_context(request->context()); |
| 480 retry->set_load_flags(request->load_flags()); | 478 retry->set_load_flags(request->load_flags()); |
| 481 | 479 |
| 482 switch (info->type_) { | 480 switch (info->type_) { |
| 483 case UpdateJobInfo::MANIFEST_FETCH: | 481 case UpdateJobInfo::MANIFEST_FETCH: |
| 484 case UpdateJobInfo::MANIFEST_REFETCH: | 482 case UpdateJobInfo::MANIFEST_REFETCH: |
| 485 manifest_url_request_ = retry; | 483 manifest_url_request_ = retry; |
| 486 manifest_data_.clear(); | 484 manifest_data_.clear(); |
| 487 break; | 485 break; |
| (...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 591 | 589 |
| 592 group_->SetUpdateStatus(AppCacheGroup::DOWNLOADING); | 590 group_->SetUpdateStatus(AppCacheGroup::DOWNLOADING); |
| 593 NotifyAllAssociatedHosts(DOWNLOADING_EVENT); | 591 NotifyAllAssociatedHosts(DOWNLOADING_EVENT); |
| 594 FetchUrls(); | 592 FetchUrls(); |
| 595 FetchMasterEntries(); | 593 FetchMasterEntries(); |
| 596 MaybeCompleteUpdate(); // if not done, continues when async fetches complete | 594 MaybeCompleteUpdate(); // if not done, continues when async fetches complete |
| 597 } | 595 } |
| 598 | 596 |
| 599 void AppCacheUpdateJob::HandleUrlFetchCompleted(URLRequest* request) { | 597 void AppCacheUpdateJob::HandleUrlFetchCompleted(URLRequest* request) { |
| 600 DCHECK(internal_state_ == DOWNLOADING); | 598 DCHECK(internal_state_ == DOWNLOADING); |
| 599 UpdateJobInfo* info = GetUpdateJobInfo(request); | |
| 601 | 600 |
| 602 const GURL& url = request->original_url(); | 601 const GURL& url = request->original_url(); |
| 603 pending_url_fetches_.erase(url); | 602 pending_url_fetches_.erase(url); |
| 604 NotifyAllProgress(url); | 603 NotifyAllProgress(url); |
| 605 ++url_fetches_completed_; | 604 ++url_fetches_completed_; |
| 606 | 605 |
| 607 int response_code = request->status().is_success() | 606 int response_code = request->status().is_success() |
| 608 ? request->GetResponseCode() : -1; | 607 ? request->GetResponseCode() : -1; |
| 609 AppCacheEntry& entry = url_file_list_.find(url)->second; | 608 AppCacheEntry& entry = url_file_list_.find(url)->second; |
| 610 | 609 |
| 611 if (request->status().is_success() && (response_code / 100 == 2)) { | 610 if (response_code / 100 == 2) { |
| 612 // Associate storage with the new entry. | 611 // Associate storage with the new entry. |
| 613 UpdateJobInfo* info = | |
| 614 static_cast<UpdateJobInfo*>(request->GetUserData(this)); | |
| 615 DCHECK(info->response_writer_.get()); | 612 DCHECK(info->response_writer_.get()); |
| 616 entry.set_response_id(info->response_writer_->response_id()); | 613 entry.set_response_id(info->response_writer_->response_id()); |
| 617 entry.set_response_size(info->response_writer_->amount_written()); | 614 entry.set_response_size(info->response_writer_->amount_written()); |
| 618 | |
| 619 if (!inprogress_cache_->AddOrModifyEntry(url, entry)) | 615 if (!inprogress_cache_->AddOrModifyEntry(url, entry)) |
| 620 duplicate_response_ids_.push_back(entry.response_id()); | 616 duplicate_response_ids_.push_back(entry.response_id()); |
| 621 | 617 |
| 622 // Foreign entries will be detected during cache selection. | 618 // Foreign entries will be detected during cache selection. |
| 623 // Note: 6.9.4, step 17.9 possible optimization: if resource is HTML or XML | 619 // Note: 6.9.4, step 17.9 possible optimization: if resource is HTML or XML |
| 624 // file whose root element is an html element with a manifest attribute | 620 // file whose root element is an html element with a manifest attribute |
| 625 // whose value doesn't match the manifest url of the application cache | 621 // whose value doesn't match the manifest url of the application cache |
| 626 // being processed, mark the entry as being foreign. | 622 // being processed, mark the entry as being foreign. |
| 627 } else { | 623 } else { |
| 628 LOG(INFO) << "Request status: " << request->status().status() | 624 LOG(INFO) << "Request status: " << request->status().status() |
| 629 << " os_error: " << request->status().os_error() | 625 << " os_error: " << request->status().os_error() |
| 630 << " response code: " << response_code; | 626 << " response code: " << response_code; |
| 631 if (entry.IsExplicit() || entry.IsFallback()) { | 627 if (entry.IsExplicit() || entry.IsFallback()) { |
| 632 const char* kFormatString = "Resource fetch failed (%d) %s"; | 628 if (response_code == 304 && info->existing_entry_.has_response_id()) { |
| 633 const std::string message = StringPrintf(kFormatString, response_code, | 629 // Copy the response from the newest complete cache. |
|
rvargas (doing something else)
2010/07/20 19:17:18
maybe change from the newest complete cache -> fro
| |
| 634 request->url().spec().c_str()); | 630 entry.set_response_id(info->existing_entry_.response_id()); |
| 635 HandleCacheFailure(message); | 631 entry.set_response_size(info->existing_entry_.response_size()); |
| 636 return; | 632 inprogress_cache_->AddOrModifyEntry(url, entry); |
| 633 } else { | |
| 634 const char* kFormatString = "Resource fetch failed (%d) %s"; | |
| 635 const std::string message = StringPrintf(kFormatString, response_code, | |
| 636 request->url().spec().c_str()); | |
| 637 HandleCacheFailure(message); | |
| 638 return; | |
| 639 } | |
| 637 } else if (response_code == 404 || response_code == 410) { | 640 } else if (response_code == 404 || response_code == 410) { |
| 638 // Entry is skipped. They are dropped from the cache. | 641 // Entry is skipped. They are dropped from the cache. |
| 639 } else if (update_type_ == UPGRADE_ATTEMPT) { | 642 } else if (update_type_ == UPGRADE_ATTEMPT && |
| 643 info->existing_entry_.has_response_id()) { | |
| 640 // Copy the response from the newest complete cache. | 644 // Copy the response from the newest complete cache. |
| 641 AppCache* cache = group_->newest_complete_cache(); | 645 // TODO(michaeln): Not sure this is a good idea. This is spec compliant |
| 642 AppCacheEntry* copy = cache->GetEntry(url); | 646 // but the old resource may or may not be compatible with the new contents |
| 643 if (copy) { | 647 // of the cache. Impossible to know one way or the other. |
| 644 entry.set_response_id(copy->response_id()); | 648 entry.set_response_id(info->existing_entry_.response_id()); |
| 645 entry.set_response_size(copy->response_size()); | 649 entry.set_response_size(info->existing_entry_.response_size()); |
| 646 inprogress_cache_->AddOrModifyEntry(url, entry); | 650 inprogress_cache_->AddOrModifyEntry(url, entry); |
| 647 } | |
| 648 } | 651 } |
| 649 } | 652 } |
| 650 | 653 |
| 651 // Fetch another URL now that one request has completed. | 654 // Fetch another URL now that one request has completed. |
| 652 DCHECK(internal_state_ != CACHE_FAILURE); | 655 DCHECK(internal_state_ != CACHE_FAILURE); |
| 653 FetchUrls(); | 656 FetchUrls(); |
| 654 MaybeCompleteUpdate(); | 657 MaybeCompleteUpdate(); |
| 655 } | 658 } |
| 656 | 659 |
| 657 void AppCacheUpdateJob::HandleMasterEntryFetchCompleted(URLRequest* request) { | 660 void AppCacheUpdateJob::HandleMasterEntryFetchCompleted(URLRequest* request) { |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 669 int response_code = request->status().is_success() | 672 int response_code = request->status().is_success() |
| 670 ? request->GetResponseCode() : -1; | 673 ? request->GetResponseCode() : -1; |
| 671 | 674 |
| 672 PendingMasters::iterator found = pending_master_entries_.find(url); | 675 PendingMasters::iterator found = pending_master_entries_.find(url); |
| 673 DCHECK(found != pending_master_entries_.end()); | 676 DCHECK(found != pending_master_entries_.end()); |
| 674 PendingHosts& hosts = found->second; | 677 PendingHosts& hosts = found->second; |
| 675 | 678 |
| 676 // Section 6.9.4. No update case: step 7.3, else step 22. | 679 // Section 6.9.4. No update case: step 7.3, else step 22. |
| 677 if (response_code / 100 == 2) { | 680 if (response_code / 100 == 2) { |
| 678 // Add fetched master entry to the appropriate cache. | 681 // Add fetched master entry to the appropriate cache. |
| 679 UpdateJobInfo* info = | 682 UpdateJobInfo* info = GetUpdateJobInfo(request); |
| 680 static_cast<UpdateJobInfo*>(request->GetUserData(this)); | |
| 681 AppCache* cache = inprogress_cache_ ? inprogress_cache_.get() : | 683 AppCache* cache = inprogress_cache_ ? inprogress_cache_.get() : |
| 682 group_->newest_complete_cache(); | 684 group_->newest_complete_cache(); |
| 683 DCHECK(info->response_writer_.get()); | 685 DCHECK(info->response_writer_.get()); |
| 684 AppCacheEntry master_entry(AppCacheEntry::MASTER, | 686 AppCacheEntry master_entry(AppCacheEntry::MASTER, |
| 685 info->response_writer_->response_id(), | 687 info->response_writer_->response_id(), |
| 686 info->response_writer_->amount_written()); | 688 info->response_writer_->amount_written()); |
| 687 if (!cache->AddOrModifyEntry(url, master_entry)) | 689 if (!cache->AddOrModifyEntry(url, master_entry)) |
| 688 duplicate_response_ids_.push_back(master_entry.response_id()); | 690 duplicate_response_ids_.push_back(master_entry.response_id()); |
| 689 | 691 |
| 690 // In no-update case, associate host with the newest cache. | 692 // In no-update case, associate host with the newest cache. |
| (...skipping 245 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 936 AddUrlToFileList(it->first, AppCacheEntry::MASTER); | 938 AddUrlToFileList(it->first, AppCacheEntry::MASTER); |
| 937 } | 939 } |
| 938 } | 940 } |
| 939 } | 941 } |
| 940 | 942 |
| 941 void AppCacheUpdateJob::AddUrlToFileList(const GURL& url, int type) { | 943 void AppCacheUpdateJob::AddUrlToFileList(const GURL& url, int type) { |
| 942 std::pair<AppCache::EntryMap::iterator, bool> ret = url_file_list_.insert( | 944 std::pair<AppCache::EntryMap::iterator, bool> ret = url_file_list_.insert( |
| 943 AppCache::EntryMap::value_type(url, AppCacheEntry(type))); | 945 AppCache::EntryMap::value_type(url, AppCacheEntry(type))); |
| 944 | 946 |
| 945 if (ret.second) | 947 if (ret.second) |
| 946 urls_to_fetch_.push_back(UrlsToFetch(url, false)); | 948 urls_to_fetch_.push_back(UrlToFetch(url, false, NULL)); |
| 947 else | 949 else |
| 948 ret.first->second.add_types(type); // URL already exists. Merge types. | 950 ret.first->second.add_types(type); // URL already exists. Merge types. |
| 949 } | 951 } |
| 950 | 952 |
| 951 void AppCacheUpdateJob::FetchUrls() { | 953 void AppCacheUpdateJob::FetchUrls() { |
| 952 DCHECK(internal_state_ == DOWNLOADING); | 954 DCHECK(internal_state_ == DOWNLOADING); |
| 953 | 955 |
| 954 // Fetch each URL in the list according to section 6.9.4 step 17.1-17.3. | 956 // Fetch each URL in the list according to section 6.9.4 step 17.1-17.3. |
| 955 // Fetch up to the concurrent limit. Other fetches will be triggered as each | 957 // Fetch up to the concurrent limit. Other fetches will be triggered as each |
| 956 // each fetch completes. | 958 // each fetch completes. |
| 957 while (pending_url_fetches_.size() < kMaxConcurrentUrlFetches && | 959 while (pending_url_fetches_.size() < kMaxConcurrentUrlFetches && |
| 958 !urls_to_fetch_.empty()) { | 960 !urls_to_fetch_.empty()) { |
| 959 const GURL url = urls_to_fetch_.front().first; | 961 UrlToFetch url_to_fetch = urls_to_fetch_.front(); |
| 960 bool storage_checked = urls_to_fetch_.front().second; | |
| 961 urls_to_fetch_.pop_front(); | 962 urls_to_fetch_.pop_front(); |
| 962 | 963 |
| 963 AppCache::EntryMap::iterator it = url_file_list_.find(url); | 964 AppCache::EntryMap::iterator it = url_file_list_.find(url_to_fetch.url); |
| 964 DCHECK(it != url_file_list_.end()); | 965 DCHECK(it != url_file_list_.end()); |
| 965 AppCacheEntry& entry = it->second; | 966 AppCacheEntry& entry = it->second; |
| 966 if (ShouldSkipUrlFetch(entry)) { | 967 if (ShouldSkipUrlFetch(entry)) { |
| 967 NotifyAllProgress(url); | 968 NotifyAllProgress(url_to_fetch.url); |
| 968 ++url_fetches_completed_; | 969 ++url_fetches_completed_; |
| 969 } else if (AlreadyFetchedEntry(url, entry.types())) { | 970 } else if (AlreadyFetchedEntry(url_to_fetch.url, entry.types())) { |
| 970 NotifyAllProgress(url); | 971 NotifyAllProgress(url_to_fetch.url); |
| 971 ++url_fetches_completed_; // saved a URL request | 972 ++url_fetches_completed_; // saved a URL request |
| 972 } else if (!storage_checked && MaybeLoadFromNewestCache(url, entry)) { | 973 } else if (!url_to_fetch.storage_checked && |
| 974 MaybeLoadFromNewestCache(url_to_fetch.url, entry)) { | |
| 973 // Continues asynchronously after data is loaded from newest cache. | 975 // Continues asynchronously after data is loaded from newest cache. |
| 974 } else { | 976 } else { |
| 977 UpdateJobInfo* info = new UpdateJobInfo(UpdateJobInfo::URL_FETCH); | |
| 978 const net::HttpResponseInfo* http_info = NULL; | |
| 979 if (url_to_fetch.existing_response_info.get()) { | |
| 980 DCHECK(group_->newest_complete_cache()); | |
| 981 AppCacheEntry* existing_entry = | |
| 982 group_->newest_complete_cache()->GetEntry(url_to_fetch.url); | |
| 983 DCHECK(existing_entry); | |
| 984 DCHECK(existing_entry->response_id() == | |
| 985 url_to_fetch.existing_response_info->response_id()); | |
| 986 info->existing_entry_ = *existing_entry; | |
| 987 http_info = url_to_fetch.existing_response_info->http_response_info(); | |
| 988 } | |
| 989 | |
| 975 // Send URL request for the resource. | 990 // Send URL request for the resource. |
| 976 URLRequest* request = new URLRequest(url, this); | 991 URLRequest* request = new URLRequest(url_to_fetch.url, this); |
| 977 request->SetUserData(this, new UpdateJobInfo(UpdateJobInfo::URL_FETCH)); | 992 request->SetUserData(this, info); |
| 978 request->set_context(service_->request_context()); | 993 request->set_context(service_->request_context()); |
| 979 request->set_load_flags( | 994 request->set_load_flags( |
| 980 request->load_flags() | net::LOAD_DISABLE_INTERCEPT); | 995 request->load_flags() | net::LOAD_DISABLE_INTERCEPT); |
| 996 if (http_info) | |
| 997 AddConditionalHeaders(request, http_info); | |
| 981 request->Start(); | 998 request->Start(); |
| 982 pending_url_fetches_.insert(PendingUrlFetches::value_type(url, request)); | 999 pending_url_fetches_.insert( |
| 1000 PendingUrlFetches::value_type(url_to_fetch.url, request)); | |
| 983 } | 1001 } |
| 984 } | 1002 } |
| 985 } | 1003 } |
| 986 | 1004 |
| 987 void AppCacheUpdateJob::CancelAllUrlFetches() { | 1005 void AppCacheUpdateJob::CancelAllUrlFetches() { |
| 988 // Cancel any pending URL requests. | 1006 // Cancel any pending URL requests. |
| 989 for (PendingUrlFetches::iterator it = pending_url_fetches_.begin(); | 1007 for (PendingUrlFetches::iterator it = pending_url_fetches_.begin(); |
| 990 it != pending_url_fetches_.end(); ++it) { | 1008 it != pending_url_fetches_.end(); ++it) { |
| 991 delete it->second; | 1009 delete it->second; |
| 992 } | 1010 } |
| (...skipping 161 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1154 return true; | 1172 return true; |
| 1155 } | 1173 } |
| 1156 | 1174 |
| 1157 void AppCacheUpdateJob::OnResponseInfoLoaded( | 1175 void AppCacheUpdateJob::OnResponseInfoLoaded( |
| 1158 AppCacheResponseInfo* response_info, int64 response_id) { | 1176 AppCacheResponseInfo* response_info, int64 response_id) { |
| 1159 const net::HttpResponseInfo* http_info = response_info ? | 1177 const net::HttpResponseInfo* http_info = response_info ? |
| 1160 response_info->http_response_info() : NULL; | 1178 response_info->http_response_info() : NULL; |
| 1161 | 1179 |
| 1162 // Needed response info for a manifest fetch request. | 1180 // Needed response info for a manifest fetch request. |
| 1163 if (internal_state_ == FETCH_MANIFEST) { | 1181 if (internal_state_ == FETCH_MANIFEST) { |
| 1164 AddHttpHeadersAndFetch(manifest_url_request_, http_info); | 1182 if (http_info) |
| 1183 AddConditionalHeaders(manifest_url_request_, http_info); | |
| 1184 manifest_url_request_->Start(); | |
| 1165 return; | 1185 return; |
| 1166 } | 1186 } |
| 1167 | 1187 |
| 1168 LoadingResponses::iterator found = loading_responses_.find(response_id); | 1188 LoadingResponses::iterator found = loading_responses_.find(response_id); |
| 1169 DCHECK(found != loading_responses_.end()); | 1189 DCHECK(found != loading_responses_.end()); |
| 1170 const GURL& url = found->second; | 1190 const GURL& url = found->second; |
| 1171 | 1191 |
| 1172 if (!http_info) { | 1192 if (!http_info) { |
| 1173 LoadFromNewestCacheFailed(url); // no response found | 1193 LoadFromNewestCacheFailed(url, NULL); // no response found |
| 1174 } else { | 1194 } else { |
| 1175 // Check if response can be re-used according to HTTP caching semantics. | 1195 // Check if response can be re-used according to HTTP caching semantics. |
| 1176 // Responses with a "vary" header get treated as expired. | 1196 // Responses with a "vary" header get treated as expired. |
| 1177 const std::string name = "vary"; | 1197 const std::string name = "vary"; |
| 1178 std::string value; | 1198 std::string value; |
| 1179 void* iter = NULL; | 1199 void* iter = NULL; |
| 1180 if (http_info->headers->RequiresValidation(http_info->request_time, | 1200 if (http_info->headers->RequiresValidation(http_info->request_time, |
| 1181 http_info->response_time, | 1201 http_info->response_time, |
| 1182 base::Time::Now()) || | 1202 base::Time::Now()) || |
| 1183 http_info->headers->EnumerateHeader(&iter, name, &value)) { | 1203 http_info->headers->EnumerateHeader(&iter, name, &value)) { |
| 1184 // TODO(michaeln): Make a conditional request when we can in this case. | 1204 // TODO(michaeln): Make a conditional request when we can in this case. |
| 1185 LoadFromNewestCacheFailed(url); | 1205 LoadFromNewestCacheFailed(url, response_info); |
| 1186 } else { | 1206 } else { |
| 1187 DCHECK(group_->newest_complete_cache()); | 1207 DCHECK(group_->newest_complete_cache()); |
| 1188 AppCacheEntry* copy_me = group_->newest_complete_cache()->GetEntry(url); | 1208 AppCacheEntry* copy_me = group_->newest_complete_cache()->GetEntry(url); |
| 1189 DCHECK(copy_me); | 1209 DCHECK(copy_me); |
| 1190 DCHECK(copy_me->response_id() == response_id); | 1210 DCHECK(copy_me->response_id() == response_id); |
| 1191 | 1211 |
| 1192 AppCache::EntryMap::iterator it = url_file_list_.find(url); | 1212 AppCache::EntryMap::iterator it = url_file_list_.find(url); |
| 1193 DCHECK(it != url_file_list_.end()); | 1213 DCHECK(it != url_file_list_.end()); |
| 1194 AppCacheEntry& entry = it->second; | 1214 AppCacheEntry& entry = it->second; |
| 1195 entry.set_response_id(response_id); | 1215 entry.set_response_id(response_id); |
| 1196 entry.set_response_size(copy_me->response_size()); | 1216 entry.set_response_size(copy_me->response_size()); |
| 1197 inprogress_cache_->AddOrModifyEntry(url, entry); | 1217 inprogress_cache_->AddOrModifyEntry(url, entry); |
| 1198 NotifyAllProgress(url); | 1218 NotifyAllProgress(url); |
| 1199 ++url_fetches_completed_; | 1219 ++url_fetches_completed_; |
| 1200 } | 1220 } |
| 1201 } | 1221 } |
| 1202 loading_responses_.erase(found); | 1222 loading_responses_.erase(found); |
| 1203 | 1223 |
| 1204 MaybeCompleteUpdate(); | 1224 MaybeCompleteUpdate(); |
| 1205 } | 1225 } |
| 1206 | 1226 |
| 1207 void AppCacheUpdateJob::LoadFromNewestCacheFailed(const GURL& url) { | 1227 void AppCacheUpdateJob::LoadFromNewestCacheFailed( |
| 1228 const GURL& url, AppCacheResponseInfo* response_info) { | |
| 1208 if (internal_state_ == CACHE_FAILURE) | 1229 if (internal_state_ == CACHE_FAILURE) |
| 1209 return; | 1230 return; |
| 1210 | 1231 |
| 1211 // Re-insert url at front of fetch list. Indicate storage has been checked. | 1232 // Re-insert url at front of fetch list. Indicate storage has been checked. |
| 1212 urls_to_fetch_.push_front(AppCacheUpdateJob::UrlsToFetch(url, true)); | 1233 urls_to_fetch_.push_front(UrlToFetch(url, true, response_info)); |
| 1213 FetchUrls(); | 1234 FetchUrls(); |
| 1214 } | 1235 } |
| 1215 | 1236 |
| 1216 void AppCacheUpdateJob::MaybeCompleteUpdate() { | 1237 void AppCacheUpdateJob::MaybeCompleteUpdate() { |
| 1217 DCHECK(internal_state_ != CACHE_FAILURE); | 1238 DCHECK(internal_state_ != CACHE_FAILURE); |
| 1218 | 1239 |
| 1219 // Must wait for any pending master entries or url fetches to complete. | 1240 // Must wait for any pending master entries or url fetches to complete. |
| 1220 if (master_entries_completed_ != pending_master_entries_.size() || | 1241 if (master_entries_completed_ != pending_master_entries_.size() || |
| 1221 url_fetches_completed_ != url_file_list_.size()) { | 1242 url_fetches_completed_ != url_file_list_.size()) { |
| 1222 DCHECK(internal_state_ != COMPLETED); | 1243 DCHECK(internal_state_ != COMPLETED); |
| (...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1343 | 1364 |
| 1344 // Break the connection with the group so the group cannot call delete | 1365 // Break the connection with the group so the group cannot call delete |
| 1345 // on this object after we've posted a task to delete ourselves. | 1366 // on this object after we've posted a task to delete ourselves. |
| 1346 group_->SetUpdateStatus(AppCacheGroup::IDLE); | 1367 group_->SetUpdateStatus(AppCacheGroup::IDLE); |
| 1347 group_ = NULL; | 1368 group_ = NULL; |
| 1348 | 1369 |
| 1349 MessageLoop::current()->DeleteSoon(FROM_HERE, this); | 1370 MessageLoop::current()->DeleteSoon(FROM_HERE, this); |
| 1350 } | 1371 } |
| 1351 | 1372 |
| 1352 } // namespace appcache | 1373 } // namespace appcache |
| OLD | NEW |