1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "content/browser/service_worker/service_worker_cache.h" 6 7 #include <string> 8 9 #include "base/files/file_path.h" 10 #include "base/guid.h" 11 #include "base/message_loop/message_loop_proxy.h" 12 #include "base/strings/string_util.h" 13 #include "content/browser/service_worker/service_worker_cache.pb.h" 14 #include "content/public/browser/browser_thread.h" 15 #include "net/base/io_buffer.h" 16 #include "net/base/net_errors.h" 17 #include "net/disk_cache/disk_cache.h" 18 #include "net/url_request/url_request_context.h" 19 #include "storage/browser/blob/blob_data_handle.h" 20 #include "storage/browser/blob/blob_storage_context.h" 21 #include "storage/browser/blob/blob_url_request_job_factory.h" 22 23 namespace content { 24 25 namespace { 26 27 typedef scoped_ptr<disk_cache::Backend> ScopedBackendPtr; 28 typedef base::Callback<void(bool)> BoolCallback; 29 typedef base::Callback<void(disk_cache::ScopedEntryPtr, bool)> 30 EntryBoolCallback; 31 typedef base::Callback<void(scoped_ptr<ServiceWorkerRequestResponseHeaders>)> 32 HeadersCallback; 33 34 enum EntryIndex { INDEX_HEADERS = 0, INDEX_RESPONSE_BODY }; 35 36 // The maximum size of an individual cache. Ultimately cache size is controlled 37 // per-origin. 38 const int kMaxCacheBytes = 512 * 1024 * 1024; 39 40 // Buffer size for cache and blob reading/writing. 41 const int kBufferSize = 1024 * 512; 42 43 struct ResponseReadContext { 44 ResponseReadContext(scoped_refptr<net::IOBufferWithSize> buff, 45 scoped_refptr<storage::BlobData> blob) 46 : buffer(buff), blob_data(blob), total_bytes_read(0) {} 47 48 scoped_refptr<net::IOBufferWithSize> buffer; 49 scoped_refptr<storage::BlobData> blob_data; 50 int total_bytes_read; 51 52 DISALLOW_COPY_AND_ASSIGN(ResponseReadContext); 53 }; 54 55 // Streams data from a blob and writes it to a given disk_cache::Entry. 56 class BlobReader : public net::URLRequest::Delegate { 57 public: 58 typedef base::Callback<void(disk_cache::ScopedEntryPtr, bool)> 59 EntryBoolCallback; 60 61 BlobReader(disk_cache::ScopedEntryPtr entry) 62 : cache_entry_offset_(0), 63 buffer_(new net::IOBufferWithSize(kBufferSize)), 64 weak_ptr_factory_(this) { 65 DCHECK(entry); 66 entry_ = entry.Pass(); 67 } 68 69 void StreamBlobToCache(net::URLRequestContext* request_context, 70 scoped_ptr<storage::BlobDataHandle> blob_data_handle, 71 const EntryBoolCallback& callback) { 72 callback_ = callback; 73 blob_request_ = storage::BlobProtocolHandler::CreateBlobRequest( 74 blob_data_handle.Pass(), request_context, this); 75 blob_request_->Start(); 76 } 77 78 // net::URLRequest::Delegate overrides for reading blobs. 79 virtual void OnReceivedRedirect(net::URLRequest* request, 80 const net::RedirectInfo& redirect_info, 81 bool* defer_redirect) OVERRIDE { 82 NOTREACHED(); 83 } 84 virtual void OnAuthRequired(net::URLRequest* request, 85 net::AuthChallengeInfo* auth_info) OVERRIDE { 86 NOTREACHED(); 87 } 88 virtual void OnCertificateRequested( 89 net::URLRequest* request, 90 net::SSLCertRequestInfo* cert_request_info) OVERRIDE { 91 NOTREACHED(); 92 } 93 virtual void OnSSLCertificateError(net::URLRequest* request, 94 const net::SSLInfo& ssl_info, 95 bool fatal) OVERRIDE { 96 NOTREACHED(); 97 } 98 virtual void OnBeforeNetworkStart(net::URLRequest* request, 99 bool* defer) OVERRIDE { 100 NOTREACHED(); 101 } 102 103 virtual void OnResponseStarted(net::URLRequest* request) OVERRIDE { 104 if (!request->status().is_success()) { 105 callback_.Run(entry_.Pass(), false); 106 return; 107 } 108 ReadFromBlob(); 109 } 110 111 virtual void ReadFromBlob() { 112 int bytes_read = 0; 113 bool done = 114 blob_request_->Read(buffer_.get(), buffer_->size(), &bytes_read); 115 if (done) 116 OnReadCompleted(blob_request_.get(), bytes_read); 117 } 118 119 virtual void OnReadCompleted(net::URLRequest* request, 120 int bytes_read) OVERRIDE { 121 if (!request->status().is_success()) { 122 callback_.Run(entry_.Pass(), false); 123 return; 124 } 125 126 if (bytes_read == 0) { 127 callback_.Run(entry_.Pass(), true); 128 return; 129 } 130 131 net::CompletionCallback cache_write_callback = 132 base::Bind(&BlobReader::DidWriteDataToEntry, 133 weak_ptr_factory_.GetWeakPtr(), 134 bytes_read); 135 136 int rv = entry_->WriteData(INDEX_RESPONSE_BODY, 137 cache_entry_offset_, 138 buffer_.get(), 139 bytes_read, 140 cache_write_callback, 141 true /* truncate */); 142 if (rv != net::ERR_IO_PENDING) 143 cache_write_callback.Run(rv); 144 } 145 146 void DidWriteDataToEntry(int expected_bytes, int rv) { 147 if (rv != expected_bytes) { 148 callback_.Run(entry_.Pass(), false); 149 return; 150 } 151 152 cache_entry_offset_ += rv; 153 ReadFromBlob(); 154 } 155 156 private: 157 int cache_entry_offset_; 158 disk_cache::ScopedEntryPtr entry_; 159 scoped_ptr<net::URLRequest> blob_request_; 160 EntryBoolCallback callback_; 161 scoped_refptr<net::IOBufferWithSize> buffer_; 162 base::WeakPtrFactory<BlobReader> weak_ptr_factory_; 163 }; 164 165 // Put callbacks 166 void PutDidCreateEntry(scoped_ptr<ServiceWorkerFetchRequest> request, 167 scoped_ptr<ServiceWorkerResponse> response, 168 const ServiceWorkerCache::ErrorCallback& callback, 169 scoped_ptr<disk_cache::Entry*> entryptr, 170 scoped_ptr<storage::BlobDataHandle> blob_data_handle, 171 net::URLRequestContext* request_context, 172 int rv); 173 void PutDidWriteHeaders(scoped_ptr<ServiceWorkerResponse> response, 174 const ServiceWorkerCache::ErrorCallback& callback, 175 disk_cache::ScopedEntryPtr entry, 176 scoped_ptr<storage::BlobDataHandle> blob_data_handle, 177 net::URLRequestContext* request_context, 178 int expected_bytes, 179 int rv); 180 void PutDidWriteBlobToCache(const ServiceWorkerCache::ErrorCallback& callback, 181 scoped_ptr<BlobReader> blob_reader, 182 disk_cache::ScopedEntryPtr entry, 183 bool success); 184 185 // Match callbacks 186 void MatchDidOpenEntry(scoped_ptr<ServiceWorkerFetchRequest> request, 187 const ServiceWorkerCache::ResponseCallback& callback, 188 base::WeakPtr<storage::BlobStorageContext> blob_storage, 189 scoped_ptr<disk_cache::Entry*> entryptr, 190 int rv); 191 void MatchDidReadHeaderData( 192 scoped_ptr<ServiceWorkerFetchRequest> request, 193 const ServiceWorkerCache::ResponseCallback& callback, 194 base::WeakPtr<storage::BlobStorageContext> blob_storage, 195 disk_cache::ScopedEntryPtr entry, 196 scoped_ptr<ServiceWorkerRequestResponseHeaders> headers); 197 void MatchDidReadResponseBodyData( 198 scoped_ptr<ServiceWorkerFetchRequest> request, 199 const ServiceWorkerCache::ResponseCallback& callback, 200 base::WeakPtr<storage::BlobStorageContext> blob_storage, 201 disk_cache::ScopedEntryPtr entry, 202 scoped_ptr<ServiceWorkerResponse> response, 203 scoped_ptr<ResponseReadContext> response_context, 204 int rv); 205 void MatchDoneWithBody(scoped_ptr<ServiceWorkerFetchRequest> request, 206 const ServiceWorkerCache::ResponseCallback& callback, 207 base::WeakPtr<storage::BlobStorageContext> blob_storage, 208 scoped_ptr<ServiceWorkerResponse> response, 209 scoped_ptr<ResponseReadContext> response_context); 210 211 // Delete callbacks 212 void DeleteDidOpenEntry(scoped_ptr<ServiceWorkerFetchRequest> request, 213 const ServiceWorkerCache::ErrorCallback& callback, 214 scoped_ptr<disk_cache::Entry*> entryptr, 215 int rv); 216 217 // Copy headers out of a cache entry and into a protobuf. The callback is 218 // guaranteed to be run. 219 void ReadHeaders(disk_cache::Entry* entry, const HeadersCallback& callback); 220 void ReadHeadersDidReadHeaderData( 221 disk_cache::Entry* entry, 222 const HeadersCallback& callback, 223 const scoped_refptr<net::IOBufferWithSize>& buffer, 224 int rv); 225 226 // CreateBackend callbacks 227 void CreateBackendDidCreate(const ServiceWorkerCache::ErrorCallback& callback, 228 scoped_ptr<ScopedBackendPtr> backend_ptr, 229 base::WeakPtr<ServiceWorkerCache> cache, 230 int rv); 231 232 void PutDidCreateEntry(scoped_ptr<ServiceWorkerFetchRequest> request, 233 scoped_ptr<ServiceWorkerResponse> response, 234 const ServiceWorkerCache::ErrorCallback& callback, 235 scoped_ptr<disk_cache::Entry*> entryptr, 236 scoped_ptr<storage::BlobDataHandle> blob_data_handle, 237 net::URLRequestContext* request_context, 238 int rv) { 239 if (rv != net::OK) { 240 callback.Run(ServiceWorkerCache::ErrorTypeExists); 241 return; 242 } 243 244 DCHECK(entryptr); 245 disk_cache::ScopedEntryPtr entry(*entryptr); 246 247 ServiceWorkerRequestResponseHeaders headers; 248 headers.set_method(request->method); 249 250 headers.set_status_code(response->status_code); 251 headers.set_status_text(response->status_text); 252 for (ServiceWorkerHeaderMap::const_iterator it = request->headers.begin(); 253 it != request->headers.end(); 254 ++it) { 255 ServiceWorkerRequestResponseHeaders::HeaderMap* header_map = 256 headers.add_request_headers(); 257 header_map->set_name(it->first); 258 header_map->set_value(it->second); 259 } 260 261 for (ServiceWorkerHeaderMap::const_iterator it = response->headers.begin(); 262 it != response->headers.end(); 263 ++it) { 264 ServiceWorkerRequestResponseHeaders::HeaderMap* header_map = 265 headers.add_response_headers(); 266 header_map->set_name(it->first); 267 header_map->set_value(it->second); 268 } 269 270 scoped_ptr<std::string> serialized(new std::string()); 271 if (!headers.SerializeToString(serialized.get())) { 272 callback.Run(ServiceWorkerCache::ErrorTypeStorage); 273 return; 274 } 275 276 scoped_refptr<net::StringIOBuffer> buffer( 277 new net::StringIOBuffer(serialized.Pass())); 278 279 // Get a temporary copy of the entry pointer before passing it in base::Bind. 280 disk_cache::Entry* tmp_entry_ptr = entry.get(); 281 282 net::CompletionCallback write_headers_callback = 283 base::Bind(PutDidWriteHeaders, 284 base::Passed(response.Pass()), 285 callback, 286 base::Passed(entry.Pass()), 287 base::Passed(blob_data_handle.Pass()), 288 request_context, 289 buffer->size()); 290 291 rv = tmp_entry_ptr->WriteData(INDEX_HEADERS, 292 0 /* offset */, 293 buffer.get(), 294 buffer->size(), 295 write_headers_callback, 296 true /* truncate */); 297 298 if (rv != net::ERR_IO_PENDING) 299 write_headers_callback.Run(rv); 300 } 301 302 void PutDidWriteHeaders(scoped_ptr<ServiceWorkerResponse> response, 303 const ServiceWorkerCache::ErrorCallback& callback, 304 disk_cache::ScopedEntryPtr entry, 305 scoped_ptr<storage::BlobDataHandle> blob_data_handle, 306 net::URLRequestContext* request_context, 307 int expected_bytes, 308 int rv) { 309 if (rv != expected_bytes) { 310 entry->Doom(); 311 callback.Run(ServiceWorkerCache::ErrorTypeStorage); 312 return; 313 } 314 315 // The metadata is written, now for the response content. The data is streamed 316 // from the blob into the cache entry. 317 318 if (response->blob_uuid.empty()) { 319 callback.Run(ServiceWorkerCache::ErrorTypeOK); 320 return; 321 } 322 323 DCHECK(blob_data_handle); 324 325 scoped_ptr<BlobReader> reader(new BlobReader(entry.Pass())); 326 BlobReader* reader_ptr = reader.get(); 327 328 reader_ptr->StreamBlobToCache( 329 request_context, 330 blob_data_handle.Pass(), 331 base::Bind( 332 PutDidWriteBlobToCache, callback, base::Passed(reader.Pass()))); 333 } 334 335 void PutDidWriteBlobToCache(const ServiceWorkerCache::ErrorCallback& callback, 336 scoped_ptr<BlobReader> blob_reader, 337 disk_cache::ScopedEntryPtr entry, 338 bool success) { 339 if (!success) { 340 entry->Doom(); 341 callback.Run(ServiceWorkerCache::ErrorTypeStorage); 342 return; 343 } 344 345 callback.Run(ServiceWorkerCache::ErrorTypeOK); 346 } 347 348 void MatchDidOpenEntry(scoped_ptr<ServiceWorkerFetchRequest> request, 349 const ServiceWorkerCache::ResponseCallback& callback, 350 base::WeakPtr<storage::BlobStorageContext> blob_storage, 351 scoped_ptr<disk_cache::Entry*> entryptr, 352 int rv) { 353 if (rv != net::OK) { 354 callback.Run(ServiceWorkerCache::ErrorTypeNotFound, 355 scoped_ptr<ServiceWorkerResponse>(), 356 scoped_ptr<storage::BlobDataHandle>()); 357 return; 358 } 359 360 DCHECK(entryptr); 361 disk_cache::ScopedEntryPtr entry(*entryptr); 362 363 // Copy the entry pointer before passing it in base::Bind. 364 disk_cache::Entry* tmp_entry_ptr = entry.get(); 365 366 HeadersCallback headers_callback = base::Bind(MatchDidReadHeaderData, 367 base::Passed(request.Pass()), 368 callback, 369 blob_storage, 370 base::Passed(entry.Pass())); 371 372 ReadHeaders(tmp_entry_ptr, headers_callback); 373 } 374 375 bool VaryMatches(const ServiceWorkerHeaderMap& request, 376 const ServiceWorkerHeaderMap& cached_request, 377 const ServiceWorkerHeaderMap& response) { 378 ServiceWorkerHeaderMap::const_iterator vary_iter = response.find("vary"); 379 if (vary_iter == response.end()) 380 return true; 381 382 std::vector<std::string> vary_keys; 383 Tokenize(vary_iter->second, ",", &vary_keys); 384 for (std::vector<std::string>::const_iterator it = vary_keys.begin(); 385 it != vary_keys.end(); 386 ++it) { 387 std::string trimmed; 388 base::TrimWhitespaceASCII(*it, base::TRIM_ALL, &trimmed); 389 if (trimmed == "*") 390 return false; 391 392 ServiceWorkerHeaderMap::const_iterator request_iter = request.find(trimmed); 393 ServiceWorkerHeaderMap::const_iterator cached_request_iter = 394 cached_request.find(trimmed); 395 396 // If the header exists in one but not the other, no match. 397 if ((request_iter == request.end()) != 398 (cached_request_iter == cached_request.end())) 399 return false; 400 401 // If the header exists in one, it exists in both. Verify that the values 402 // are equal. 403 if (request_iter != request.end() && 404 request_iter->second != cached_request_iter->second) 405 return false; 406 } 407 408 return true; 409 } 410 411 void MatchDidReadHeaderData( 412 scoped_ptr<ServiceWorkerFetchRequest> request, 413 const ServiceWorkerCache::ResponseCallback& callback, 414 base::WeakPtr<storage::BlobStorageContext> blob_storage, 415 disk_cache::ScopedEntryPtr entry, 416 scoped_ptr<ServiceWorkerRequestResponseHeaders> headers) { 417 if (!headers) { 418 callback.Run(ServiceWorkerCache::ErrorTypeStorage, 419 scoped_ptr<ServiceWorkerResponse>(), 420 scoped_ptr<storage::BlobDataHandle>()); 421 return; 422 } 423 424 scoped_ptr<ServiceWorkerResponse> response( 425 new ServiceWorkerResponse(request->url, 426 headers->status_code(), 427 headers->status_text(), 428 ServiceWorkerHeaderMap(), 429 "")); 430 431 for (int i = 0; i < headers->response_headers_size(); ++i) { 432 const ServiceWorkerRequestResponseHeaders::HeaderMap header = 433 headers->response_headers(i); 434 response->headers.insert(std::make_pair(header.name(), header.value())); 435 } 436 437 ServiceWorkerHeaderMap cached_request_headers; 438 for (int i = 0; i < headers->request_headers_size(); ++i) { 439 const ServiceWorkerRequestResponseHeaders::HeaderMap header = 440 headers->request_headers(i); 441 cached_request_headers[header.name()] = header.value(); 442 } 443 444 if (!VaryMatches( 445 request->headers, cached_request_headers, response->headers)) { 446 callback.Run(ServiceWorkerCache::ErrorTypeNotFound, 447 scoped_ptr<ServiceWorkerResponse>(), 448 scoped_ptr<storage::BlobDataHandle>()); 449 return; 450 } 451 452 if (entry->GetDataSize(INDEX_RESPONSE_BODY) == 0) { 453 callback.Run(ServiceWorkerCache::ErrorTypeOK, 454 response.Pass(), 455 scoped_ptr<storage::BlobDataHandle>()); 456 return; 457 } 458 459 // Stream the response body into a blob. 460 if (!blob_storage) { 461 callback.Run(ServiceWorkerCache::ErrorTypeStorage, 462 scoped_ptr<ServiceWorkerResponse>(), 463 scoped_ptr<storage::BlobDataHandle>()); 464 return; 465 } 466 467 response->blob_uuid = base::GenerateGUID(); 468 469 scoped_refptr<storage::BlobData> blob_data = 470 new storage::BlobData(response->blob_uuid); 471 scoped_refptr<net::IOBufferWithSize> response_body_buffer( 472 new net::IOBufferWithSize(kBufferSize)); 473 474 scoped_ptr<ResponseReadContext> read_context( 475 new ResponseReadContext(response_body_buffer, blob_data)); 476 477 // Copy the entry pointer before passing it in base::Bind. 478 disk_cache::Entry* tmp_entry_ptr = entry.get(); 479 480 net::CompletionCallback read_callback = 481 base::Bind(MatchDidReadResponseBodyData, 482 base::Passed(request.Pass()), 483 callback, 484 blob_storage, 485 base::Passed(entry.Pass()), 486 base::Passed(response.Pass()), 487 base::Passed(read_context.Pass())); 488 489 int read_rv = tmp_entry_ptr->ReadData(INDEX_RESPONSE_BODY, 490 0, 491 response_body_buffer.get(), 492 response_body_buffer->size(), 493 read_callback); 494 495 if (read_rv != net::ERR_IO_PENDING) 496 read_callback.Run(read_rv); 497 } 498 499 void MatchDidReadResponseBodyData( 500 scoped_ptr<ServiceWorkerFetchRequest> request, 501 const ServiceWorkerCache::ResponseCallback& callback, 502 base::WeakPtr<storage::BlobStorageContext> blob_storage, 503 disk_cache::ScopedEntryPtr entry, 504 scoped_ptr<ServiceWorkerResponse> response, 505 scoped_ptr<ResponseReadContext> response_context, 506 int rv) { 507 if (rv < 0) { 508 callback.Run(ServiceWorkerCache::ErrorTypeStorage, 509 scoped_ptr<ServiceWorkerResponse>(), 510 scoped_ptr<storage::BlobDataHandle>()); 511 return; 512 } 513 514 if (rv == 0) { 515 MatchDoneWithBody(request.Pass(), 516 callback, 517 blob_storage, 518 response.Pass(), 519 response_context.Pass()); 520 return; 521 } 522 523 // TODO(jkarlin): This copying of the the entire cache response into memory is 524 // awful. Create a new interface around SimpleCache that provides access the 525 // data directly from the file. See bug http://crbug.com/403493. 526 response_context->blob_data->AppendData(response_context->buffer->data(), rv); 527 response_context->total_bytes_read += rv; 528 int total_bytes_read = response_context->total_bytes_read; 529 530 // Grab some pointers before passing them in bind. 531 net::IOBufferWithSize* buffer = response_context->buffer.get(); 532 disk_cache::Entry* tmp_entry_ptr = entry.get(); 533 534 net::CompletionCallback read_callback = 535 base::Bind(MatchDidReadResponseBodyData, 536 base::Passed(request.Pass()), 537 callback, 538 blob_storage, 539 base::Passed(entry.Pass()), 540 base::Passed(response.Pass()), 541 base::Passed(response_context.Pass())); 542 543 int read_rv = tmp_entry_ptr->ReadData(INDEX_RESPONSE_BODY, 544 total_bytes_read, 545 buffer, 546 buffer->size(), 547 read_callback); 548 549 if (read_rv != net::ERR_IO_PENDING) 550 read_callback.Run(read_rv); 551 } 552 553 void MatchDoneWithBody(scoped_ptr<ServiceWorkerFetchRequest> request, 554 const ServiceWorkerCache::ResponseCallback& callback, 555 base::WeakPtr<storage::BlobStorageContext> blob_storage, 556 scoped_ptr<ServiceWorkerResponse> response, 557 scoped_ptr<ResponseReadContext> response_context) { 558 if (!blob_storage) { 559 callback.Run(ServiceWorkerCache::ErrorTypeStorage, 560 scoped_ptr<ServiceWorkerResponse>(), 561 scoped_ptr<storage::BlobDataHandle>()); 562 return; 563 } 564 565 scoped_ptr<storage::BlobDataHandle> blob_data_handle( 566 blob_storage->AddFinishedBlob(response_context->blob_data.get())); 567 568 callback.Run(ServiceWorkerCache::ErrorTypeOK, 569 response.Pass(), 570 blob_data_handle.Pass()); 571 } 572 573 void DeleteDidOpenEntry(scoped_ptr<ServiceWorkerFetchRequest> request, 574 const ServiceWorkerCache::ErrorCallback& callback, 575 scoped_ptr<disk_cache::Entry*> entryptr, 576 int rv) { 577 if (rv != net::OK) { 578 callback.Run(ServiceWorkerCache::ErrorTypeNotFound); 579 return; 580 } 581 582 DCHECK(entryptr); 583 disk_cache::ScopedEntryPtr entry(*entryptr); 584 585 entry->Doom(); 586 callback.Run(ServiceWorkerCache::ErrorTypeOK); 587 } 588 589 void ReadHeaders(disk_cache::Entry* entry, const HeadersCallback& callback) { 590 DCHECK(entry); 591 592 scoped_refptr<net::IOBufferWithSize> buffer( 593 new net::IOBufferWithSize(entry->GetDataSize(INDEX_HEADERS))); 594 595 net::CompletionCallback read_header_callback = 596 base::Bind(ReadHeadersDidReadHeaderData, entry, callback, buffer); 597 598 int read_rv = entry->ReadData( 599 INDEX_HEADERS, 0, buffer.get(), buffer->size(), read_header_callback); 600 601 if (read_rv != net::ERR_IO_PENDING) 602 read_header_callback.Run(read_rv); 603 } 604 605 void ReadHeadersDidReadHeaderData( 606 disk_cache::Entry* entry, 607 const HeadersCallback& callback, 608 const scoped_refptr<net::IOBufferWithSize>& buffer, 609 int rv) { 610 if (rv != buffer->size()) { 611 callback.Run(scoped_ptr<ServiceWorkerRequestResponseHeaders>()); 612 return; 613 } 614 615 scoped_ptr<ServiceWorkerRequestResponseHeaders> headers( 616 new ServiceWorkerRequestResponseHeaders()); 617 618 if (!headers->ParseFromArray(buffer->data(), buffer->size())) { 619 callback.Run(scoped_ptr<ServiceWorkerRequestResponseHeaders>()); 620 return; 621 } 622 623 callback.Run(headers.Pass()); 624 } 625 626 void CreateBackendDidCreate(const ServiceWorkerCache::ErrorCallback& callback, 627 scoped_ptr<ScopedBackendPtr> backend_ptr, 628 base::WeakPtr<ServiceWorkerCache> cache, 629 int rv) { 630 if (rv != net::OK || !cache) { 631 callback.Run(ServiceWorkerCache::ErrorTypeStorage); 632 return; 633 } 634 635 cache->set_backend(backend_ptr->Pass()); 636 callback.Run(ServiceWorkerCache::ErrorTypeOK); 637 } 638 639 } // namespace 640 641 // The state needed to pass between ServiceWorkerCache::Keys callbacks. 642 struct ServiceWorkerCache::KeysContext { 643 KeysContext(const ServiceWorkerCache::RequestsCallback& callback, 644 base::WeakPtr<ServiceWorkerCache> cache) 645 : original_callback(callback), 646 cache(cache), 647 out_keys(new ServiceWorkerCache::Requests()), 648 enumerated_entry(NULL) {} 649 650 ~KeysContext() { 651 for (size_t i = 0, max = entries.size(); i < max; ++i) 652 entries[i]->Close(); 653 if (enumerated_entry) 654 enumerated_entry->Close(); 655 } 656 657 // The callback passed to the Keys() function. 658 ServiceWorkerCache::RequestsCallback original_callback; 659 660 // The ServiceWorkerCache that Keys was called on. 661 base::WeakPtr<ServiceWorkerCache> cache; 662 663 // The vector of open entries in the backend. 664 Entries entries; 665 666 // The output of the Keys function. 667 scoped_ptr<ServiceWorkerCache::Requests> out_keys; 668 669 // Used for enumerating cache entries. 670 scoped_ptr<disk_cache::Backend::Iterator> backend_iterator; 671 disk_cache::Entry* enumerated_entry; 672 }; 673 674 // static 675 scoped_refptr<ServiceWorkerCache> ServiceWorkerCache::CreateMemoryCache( 676 net::URLRequestContext* request_context, 677 base::WeakPtr<storage::BlobStorageContext> blob_context) { 678 return make_scoped_refptr( 679 new ServiceWorkerCache(base::FilePath(), request_context, blob_context)); 680 } 681 682 // static 683 scoped_refptr<ServiceWorkerCache> ServiceWorkerCache::CreatePersistentCache( 684 const base::FilePath& path, 685 net::URLRequestContext* request_context, 686 base::WeakPtr<storage::BlobStorageContext> blob_context) { 687 return make_scoped_refptr( 688 new ServiceWorkerCache(path, request_context, blob_context)); 689 } 690 691 ServiceWorkerCache::~ServiceWorkerCache() { 692 } 693 694 base::WeakPtr<ServiceWorkerCache> ServiceWorkerCache::AsWeakPtr() { 695 return weak_ptr_factory_.GetWeakPtr(); 696 } 697 698 void ServiceWorkerCache::Put(scoped_ptr<ServiceWorkerFetchRequest> request, 699 scoped_ptr<ServiceWorkerResponse> response, 700 const ErrorCallback& callback) { 701 scoped_ptr<storage::BlobDataHandle> blob_data_handle; 702 703 if (!response->blob_uuid.empty()) { 704 if (!blob_storage_context_) { 705 callback.Run(ErrorTypeStorage); 706 return; 707 } 708 blob_data_handle = 709 blob_storage_context_->GetBlobDataFromUUID(response->blob_uuid); 710 if (!blob_data_handle) { 711 callback.Run(ErrorTypeStorage); 712 return; 713 } 714 } 715 716 base::Closure continuation = base::Bind(&ServiceWorkerCache::PutImpl, 717 weak_ptr_factory_.GetWeakPtr(), 718 base::Passed(request.Pass()), 719 base::Passed(response.Pass()), 720 base::Passed(blob_data_handle.Pass()), 721 callback); 722 723 if (!initialized_) { 724 Init(continuation); 725 return; 726 } 727 728 continuation.Run(); 729 } 730 731 void ServiceWorkerCache::Match(scoped_ptr<ServiceWorkerFetchRequest> request, 732 const ResponseCallback& callback) { 733 if (!initialized_) { 734 Init(base::Bind(&ServiceWorkerCache::Match, 735 weak_ptr_factory_.GetWeakPtr(), 736 base::Passed(request.Pass()), 737 callback)); 738 return; 739 } 740 if (!backend_) { 741 callback.Run(ErrorTypeStorage, 742 scoped_ptr<ServiceWorkerResponse>(), 743 scoped_ptr<storage::BlobDataHandle>()); 744 return; 745 } 746 747 scoped_ptr<disk_cache::Entry*> entry(new disk_cache::Entry*); 748 749 disk_cache::Entry** entry_ptr = entry.get(); 750 751 ServiceWorkerFetchRequest* request_ptr = request.get(); 752 753 net::CompletionCallback open_entry_callback = 754 base::Bind(MatchDidOpenEntry, 755 base::Passed(request.Pass()), 756 callback, 757 blob_storage_context_, 758 base::Passed(entry.Pass())); 759 760 int rv = backend_->OpenEntry( 761 request_ptr->url.spec(), entry_ptr, open_entry_callback); 762 if (rv != net::ERR_IO_PENDING) 763 open_entry_callback.Run(rv); 764 } 765 766 void ServiceWorkerCache::Delete(scoped_ptr<ServiceWorkerFetchRequest> request, 767 const ErrorCallback& callback) { 768 if (!initialized_) { 769 Init(base::Bind(&ServiceWorkerCache::Delete, 770 weak_ptr_factory_.GetWeakPtr(), 771 base::Passed(request.Pass()), 772 callback)); 773 return; 774 } 775 if (!backend_) { 776 callback.Run(ErrorTypeStorage); 777 return; 778 } 779 780 scoped_ptr<disk_cache::Entry*> entry(new disk_cache::Entry*); 781 782 disk_cache::Entry** entry_ptr = entry.get(); 783 784 ServiceWorkerFetchRequest* request_ptr = request.get(); 785 786 net::CompletionCallback open_entry_callback = 787 base::Bind(DeleteDidOpenEntry, 788 base::Passed(request.Pass()), 789 callback, 790 base::Passed(entry.Pass())); 791 792 int rv = backend_->OpenEntry( 793 request_ptr->url.spec(), entry_ptr, open_entry_callback); 794 if (rv != net::ERR_IO_PENDING) 795 open_entry_callback.Run(rv); 796 } 797 798 void ServiceWorkerCache::Keys(const RequestsCallback& callback) { 799 if (!initialized_) { 800 Init(base::Bind( 801 &ServiceWorkerCache::Keys, weak_ptr_factory_.GetWeakPtr(), callback)); 802 return; 803 } 804 if (!backend_) { 805 callback.Run(ErrorTypeStorage, scoped_ptr<Requests>()); 806 return; 807 } 808 809 // 1. Iterate through all of the entries, open them, and add them to a vector. 810 // 2. For each open entry: 811 // 2.1. Read the headers into a protobuf. 812 // 2.2. Copy the protobuf into a ServiceWorkerFetchRequest (a "key"). 813 // 2.3. Push the response into a vector of requests to be returned. 814 // 3. Return the vector of requests (keys). 815 816 // The entries have to be loaded into a vector first because enumeration loops 817 // forever if you read data from a cache entry while enumerating. 818 819 scoped_ptr<KeysContext> keys_context( 820 new KeysContext(callback, weak_ptr_factory_.GetWeakPtr())); 821 822 keys_context->backend_iterator = backend_->CreateIterator(); 823 disk_cache::Backend::Iterator& iterator = *keys_context->backend_iterator; 824 disk_cache::Entry** enumerated_entry = &keys_context->enumerated_entry; 825 826 net::CompletionCallback open_entry_callback = 827 base::Bind(KeysDidOpenNextEntry, base::Passed(keys_context.Pass())); 828 829 int rv = iterator.OpenNextEntry(enumerated_entry, open_entry_callback); 830 831 if (rv != net::ERR_IO_PENDING) 832 open_entry_callback.Run(rv); 833 } 834 835 void ServiceWorkerCache::Close() { 836 backend_.reset(); 837 } 838 839 ServiceWorkerCache::ServiceWorkerCache( 840 const base::FilePath& path, 841 net::URLRequestContext* request_context, 842 base::WeakPtr<storage::BlobStorageContext> blob_context) 843 : path_(path), 844 request_context_(request_context), 845 blob_storage_context_(blob_context), 846 initialized_(false), 847 weak_ptr_factory_(this) { 848 } 849 850 void ServiceWorkerCache::PutImpl( 851 scoped_ptr<ServiceWorkerFetchRequest> request, 852 scoped_ptr<ServiceWorkerResponse> response, 853 scoped_ptr<storage::BlobDataHandle> blob_data_handle, 854 const ErrorCallback& callback) { 855 if (!backend_) { 856 callback.Run(ErrorTypeStorage); 857 return; 858 } 859 860 scoped_ptr<disk_cache::Entry*> entry(new disk_cache::Entry*); 861 862 disk_cache::Entry** entry_ptr = entry.get(); 863 864 ServiceWorkerFetchRequest* request_ptr = request.get(); 865 866 net::CompletionCallback create_entry_callback = 867 base::Bind(PutDidCreateEntry, 868 base::Passed(request.Pass()), 869 base::Passed(response.Pass()), 870 callback, 871 base::Passed(entry.Pass()), 872 base::Passed(blob_data_handle.Pass()), 873 request_context_); 874 875 int rv = backend_->CreateEntry( 876 request_ptr->url.spec(), entry_ptr, create_entry_callback); 877 878 if (rv != net::ERR_IO_PENDING) 879 create_entry_callback.Run(rv); 880 } 881 882 // static 883 void ServiceWorkerCache::KeysDidOpenNextEntry( 884 scoped_ptr<KeysContext> keys_context, 885 int rv) { 886 if (rv == net::ERR_FAILED) { 887 DCHECK(!keys_context->enumerated_entry); 888 // Enumeration is complete, extract the requests from the entries. 889 Entries::iterator iter = keys_context->entries.begin(); 890 KeysProcessNextEntry(keys_context.Pass(), iter); 891 return; 892 } 893 894 base::WeakPtr<ServiceWorkerCache> cache = keys_context->cache; 895 if (rv < 0 || !cache) { 896 keys_context->original_callback.Run(ErrorTypeStorage, 897 scoped_ptr<Requests>()); 898 return; 899 } 900 901 if (!cache->backend_) { 902 keys_context->original_callback.Run(ErrorTypeNotFound, 903 scoped_ptr<Requests>()); 904 return; 905 } 906 907 // Store the entry. 908 keys_context->entries.push_back(keys_context->enumerated_entry); 909 keys_context->enumerated_entry = NULL; 910 911 // Enumerate the next entry. 912 disk_cache::Backend::Iterator& iterator = *keys_context->backend_iterator; 913 disk_cache::Entry** enumerated_entry = &keys_context->enumerated_entry; 914 net::CompletionCallback open_entry_callback = 915 base::Bind(KeysDidOpenNextEntry, base::Passed(keys_context.Pass())); 916 917 rv = iterator.OpenNextEntry(enumerated_entry, open_entry_callback); 918 919 if (rv != net::ERR_IO_PENDING) 920 open_entry_callback.Run(rv); 921 } 922 923 // static 924 void ServiceWorkerCache::KeysProcessNextEntry( 925 scoped_ptr<KeysContext> keys_context, 926 const Entries::iterator& iter) { 927 if (iter == keys_context->entries.end()) { 928 // All done. Return all of the keys. 929 keys_context->original_callback.Run(ErrorTypeOK, 930 keys_context->out_keys.Pass()); 931 return; 932 } 933 934 ReadHeaders( 935 *iter, 936 base::Bind(KeysDidReadHeaders, base::Passed(keys_context.Pass()), iter)); 937 } 938 939 // static 940 void ServiceWorkerCache::KeysDidReadHeaders( 941 scoped_ptr<KeysContext> keys_context, 942 const Entries::iterator& iter, 943 scoped_ptr<ServiceWorkerRequestResponseHeaders> headers) { 944 disk_cache::Entry* entry = *iter; 945 946 if (headers) { 947 keys_context->out_keys->push_back( 948 ServiceWorkerFetchRequest(GURL(entry->GetKey()), 949 headers->method(), 950 ServiceWorkerHeaderMap(), 951 GURL(), 952 false)); 953 954 ServiceWorkerHeaderMap& req_headers = 955 keys_context->out_keys->back().headers; 956 957 for (int i = 0; i < headers->request_headers_size(); ++i) { 958 const ServiceWorkerRequestResponseHeaders::HeaderMap header = 959 headers->request_headers(i); 960 req_headers.insert(std::make_pair(header.name(), header.value())); 961 } 962 } else { 963 entry->Doom(); 964 } 965 966 KeysProcessNextEntry(keys_context.Pass(), iter + 1); 967 } 968 969 void ServiceWorkerCache::CreateBackend(const ErrorCallback& callback) { 970 DCHECK(!backend_); 971 972 // Use APP_CACHE as opposed to DISK_CACHE to prevent cache eviction. 973 net::CacheType cache_type = 974 path_.empty() ? net::MEMORY_CACHE : net::APP_CACHE; 975 976 scoped_ptr<ScopedBackendPtr> backend_ptr(new ScopedBackendPtr()); 977 978 // Temporary pointer so that backend_ptr can be Pass()'d in Bind below. 979 ScopedBackendPtr* backend = backend_ptr.get(); 980 981 net::CompletionCallback create_cache_callback = 982 base::Bind(CreateBackendDidCreate, 983 callback, 984 base::Passed(backend_ptr.Pass()), 985 weak_ptr_factory_.GetWeakPtr()); 986 987 // TODO(jkarlin): Use the cache MessageLoopProxy that ServiceWorkerCacheCore 988 // has for disk caches. 989 int rv = disk_cache::CreateCacheBackend( 990 cache_type, 991 net::CACHE_BACKEND_SIMPLE, 992 path_, 993 kMaxCacheBytes, 994 false, /* force */ 995 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::CACHE).get(), 996 NULL, 997 backend, 998 create_cache_callback); 999 if (rv != net::ERR_IO_PENDING) 1000 create_cache_callback.Run(rv); 1001 } 1002 1003 void ServiceWorkerCache::Init(const base::Closure& callback) { 1004 init_callbacks_.push_back(callback); 1005 1006 // If this isn't the first call to Init then return as the initialization 1007 // has already started. 1008 if (init_callbacks_.size() > 1u) 1009 return; 1010 1011 CreateBackend(base::Bind(&ServiceWorkerCache::InitDone, 1012 weak_ptr_factory_.GetWeakPtr())); 1013 } 1014 1015 void ServiceWorkerCache::InitDone(ErrorType error) { 1016 initialized_ = true; 1017 for (std::vector<base::Closure>::iterator it = init_callbacks_.begin(); 1018 it != init_callbacks_.end(); 1019 ++it) { 1020 it->Run(); 1021 } 1022 init_callbacks_.clear(); 1023 } 1024 1025 } // namespace content 1026