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"
27 typedef scoped_ptr<disk_cache::Backend> ScopedBackendPtr;
28 typedef base::Callback<void(bool)> BoolCallback;
29 typedef base::Callback<void(disk_cache::ScopedEntryPtr, bool)>
31 typedef base::Callback<void(scoped_ptr<ServiceWorkerRequestResponseHeaders>)>
34 enum EntryIndex { INDEX_HEADERS = 0, INDEX_RESPONSE_BODY };
36 // The maximum size of an individual cache. Ultimately cache size is controlled
38 const int kMaxCacheBytes = 512 * 1024 * 1024;
40 // Buffer size for cache and blob reading/writing.
41 const int kBufferSize = 1024 * 512;
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) {}
48 scoped_refptr<net::IOBufferWithSize> buffer;
49 scoped_refptr<storage::BlobData> blob_data;
52 DISALLOW_COPY_AND_ASSIGN(ResponseReadContext);
55 // Streams data from a blob and writes it to a given disk_cache::Entry.
56 class BlobReader : public net::URLRequest::Delegate {
58 typedef base::Callback<void(disk_cache::ScopedEntryPtr, bool)>
61 BlobReader(disk_cache::ScopedEntryPtr entry)
62 : cache_entry_offset_(0),
63 buffer_(new net::IOBufferWithSize(kBufferSize)),
64 weak_ptr_factory_(this) {
66 entry_ = entry.Pass();
69 void StreamBlobToCache(net::URLRequestContext* request_context,
70 scoped_ptr<storage::BlobDataHandle> blob_data_handle,
71 const EntryBoolCallback& callback) {
73 blob_request_ = storage::BlobProtocolHandler::CreateBlobRequest(
74 blob_data_handle.Pass(), request_context, this);
75 blob_request_->Start();
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 {
84 virtual void OnAuthRequired(net::URLRequest* request,
85 net::AuthChallengeInfo* auth_info) OVERRIDE {
88 virtual void OnCertificateRequested(
89 net::URLRequest* request,
90 net::SSLCertRequestInfo* cert_request_info) OVERRIDE {
93 virtual void OnSSLCertificateError(net::URLRequest* request,
94 const net::SSLInfo& ssl_info,
95 bool fatal) OVERRIDE {
98 virtual void OnBeforeNetworkStart(net::URLRequest* request,
99 bool* defer) OVERRIDE {
103 virtual void OnResponseStarted(net::URLRequest* request) OVERRIDE {
104 if (!request->status().is_success()) {
105 callback_.Run(entry_.Pass(), false);
111 virtual void ReadFromBlob() {
114 blob_request_->Read(buffer_.get(), buffer_->size(), &bytes_read);
116 OnReadCompleted(blob_request_.get(), bytes_read);
119 virtual void OnReadCompleted(net::URLRequest* request,
120 int bytes_read) OVERRIDE {
121 if (!request->status().is_success()) {
122 callback_.Run(entry_.Pass(), false);
126 if (bytes_read == 0) {
127 callback_.Run(entry_.Pass(), true);
131 net::CompletionCallback cache_write_callback =
132 base::Bind(&BlobReader::DidWriteDataToEntry,
133 weak_ptr_factory_.GetWeakPtr(),
136 int rv = entry_->WriteData(INDEX_RESPONSE_BODY,
140 cache_write_callback,
141 true /* truncate */);
142 if (rv != net::ERR_IO_PENDING)
143 cache_write_callback.Run(rv);
146 void DidWriteDataToEntry(int expected_bytes, int rv) {
147 if (rv != expected_bytes) {
148 callback_.Run(entry_.Pass(), false);
152 cache_entry_offset_ += rv;
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_;
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,
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,
180 void PutDidWriteBlobToCache(const ServiceWorkerCache::ErrorCallback& callback,
181 scoped_ptr<BlobReader> blob_reader,
182 disk_cache::ScopedEntryPtr entry,
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,
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,
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);
212 void DeleteDidOpenEntry(scoped_ptr<ServiceWorkerFetchRequest> request,
213 const ServiceWorkerCache::ErrorCallback& callback,
214 scoped_ptr<disk_cache::Entry*> entryptr,
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,
226 // CreateBackend callbacks
227 void CreateBackendDidCreate(const ServiceWorkerCache::ErrorCallback& callback,
228 scoped_ptr<ScopedBackendPtr> backend_ptr,
229 base::WeakPtr<ServiceWorkerCache> cache,
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,
240 callback.Run(ServiceWorkerCache::ErrorTypeExists);
245 disk_cache::ScopedEntryPtr entry(*entryptr);
247 ServiceWorkerRequestResponseHeaders headers;
248 headers.set_method(request->method);
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();
255 ServiceWorkerRequestResponseHeaders::HeaderMap* header_map =
256 headers.add_request_headers();
257 header_map->set_name(it->first);
258 header_map->set_value(it->second);
261 for (ServiceWorkerHeaderMap::const_iterator it = response->headers.begin();
262 it != response->headers.end();
264 ServiceWorkerRequestResponseHeaders::HeaderMap* header_map =
265 headers.add_response_headers();
266 header_map->set_name(it->first);
267 header_map->set_value(it->second);
270 scoped_ptr<std::string> serialized(new std::string());
271 if (!headers.SerializeToString(serialized.get())) {
272 callback.Run(ServiceWorkerCache::ErrorTypeStorage);
276 scoped_refptr<net::StringIOBuffer> buffer(
277 new net::StringIOBuffer(serialized.Pass()));
279 // Get a temporary copy of the entry pointer before passing it in base::Bind.
280 disk_cache::Entry* tmp_entry_ptr = entry.get();
282 net::CompletionCallback write_headers_callback =
283 base::Bind(PutDidWriteHeaders,
284 base::Passed(response.Pass()),
286 base::Passed(entry.Pass()),
287 base::Passed(blob_data_handle.Pass()),
291 rv = tmp_entry_ptr->WriteData(INDEX_HEADERS,
295 write_headers_callback,
296 true /* truncate */);
298 if (rv != net::ERR_IO_PENDING)
299 write_headers_callback.Run(rv);
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,
309 if (rv != expected_bytes) {
311 callback.Run(ServiceWorkerCache::ErrorTypeStorage);
315 // The metadata is written, now for the response content. The data is streamed
316 // from the blob into the cache entry.
318 if (response->blob_uuid.empty()) {
319 callback.Run(ServiceWorkerCache::ErrorTypeOK);
323 DCHECK(blob_data_handle);
325 scoped_ptr<BlobReader> reader(new BlobReader(entry.Pass()));
326 BlobReader* reader_ptr = reader.get();
328 reader_ptr->StreamBlobToCache(
330 blob_data_handle.Pass(),
332 PutDidWriteBlobToCache, callback, base::Passed(reader.Pass())));
335 void PutDidWriteBlobToCache(const ServiceWorkerCache::ErrorCallback& callback,
336 scoped_ptr<BlobReader> blob_reader,
337 disk_cache::ScopedEntryPtr entry,
341 callback.Run(ServiceWorkerCache::ErrorTypeStorage);
345 callback.Run(ServiceWorkerCache::ErrorTypeOK);
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,
354 callback.Run(ServiceWorkerCache::ErrorTypeNotFound,
355 scoped_ptr<ServiceWorkerResponse>(),
356 scoped_ptr<storage::BlobDataHandle>());
361 disk_cache::ScopedEntryPtr entry(*entryptr);
363 // Copy the entry pointer before passing it in base::Bind.
364 disk_cache::Entry* tmp_entry_ptr = entry.get();
366 HeadersCallback headers_callback = base::Bind(MatchDidReadHeaderData,
367 base::Passed(request.Pass()),
370 base::Passed(entry.Pass()));
372 ReadHeaders(tmp_entry_ptr, headers_callback);
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())
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();
388 base::TrimWhitespaceASCII(*it, base::TRIM_ALL, &trimmed);
392 ServiceWorkerHeaderMap::const_iterator request_iter = request.find(trimmed);
393 ServiceWorkerHeaderMap::const_iterator cached_request_iter =
394 cached_request.find(trimmed);
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()))
401 // If the header exists in one, it exists in both. Verify that the values
403 if (request_iter != request.end() &&
404 request_iter->second != cached_request_iter->second)
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) {
418 callback.Run(ServiceWorkerCache::ErrorTypeStorage,
419 scoped_ptr<ServiceWorkerResponse>(),
420 scoped_ptr<storage::BlobDataHandle>());
424 scoped_ptr<ServiceWorkerResponse> response(
425 new ServiceWorkerResponse(request->url,
426 headers->status_code(),
427 headers->status_text(),
428 ServiceWorkerHeaderMap(),
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()));
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();
445 request->headers, cached_request_headers, response->headers)) {
446 callback.Run(ServiceWorkerCache::ErrorTypeNotFound,
447 scoped_ptr<ServiceWorkerResponse>(),
448 scoped_ptr<storage::BlobDataHandle>());
452 if (entry->GetDataSize(INDEX_RESPONSE_BODY) == 0) {
453 callback.Run(ServiceWorkerCache::ErrorTypeOK,
455 scoped_ptr<storage::BlobDataHandle>());
459 // Stream the response body into a blob.
461 callback.Run(ServiceWorkerCache::ErrorTypeStorage,
462 scoped_ptr<ServiceWorkerResponse>(),
463 scoped_ptr<storage::BlobDataHandle>());
467 response->blob_uuid = base::GenerateGUID();
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));
474 scoped_ptr<ResponseReadContext> read_context(
475 new ResponseReadContext(response_body_buffer, blob_data));
477 // Copy the entry pointer before passing it in base::Bind.
478 disk_cache::Entry* tmp_entry_ptr = entry.get();
480 net::CompletionCallback read_callback =
481 base::Bind(MatchDidReadResponseBodyData,
482 base::Passed(request.Pass()),
485 base::Passed(entry.Pass()),
486 base::Passed(response.Pass()),
487 base::Passed(read_context.Pass()));
489 int read_rv = tmp_entry_ptr->ReadData(INDEX_RESPONSE_BODY,
491 response_body_buffer.get(),
492 response_body_buffer->size(),
495 if (read_rv != net::ERR_IO_PENDING)
496 read_callback.Run(read_rv);
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,
508 callback.Run(ServiceWorkerCache::ErrorTypeStorage,
509 scoped_ptr<ServiceWorkerResponse>(),
510 scoped_ptr<storage::BlobDataHandle>());
515 MatchDoneWithBody(request.Pass(),
519 response_context.Pass());
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;
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();
534 net::CompletionCallback read_callback =
535 base::Bind(MatchDidReadResponseBodyData,
536 base::Passed(request.Pass()),
539 base::Passed(entry.Pass()),
540 base::Passed(response.Pass()),
541 base::Passed(response_context.Pass()));
543 int read_rv = tmp_entry_ptr->ReadData(INDEX_RESPONSE_BODY,
549 if (read_rv != net::ERR_IO_PENDING)
550 read_callback.Run(read_rv);
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) {
559 callback.Run(ServiceWorkerCache::ErrorTypeStorage,
560 scoped_ptr<ServiceWorkerResponse>(),
561 scoped_ptr<storage::BlobDataHandle>());
565 scoped_ptr<storage::BlobDataHandle> blob_data_handle(
566 blob_storage->AddFinishedBlob(response_context->blob_data.get()));
568 callback.Run(ServiceWorkerCache::ErrorTypeOK,
570 blob_data_handle.Pass());
573 void DeleteDidOpenEntry(scoped_ptr<ServiceWorkerFetchRequest> request,
574 const ServiceWorkerCache::ErrorCallback& callback,
575 scoped_ptr<disk_cache::Entry*> entryptr,
578 callback.Run(ServiceWorkerCache::ErrorTypeNotFound);
583 disk_cache::ScopedEntryPtr entry(*entryptr);
586 callback.Run(ServiceWorkerCache::ErrorTypeOK);
589 void ReadHeaders(disk_cache::Entry* entry, const HeadersCallback& callback) {
592 scoped_refptr<net::IOBufferWithSize> buffer(
593 new net::IOBufferWithSize(entry->GetDataSize(INDEX_HEADERS)));
595 net::CompletionCallback read_header_callback =
596 base::Bind(ReadHeadersDidReadHeaderData, entry, callback, buffer);
598 int read_rv = entry->ReadData(
599 INDEX_HEADERS, 0, buffer.get(), buffer->size(), read_header_callback);
601 if (read_rv != net::ERR_IO_PENDING)
602 read_header_callback.Run(read_rv);
605 void ReadHeadersDidReadHeaderData(
606 disk_cache::Entry* entry,
607 const HeadersCallback& callback,
608 const scoped_refptr<net::IOBufferWithSize>& buffer,
610 if (rv != buffer->size()) {
611 callback.Run(scoped_ptr<ServiceWorkerRequestResponseHeaders>());
615 scoped_ptr<ServiceWorkerRequestResponseHeaders> headers(
616 new ServiceWorkerRequestResponseHeaders());
618 if (!headers->ParseFromArray(buffer->data(), buffer->size())) {
619 callback.Run(scoped_ptr<ServiceWorkerRequestResponseHeaders>());
623 callback.Run(headers.Pass());
626 void CreateBackendDidCreate(const ServiceWorkerCache::ErrorCallback& callback,
627 scoped_ptr<ScopedBackendPtr> backend_ptr,
628 base::WeakPtr<ServiceWorkerCache> cache,
630 if (rv != net::OK || !cache) {
631 callback.Run(ServiceWorkerCache::ErrorTypeStorage);
635 cache->set_backend(backend_ptr->Pass());
636 callback.Run(ServiceWorkerCache::ErrorTypeOK);
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),
647 out_keys(new ServiceWorkerCache::Requests()),
648 enumerated_entry(NULL) {}
651 for (size_t i = 0, max = entries.size(); i < max; ++i)
653 if (enumerated_entry)
654 enumerated_entry->Close();
657 // The callback passed to the Keys() function.
658 ServiceWorkerCache::RequestsCallback original_callback;
660 // The ServiceWorkerCache that Keys was called on.
661 base::WeakPtr<ServiceWorkerCache> cache;
663 // The vector of open entries in the backend.
666 // The output of the Keys function.
667 scoped_ptr<ServiceWorkerCache::Requests> out_keys;
669 // Used for enumerating cache entries.
670 scoped_ptr<disk_cache::Backend::Iterator> backend_iterator;
671 disk_cache::Entry* enumerated_entry;
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));
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));
691 ServiceWorkerCache::~ServiceWorkerCache() {
694 base::WeakPtr<ServiceWorkerCache> ServiceWorkerCache::AsWeakPtr() {
695 return weak_ptr_factory_.GetWeakPtr();
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;
703 if (!response->blob_uuid.empty()) {
704 if (!blob_storage_context_) {
705 callback.Run(ErrorTypeStorage);
709 blob_storage_context_->GetBlobDataFromUUID(response->blob_uuid);
710 if (!blob_data_handle) {
711 callback.Run(ErrorTypeStorage);
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()),
731 void ServiceWorkerCache::Match(scoped_ptr<ServiceWorkerFetchRequest> request,
732 const ResponseCallback& callback) {
734 Init(base::Bind(&ServiceWorkerCache::Match,
735 weak_ptr_factory_.GetWeakPtr(),
736 base::Passed(request.Pass()),
741 callback.Run(ErrorTypeStorage,
742 scoped_ptr<ServiceWorkerResponse>(),
743 scoped_ptr<storage::BlobDataHandle>());
747 scoped_ptr<disk_cache::Entry*> entry(new disk_cache::Entry*);
749 disk_cache::Entry** entry_ptr = entry.get();
751 ServiceWorkerFetchRequest* request_ptr = request.get();
753 net::CompletionCallback open_entry_callback =
754 base::Bind(MatchDidOpenEntry,
755 base::Passed(request.Pass()),
757 blob_storage_context_,
758 base::Passed(entry.Pass()));
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);
766 void ServiceWorkerCache::Delete(scoped_ptr<ServiceWorkerFetchRequest> request,
767 const ErrorCallback& callback) {
769 Init(base::Bind(&ServiceWorkerCache::Delete,
770 weak_ptr_factory_.GetWeakPtr(),
771 base::Passed(request.Pass()),
776 callback.Run(ErrorTypeStorage);
780 scoped_ptr<disk_cache::Entry*> entry(new disk_cache::Entry*);
782 disk_cache::Entry** entry_ptr = entry.get();
784 ServiceWorkerFetchRequest* request_ptr = request.get();
786 net::CompletionCallback open_entry_callback =
787 base::Bind(DeleteDidOpenEntry,
788 base::Passed(request.Pass()),
790 base::Passed(entry.Pass()));
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);
798 void ServiceWorkerCache::Keys(const RequestsCallback& callback) {
801 &ServiceWorkerCache::Keys, weak_ptr_factory_.GetWeakPtr(), callback));
805 callback.Run(ErrorTypeStorage, scoped_ptr<Requests>());
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).
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.
819 scoped_ptr<KeysContext> keys_context(
820 new KeysContext(callback, weak_ptr_factory_.GetWeakPtr()));
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;
826 net::CompletionCallback open_entry_callback =
827 base::Bind(KeysDidOpenNextEntry, base::Passed(keys_context.Pass()));
829 int rv = iterator.OpenNextEntry(enumerated_entry, open_entry_callback);
831 if (rv != net::ERR_IO_PENDING)
832 open_entry_callback.Run(rv);
835 void ServiceWorkerCache::Close() {
839 ServiceWorkerCache::ServiceWorkerCache(
840 const base::FilePath& path,
841 net::URLRequestContext* request_context,
842 base::WeakPtr<storage::BlobStorageContext> blob_context)
844 request_context_(request_context),
845 blob_storage_context_(blob_context),
847 weak_ptr_factory_(this) {
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) {
856 callback.Run(ErrorTypeStorage);
860 scoped_ptr<disk_cache::Entry*> entry(new disk_cache::Entry*);
862 disk_cache::Entry** entry_ptr = entry.get();
864 ServiceWorkerFetchRequest* request_ptr = request.get();
866 net::CompletionCallback create_entry_callback =
867 base::Bind(PutDidCreateEntry,
868 base::Passed(request.Pass()),
869 base::Passed(response.Pass()),
871 base::Passed(entry.Pass()),
872 base::Passed(blob_data_handle.Pass()),
875 int rv = backend_->CreateEntry(
876 request_ptr->url.spec(), entry_ptr, create_entry_callback);
878 if (rv != net::ERR_IO_PENDING)
879 create_entry_callback.Run(rv);
883 void ServiceWorkerCache::KeysDidOpenNextEntry(
884 scoped_ptr<KeysContext> keys_context,
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);
894 base::WeakPtr<ServiceWorkerCache> cache = keys_context->cache;
895 if (rv < 0 || !cache) {
896 keys_context->original_callback.Run(ErrorTypeStorage,
897 scoped_ptr<Requests>());
901 if (!cache->backend_) {
902 keys_context->original_callback.Run(ErrorTypeNotFound,
903 scoped_ptr<Requests>());
908 keys_context->entries.push_back(keys_context->enumerated_entry);
909 keys_context->enumerated_entry = NULL;
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()));
917 rv = iterator.OpenNextEntry(enumerated_entry, open_entry_callback);
919 if (rv != net::ERR_IO_PENDING)
920 open_entry_callback.Run(rv);
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());
936 base::Bind(KeysDidReadHeaders, base::Passed(keys_context.Pass()), iter));
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;
947 keys_context->out_keys->push_back(
948 ServiceWorkerFetchRequest(GURL(entry->GetKey()),
950 ServiceWorkerHeaderMap(),
954 ServiceWorkerHeaderMap& req_headers =
955 keys_context->out_keys->back().headers;
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()));
966 KeysProcessNextEntry(keys_context.Pass(), iter + 1);
969 void ServiceWorkerCache::CreateBackend(const ErrorCallback& callback) {
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;
976 scoped_ptr<ScopedBackendPtr> backend_ptr(new ScopedBackendPtr());
978 // Temporary pointer so that backend_ptr can be Pass()'d in Bind below.
979 ScopedBackendPtr* backend = backend_ptr.get();
981 net::CompletionCallback create_cache_callback =
982 base::Bind(CreateBackendDidCreate,
984 base::Passed(backend_ptr.Pass()),
985 weak_ptr_factory_.GetWeakPtr());
987 // TODO(jkarlin): Use the cache MessageLoopProxy that ServiceWorkerCacheCore
988 // has for disk caches.
989 int rv = disk_cache::CreateCacheBackend(
991 net::CACHE_BACKEND_SIMPLE,
995 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::CACHE).get(),
998 create_cache_callback);
999 if (rv != net::ERR_IO_PENDING)
1000 create_cache_callback.Run(rv);
1003 void ServiceWorkerCache::Init(const base::Closure& callback) {
1004 init_callbacks_.push_back(callback);
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)
1011 CreateBackend(base::Bind(&ServiceWorkerCache::InitDone,
1012 weak_ptr_factory_.GetWeakPtr()));
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();
1022 init_callbacks_.clear();
1025 } // namespace content