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.
5 #include "content/browser/service_worker/service_worker_cache.h"
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 #include "storage/browser/quota/quota_manager_proxy.h"
23 #include "third_party/WebKit/public/platform/WebServiceWorkerResponseType.h"
29 typedef scoped_ptr<disk_cache::Backend> ScopedBackendPtr;
30 typedef base::Callback<void(bool)> BoolCallback;
31 typedef base::Callback<void(disk_cache::ScopedEntryPtr, bool)>
33 typedef base::Callback<void(scoped_ptr<ServiceWorkerCacheMetadata>)>
36 enum EntryIndex { INDEX_HEADERS = 0, INDEX_RESPONSE_BODY };
38 // The maximum size of an individual cache. Ultimately cache size is controlled
40 const int kMaxCacheBytes = 512 * 1024 * 1024;
42 // Buffer size for cache and blob reading/writing.
43 const int kBufferSize = 1024 * 512;
45 void NotReachedCompletionCallback(int rv) {
49 blink::WebServiceWorkerResponseType ProtoResponseTypeToWebResponseType(
50 ServiceWorkerCacheResponse::ResponseType response_type) {
51 switch (response_type) {
52 case ServiceWorkerCacheResponse::BASIC_TYPE:
53 return blink::WebServiceWorkerResponseTypeBasic;
54 case ServiceWorkerCacheResponse::CORS_TYPE:
55 return blink::WebServiceWorkerResponseTypeCORS;
56 case ServiceWorkerCacheResponse::DEFAULT_TYPE:
57 return blink::WebServiceWorkerResponseTypeDefault;
58 case ServiceWorkerCacheResponse::ERROR_TYPE:
59 return blink::WebServiceWorkerResponseTypeError;
60 case ServiceWorkerCacheResponse::OPAQUE_TYPE:
61 return blink::WebServiceWorkerResponseTypeOpaque;
64 return blink::WebServiceWorkerResponseTypeOpaque;
67 ServiceWorkerCacheResponse::ResponseType WebResponseTypeToProtoResponseType(
68 blink::WebServiceWorkerResponseType response_type) {
69 switch (response_type) {
70 case blink::WebServiceWorkerResponseTypeBasic:
71 return ServiceWorkerCacheResponse::BASIC_TYPE;
72 case blink::WebServiceWorkerResponseTypeCORS:
73 return ServiceWorkerCacheResponse::CORS_TYPE;
74 case blink::WebServiceWorkerResponseTypeDefault:
75 return ServiceWorkerCacheResponse::DEFAULT_TYPE;
76 case blink::WebServiceWorkerResponseTypeError:
77 return ServiceWorkerCacheResponse::ERROR_TYPE;
78 case blink::WebServiceWorkerResponseTypeOpaque:
79 return ServiceWorkerCacheResponse::OPAQUE_TYPE;
82 return ServiceWorkerCacheResponse::OPAQUE_TYPE;
85 struct ResponseReadContext {
86 ResponseReadContext(scoped_refptr<net::IOBufferWithSize> buff,
87 scoped_refptr<storage::BlobData> blob)
88 : buffer(buff), blob_data(blob), total_bytes_read(0) {}
90 scoped_refptr<net::IOBufferWithSize> buffer;
91 scoped_refptr<storage::BlobData> blob_data;
94 DISALLOW_COPY_AND_ASSIGN(ResponseReadContext);
98 void MatchDidOpenEntry(scoped_ptr<ServiceWorkerFetchRequest> request,
99 const ServiceWorkerCache::ResponseCallback& callback,
100 base::WeakPtr<storage::BlobStorageContext> blob_storage,
101 scoped_ptr<disk_cache::Entry*> entryptr,
103 void MatchDidReadMetadata(
104 scoped_ptr<ServiceWorkerFetchRequest> request,
105 const ServiceWorkerCache::ResponseCallback& callback,
106 base::WeakPtr<storage::BlobStorageContext> blob_storage,
107 disk_cache::ScopedEntryPtr entry,
108 scoped_ptr<ServiceWorkerCacheMetadata> headers);
109 void MatchDidReadResponseBodyData(
110 scoped_ptr<ServiceWorkerFetchRequest> request,
111 const ServiceWorkerCache::ResponseCallback& callback,
112 base::WeakPtr<storage::BlobStorageContext> blob_storage,
113 disk_cache::ScopedEntryPtr entry,
114 scoped_ptr<ServiceWorkerResponse> response,
115 scoped_ptr<ResponseReadContext> response_context,
117 void MatchDoneWithBody(scoped_ptr<ServiceWorkerFetchRequest> request,
118 const ServiceWorkerCache::ResponseCallback& callback,
119 base::WeakPtr<storage::BlobStorageContext> blob_storage,
120 scoped_ptr<ServiceWorkerResponse> response,
121 scoped_ptr<ResponseReadContext> response_context);
124 void DeleteDidOpenEntry(
126 scoped_ptr<ServiceWorkerFetchRequest> request,
127 const ServiceWorkerCache::ErrorCallback& callback,
128 scoped_ptr<disk_cache::Entry*> entryptr,
129 const scoped_refptr<storage::QuotaManagerProxy>& quota_manager_proxy,
132 // Copy headers out of a cache entry and into a protobuf. The callback is
133 // guaranteed to be run.
134 void ReadMetadata(disk_cache::Entry* entry, const MetadataCallback& callback);
135 void ReadMetadataDidReadMetadata(
136 disk_cache::Entry* entry,
137 const MetadataCallback& callback,
138 const scoped_refptr<net::IOBufferWithSize>& buffer,
141 // CreateBackend callbacks
142 void CreateBackendDidCreate(const ServiceWorkerCache::ErrorCallback& callback,
143 scoped_ptr<ScopedBackendPtr> backend_ptr,
144 base::WeakPtr<ServiceWorkerCache> cache,
147 void MatchDidOpenEntry(scoped_ptr<ServiceWorkerFetchRequest> request,
148 const ServiceWorkerCache::ResponseCallback& callback,
149 base::WeakPtr<storage::BlobStorageContext> blob_storage,
150 scoped_ptr<disk_cache::Entry*> entryptr,
153 callback.Run(ServiceWorkerCache::ErrorTypeNotFound,
154 scoped_ptr<ServiceWorkerResponse>(),
155 scoped_ptr<storage::BlobDataHandle>());
160 disk_cache::ScopedEntryPtr entry(*entryptr);
162 // Copy the entry pointer before passing it in base::Bind.
163 disk_cache::Entry* tmp_entry_ptr = entry.get();
165 MetadataCallback headers_callback = base::Bind(MatchDidReadMetadata,
166 base::Passed(request.Pass()),
169 base::Passed(entry.Pass()));
171 ReadMetadata(tmp_entry_ptr, headers_callback);
174 bool VaryMatches(const ServiceWorkerHeaderMap& request,
175 const ServiceWorkerHeaderMap& cached_request,
176 const ServiceWorkerHeaderMap& response) {
177 ServiceWorkerHeaderMap::const_iterator vary_iter = response.find("vary");
178 if (vary_iter == response.end())
181 std::vector<std::string> vary_keys;
182 Tokenize(vary_iter->second, ",", &vary_keys);
183 for (std::vector<std::string>::const_iterator it = vary_keys.begin();
184 it != vary_keys.end();
187 base::TrimWhitespaceASCII(*it, base::TRIM_ALL, &trimmed);
191 ServiceWorkerHeaderMap::const_iterator request_iter = request.find(trimmed);
192 ServiceWorkerHeaderMap::const_iterator cached_request_iter =
193 cached_request.find(trimmed);
195 // If the header exists in one but not the other, no match.
196 if ((request_iter == request.end()) !=
197 (cached_request_iter == cached_request.end()))
200 // If the header exists in one, it exists in both. Verify that the values
202 if (request_iter != request.end() &&
203 request_iter->second != cached_request_iter->second)
210 void MatchDidReadMetadata(
211 scoped_ptr<ServiceWorkerFetchRequest> request,
212 const ServiceWorkerCache::ResponseCallback& callback,
213 base::WeakPtr<storage::BlobStorageContext> blob_storage,
214 disk_cache::ScopedEntryPtr entry,
215 scoped_ptr<ServiceWorkerCacheMetadata> metadata) {
217 callback.Run(ServiceWorkerCache::ErrorTypeStorage,
218 scoped_ptr<ServiceWorkerResponse>(),
219 scoped_ptr<storage::BlobDataHandle>());
223 scoped_ptr<ServiceWorkerResponse> response(new ServiceWorkerResponse(
225 metadata->response().status_code(),
226 metadata->response().status_text(),
227 ProtoResponseTypeToWebResponseType(metadata->response().response_type()),
228 ServiceWorkerHeaderMap(),
232 if (metadata->response().has_url())
233 response->url = GURL(metadata->response().url());
235 for (int i = 0; i < metadata->response().headers_size(); ++i) {
236 const ServiceWorkerCacheHeaderMap header = metadata->response().headers(i);
237 response->headers.insert(std::make_pair(header.name(), header.value()));
240 ServiceWorkerHeaderMap cached_request_headers;
241 for (int i = 0; i < metadata->request().headers_size(); ++i) {
242 const ServiceWorkerCacheHeaderMap header = metadata->request().headers(i);
243 cached_request_headers[header.name()] = header.value();
247 request->headers, cached_request_headers, response->headers)) {
248 callback.Run(ServiceWorkerCache::ErrorTypeNotFound,
249 scoped_ptr<ServiceWorkerResponse>(),
250 scoped_ptr<storage::BlobDataHandle>());
254 if (entry->GetDataSize(INDEX_RESPONSE_BODY) == 0) {
255 callback.Run(ServiceWorkerCache::ErrorTypeOK,
257 scoped_ptr<storage::BlobDataHandle>());
261 // Stream the response body into a blob.
263 callback.Run(ServiceWorkerCache::ErrorTypeStorage,
264 scoped_ptr<ServiceWorkerResponse>(),
265 scoped_ptr<storage::BlobDataHandle>());
269 response->blob_uuid = base::GenerateGUID();
271 scoped_refptr<storage::BlobData> blob_data =
272 new storage::BlobData(response->blob_uuid);
273 scoped_refptr<net::IOBufferWithSize> response_body_buffer(
274 new net::IOBufferWithSize(kBufferSize));
276 scoped_ptr<ResponseReadContext> read_context(
277 new ResponseReadContext(response_body_buffer, blob_data));
279 // Copy the entry pointer before passing it in base::Bind.
280 disk_cache::Entry* tmp_entry_ptr = entry.get();
282 net::CompletionCallback read_callback =
283 base::Bind(MatchDidReadResponseBodyData,
284 base::Passed(request.Pass()),
287 base::Passed(entry.Pass()),
288 base::Passed(response.Pass()),
289 base::Passed(read_context.Pass()));
291 int read_rv = tmp_entry_ptr->ReadData(INDEX_RESPONSE_BODY,
293 response_body_buffer.get(),
294 response_body_buffer->size(),
297 if (read_rv != net::ERR_IO_PENDING)
298 read_callback.Run(read_rv);
301 void MatchDidReadResponseBodyData(
302 scoped_ptr<ServiceWorkerFetchRequest> request,
303 const ServiceWorkerCache::ResponseCallback& callback,
304 base::WeakPtr<storage::BlobStorageContext> blob_storage,
305 disk_cache::ScopedEntryPtr entry,
306 scoped_ptr<ServiceWorkerResponse> response,
307 scoped_ptr<ResponseReadContext> response_context,
310 callback.Run(ServiceWorkerCache::ErrorTypeStorage,
311 scoped_ptr<ServiceWorkerResponse>(),
312 scoped_ptr<storage::BlobDataHandle>());
317 response->blob_uuid = response_context->blob_data->uuid();
318 response->blob_size = response_context->total_bytes_read;
319 MatchDoneWithBody(request.Pass(),
323 response_context.Pass());
327 // TODO(jkarlin): This copying of the the entire cache response into memory is
328 // awful. Create a new interface around SimpleCache that provides access the
329 // data directly from the file. See bug http://crbug.com/403493.
330 response_context->blob_data->AppendData(response_context->buffer->data(), rv);
331 response_context->total_bytes_read += rv;
332 int total_bytes_read = response_context->total_bytes_read;
334 // Grab some pointers before passing them in bind.
335 net::IOBufferWithSize* buffer = response_context->buffer.get();
336 disk_cache::Entry* tmp_entry_ptr = entry.get();
338 net::CompletionCallback read_callback =
339 base::Bind(MatchDidReadResponseBodyData,
340 base::Passed(request.Pass()),
343 base::Passed(entry.Pass()),
344 base::Passed(response.Pass()),
345 base::Passed(response_context.Pass()));
347 int read_rv = tmp_entry_ptr->ReadData(INDEX_RESPONSE_BODY,
353 if (read_rv != net::ERR_IO_PENDING)
354 read_callback.Run(read_rv);
357 void MatchDoneWithBody(scoped_ptr<ServiceWorkerFetchRequest> request,
358 const ServiceWorkerCache::ResponseCallback& callback,
359 base::WeakPtr<storage::BlobStorageContext> blob_storage,
360 scoped_ptr<ServiceWorkerResponse> response,
361 scoped_ptr<ResponseReadContext> response_context) {
363 callback.Run(ServiceWorkerCache::ErrorTypeStorage,
364 scoped_ptr<ServiceWorkerResponse>(),
365 scoped_ptr<storage::BlobDataHandle>());
369 scoped_ptr<storage::BlobDataHandle> blob_data_handle(
370 blob_storage->AddFinishedBlob(response_context->blob_data.get()));
372 callback.Run(ServiceWorkerCache::ErrorTypeOK,
374 blob_data_handle.Pass());
377 void DeleteDidOpenEntry(
379 scoped_ptr<ServiceWorkerFetchRequest> request,
380 const ServiceWorkerCache::ErrorCallback& callback,
381 scoped_ptr<disk_cache::Entry*> entryptr,
382 const scoped_refptr<storage::QuotaManagerProxy>& quota_manager_proxy,
385 callback.Run(ServiceWorkerCache::ErrorTypeNotFound);
390 disk_cache::ScopedEntryPtr entry(*entryptr);
392 if (quota_manager_proxy.get()) {
393 quota_manager_proxy->NotifyStorageModified(
394 storage::QuotaClient::kServiceWorkerCache,
396 storage::kStorageTypeTemporary,
397 -1 * (entry->GetDataSize(INDEX_HEADERS) +
398 entry->GetDataSize(INDEX_RESPONSE_BODY)));
402 callback.Run(ServiceWorkerCache::ErrorTypeOK);
405 void ReadMetadata(disk_cache::Entry* entry, const MetadataCallback& callback) {
408 scoped_refptr<net::IOBufferWithSize> buffer(
409 new net::IOBufferWithSize(entry->GetDataSize(INDEX_HEADERS)));
411 net::CompletionCallback read_header_callback =
412 base::Bind(ReadMetadataDidReadMetadata, entry, callback, buffer);
414 int read_rv = entry->ReadData(
415 INDEX_HEADERS, 0, buffer.get(), buffer->size(), read_header_callback);
417 if (read_rv != net::ERR_IO_PENDING)
418 read_header_callback.Run(read_rv);
421 void ReadMetadataDidReadMetadata(
422 disk_cache::Entry* entry,
423 const MetadataCallback& callback,
424 const scoped_refptr<net::IOBufferWithSize>& buffer,
426 if (rv != buffer->size()) {
427 callback.Run(scoped_ptr<ServiceWorkerCacheMetadata>());
431 scoped_ptr<ServiceWorkerCacheMetadata> metadata(
432 new ServiceWorkerCacheMetadata());
434 if (!metadata->ParseFromArray(buffer->data(), buffer->size())) {
435 callback.Run(scoped_ptr<ServiceWorkerCacheMetadata>());
439 callback.Run(metadata.Pass());
442 void CreateBackendDidCreate(const ServiceWorkerCache::ErrorCallback& callback,
443 scoped_ptr<ScopedBackendPtr> backend_ptr,
444 base::WeakPtr<ServiceWorkerCache> cache,
446 if (rv != net::OK || !cache) {
447 callback.Run(ServiceWorkerCache::ErrorTypeStorage);
451 cache->set_backend(backend_ptr->Pass());
452 callback.Run(ServiceWorkerCache::ErrorTypeOK);
457 // Streams data from a blob and writes it to a given disk_cache::Entry.
458 class ServiceWorkerCache::BlobReader : public net::URLRequest::Delegate {
460 typedef base::Callback<void(disk_cache::ScopedEntryPtr, bool)>
461 EntryAndBoolCallback;
464 : cache_entry_offset_(0),
465 buffer_(new net::IOBufferWithSize(kBufferSize)),
466 weak_ptr_factory_(this) {}
468 // |entry| is passed to the callback once complete.
469 void StreamBlobToCache(disk_cache::ScopedEntryPtr entry,
470 net::URLRequestContext* request_context,
471 scoped_ptr<storage::BlobDataHandle> blob_data_handle,
472 const EntryAndBoolCallback& callback) {
474 entry_ = entry.Pass();
475 callback_ = callback;
476 blob_request_ = storage::BlobProtocolHandler::CreateBlobRequest(
477 blob_data_handle.Pass(), request_context, this);
478 blob_request_->Start();
481 // net::URLRequest::Delegate overrides for reading blobs.
482 void OnReceivedRedirect(net::URLRequest* request,
483 const net::RedirectInfo& redirect_info,
484 bool* defer_redirect) override {
487 void OnAuthRequired(net::URLRequest* request,
488 net::AuthChallengeInfo* auth_info) override {
491 void OnCertificateRequested(
492 net::URLRequest* request,
493 net::SSLCertRequestInfo* cert_request_info) override {
496 void OnSSLCertificateError(net::URLRequest* request,
497 const net::SSLInfo& ssl_info,
498 bool fatal) override {
501 void OnBeforeNetworkStart(net::URLRequest* request, bool* defer) override {
505 void OnResponseStarted(net::URLRequest* request) override {
506 if (!request->status().is_success()) {
507 callback_.Run(entry_.Pass(), false);
513 virtual void ReadFromBlob() {
516 blob_request_->Read(buffer_.get(), buffer_->size(), &bytes_read);
518 OnReadCompleted(blob_request_.get(), bytes_read);
521 void OnReadCompleted(net::URLRequest* request, int bytes_read) override {
522 if (!request->status().is_success()) {
523 callback_.Run(entry_.Pass(), false);
527 if (bytes_read == 0) {
528 callback_.Run(entry_.Pass(), true);
532 net::CompletionCallback cache_write_callback =
533 base::Bind(&BlobReader::DidWriteDataToEntry,
534 weak_ptr_factory_.GetWeakPtr(),
537 int rv = entry_->WriteData(INDEX_RESPONSE_BODY,
541 cache_write_callback,
542 true /* truncate */);
543 if (rv != net::ERR_IO_PENDING)
544 cache_write_callback.Run(rv);
547 void DidWriteDataToEntry(int expected_bytes, int rv) {
548 if (rv != expected_bytes) {
549 callback_.Run(entry_.Pass(), false);
553 cache_entry_offset_ += rv;
558 int cache_entry_offset_;
559 disk_cache::ScopedEntryPtr entry_;
560 scoped_ptr<net::URLRequest> blob_request_;
561 EntryAndBoolCallback callback_;
562 scoped_refptr<net::IOBufferWithSize> buffer_;
563 base::WeakPtrFactory<BlobReader> weak_ptr_factory_;
566 // The state needed to pass between ServiceWorkerCache::Keys callbacks.
567 struct ServiceWorkerCache::KeysContext {
568 KeysContext(const ServiceWorkerCache::RequestsCallback& callback,
569 base::WeakPtr<ServiceWorkerCache> cache)
570 : original_callback(callback),
572 out_keys(new ServiceWorkerCache::Requests()),
573 enumerated_entry(NULL) {}
576 for (size_t i = 0, max = entries.size(); i < max; ++i)
578 if (enumerated_entry)
579 enumerated_entry->Close();
582 // The callback passed to the Keys() function.
583 ServiceWorkerCache::RequestsCallback original_callback;
585 // The ServiceWorkerCache that Keys was called on.
586 base::WeakPtr<ServiceWorkerCache> cache;
588 // The vector of open entries in the backend.
591 // The output of the Keys function.
592 scoped_ptr<ServiceWorkerCache::Requests> out_keys;
594 // Used for enumerating cache entries.
595 scoped_ptr<disk_cache::Backend::Iterator> backend_iterator;
596 disk_cache::Entry* enumerated_entry;
598 DISALLOW_COPY_AND_ASSIGN(KeysContext);
601 // The state needed to pass between ServiceWorkerCache::Put callbacks.
602 struct ServiceWorkerCache::PutContext {
605 scoped_ptr<ServiceWorkerFetchRequest> request,
606 scoped_ptr<ServiceWorkerResponse> response,
607 scoped_ptr<storage::BlobDataHandle> blob_data_handle,
608 const ServiceWorkerCache::ResponseCallback& callback,
609 base::WeakPtr<ServiceWorkerCache> cache,
610 net::URLRequestContext* request_context,
611 const scoped_refptr<storage::QuotaManagerProxy>& quota_manager_proxy)
613 request(request.Pass()),
614 response(response.Pass()),
615 blob_data_handle(blob_data_handle.Pass()),
618 request_context(request_context),
619 quota_manager_proxy(quota_manager_proxy),
623 cache_entry->Close();
626 // Input parameters to the Put function.
628 scoped_ptr<ServiceWorkerFetchRequest> request;
629 scoped_ptr<ServiceWorkerResponse> response;
630 scoped_ptr<storage::BlobDataHandle> blob_data_handle;
631 ServiceWorkerCache::ResponseCallback callback;
632 base::WeakPtr<ServiceWorkerCache> cache;
633 net::URLRequestContext* request_context;
634 scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy;
636 // This isn't a scoped_ptr because the disk_cache needs an Entry** as input to
638 disk_cache::Entry* cache_entry;
640 // The BlobDataHandle for the output ServiceWorkerResponse.
641 scoped_ptr<storage::BlobDataHandle> out_blob_data_handle;
643 DISALLOW_COPY_AND_ASSIGN(PutContext);
647 scoped_refptr<ServiceWorkerCache> ServiceWorkerCache::CreateMemoryCache(
649 net::URLRequestContext* request_context,
650 const scoped_refptr<storage::QuotaManagerProxy>& quota_manager_proxy,
651 base::WeakPtr<storage::BlobStorageContext> blob_context) {
652 return make_scoped_refptr(new ServiceWorkerCache(origin,
660 scoped_refptr<ServiceWorkerCache> ServiceWorkerCache::CreatePersistentCache(
662 const base::FilePath& path,
663 net::URLRequestContext* request_context,
664 const scoped_refptr<storage::QuotaManagerProxy>& quota_manager_proxy,
665 base::WeakPtr<storage::BlobStorageContext> blob_context) {
666 return make_scoped_refptr(new ServiceWorkerCache(
667 origin, path, request_context, quota_manager_proxy, blob_context));
670 ServiceWorkerCache::~ServiceWorkerCache() {
673 base::WeakPtr<ServiceWorkerCache> ServiceWorkerCache::AsWeakPtr() {
674 return weak_ptr_factory_.GetWeakPtr();
677 void ServiceWorkerCache::Put(scoped_ptr<ServiceWorkerFetchRequest> request,
678 scoped_ptr<ServiceWorkerResponse> response,
679 const ResponseCallback& callback) {
681 ResponseCallback pending_callback =
682 base::Bind(&ServiceWorkerCache::PendingResponseCallback,
683 weak_ptr_factory_.GetWeakPtr(), callback);
684 scoped_ptr<storage::BlobDataHandle> blob_data_handle;
686 if (!response->blob_uuid.empty()) {
687 if (!blob_storage_context_) {
688 pending_callback.Run(ErrorTypeStorage,
689 scoped_ptr<ServiceWorkerResponse>(),
690 scoped_ptr<storage::BlobDataHandle>());
694 blob_storage_context_->GetBlobDataFromUUID(response->blob_uuid);
695 if (!blob_data_handle) {
696 pending_callback.Run(ErrorTypeStorage,
697 scoped_ptr<ServiceWorkerResponse>(),
698 scoped_ptr<storage::BlobDataHandle>());
703 scoped_ptr<PutContext> put_context(new PutContext(
704 origin_, request.Pass(), response.Pass(), blob_data_handle.Pass(),
705 pending_callback, weak_ptr_factory_.GetWeakPtr(), request_context_,
706 quota_manager_proxy_));
708 if (put_context->blob_data_handle) {
709 // Grab another handle to the blob for the callback response.
710 put_context->out_blob_data_handle =
711 blob_storage_context_->GetBlobDataFromUUID(
712 put_context->response->blob_uuid);
715 base::Closure continuation = base::Bind(&ServiceWorkerCache::PutImpl,
716 base::Passed(put_context.Pass()));
726 void ServiceWorkerCache::Match(scoped_ptr<ServiceWorkerFetchRequest> request,
727 const ResponseCallback& callback) {
729 ResponseCallback pending_callback =
730 base::Bind(&ServiceWorkerCache::PendingResponseCallback,
731 weak_ptr_factory_.GetWeakPtr(), callback);
734 Init(base::Bind(&ServiceWorkerCache::Match, weak_ptr_factory_.GetWeakPtr(),
735 base::Passed(request.Pass()), pending_callback));
739 pending_callback.Run(ErrorTypeStorage, scoped_ptr<ServiceWorkerResponse>(),
740 scoped_ptr<storage::BlobDataHandle>());
744 scoped_ptr<disk_cache::Entry*> entry(new disk_cache::Entry*);
746 disk_cache::Entry** entry_ptr = entry.get();
748 ServiceWorkerFetchRequest* request_ptr = request.get();
750 net::CompletionCallback open_entry_callback = base::Bind(
751 MatchDidOpenEntry, base::Passed(request.Pass()), pending_callback,
752 blob_storage_context_, base::Passed(entry.Pass()));
754 int rv = backend_->OpenEntry(
755 request_ptr->url.spec(), entry_ptr, open_entry_callback);
756 if (rv != net::ERR_IO_PENDING)
757 open_entry_callback.Run(rv);
760 void ServiceWorkerCache::Delete(scoped_ptr<ServiceWorkerFetchRequest> request,
761 const ErrorCallback& callback) {
763 ErrorCallback pending_callback =
764 base::Bind(&ServiceWorkerCache::PendingErrorCallback,
765 weak_ptr_factory_.GetWeakPtr(), callback);
768 Init(base::Bind(&ServiceWorkerCache::Delete, weak_ptr_factory_.GetWeakPtr(),
769 base::Passed(request.Pass()), pending_callback));
773 pending_callback.Run(ErrorTypeStorage);
777 scoped_ptr<disk_cache::Entry*> entry(new disk_cache::Entry*);
779 disk_cache::Entry** entry_ptr = entry.get();
781 ServiceWorkerFetchRequest* request_ptr = request.get();
783 net::CompletionCallback open_entry_callback = base::Bind(
784 DeleteDidOpenEntry, origin_, base::Passed(request.Pass()),
785 pending_callback, base::Passed(entry.Pass()), quota_manager_proxy_);
787 int rv = backend_->OpenEntry(
788 request_ptr->url.spec(), entry_ptr, open_entry_callback);
789 if (rv != net::ERR_IO_PENDING)
790 open_entry_callback.Run(rv);
793 void ServiceWorkerCache::Keys(const RequestsCallback& callback) {
795 RequestsCallback pending_callback =
796 base::Bind(&ServiceWorkerCache::PendingRequestsCallback,
797 weak_ptr_factory_.GetWeakPtr(), callback);
799 Init(base::Bind(&ServiceWorkerCache::Keys, weak_ptr_factory_.GetWeakPtr(),
804 pending_callback.Run(ErrorTypeStorage, scoped_ptr<Requests>());
808 // 1. Iterate through all of the entries, open them, and add them to a vector.
809 // 2. For each open entry:
810 // 2.1. Read the headers into a protobuf.
811 // 2.2. Copy the protobuf into a ServiceWorkerFetchRequest (a "key").
812 // 2.3. Push the response into a vector of requests to be returned.
813 // 3. Return the vector of requests (keys).
815 // The entries have to be loaded into a vector first because enumeration loops
816 // forever if you read data from a cache entry while enumerating.
818 scoped_ptr<KeysContext> keys_context(
819 new KeysContext(pending_callback, weak_ptr_factory_.GetWeakPtr()));
821 keys_context->backend_iterator = backend_->CreateIterator();
822 disk_cache::Backend::Iterator& iterator = *keys_context->backend_iterator;
823 disk_cache::Entry** enumerated_entry = &keys_context->enumerated_entry;
825 net::CompletionCallback open_entry_callback =
826 base::Bind(KeysDidOpenNextEntry, base::Passed(keys_context.Pass()));
828 int rv = iterator.OpenNextEntry(enumerated_entry, open_entry_callback);
830 if (rv != net::ERR_IO_PENDING)
831 open_entry_callback.Run(rv);
834 void ServiceWorkerCache::Close(const base::Closure& callback) {
835 DCHECK(!initialized_ || backend_)
836 << "Don't call ServiceWorkerCache::Close() twice.";
838 if (pending_ops_ > 0) {
839 DCHECK(ops_complete_callback_.is_null());
840 initialized_ = true; // So that future operations halt.
841 ops_complete_callback_ = base::Bind(
842 &ServiceWorkerCache::Close, weak_ptr_factory_.GetWeakPtr(), callback);
851 int64 ServiceWorkerCache::MemoryBackedSize() const {
852 if (!backend_ || !memory_only_)
855 scoped_ptr<disk_cache::Backend::Iterator> backend_iter =
856 backend_->CreateIterator();
857 disk_cache::Entry* entry = nullptr;
861 std::vector<disk_cache::Entry*> entries;
863 while ((rv = backend_iter->OpenNextEntry(
864 &entry, base::Bind(NotReachedCompletionCallback))) == net::OK) {
865 entries.push_back(entry); // Open the entries without mutating them.
868 net::ERR_IO_PENDING); // Expect all memory ops to be synchronous.
870 for (disk_cache::Entry* entry : entries) {
871 sum += entry->GetDataSize(INDEX_HEADERS) +
872 entry->GetDataSize(INDEX_RESPONSE_BODY);
879 ServiceWorkerCache::ServiceWorkerCache(
881 const base::FilePath& path,
882 net::URLRequestContext* request_context,
883 const scoped_refptr<storage::QuotaManagerProxy>& quota_manager_proxy,
884 base::WeakPtr<storage::BlobStorageContext> blob_context)
887 request_context_(request_context),
888 quota_manager_proxy_(quota_manager_proxy),
889 blob_storage_context_(blob_context),
891 memory_only_(path.empty()),
893 weak_ptr_factory_(this) {
897 void ServiceWorkerCache::PutImpl(scoped_ptr<PutContext> put_context) {
898 if (!put_context->cache || !put_context->cache->backend_) {
899 put_context->callback.Run(ErrorTypeStorage,
900 scoped_ptr<ServiceWorkerResponse>(),
901 scoped_ptr<storage::BlobDataHandle>());
905 scoped_ptr<ServiceWorkerFetchRequest> request_copy(
906 new ServiceWorkerFetchRequest(*put_context->request));
907 ServiceWorkerCache* cache_ptr = put_context->cache.get();
909 cache_ptr->Delete(request_copy.Pass(),
910 base::Bind(PutDidDelete, base::Passed(put_context.Pass())));
914 void ServiceWorkerCache::PutDidDelete(scoped_ptr<PutContext> put_context,
915 ErrorType delete_error) {
916 if (!put_context->cache || !put_context->cache->backend_) {
917 put_context->callback.Run(ErrorTypeStorage,
918 scoped_ptr<ServiceWorkerResponse>(),
919 scoped_ptr<storage::BlobDataHandle>());
923 disk_cache::Entry** entry_ptr = &put_context->cache_entry;
924 ServiceWorkerFetchRequest* request_ptr = put_context->request.get();
925 disk_cache::Backend* backend_ptr = put_context->cache->backend_.get();
927 net::CompletionCallback create_entry_callback =
928 base::Bind(PutDidCreateEntry, base::Passed(put_context.Pass()));
930 int create_rv = backend_ptr->CreateEntry(
931 request_ptr->url.spec(), entry_ptr, create_entry_callback);
933 if (create_rv != net::ERR_IO_PENDING)
934 create_entry_callback.Run(create_rv);
938 void ServiceWorkerCache::PutDidCreateEntry(scoped_ptr<PutContext> put_context,
941 put_context->callback.Run(ServiceWorkerCache::ErrorTypeExists,
942 scoped_ptr<ServiceWorkerResponse>(),
943 scoped_ptr<storage::BlobDataHandle>());
947 DCHECK(put_context->cache_entry);
949 ServiceWorkerCacheMetadata metadata;
950 ServiceWorkerCacheRequest* request_metadata = metadata.mutable_request();
951 request_metadata->set_method(put_context->request->method);
952 for (ServiceWorkerHeaderMap::const_iterator it =
953 put_context->request->headers.begin();
954 it != put_context->request->headers.end();
956 ServiceWorkerCacheHeaderMap* header_map = request_metadata->add_headers();
957 header_map->set_name(it->first);
958 header_map->set_value(it->second);
961 ServiceWorkerCacheResponse* response_metadata = metadata.mutable_response();
962 response_metadata->set_status_code(put_context->response->status_code);
963 response_metadata->set_status_text(put_context->response->status_text);
964 response_metadata->set_response_type(
965 WebResponseTypeToProtoResponseType(put_context->response->response_type));
966 response_metadata->set_url(put_context->response->url.spec());
967 for (ServiceWorkerHeaderMap::const_iterator it =
968 put_context->response->headers.begin();
969 it != put_context->response->headers.end();
971 ServiceWorkerCacheHeaderMap* header_map = response_metadata->add_headers();
972 header_map->set_name(it->first);
973 header_map->set_value(it->second);
976 scoped_ptr<std::string> serialized(new std::string());
977 if (!metadata.SerializeToString(serialized.get())) {
978 put_context->callback.Run(ServiceWorkerCache::ErrorTypeStorage,
979 scoped_ptr<ServiceWorkerResponse>(),
980 scoped_ptr<storage::BlobDataHandle>());
984 scoped_refptr<net::StringIOBuffer> buffer(
985 new net::StringIOBuffer(serialized.Pass()));
987 // Get a temporary copy of the entry pointer before passing it in base::Bind.
988 disk_cache::Entry* tmp_entry_ptr = put_context->cache_entry;
990 net::CompletionCallback write_headers_callback = base::Bind(
991 PutDidWriteHeaders, base::Passed(put_context.Pass()), buffer->size());
993 rv = tmp_entry_ptr->WriteData(INDEX_HEADERS,
997 write_headers_callback,
998 true /* truncate */);
1000 if (rv != net::ERR_IO_PENDING)
1001 write_headers_callback.Run(rv);
1005 void ServiceWorkerCache::PutDidWriteHeaders(scoped_ptr<PutContext> put_context,
1008 if (rv != expected_bytes) {
1009 put_context->cache_entry->Doom();
1010 put_context->callback.Run(ServiceWorkerCache::ErrorTypeStorage,
1011 scoped_ptr<ServiceWorkerResponse>(),
1012 scoped_ptr<storage::BlobDataHandle>());
1016 // The metadata is written, now for the response content. The data is streamed
1017 // from the blob into the cache entry.
1019 if (put_context->response->blob_uuid.empty()) {
1020 if (put_context->quota_manager_proxy.get()) {
1021 put_context->quota_manager_proxy->NotifyStorageModified(
1022 storage::QuotaClient::kServiceWorkerCache,
1023 put_context->origin,
1024 storage::kStorageTypeTemporary,
1025 put_context->cache_entry->GetDataSize(INDEX_HEADERS));
1028 put_context->callback.Run(ServiceWorkerCache::ErrorTypeOK,
1029 put_context->response.Pass(),
1030 scoped_ptr<storage::BlobDataHandle>());
1034 DCHECK(put_context->blob_data_handle);
1036 disk_cache::ScopedEntryPtr entry(put_context->cache_entry);
1037 put_context->cache_entry = NULL;
1038 scoped_ptr<BlobReader> reader(new BlobReader());
1039 BlobReader* reader_ptr = reader.get();
1041 // Grab some pointers before passing put_context in Bind.
1042 net::URLRequestContext* request_context = put_context->request_context;
1043 scoped_ptr<storage::BlobDataHandle> blob_data_handle =
1044 put_context->blob_data_handle.Pass();
1046 reader_ptr->StreamBlobToCache(entry.Pass(),
1048 blob_data_handle.Pass(),
1049 base::Bind(PutDidWriteBlobToCache,
1050 base::Passed(put_context.Pass()),
1051 base::Passed(reader.Pass())));
1055 void ServiceWorkerCache::PutDidWriteBlobToCache(
1056 scoped_ptr<PutContext> put_context,
1057 scoped_ptr<BlobReader> blob_reader,
1058 disk_cache::ScopedEntryPtr entry,
1061 put_context->cache_entry = entry.release();
1064 put_context->cache_entry->Doom();
1065 put_context->callback.Run(ServiceWorkerCache::ErrorTypeStorage,
1066 scoped_ptr<ServiceWorkerResponse>(),
1067 scoped_ptr<storage::BlobDataHandle>());
1071 if (put_context->quota_manager_proxy.get()) {
1072 put_context->quota_manager_proxy->NotifyStorageModified(
1073 storage::QuotaClient::kServiceWorkerCache,
1074 put_context->origin,
1075 storage::kStorageTypeTemporary,
1076 put_context->cache_entry->GetDataSize(INDEX_HEADERS) +
1077 put_context->cache_entry->GetDataSize(INDEX_RESPONSE_BODY));
1080 put_context->callback.Run(ServiceWorkerCache::ErrorTypeOK,
1081 put_context->response.Pass(),
1082 put_context->out_blob_data_handle.Pass());
1086 void ServiceWorkerCache::KeysDidOpenNextEntry(
1087 scoped_ptr<KeysContext> keys_context,
1089 if (rv == net::ERR_FAILED) {
1090 DCHECK(!keys_context->enumerated_entry);
1091 // Enumeration is complete, extract the requests from the entries.
1092 Entries::iterator iter = keys_context->entries.begin();
1093 KeysProcessNextEntry(keys_context.Pass(), iter);
1097 base::WeakPtr<ServiceWorkerCache> cache = keys_context->cache;
1098 if (rv < 0 || !cache) {
1099 keys_context->original_callback.Run(ErrorTypeStorage,
1100 scoped_ptr<Requests>());
1104 if (!cache->backend_) {
1105 keys_context->original_callback.Run(ErrorTypeNotFound,
1106 scoped_ptr<Requests>());
1111 keys_context->entries.push_back(keys_context->enumerated_entry);
1112 keys_context->enumerated_entry = NULL;
1114 // Enumerate the next entry.
1115 disk_cache::Backend::Iterator& iterator = *keys_context->backend_iterator;
1116 disk_cache::Entry** enumerated_entry = &keys_context->enumerated_entry;
1117 net::CompletionCallback open_entry_callback =
1118 base::Bind(KeysDidOpenNextEntry, base::Passed(keys_context.Pass()));
1120 rv = iterator.OpenNextEntry(enumerated_entry, open_entry_callback);
1122 if (rv != net::ERR_IO_PENDING)
1123 open_entry_callback.Run(rv);
1127 void ServiceWorkerCache::KeysProcessNextEntry(
1128 scoped_ptr<KeysContext> keys_context,
1129 const Entries::iterator& iter) {
1130 if (iter == keys_context->entries.end()) {
1131 // All done. Return all of the keys.
1132 keys_context->original_callback.Run(ErrorTypeOK,
1133 keys_context->out_keys.Pass());
1139 base::Bind(KeysDidReadMetadata, base::Passed(keys_context.Pass()), iter));
1143 void ServiceWorkerCache::KeysDidReadMetadata(
1144 scoped_ptr<KeysContext> keys_context,
1145 const Entries::iterator& iter,
1146 scoped_ptr<ServiceWorkerCacheMetadata> metadata) {
1147 disk_cache::Entry* entry = *iter;
1150 keys_context->out_keys->push_back(
1151 ServiceWorkerFetchRequest(GURL(entry->GetKey()),
1152 metadata->request().method(),
1153 ServiceWorkerHeaderMap(),
1157 ServiceWorkerHeaderMap& req_headers =
1158 keys_context->out_keys->back().headers;
1160 for (int i = 0; i < metadata->request().headers_size(); ++i) {
1161 const ServiceWorkerCacheHeaderMap header = metadata->request().headers(i);
1162 req_headers.insert(std::make_pair(header.name(), header.value()));
1168 KeysProcessNextEntry(keys_context.Pass(), iter + 1);
1171 void ServiceWorkerCache::CreateBackend(const ErrorCallback& callback) {
1174 // Use APP_CACHE as opposed to DISK_CACHE to prevent cache eviction.
1175 net::CacheType cache_type = memory_only_ ? net::MEMORY_CACHE : net::APP_CACHE;
1177 scoped_ptr<ScopedBackendPtr> backend_ptr(new ScopedBackendPtr());
1179 // Temporary pointer so that backend_ptr can be Pass()'d in Bind below.
1180 ScopedBackendPtr* backend = backend_ptr.get();
1182 net::CompletionCallback create_cache_callback =
1183 base::Bind(CreateBackendDidCreate,
1185 base::Passed(backend_ptr.Pass()),
1186 weak_ptr_factory_.GetWeakPtr());
1188 // TODO(jkarlin): Use the cache MessageLoopProxy that ServiceWorkerCacheCore
1189 // has for disk caches.
1190 int rv = disk_cache::CreateCacheBackend(
1192 net::CACHE_BACKEND_SIMPLE,
1196 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::CACHE).get(),
1199 create_cache_callback);
1200 if (rv != net::ERR_IO_PENDING)
1201 create_cache_callback.Run(rv);
1204 void ServiceWorkerCache::Init(const base::Closure& callback) {
1205 DCHECK(!initialized_);
1206 init_callbacks_.push_back(callback);
1208 // If this isn't the first call to Init then return as the initialization
1209 // has already started.
1210 if (init_callbacks_.size() > 1u)
1213 CreateBackend(base::Bind(&ServiceWorkerCache::InitDone,
1214 weak_ptr_factory_.GetWeakPtr()));
1217 void ServiceWorkerCache::InitDone(ErrorType error) {
1218 initialized_ = true;
1219 for (std::vector<base::Closure>::iterator it = init_callbacks_.begin();
1220 it != init_callbacks_.end();
1224 init_callbacks_.clear();
1227 void ServiceWorkerCache::DecPendingOps() {
1228 DCHECK(pending_ops_ > 0);
1230 if (pending_ops_ == 0 && !ops_complete_callback_.is_null()) {
1231 ops_complete_callback_.Run();
1232 ops_complete_callback_.Reset();
1236 void ServiceWorkerCache::PendingErrorCallback(const ErrorCallback& callback,
1238 callback.Run(error);
1242 void ServiceWorkerCache::PendingResponseCallback(
1243 const ResponseCallback& callback,
1245 scoped_ptr<ServiceWorkerResponse> response,
1246 scoped_ptr<storage::BlobDataHandle> blob_data_handle) {
1247 callback.Run(error, response.Pass(), blob_data_handle.Pass());
1251 void ServiceWorkerCache::PendingRequestsCallback(
1252 const RequestsCallback& callback,
1254 scoped_ptr<Requests> requests) {
1255 callback.Run(error, requests.Pass());
1259 } // namespace content