[ServiceWorker] Pipe the Stream URL from blink to the chromium
[chromium-blink-merge.git] / content / browser / service_worker / service_worker_cache.cc
blob3011356f4f17f3b81f8ae0867ae561e265da2fd2
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"
7 #include <string>
9 #include "base/files/file_path.h"
10 #include "base/guid.h"
11 #include "base/message_loop/message_loop_proxy.h"
12 #include "base/profiler/scoped_tracker.h"
13 #include "base/strings/string_util.h"
14 #include "content/browser/service_worker/service_worker_cache.pb.h"
15 #include "content/public/browser/browser_thread.h"
16 #include "net/base/io_buffer.h"
17 #include "net/base/net_errors.h"
18 #include "net/disk_cache/disk_cache.h"
19 #include "net/url_request/url_request_context.h"
20 #include "storage/browser/blob/blob_data_handle.h"
21 #include "storage/browser/blob/blob_storage_context.h"
22 #include "storage/browser/blob/blob_url_request_job_factory.h"
23 #include "storage/browser/quota/quota_manager_proxy.h"
24 #include "third_party/WebKit/public/platform/WebServiceWorkerResponseType.h"
26 namespace content {
28 namespace {
30 typedef base::Callback<void(bool)> BoolCallback;
31 typedef base::Callback<void(disk_cache::ScopedEntryPtr, bool)>
32 EntryBoolCallback;
33 typedef base::Callback<void(scoped_ptr<ServiceWorkerCacheMetadata>)>
34 MetadataCallback;
36 enum EntryIndex { INDEX_HEADERS = 0, INDEX_RESPONSE_BODY };
38 // The maximum size of an individual cache. Ultimately cache size is controlled
39 // per-origin.
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) {
46 NOTREACHED();
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;
63 NOTREACHED();
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;
81 NOTREACHED();
82 return ServiceWorkerCacheResponse::OPAQUE_TYPE;
85 // Copy headers out of a cache entry and into a protobuf. The callback is
86 // guaranteed to be run.
87 void ReadMetadata(disk_cache::Entry* entry, const MetadataCallback& callback);
88 void ReadMetadataDidReadMetadata(
89 disk_cache::Entry* entry,
90 const MetadataCallback& callback,
91 const scoped_refptr<net::IOBufferWithSize>& buffer,
92 int rv);
95 bool VaryMatches(const ServiceWorkerHeaderMap& request,
96 const ServiceWorkerHeaderMap& cached_request,
97 const ServiceWorkerHeaderMap& response) {
98 ServiceWorkerHeaderMap::const_iterator vary_iter = response.find("vary");
99 if (vary_iter == response.end())
100 return true;
102 std::vector<std::string> vary_keys;
103 Tokenize(vary_iter->second, ",", &vary_keys);
104 for (std::vector<std::string>::const_iterator it = vary_keys.begin();
105 it != vary_keys.end();
106 ++it) {
107 std::string trimmed;
108 base::TrimWhitespaceASCII(*it, base::TRIM_ALL, &trimmed);
109 if (trimmed == "*")
110 return false;
112 ServiceWorkerHeaderMap::const_iterator request_iter = request.find(trimmed);
113 ServiceWorkerHeaderMap::const_iterator cached_request_iter =
114 cached_request.find(trimmed);
116 // If the header exists in one but not the other, no match.
117 if ((request_iter == request.end()) !=
118 (cached_request_iter == cached_request.end()))
119 return false;
121 // If the header exists in one, it exists in both. Verify that the values
122 // are equal.
123 if (request_iter != request.end() &&
124 request_iter->second != cached_request_iter->second)
125 return false;
128 return true;
132 void ReadMetadata(disk_cache::Entry* entry, const MetadataCallback& callback) {
133 DCHECK(entry);
135 scoped_refptr<net::IOBufferWithSize> buffer(
136 new net::IOBufferWithSize(entry->GetDataSize(INDEX_HEADERS)));
138 net::CompletionCallback read_header_callback =
139 base::Bind(ReadMetadataDidReadMetadata, entry, callback, buffer);
141 int read_rv = entry->ReadData(
142 INDEX_HEADERS, 0, buffer.get(), buffer->size(),
143 tracked_objects::ScopedTracker::TrackCallback(
144 FROM_HERE_WITH_EXPLICIT_FUNCTION("422516 ReadMetadata"),
145 read_header_callback));
147 if (read_rv != net::ERR_IO_PENDING)
148 read_header_callback.Run(read_rv);
151 void ReadMetadataDidReadMetadata(
152 disk_cache::Entry* entry,
153 const MetadataCallback& callback,
154 const scoped_refptr<net::IOBufferWithSize>& buffer,
155 int rv) {
156 if (rv != buffer->size()) {
157 callback.Run(scoped_ptr<ServiceWorkerCacheMetadata>());
158 return;
161 scoped_ptr<ServiceWorkerCacheMetadata> metadata(
162 new ServiceWorkerCacheMetadata());
164 if (!metadata->ParseFromArray(buffer->data(), buffer->size())) {
165 callback.Run(scoped_ptr<ServiceWorkerCacheMetadata>());
166 return;
169 callback.Run(metadata.Pass());
172 } // namespace
174 // Streams data from a blob and writes it to a given disk_cache::Entry.
175 class ServiceWorkerCache::BlobReader : public net::URLRequest::Delegate {
176 public:
177 typedef base::Callback<void(disk_cache::ScopedEntryPtr, bool)>
178 EntryAndBoolCallback;
180 BlobReader()
181 : cache_entry_offset_(0),
182 buffer_(new net::IOBufferWithSize(kBufferSize)),
183 weak_ptr_factory_(this) {}
185 // |entry| is passed to the callback once complete.
186 void StreamBlobToCache(disk_cache::ScopedEntryPtr entry,
187 net::URLRequestContext* request_context,
188 scoped_ptr<storage::BlobDataHandle> blob_data_handle,
189 const EntryAndBoolCallback& callback) {
190 DCHECK(entry);
191 entry_ = entry.Pass();
192 callback_ = callback;
193 blob_request_ = storage::BlobProtocolHandler::CreateBlobRequest(
194 blob_data_handle.Pass(), request_context, this);
195 blob_request_->Start();
198 // net::URLRequest::Delegate overrides for reading blobs.
199 void OnReceivedRedirect(net::URLRequest* request,
200 const net::RedirectInfo& redirect_info,
201 bool* defer_redirect) override {
202 NOTREACHED();
204 void OnAuthRequired(net::URLRequest* request,
205 net::AuthChallengeInfo* auth_info) override {
206 NOTREACHED();
208 void OnCertificateRequested(
209 net::URLRequest* request,
210 net::SSLCertRequestInfo* cert_request_info) override {
211 NOTREACHED();
213 void OnSSLCertificateError(net::URLRequest* request,
214 const net::SSLInfo& ssl_info,
215 bool fatal) override {
216 NOTREACHED();
218 void OnBeforeNetworkStart(net::URLRequest* request, bool* defer) override {
219 NOTREACHED();
222 void OnResponseStarted(net::URLRequest* request) override {
223 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed.
224 tracked_objects::ScopedTracker tracking_profile(
225 FROM_HERE_WITH_EXPLICIT_FUNCTION(
226 "423948 ServiceWorkerCache::BlobReader::OnResponseStarted"));
228 if (!request->status().is_success()) {
229 callback_.Run(entry_.Pass(), false);
230 return;
232 ReadFromBlob();
235 virtual void ReadFromBlob() {
236 int bytes_read = 0;
237 bool done =
238 blob_request_->Read(buffer_.get(), buffer_->size(), &bytes_read);
239 if (done)
240 OnReadCompleted(blob_request_.get(), bytes_read);
243 void OnReadCompleted(net::URLRequest* request, int bytes_read) override {
244 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed.
245 tracked_objects::ScopedTracker tracking_profile(
246 FROM_HERE_WITH_EXPLICIT_FUNCTION(
247 "423948 ServiceWorkerCache::BlobReader::OnReadCompleted"));
249 if (!request->status().is_success()) {
250 callback_.Run(entry_.Pass(), false);
251 return;
254 if (bytes_read == 0) {
255 callback_.Run(entry_.Pass(), true);
256 return;
259 net::CompletionCallback cache_write_callback =
260 base::Bind(&BlobReader::DidWriteDataToEntry,
261 weak_ptr_factory_.GetWeakPtr(),
262 bytes_read);
264 int rv = entry_->WriteData(INDEX_RESPONSE_BODY,
265 cache_entry_offset_,
266 buffer_.get(),
267 bytes_read,
268 cache_write_callback,
269 true /* truncate */);
270 if (rv != net::ERR_IO_PENDING)
271 cache_write_callback.Run(rv);
274 void DidWriteDataToEntry(int expected_bytes, int rv) {
275 if (rv != expected_bytes) {
276 callback_.Run(entry_.Pass(), false);
277 return;
280 cache_entry_offset_ += rv;
281 ReadFromBlob();
284 private:
285 int cache_entry_offset_;
286 disk_cache::ScopedEntryPtr entry_;
287 scoped_ptr<net::URLRequest> blob_request_;
288 EntryAndBoolCallback callback_;
289 scoped_refptr<net::IOBufferWithSize> buffer_;
290 base::WeakPtrFactory<BlobReader> weak_ptr_factory_;
293 // The state needed to pass between ServiceWorkerCache::Keys callbacks.
294 struct ServiceWorkerCache::KeysContext {
295 KeysContext(const ServiceWorkerCache::RequestsCallback& callback)
296 : original_callback(callback),
297 out_keys(new ServiceWorkerCache::Requests()),
298 enumerated_entry(NULL) {}
300 ~KeysContext() {
301 for (size_t i = 0, max = entries.size(); i < max; ++i)
302 entries[i]->Close();
303 if (enumerated_entry)
304 enumerated_entry->Close();
307 // The callback passed to the Keys() function.
308 ServiceWorkerCache::RequestsCallback original_callback;
310 // The vector of open entries in the backend.
311 Entries entries;
313 // The output of the Keys function.
314 scoped_ptr<ServiceWorkerCache::Requests> out_keys;
316 // Used for enumerating cache entries.
317 scoped_ptr<disk_cache::Backend::Iterator> backend_iterator;
318 disk_cache::Entry* enumerated_entry;
320 DISALLOW_COPY_AND_ASSIGN(KeysContext);
323 struct ServiceWorkerCache::MatchContext {
324 MatchContext(scoped_ptr<ServiceWorkerFetchRequest> request,
325 const ServiceWorkerCache::ResponseCallback& callback,
326 base::WeakPtr<storage::BlobStorageContext> blob_storage_context)
327 : request(request.Pass()),
328 original_callback(callback),
329 blob_storage_context(blob_storage_context),
330 entry(nullptr),
331 total_bytes_read(0) {}
333 ~MatchContext() {
334 if (entry)
335 entry->Close();
338 // Input
339 scoped_ptr<ServiceWorkerFetchRequest> request;
340 ServiceWorkerCache::ResponseCallback original_callback;
341 base::WeakPtr<storage::BlobStorageContext> blob_storage_context;
342 disk_cache::Entry* entry;
344 // Output
345 scoped_ptr<ServiceWorkerResponse> response;
346 scoped_refptr<storage::BlobData> blob_data;
348 // For reading the cache entry data into a blob.
349 scoped_refptr<net::IOBufferWithSize> response_body_buffer;
350 size_t total_bytes_read;
352 DISALLOW_COPY_AND_ASSIGN(MatchContext);
355 // The state needed to pass between ServiceWorkerCache::Put callbacks.
356 struct ServiceWorkerCache::PutContext {
357 PutContext(
358 const GURL& origin,
359 scoped_ptr<ServiceWorkerFetchRequest> request,
360 scoped_ptr<ServiceWorkerResponse> response,
361 scoped_ptr<storage::BlobDataHandle> blob_data_handle,
362 const ServiceWorkerCache::ResponseCallback& callback,
363 net::URLRequestContext* request_context,
364 const scoped_refptr<storage::QuotaManagerProxy>& quota_manager_proxy)
365 : origin(origin),
366 request(request.Pass()),
367 response(response.Pass()),
368 blob_data_handle(blob_data_handle.Pass()),
369 callback(callback),
370 request_context(request_context),
371 quota_manager_proxy(quota_manager_proxy),
372 cache_entry(NULL) {}
373 ~PutContext() {
374 if (cache_entry)
375 cache_entry->Close();
378 // Input parameters to the Put function.
379 GURL origin;
380 scoped_ptr<ServiceWorkerFetchRequest> request;
381 scoped_ptr<ServiceWorkerResponse> response;
382 scoped_ptr<storage::BlobDataHandle> blob_data_handle;
383 ServiceWorkerCache::ResponseCallback callback;
384 net::URLRequestContext* request_context;
385 scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy;
387 // This isn't a scoped_ptr because the disk_cache needs an Entry** as input to
388 // CreateEntry.
389 disk_cache::Entry* cache_entry;
391 // The BlobDataHandle for the output ServiceWorkerResponse.
392 scoped_ptr<storage::BlobDataHandle> out_blob_data_handle;
394 DISALLOW_COPY_AND_ASSIGN(PutContext);
397 // static
398 scoped_refptr<ServiceWorkerCache> ServiceWorkerCache::CreateMemoryCache(
399 const GURL& origin,
400 net::URLRequestContext* request_context,
401 const scoped_refptr<storage::QuotaManagerProxy>& quota_manager_proxy,
402 base::WeakPtr<storage::BlobStorageContext> blob_context) {
403 return make_scoped_refptr(new ServiceWorkerCache(origin,
404 base::FilePath(),
405 request_context,
406 quota_manager_proxy,
407 blob_context));
410 // static
411 scoped_refptr<ServiceWorkerCache> ServiceWorkerCache::CreatePersistentCache(
412 const GURL& origin,
413 const base::FilePath& path,
414 net::URLRequestContext* request_context,
415 const scoped_refptr<storage::QuotaManagerProxy>& quota_manager_proxy,
416 base::WeakPtr<storage::BlobStorageContext> blob_context) {
417 return make_scoped_refptr(new ServiceWorkerCache(
418 origin, path, request_context, quota_manager_proxy, blob_context));
421 ServiceWorkerCache::~ServiceWorkerCache() {
424 base::WeakPtr<ServiceWorkerCache> ServiceWorkerCache::AsWeakPtr() {
425 return weak_ptr_factory_.GetWeakPtr();
428 void ServiceWorkerCache::Put(scoped_ptr<ServiceWorkerFetchRequest> request,
429 scoped_ptr<ServiceWorkerResponse> response,
430 const ResponseCallback& callback) {
431 IncPendingOps();
432 ResponseCallback pending_callback =
433 base::Bind(&ServiceWorkerCache::PendingResponseCallback,
434 weak_ptr_factory_.GetWeakPtr(), callback);
435 scoped_ptr<storage::BlobDataHandle> blob_data_handle;
437 if (!response->blob_uuid.empty()) {
438 if (!blob_storage_context_) {
439 pending_callback.Run(ErrorTypeStorage,
440 scoped_ptr<ServiceWorkerResponse>(),
441 scoped_ptr<storage::BlobDataHandle>());
442 return;
444 blob_data_handle =
445 blob_storage_context_->GetBlobDataFromUUID(response->blob_uuid);
446 if (!blob_data_handle) {
447 pending_callback.Run(ErrorTypeStorage,
448 scoped_ptr<ServiceWorkerResponse>(),
449 scoped_ptr<storage::BlobDataHandle>());
450 return;
454 scoped_ptr<PutContext> put_context(new PutContext(
455 origin_, request.Pass(), response.Pass(), blob_data_handle.Pass(),
456 pending_callback, request_context_, quota_manager_proxy_));
458 if (put_context->blob_data_handle) {
459 // Grab another handle to the blob for the callback response.
460 put_context->out_blob_data_handle =
461 blob_storage_context_->GetBlobDataFromUUID(
462 put_context->response->blob_uuid);
465 base::Closure continuation =
466 base::Bind(&ServiceWorkerCache::PutImpl, weak_ptr_factory_.GetWeakPtr(),
467 base::Passed(put_context.Pass()));
469 if (backend_state_ == BACKEND_UNINITIALIZED) {
470 InitBackend(continuation);
471 return;
474 continuation.Run();
477 void ServiceWorkerCache::Match(scoped_ptr<ServiceWorkerFetchRequest> request,
478 const ResponseCallback& callback) {
479 IncPendingOps();
480 ResponseCallback pending_callback =
481 base::Bind(&ServiceWorkerCache::PendingResponseCallback,
482 weak_ptr_factory_.GetWeakPtr(), callback);
484 switch (backend_state_) {
485 case BACKEND_UNINITIALIZED:
486 InitBackend(base::Bind(&ServiceWorkerCache::Match,
487 weak_ptr_factory_.GetWeakPtr(),
488 base::Passed(request.Pass()), pending_callback));
489 return;
490 case BACKEND_CLOSED:
491 pending_callback.Run(ErrorTypeStorage,
492 scoped_ptr<ServiceWorkerResponse>(),
493 scoped_ptr<storage::BlobDataHandle>());
494 return;
495 case BACKEND_OPEN:
496 DCHECK(backend_);
497 break;
500 scoped_ptr<MatchContext> match_context(new MatchContext(
501 request.Pass(), pending_callback, blob_storage_context_));
503 disk_cache::Entry** entry_ptr = &match_context->entry;
504 ServiceWorkerFetchRequest* request_ptr = match_context->request.get();
506 net::CompletionCallback open_entry_callback = base::Bind(
507 &ServiceWorkerCache::MatchDidOpenEntry, weak_ptr_factory_.GetWeakPtr(),
508 base::Passed(match_context.Pass()));
510 int rv = backend_->OpenEntry(
511 request_ptr->url.spec(), entry_ptr, open_entry_callback);
512 if (rv != net::ERR_IO_PENDING)
513 open_entry_callback.Run(rv);
516 void ServiceWorkerCache::Delete(scoped_ptr<ServiceWorkerFetchRequest> request,
517 const ErrorCallback& callback) {
518 IncPendingOps();
519 ErrorCallback pending_callback =
520 base::Bind(&ServiceWorkerCache::PendingErrorCallback,
521 weak_ptr_factory_.GetWeakPtr(), callback);
523 switch (backend_state_) {
524 case BACKEND_UNINITIALIZED:
525 InitBackend(base::Bind(&ServiceWorkerCache::Delete,
526 weak_ptr_factory_.GetWeakPtr(),
527 base::Passed(request.Pass()), pending_callback));
528 return;
529 case BACKEND_CLOSED:
530 pending_callback.Run(ErrorTypeStorage);
531 return;
532 case BACKEND_OPEN:
533 DCHECK(backend_);
534 break;
537 scoped_ptr<disk_cache::Entry*> entry(new disk_cache::Entry*);
539 disk_cache::Entry** entry_ptr = entry.get();
541 ServiceWorkerFetchRequest* request_ptr = request.get();
543 net::CompletionCallback open_entry_callback = base::Bind(
544 &ServiceWorkerCache::DeleteDidOpenEntry, weak_ptr_factory_.GetWeakPtr(),
545 origin_, base::Passed(request.Pass()), pending_callback,
546 base::Passed(entry.Pass()), quota_manager_proxy_);
548 int rv = backend_->OpenEntry(
549 request_ptr->url.spec(), entry_ptr, open_entry_callback);
550 if (rv != net::ERR_IO_PENDING)
551 open_entry_callback.Run(rv);
554 void ServiceWorkerCache::Keys(const RequestsCallback& callback) {
555 IncPendingOps();
556 RequestsCallback pending_callback =
557 base::Bind(&ServiceWorkerCache::PendingRequestsCallback,
558 weak_ptr_factory_.GetWeakPtr(), callback);
560 switch (backend_state_) {
561 case BACKEND_UNINITIALIZED:
562 InitBackend(base::Bind(&ServiceWorkerCache::Keys,
563 weak_ptr_factory_.GetWeakPtr(), pending_callback));
564 return;
565 case BACKEND_CLOSED:
566 pending_callback.Run(ErrorTypeStorage, scoped_ptr<Requests>());
567 return;
568 case BACKEND_OPEN:
569 DCHECK(backend_);
570 break;
573 // 1. Iterate through all of the entries, open them, and add them to a vector.
574 // 2. For each open entry:
575 // 2.1. Read the headers into a protobuf.
576 // 2.2. Copy the protobuf into a ServiceWorkerFetchRequest (a "key").
577 // 2.3. Push the response into a vector of requests to be returned.
578 // 3. Return the vector of requests (keys).
580 // The entries have to be loaded into a vector first because enumeration loops
581 // forever if you read data from a cache entry while enumerating.
583 scoped_ptr<KeysContext> keys_context(new KeysContext(pending_callback));
585 keys_context->backend_iterator = backend_->CreateIterator();
586 disk_cache::Backend::Iterator& iterator = *keys_context->backend_iterator;
587 disk_cache::Entry** enumerated_entry = &keys_context->enumerated_entry;
589 net::CompletionCallback open_entry_callback = base::Bind(
590 &ServiceWorkerCache::KeysDidOpenNextEntry, weak_ptr_factory_.GetWeakPtr(),
591 base::Passed(keys_context.Pass()));
593 int rv = iterator.OpenNextEntry(enumerated_entry, open_entry_callback);
595 if (rv != net::ERR_IO_PENDING)
596 open_entry_callback.Run(rv);
599 void ServiceWorkerCache::Close(const base::Closure& callback) {
600 DCHECK(backend_state_ != BACKEND_CLOSED)
601 << "Don't call ServiceWorkerCache::Close() twice.";
603 backend_state_ = BACKEND_CLOSED;
604 if (pending_ops_ > 0) {
605 DCHECK(ops_complete_callback_.is_null());
606 ops_complete_callback_ =
607 base::Bind(&ServiceWorkerCache::CloseImpl,
608 weak_ptr_factory_.GetWeakPtr(), callback);
609 return;
612 CloseImpl(callback);
615 int64 ServiceWorkerCache::MemoryBackedSize() const {
616 if (backend_state_ != BACKEND_OPEN || !memory_only_)
617 return 0;
619 scoped_ptr<disk_cache::Backend::Iterator> backend_iter =
620 backend_->CreateIterator();
621 disk_cache::Entry* entry = nullptr;
623 int64 sum = 0;
625 std::vector<disk_cache::Entry*> entries;
626 int rv = net::OK;
627 while ((rv = backend_iter->OpenNextEntry(
628 &entry, base::Bind(NotReachedCompletionCallback))) == net::OK) {
629 entries.push_back(entry); // Open the entries without mutating them.
631 DCHECK(rv !=
632 net::ERR_IO_PENDING); // Expect all memory ops to be synchronous.
634 for (disk_cache::Entry* entry : entries) {
635 sum += entry->GetDataSize(INDEX_HEADERS) +
636 entry->GetDataSize(INDEX_RESPONSE_BODY);
637 entry->Close();
640 return sum;
643 ServiceWorkerCache::ServiceWorkerCache(
644 const GURL& origin,
645 const base::FilePath& path,
646 net::URLRequestContext* request_context,
647 const scoped_refptr<storage::QuotaManagerProxy>& quota_manager_proxy,
648 base::WeakPtr<storage::BlobStorageContext> blob_context)
649 : origin_(origin),
650 path_(path),
651 request_context_(request_context),
652 quota_manager_proxy_(quota_manager_proxy),
653 blob_storage_context_(blob_context),
654 backend_state_(BACKEND_UNINITIALIZED),
655 memory_only_(path.empty()),
656 pending_ops_(0),
657 weak_ptr_factory_(this) {
660 void ServiceWorkerCache::MatchDidOpenEntry(
661 scoped_ptr<MatchContext> match_context,
662 int rv) {
663 if (rv != net::OK) {
664 match_context->original_callback.Run(ServiceWorkerCache::ErrorTypeNotFound,
665 scoped_ptr<ServiceWorkerResponse>(),
666 scoped_ptr<storage::BlobDataHandle>());
667 return;
670 // Copy the entry pointer before passing it in base::Bind.
671 disk_cache::Entry* tmp_entry_ptr = match_context->entry;
672 DCHECK(tmp_entry_ptr);
674 MetadataCallback headers_callback = base::Bind(
675 &ServiceWorkerCache::MatchDidReadMetadata, weak_ptr_factory_.GetWeakPtr(),
676 base::Passed(match_context.Pass()));
678 ReadMetadata(tmp_entry_ptr, headers_callback);
681 void ServiceWorkerCache::MatchDidReadMetadata(
682 scoped_ptr<MatchContext> match_context,
683 scoped_ptr<ServiceWorkerCacheMetadata> metadata) {
684 if (!metadata) {
685 match_context->original_callback.Run(ServiceWorkerCache::ErrorTypeStorage,
686 scoped_ptr<ServiceWorkerResponse>(),
687 scoped_ptr<storage::BlobDataHandle>());
688 return;
691 match_context->response.reset(new ServiceWorkerResponse(
692 match_context->request->url, metadata->response().status_code(),
693 metadata->response().status_text(),
694 ProtoResponseTypeToWebResponseType(metadata->response().response_type()),
695 ServiceWorkerHeaderMap(), "", 0, GURL()));
697 ServiceWorkerResponse* response = match_context->response.get();
699 if (metadata->response().has_url())
700 response->url = GURL(metadata->response().url());
702 for (int i = 0; i < metadata->response().headers_size(); ++i) {
703 const ServiceWorkerCacheHeaderMap header = metadata->response().headers(i);
704 response->headers.insert(std::make_pair(header.name(), header.value()));
707 ServiceWorkerHeaderMap cached_request_headers;
708 for (int i = 0; i < metadata->request().headers_size(); ++i) {
709 const ServiceWorkerCacheHeaderMap header = metadata->request().headers(i);
710 cached_request_headers[header.name()] = header.value();
713 if (!VaryMatches(match_context->request->headers, cached_request_headers,
714 response->headers)) {
715 match_context->original_callback.Run(ServiceWorkerCache::ErrorTypeNotFound,
716 scoped_ptr<ServiceWorkerResponse>(),
717 scoped_ptr<storage::BlobDataHandle>());
718 return;
721 if (match_context->entry->GetDataSize(INDEX_RESPONSE_BODY) == 0) {
722 match_context->original_callback.Run(ServiceWorkerCache::ErrorTypeOK,
723 match_context->response.Pass(),
724 scoped_ptr<storage::BlobDataHandle>());
725 return;
728 // Stream the response body into a blob.
729 if (!match_context->blob_storage_context) {
730 match_context->original_callback.Run(ServiceWorkerCache::ErrorTypeStorage,
731 scoped_ptr<ServiceWorkerResponse>(),
732 scoped_ptr<storage::BlobDataHandle>());
733 return;
736 response->blob_uuid = base::GenerateGUID();
738 match_context->blob_data = new storage::BlobData(response->blob_uuid);
739 match_context->response_body_buffer = new net::IOBufferWithSize(kBufferSize);
741 disk_cache::Entry* tmp_entry_ptr = match_context->entry;
742 net::IOBufferWithSize* response_body_buffer =
743 match_context->response_body_buffer.get();
745 net::CompletionCallback read_callback = base::Bind(
746 &ServiceWorkerCache::MatchDidReadResponseBodyData,
747 weak_ptr_factory_.GetWeakPtr(), base::Passed(match_context.Pass()));
749 int read_rv =
750 tmp_entry_ptr->ReadData(INDEX_RESPONSE_BODY, 0, response_body_buffer,
751 response_body_buffer->size(), read_callback);
753 if (read_rv != net::ERR_IO_PENDING)
754 read_callback.Run(read_rv);
757 void ServiceWorkerCache::MatchDidReadResponseBodyData(
758 scoped_ptr<MatchContext> match_context,
759 int rv) {
760 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed.
761 tracked_objects::ScopedTracker tracking_profile(
762 FROM_HERE_WITH_EXPLICIT_FUNCTION(
763 "422516 ServiceWorkerCache::MatchDidReadResponseBodyData"));
765 if (rv < 0) {
766 match_context->original_callback.Run(ServiceWorkerCache::ErrorTypeStorage,
767 scoped_ptr<ServiceWorkerResponse>(),
768 scoped_ptr<storage::BlobDataHandle>());
769 return;
772 if (rv == 0) {
773 match_context->response->blob_uuid = match_context->blob_data->uuid();
774 match_context->response->blob_size = match_context->total_bytes_read;
775 MatchDoneWithBody(match_context.Pass());
776 return;
779 // TODO(jkarlin): This copying of the the entire cache response into memory is
780 // awful. Create a new interface around SimpleCache that provides access the
781 // data directly from the file. See bug http://crbug.com/403493.
782 match_context->blob_data->AppendData(
783 match_context->response_body_buffer->data(), rv);
784 match_context->total_bytes_read += rv;
785 int total_bytes_read = match_context->total_bytes_read;
787 // Grab some pointers before passing match_context in bind.
788 net::IOBufferWithSize* buffer = match_context->response_body_buffer.get();
789 disk_cache::Entry* tmp_entry_ptr = match_context->entry;
791 net::CompletionCallback read_callback = base::Bind(
792 &ServiceWorkerCache::MatchDidReadResponseBodyData,
793 weak_ptr_factory_.GetWeakPtr(), base::Passed(match_context.Pass()));
795 int read_rv = tmp_entry_ptr->ReadData(INDEX_RESPONSE_BODY, total_bytes_read,
796 buffer, buffer->size(), read_callback);
798 if (read_rv != net::ERR_IO_PENDING)
799 read_callback.Run(read_rv);
802 void ServiceWorkerCache::MatchDoneWithBody(
803 scoped_ptr<MatchContext> match_context) {
804 if (!match_context->blob_storage_context) {
805 match_context->original_callback.Run(ServiceWorkerCache::ErrorTypeStorage,
806 scoped_ptr<ServiceWorkerResponse>(),
807 scoped_ptr<storage::BlobDataHandle>());
808 return;
811 scoped_ptr<storage::BlobDataHandle> blob_data_handle(
812 match_context->blob_storage_context->AddFinishedBlob(
813 match_context->blob_data.get()));
815 match_context->original_callback.Run(ServiceWorkerCache::ErrorTypeOK,
816 match_context->response.Pass(),
817 blob_data_handle.Pass());
820 void ServiceWorkerCache::PutImpl(scoped_ptr<PutContext> put_context) {
821 if (backend_state_ != BACKEND_OPEN) {
822 put_context->callback.Run(ErrorTypeStorage,
823 scoped_ptr<ServiceWorkerResponse>(),
824 scoped_ptr<storage::BlobDataHandle>());
825 return;
828 scoped_ptr<ServiceWorkerFetchRequest> request_copy(
829 new ServiceWorkerFetchRequest(*put_context->request));
831 Delete(request_copy.Pass(), base::Bind(&ServiceWorkerCache::PutDidDelete,
832 weak_ptr_factory_.GetWeakPtr(),
833 base::Passed(put_context.Pass())));
836 void ServiceWorkerCache::PutDidDelete(scoped_ptr<PutContext> put_context,
837 ErrorType delete_error) {
838 if (backend_state_ != BACKEND_OPEN) {
839 put_context->callback.Run(ErrorTypeStorage,
840 scoped_ptr<ServiceWorkerResponse>(),
841 scoped_ptr<storage::BlobDataHandle>());
842 return;
845 disk_cache::Entry** entry_ptr = &put_context->cache_entry;
846 ServiceWorkerFetchRequest* request_ptr = put_context->request.get();
847 disk_cache::Backend* backend_ptr = backend_.get();
849 net::CompletionCallback create_entry_callback = base::Bind(
850 &ServiceWorkerCache::PutDidCreateEntry, weak_ptr_factory_.GetWeakPtr(),
851 base::Passed(put_context.Pass()));
853 int create_rv = backend_ptr->CreateEntry(
854 request_ptr->url.spec(), entry_ptr, create_entry_callback);
856 if (create_rv != net::ERR_IO_PENDING)
857 create_entry_callback.Run(create_rv);
860 void ServiceWorkerCache::PutDidCreateEntry(scoped_ptr<PutContext> put_context,
861 int rv) {
862 if (rv != net::OK) {
863 put_context->callback.Run(ServiceWorkerCache::ErrorTypeExists,
864 scoped_ptr<ServiceWorkerResponse>(),
865 scoped_ptr<storage::BlobDataHandle>());
866 return;
869 DCHECK(put_context->cache_entry);
871 ServiceWorkerCacheMetadata metadata;
872 ServiceWorkerCacheRequest* request_metadata = metadata.mutable_request();
873 request_metadata->set_method(put_context->request->method);
874 for (ServiceWorkerHeaderMap::const_iterator it =
875 put_context->request->headers.begin();
876 it != put_context->request->headers.end();
877 ++it) {
878 ServiceWorkerCacheHeaderMap* header_map = request_metadata->add_headers();
879 header_map->set_name(it->first);
880 header_map->set_value(it->second);
883 ServiceWorkerCacheResponse* response_metadata = metadata.mutable_response();
884 response_metadata->set_status_code(put_context->response->status_code);
885 response_metadata->set_status_text(put_context->response->status_text);
886 response_metadata->set_response_type(
887 WebResponseTypeToProtoResponseType(put_context->response->response_type));
888 response_metadata->set_url(put_context->response->url.spec());
889 for (ServiceWorkerHeaderMap::const_iterator it =
890 put_context->response->headers.begin();
891 it != put_context->response->headers.end();
892 ++it) {
893 ServiceWorkerCacheHeaderMap* header_map = response_metadata->add_headers();
894 header_map->set_name(it->first);
895 header_map->set_value(it->second);
898 scoped_ptr<std::string> serialized(new std::string());
899 if (!metadata.SerializeToString(serialized.get())) {
900 put_context->callback.Run(ServiceWorkerCache::ErrorTypeStorage,
901 scoped_ptr<ServiceWorkerResponse>(),
902 scoped_ptr<storage::BlobDataHandle>());
903 return;
906 scoped_refptr<net::StringIOBuffer> buffer(
907 new net::StringIOBuffer(serialized.Pass()));
909 // Get a temporary copy of the entry pointer before passing it in base::Bind.
910 disk_cache::Entry* tmp_entry_ptr = put_context->cache_entry;
912 net::CompletionCallback write_headers_callback = base::Bind(
913 &ServiceWorkerCache::PutDidWriteHeaders, weak_ptr_factory_.GetWeakPtr(),
914 base::Passed(put_context.Pass()), buffer->size());
916 rv = tmp_entry_ptr->WriteData(INDEX_HEADERS,
917 0 /* offset */,
918 buffer.get(),
919 buffer->size(),
920 write_headers_callback,
921 true /* truncate */);
923 if (rv != net::ERR_IO_PENDING)
924 write_headers_callback.Run(rv);
927 void ServiceWorkerCache::PutDidWriteHeaders(scoped_ptr<PutContext> put_context,
928 int expected_bytes,
929 int rv) {
930 if (rv != expected_bytes) {
931 put_context->cache_entry->Doom();
932 put_context->callback.Run(ServiceWorkerCache::ErrorTypeStorage,
933 scoped_ptr<ServiceWorkerResponse>(),
934 scoped_ptr<storage::BlobDataHandle>());
935 return;
938 // The metadata is written, now for the response content. The data is streamed
939 // from the blob into the cache entry.
941 if (put_context->response->blob_uuid.empty()) {
942 if (put_context->quota_manager_proxy.get()) {
943 put_context->quota_manager_proxy->NotifyStorageModified(
944 storage::QuotaClient::kServiceWorkerCache,
945 put_context->origin,
946 storage::kStorageTypeTemporary,
947 put_context->cache_entry->GetDataSize(INDEX_HEADERS));
950 put_context->callback.Run(ServiceWorkerCache::ErrorTypeOK,
951 put_context->response.Pass(),
952 scoped_ptr<storage::BlobDataHandle>());
953 return;
956 DCHECK(put_context->blob_data_handle);
958 disk_cache::ScopedEntryPtr entry(put_context->cache_entry);
959 put_context->cache_entry = NULL;
960 scoped_ptr<BlobReader> reader(new BlobReader());
961 BlobReader* reader_ptr = reader.get();
963 // Grab some pointers before passing put_context in Bind.
964 net::URLRequestContext* request_context = put_context->request_context;
965 scoped_ptr<storage::BlobDataHandle> blob_data_handle =
966 put_context->blob_data_handle.Pass();
968 reader_ptr->StreamBlobToCache(
969 entry.Pass(), request_context, blob_data_handle.Pass(),
970 base::Bind(&ServiceWorkerCache::PutDidWriteBlobToCache,
971 weak_ptr_factory_.GetWeakPtr(),
972 base::Passed(put_context.Pass()),
973 base::Passed(reader.Pass())));
976 void ServiceWorkerCache::PutDidWriteBlobToCache(
977 scoped_ptr<PutContext> put_context,
978 scoped_ptr<BlobReader> blob_reader,
979 disk_cache::ScopedEntryPtr entry,
980 bool success) {
981 DCHECK(entry);
982 put_context->cache_entry = entry.release();
984 if (!success) {
985 put_context->cache_entry->Doom();
986 put_context->callback.Run(ServiceWorkerCache::ErrorTypeStorage,
987 scoped_ptr<ServiceWorkerResponse>(),
988 scoped_ptr<storage::BlobDataHandle>());
989 return;
992 if (put_context->quota_manager_proxy.get()) {
993 put_context->quota_manager_proxy->NotifyStorageModified(
994 storage::QuotaClient::kServiceWorkerCache,
995 put_context->origin,
996 storage::kStorageTypeTemporary,
997 put_context->cache_entry->GetDataSize(INDEX_HEADERS) +
998 put_context->cache_entry->GetDataSize(INDEX_RESPONSE_BODY));
1001 put_context->callback.Run(ServiceWorkerCache::ErrorTypeOK,
1002 put_context->response.Pass(),
1003 put_context->out_blob_data_handle.Pass());
1006 void ServiceWorkerCache::DeleteDidOpenEntry(
1007 const GURL& origin,
1008 scoped_ptr<ServiceWorkerFetchRequest> request,
1009 const ServiceWorkerCache::ErrorCallback& callback,
1010 scoped_ptr<disk_cache::Entry*> entry_ptr,
1011 const scoped_refptr<storage::QuotaManagerProxy>& quota_manager_proxy,
1012 int rv) {
1013 if (rv != net::OK) {
1014 callback.Run(ServiceWorkerCache::ErrorTypeNotFound);
1015 return;
1018 DCHECK(entry_ptr);
1019 disk_cache::ScopedEntryPtr entry(*entry_ptr);
1021 if (quota_manager_proxy.get()) {
1022 quota_manager_proxy->NotifyStorageModified(
1023 storage::QuotaClient::kServiceWorkerCache, origin,
1024 storage::kStorageTypeTemporary,
1025 -1 * (entry->GetDataSize(INDEX_HEADERS) +
1026 entry->GetDataSize(INDEX_RESPONSE_BODY)));
1029 entry->Doom();
1030 callback.Run(ServiceWorkerCache::ErrorTypeOK);
1033 void ServiceWorkerCache::KeysDidOpenNextEntry(
1034 scoped_ptr<KeysContext> keys_context,
1035 int rv) {
1036 if (rv == net::ERR_FAILED) {
1037 DCHECK(!keys_context->enumerated_entry);
1038 // Enumeration is complete, extract the requests from the entries.
1039 Entries::iterator iter = keys_context->entries.begin();
1040 KeysProcessNextEntry(keys_context.Pass(), iter);
1041 return;
1044 if (rv < 0) {
1045 keys_context->original_callback.Run(ErrorTypeStorage,
1046 scoped_ptr<Requests>());
1047 return;
1050 if (backend_state_ != BACKEND_OPEN) {
1051 keys_context->original_callback.Run(ErrorTypeNotFound,
1052 scoped_ptr<Requests>());
1053 return;
1056 // Store the entry.
1057 keys_context->entries.push_back(keys_context->enumerated_entry);
1058 keys_context->enumerated_entry = NULL;
1060 // Enumerate the next entry.
1061 disk_cache::Backend::Iterator& iterator = *keys_context->backend_iterator;
1062 disk_cache::Entry** enumerated_entry = &keys_context->enumerated_entry;
1063 net::CompletionCallback open_entry_callback = base::Bind(
1064 &ServiceWorkerCache::KeysDidOpenNextEntry, weak_ptr_factory_.GetWeakPtr(),
1065 base::Passed(keys_context.Pass()));
1067 rv = iterator.OpenNextEntry(enumerated_entry, open_entry_callback);
1069 if (rv != net::ERR_IO_PENDING)
1070 open_entry_callback.Run(rv);
1073 void ServiceWorkerCache::KeysProcessNextEntry(
1074 scoped_ptr<KeysContext> keys_context,
1075 const Entries::iterator& iter) {
1076 if (iter == keys_context->entries.end()) {
1077 // All done. Return all of the keys.
1078 keys_context->original_callback.Run(ErrorTypeOK,
1079 keys_context->out_keys.Pass());
1080 return;
1083 ReadMetadata(*iter, base::Bind(&ServiceWorkerCache::KeysDidReadMetadata,
1084 weak_ptr_factory_.GetWeakPtr(),
1085 base::Passed(keys_context.Pass()), iter));
1088 void ServiceWorkerCache::KeysDidReadMetadata(
1089 scoped_ptr<KeysContext> keys_context,
1090 const Entries::iterator& iter,
1091 scoped_ptr<ServiceWorkerCacheMetadata> metadata) {
1092 disk_cache::Entry* entry = *iter;
1094 if (metadata) {
1095 keys_context->out_keys->push_back(
1096 ServiceWorkerFetchRequest(GURL(entry->GetKey()),
1097 metadata->request().method(),
1098 ServiceWorkerHeaderMap(),
1099 GURL(),
1100 false));
1102 ServiceWorkerHeaderMap& req_headers =
1103 keys_context->out_keys->back().headers;
1105 for (int i = 0; i < metadata->request().headers_size(); ++i) {
1106 const ServiceWorkerCacheHeaderMap header = metadata->request().headers(i);
1107 req_headers.insert(std::make_pair(header.name(), header.value()));
1109 } else {
1110 entry->Doom();
1113 KeysProcessNextEntry(keys_context.Pass(), iter + 1);
1116 void ServiceWorkerCache::CloseImpl(const base::Closure& callback) {
1117 DCHECK(backend_state_ == BACKEND_CLOSED);
1118 backend_.reset();
1119 callback.Run();
1122 void ServiceWorkerCache::CreateBackend(const ErrorCallback& callback) {
1123 DCHECK(!backend_);
1125 // Use APP_CACHE as opposed to DISK_CACHE to prevent cache eviction.
1126 net::CacheType cache_type = memory_only_ ? net::MEMORY_CACHE : net::APP_CACHE;
1128 scoped_ptr<ScopedBackendPtr> backend_ptr(new ScopedBackendPtr());
1130 // Temporary pointer so that backend_ptr can be Pass()'d in Bind below.
1131 ScopedBackendPtr* backend = backend_ptr.get();
1133 net::CompletionCallback create_cache_callback =
1134 base::Bind(&ServiceWorkerCache::CreateBackendDidCreate,
1135 weak_ptr_factory_.GetWeakPtr(), callback,
1136 base::Passed(backend_ptr.Pass()));
1138 // TODO(jkarlin): Use the cache MessageLoopProxy that ServiceWorkerCacheCore
1139 // has for disk caches.
1140 int rv = disk_cache::CreateCacheBackend(
1141 cache_type,
1142 net::CACHE_BACKEND_SIMPLE,
1143 path_,
1144 kMaxCacheBytes,
1145 false, /* force */
1146 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::CACHE).get(),
1147 NULL,
1148 backend,
1149 create_cache_callback);
1150 if (rv != net::ERR_IO_PENDING)
1151 create_cache_callback.Run(rv);
1154 void ServiceWorkerCache::CreateBackendDidCreate(
1155 const ServiceWorkerCache::ErrorCallback& callback,
1156 scoped_ptr<ScopedBackendPtr> backend_ptr,
1157 int rv) {
1158 if (rv != net::OK) {
1159 callback.Run(ServiceWorkerCache::ErrorTypeStorage);
1160 return;
1163 backend_ = backend_ptr->Pass();
1164 callback.Run(ServiceWorkerCache::ErrorTypeOK);
1167 void ServiceWorkerCache::InitBackend(const base::Closure& callback) {
1168 DCHECK(backend_state_ == BACKEND_UNINITIALIZED);
1169 init_callbacks_.push_back(callback);
1171 // If this isn't the first call to Init then return as the initialization
1172 // has already started.
1173 if (init_callbacks_.size() > 1u)
1174 return;
1176 CreateBackend(base::Bind(&ServiceWorkerCache::InitDone,
1177 weak_ptr_factory_.GetWeakPtr()));
1180 void ServiceWorkerCache::InitDone(ErrorType error) {
1181 backend_state_ = (error == ErrorTypeOK && backend_ &&
1182 backend_state_ == BACKEND_UNINITIALIZED)
1183 ? BACKEND_OPEN
1184 : BACKEND_CLOSED;
1185 for (std::vector<base::Closure>::iterator it = init_callbacks_.begin();
1186 it != init_callbacks_.end(); ++it) {
1187 it->Run();
1189 init_callbacks_.clear();
1192 void ServiceWorkerCache::DecPendingOps() {
1193 DCHECK(pending_ops_ > 0);
1194 pending_ops_--;
1195 if (pending_ops_ == 0 && !ops_complete_callback_.is_null()) {
1196 ops_complete_callback_.Run();
1197 ops_complete_callback_.Reset();
1201 void ServiceWorkerCache::PendingErrorCallback(const ErrorCallback& callback,
1202 ErrorType error) {
1203 callback.Run(error);
1204 DecPendingOps();
1207 void ServiceWorkerCache::PendingResponseCallback(
1208 const ResponseCallback& callback,
1209 ErrorType error,
1210 scoped_ptr<ServiceWorkerResponse> response,
1211 scoped_ptr<storage::BlobDataHandle> blob_data_handle) {
1212 callback.Run(error, response.Pass(), blob_data_handle.Pass());
1213 DecPendingOps();
1216 void ServiceWorkerCache::PendingRequestsCallback(
1217 const RequestsCallback& callback,
1218 ErrorType error,
1219 scoped_ptr<Requests> requests) {
1220 callback.Run(error, requests.Pass());
1221 DecPendingOps();
1224 } // namespace content