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 |