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/cache_storage/cache_storage_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/metrics/histogram_macros.h"
13 #include "base/strings/string_util.h"
14 #include "content/browser/cache_storage/cache_storage.pb.h"
15 #include "content/browser/cache_storage/cache_storage_scheduler.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "content/public/common/referrer.h"
18 #include "net/base/completion_callback.h"
19 #include "net/base/io_buffer.h"
20 #include "net/base/net_errors.h"
21 #include "net/disk_cache/disk_cache.h"
22 #include "net/url_request/url_request_context.h"
23 #include "storage/browser/blob/blob_data_builder.h"
24 #include "storage/browser/blob/blob_data_handle.h"
25 #include "storage/browser/blob/blob_storage_context.h"
26 #include "storage/browser/blob/blob_url_request_job_factory.h"
27 #include "storage/browser/quota/quota_manager_proxy.h"
28 #include "third_party/WebKit/public/platform/WebServiceWorkerResponseType.h"
34 typedef base::Callback
<void(disk_cache::ScopedEntryPtr
, bool)>
36 typedef base::Callback
<void(scoped_ptr
<CacheMetadata
>)> MetadataCallback
;
38 enum EntryIndex
{ INDEX_HEADERS
= 0, INDEX_RESPONSE_BODY
};
40 // The maximum size of an individual cache. Ultimately cache size is controlled
42 const int kMaxCacheBytes
= 512 * 1024 * 1024;
44 // Buffer size for cache and blob reading/writing.
45 const int kBufferSize
= 1024 * 512;
47 void NotReachedCompletionCallback(int rv
) {
51 blink::WebServiceWorkerResponseType
ProtoResponseTypeToWebResponseType(
52 CacheResponse::ResponseType response_type
) {
53 switch (response_type
) {
54 case CacheResponse::BASIC_TYPE
:
55 return blink::WebServiceWorkerResponseTypeBasic
;
56 case CacheResponse::CORS_TYPE
:
57 return blink::WebServiceWorkerResponseTypeCORS
;
58 case CacheResponse::DEFAULT_TYPE
:
59 return blink::WebServiceWorkerResponseTypeDefault
;
60 case CacheResponse::ERROR_TYPE
:
61 return blink::WebServiceWorkerResponseTypeError
;
62 case CacheResponse::OPAQUE_TYPE
:
63 return blink::WebServiceWorkerResponseTypeOpaque
;
66 return blink::WebServiceWorkerResponseTypeOpaque
;
69 CacheResponse::ResponseType
WebResponseTypeToProtoResponseType(
70 blink::WebServiceWorkerResponseType response_type
) {
71 switch (response_type
) {
72 case blink::WebServiceWorkerResponseTypeBasic
:
73 return CacheResponse::BASIC_TYPE
;
74 case blink::WebServiceWorkerResponseTypeCORS
:
75 return CacheResponse::CORS_TYPE
;
76 case blink::WebServiceWorkerResponseTypeDefault
:
77 return CacheResponse::DEFAULT_TYPE
;
78 case blink::WebServiceWorkerResponseTypeError
:
79 return CacheResponse::ERROR_TYPE
;
80 case blink::WebServiceWorkerResponseTypeOpaque
:
81 return CacheResponse::OPAQUE_TYPE
;
84 return CacheResponse::OPAQUE_TYPE
;
87 // Copy headers out of a cache entry and into a protobuf. The callback is
88 // guaranteed to be run.
89 void ReadMetadata(disk_cache::Entry
* entry
, const MetadataCallback
& callback
);
90 void ReadMetadataDidReadMetadata(
91 disk_cache::Entry
* entry
,
92 const MetadataCallback
& callback
,
93 const scoped_refptr
<net::IOBufferWithSize
>& buffer
,
96 bool VaryMatches(const ServiceWorkerHeaderMap
& request
,
97 const ServiceWorkerHeaderMap
& cached_request
,
98 const ServiceWorkerHeaderMap
& response
) {
99 ServiceWorkerHeaderMap::const_iterator vary_iter
= response
.find("vary");
100 if (vary_iter
== response
.end())
103 std::vector
<std::string
> vary_keys
;
104 Tokenize(vary_iter
->second
, ",", &vary_keys
);
105 for (std::vector
<std::string
>::const_iterator it
= vary_keys
.begin();
106 it
!= vary_keys
.end(); ++it
) {
108 base::TrimWhitespaceASCII(*it
, base::TRIM_ALL
, &trimmed
);
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()))
121 // If the header exists in one, it exists in both. Verify that the values
123 if (request_iter
!= request
.end() &&
124 request_iter
->second
!= cached_request_iter
->second
)
131 void ReadMetadata(disk_cache::Entry
* entry
, const MetadataCallback
& callback
) {
134 scoped_refptr
<net::IOBufferWithSize
> buffer(
135 new net::IOBufferWithSize(entry
->GetDataSize(INDEX_HEADERS
)));
137 net::CompletionCallback read_header_callback
=
138 base::Bind(ReadMetadataDidReadMetadata
, entry
, callback
, buffer
);
140 int read_rv
= entry
->ReadData(INDEX_HEADERS
, 0, buffer
.get(), buffer
->size(),
141 read_header_callback
);
143 if (read_rv
!= net::ERR_IO_PENDING
)
144 read_header_callback
.Run(read_rv
);
147 void ReadMetadataDidReadMetadata(
148 disk_cache::Entry
* entry
,
149 const MetadataCallback
& callback
,
150 const scoped_refptr
<net::IOBufferWithSize
>& buffer
,
152 if (rv
!= buffer
->size()) {
153 callback
.Run(scoped_ptr
<CacheMetadata
>());
157 scoped_ptr
<CacheMetadata
> metadata(new CacheMetadata());
159 if (!metadata
->ParseFromArray(buffer
->data(), buffer
->size())) {
160 callback
.Run(scoped_ptr
<CacheMetadata
>());
164 callback
.Run(metadata
.Pass());
169 // Streams data from a blob and writes it to a given disk_cache::Entry.
170 class CacheStorageCache::BlobReader
: public net::URLRequest::Delegate
{
172 typedef base::Callback
<void(disk_cache::ScopedEntryPtr
, bool)>
173 EntryAndBoolCallback
;
176 : cache_entry_offset_(0),
177 buffer_(new net::IOBufferWithSize(kBufferSize
)),
178 weak_ptr_factory_(this) {}
180 // |entry| is passed to the callback once complete.
181 void StreamBlobToCache(disk_cache::ScopedEntryPtr entry
,
182 net::URLRequestContext
* request_context
,
183 scoped_ptr
<storage::BlobDataHandle
> blob_data_handle
,
184 const EntryAndBoolCallback
& callback
) {
186 entry_
= entry
.Pass();
187 callback_
= callback
;
188 blob_request_
= storage::BlobProtocolHandler::CreateBlobRequest(
189 blob_data_handle
.Pass(), request_context
, this);
190 blob_request_
->Start();
193 // net::URLRequest::Delegate overrides for reading blobs.
194 void OnReceivedRedirect(net::URLRequest
* request
,
195 const net::RedirectInfo
& redirect_info
,
196 bool* defer_redirect
) override
{
199 void OnAuthRequired(net::URLRequest
* request
,
200 net::AuthChallengeInfo
* auth_info
) override
{
203 void OnCertificateRequested(
204 net::URLRequest
* request
,
205 net::SSLCertRequestInfo
* cert_request_info
) override
{
208 void OnSSLCertificateError(net::URLRequest
* request
,
209 const net::SSLInfo
& ssl_info
,
210 bool fatal
) override
{
213 void OnBeforeNetworkStart(net::URLRequest
* request
, bool* defer
) override
{
217 void OnResponseStarted(net::URLRequest
* request
) override
{
218 if (!request
->status().is_success()) {
219 callback_
.Run(entry_
.Pass(), false);
225 virtual void ReadFromBlob() {
228 blob_request_
->Read(buffer_
.get(), buffer_
->size(), &bytes_read
);
230 OnReadCompleted(blob_request_
.get(), bytes_read
);
233 void OnReadCompleted(net::URLRequest
* request
, int bytes_read
) override
{
234 if (!request
->status().is_success()) {
235 callback_
.Run(entry_
.Pass(), false);
239 if (bytes_read
== 0) {
240 callback_
.Run(entry_
.Pass(), true);
244 net::CompletionCallback cache_write_callback
=
245 base::Bind(&BlobReader::DidWriteDataToEntry
,
246 weak_ptr_factory_
.GetWeakPtr(), bytes_read
);
248 int rv
= entry_
->WriteData(INDEX_RESPONSE_BODY
, cache_entry_offset_
,
249 buffer_
.get(), bytes_read
, cache_write_callback
,
250 true /* truncate */);
251 if (rv
!= net::ERR_IO_PENDING
)
252 cache_write_callback
.Run(rv
);
255 void DidWriteDataToEntry(int expected_bytes
, int rv
) {
256 if (rv
!= expected_bytes
) {
257 callback_
.Run(entry_
.Pass(), false);
261 cache_entry_offset_
+= rv
;
266 int cache_entry_offset_
;
267 disk_cache::ScopedEntryPtr entry_
;
268 scoped_ptr
<net::URLRequest
> blob_request_
;
269 EntryAndBoolCallback callback_
;
270 scoped_refptr
<net::IOBufferWithSize
> buffer_
;
271 base::WeakPtrFactory
<BlobReader
> weak_ptr_factory_
;
274 // The state needed to pass between CacheStorageCache::Keys callbacks.
275 struct CacheStorageCache::KeysContext
{
276 explicit KeysContext(const CacheStorageCache::RequestsCallback
& callback
)
277 : original_callback(callback
),
278 out_keys(new CacheStorageCache::Requests()),
279 enumerated_entry(NULL
) {}
282 for (size_t i
= 0, max
= entries
.size(); i
< max
; ++i
)
284 if (enumerated_entry
)
285 enumerated_entry
->Close();
288 // The callback passed to the Keys() function.
289 CacheStorageCache::RequestsCallback original_callback
;
291 // The vector of open entries in the backend.
294 // The output of the Keys function.
295 scoped_ptr
<CacheStorageCache::Requests
> out_keys
;
297 // Used for enumerating cache entries.
298 scoped_ptr
<disk_cache::Backend::Iterator
> backend_iterator
;
299 disk_cache::Entry
* enumerated_entry
;
301 DISALLOW_COPY_AND_ASSIGN(KeysContext
);
304 struct CacheStorageCache::MatchContext
{
305 MatchContext(scoped_ptr
<ServiceWorkerFetchRequest
> request
,
306 const CacheStorageCache::ResponseCallback
& callback
,
307 base::WeakPtr
<storage::BlobStorageContext
> blob_storage_context
)
308 : request(request
.Pass()),
309 original_callback(callback
),
310 blob_storage_context(blob_storage_context
),
312 total_bytes_read(0) {}
320 scoped_ptr
<ServiceWorkerFetchRequest
> request
;
321 CacheStorageCache::ResponseCallback original_callback
;
322 base::WeakPtr
<storage::BlobStorageContext
> blob_storage_context
;
323 disk_cache::Entry
* entry
;
326 scoped_ptr
<ServiceWorkerResponse
> response
;
327 scoped_ptr
<storage::BlobDataBuilder
> blob_data
;
329 // For reading the cache entry data into a blob.
330 scoped_refptr
<net::IOBufferWithSize
> response_body_buffer
;
331 size_t total_bytes_read
;
333 DISALLOW_COPY_AND_ASSIGN(MatchContext
);
336 // The state needed to pass between CacheStorageCache::Put callbacks.
337 struct CacheStorageCache::PutContext
{
340 scoped_ptr
<ServiceWorkerFetchRequest
> request
,
341 scoped_ptr
<ServiceWorkerResponse
> response
,
342 scoped_ptr
<storage::BlobDataHandle
> blob_data_handle
,
343 const CacheStorageCache::ResponseCallback
& callback
,
344 net::URLRequestContext
* request_context
,
345 const scoped_refptr
<storage::QuotaManagerProxy
>& quota_manager_proxy
)
347 request(request
.Pass()),
348 response(response
.Pass()),
349 blob_data_handle(blob_data_handle
.Pass()),
351 request_context(request_context
),
352 quota_manager_proxy(quota_manager_proxy
),
356 cache_entry
->Close();
359 // Input parameters to the Put function.
361 scoped_ptr
<ServiceWorkerFetchRequest
> request
;
362 scoped_ptr
<ServiceWorkerResponse
> response
;
363 scoped_ptr
<storage::BlobDataHandle
> blob_data_handle
;
364 CacheStorageCache::ResponseCallback callback
;
365 net::URLRequestContext
* request_context
;
366 scoped_refptr
<storage::QuotaManagerProxy
> quota_manager_proxy
;
368 // This isn't a scoped_ptr because the disk_cache needs an Entry** as input to
370 disk_cache::Entry
* cache_entry
;
372 // The BlobDataHandle for the output ServiceWorkerResponse.
373 scoped_ptr
<storage::BlobDataHandle
> out_blob_data_handle
;
375 DISALLOW_COPY_AND_ASSIGN(PutContext
);
379 scoped_refptr
<CacheStorageCache
> CacheStorageCache::CreateMemoryCache(
381 net::URLRequestContext
* request_context
,
382 const scoped_refptr
<storage::QuotaManagerProxy
>& quota_manager_proxy
,
383 base::WeakPtr
<storage::BlobStorageContext
> blob_context
) {
384 return make_scoped_refptr(
385 new CacheStorageCache(origin
, base::FilePath(), request_context
,
386 quota_manager_proxy
, blob_context
));
390 scoped_refptr
<CacheStorageCache
> CacheStorageCache::CreatePersistentCache(
392 const base::FilePath
& path
,
393 net::URLRequestContext
* request_context
,
394 const scoped_refptr
<storage::QuotaManagerProxy
>& quota_manager_proxy
,
395 base::WeakPtr
<storage::BlobStorageContext
> blob_context
) {
396 return make_scoped_refptr(new CacheStorageCache(
397 origin
, path
, request_context
, quota_manager_proxy
, blob_context
));
400 CacheStorageCache::~CacheStorageCache() {
403 base::WeakPtr
<CacheStorageCache
> CacheStorageCache::AsWeakPtr() {
404 return weak_ptr_factory_
.GetWeakPtr();
407 void CacheStorageCache::Put(scoped_ptr
<ServiceWorkerFetchRequest
> request
,
408 scoped_ptr
<ServiceWorkerResponse
> response
,
409 const ResponseCallback
& callback
) {
410 scoped_ptr
<storage::BlobDataHandle
> blob_data_handle
;
412 if (!response
->blob_uuid
.empty()) {
413 if (!blob_storage_context_
) {
414 callback
.Run(ERROR_TYPE_STORAGE
, scoped_ptr
<ServiceWorkerResponse
>(),
415 scoped_ptr
<storage::BlobDataHandle
>());
419 blob_storage_context_
->GetBlobDataFromUUID(response
->blob_uuid
);
420 if (!blob_data_handle
) {
421 callback
.Run(ERROR_TYPE_STORAGE
, scoped_ptr
<ServiceWorkerResponse
>(),
422 scoped_ptr
<storage::BlobDataHandle
>());
427 ResponseCallback pending_callback
=
428 base::Bind(&CacheStorageCache::PendingResponseCallback
,
429 weak_ptr_factory_
.GetWeakPtr(), callback
);
431 scoped_ptr
<PutContext
> put_context(new PutContext(
432 origin_
, request
.Pass(), response
.Pass(), blob_data_handle
.Pass(),
433 pending_callback
, request_context_
, quota_manager_proxy_
));
435 if (put_context
->blob_data_handle
) {
436 // Grab another handle to the blob for the callback response.
437 put_context
->out_blob_data_handle
=
438 blob_storage_context_
->GetBlobDataFromUUID(
439 put_context
->response
->blob_uuid
);
442 if (backend_state_
== BACKEND_UNINITIALIZED
)
445 scheduler_
->ScheduleOperation(base::Bind(&CacheStorageCache::PutImpl
,
446 weak_ptr_factory_
.GetWeakPtr(),
447 base::Passed(put_context
.Pass())));
450 void CacheStorageCache::Match(scoped_ptr
<ServiceWorkerFetchRequest
> request
,
451 const ResponseCallback
& callback
) {
452 switch (backend_state_
) {
453 case BACKEND_UNINITIALIZED
:
457 callback
.Run(ERROR_TYPE_STORAGE
, scoped_ptr
<ServiceWorkerResponse
>(),
458 scoped_ptr
<storage::BlobDataHandle
>());
465 ResponseCallback pending_callback
=
466 base::Bind(&CacheStorageCache::PendingResponseCallback
,
467 weak_ptr_factory_
.GetWeakPtr(), callback
);
468 scheduler_
->ScheduleOperation(
469 base::Bind(&CacheStorageCache::MatchImpl
, weak_ptr_factory_
.GetWeakPtr(),
470 base::Passed(request
.Pass()), pending_callback
));
473 void CacheStorageCache::Delete(scoped_ptr
<ServiceWorkerFetchRequest
> request
,
474 const ErrorCallback
& callback
) {
475 switch (backend_state_
) {
476 case BACKEND_UNINITIALIZED
:
480 callback
.Run(ERROR_TYPE_STORAGE
);
486 ErrorCallback pending_callback
=
487 base::Bind(&CacheStorageCache::PendingErrorCallback
,
488 weak_ptr_factory_
.GetWeakPtr(), callback
);
489 scheduler_
->ScheduleOperation(
490 base::Bind(&CacheStorageCache::DeleteImpl
, weak_ptr_factory_
.GetWeakPtr(),
491 base::Passed(request
.Pass()), pending_callback
));
494 void CacheStorageCache::Keys(const RequestsCallback
& callback
) {
495 switch (backend_state_
) {
496 case BACKEND_UNINITIALIZED
:
500 callback
.Run(ERROR_TYPE_STORAGE
, scoped_ptr
<Requests
>());
507 RequestsCallback pending_callback
=
508 base::Bind(&CacheStorageCache::PendingRequestsCallback
,
509 weak_ptr_factory_
.GetWeakPtr(), callback
);
510 scheduler_
->ScheduleOperation(base::Bind(&CacheStorageCache::KeysImpl
,
511 weak_ptr_factory_
.GetWeakPtr(),
515 void CacheStorageCache::Close(const base::Closure
& callback
) {
516 DCHECK(backend_state_
!= BACKEND_CLOSED
)
517 << "Don't call CacheStorageCache::Close() twice.";
519 base::Closure pending_callback
=
520 base::Bind(&CacheStorageCache::PendingClosure
,
521 weak_ptr_factory_
.GetWeakPtr(), callback
);
523 scheduler_
->ScheduleOperation(base::Bind(&CacheStorageCache::CloseImpl
,
524 weak_ptr_factory_
.GetWeakPtr(),
528 int64
CacheStorageCache::MemoryBackedSize() const {
529 if (backend_state_
!= BACKEND_OPEN
|| !memory_only_
)
532 scoped_ptr
<disk_cache::Backend::Iterator
> backend_iter
=
533 backend_
->CreateIterator();
534 disk_cache::Entry
* entry
= nullptr;
538 std::vector
<disk_cache::Entry
*> entries
;
540 while ((rv
= backend_iter
->OpenNextEntry(
541 &entry
, base::Bind(NotReachedCompletionCallback
))) == net::OK
) {
542 entries
.push_back(entry
); // Open the entries without mutating them.
545 net::ERR_IO_PENDING
); // Expect all memory ops to be synchronous.
547 for (disk_cache::Entry
* entry
: entries
) {
548 sum
+= entry
->GetDataSize(INDEX_HEADERS
) +
549 entry
->GetDataSize(INDEX_RESPONSE_BODY
);
556 CacheStorageCache::CacheStorageCache(
558 const base::FilePath
& path
,
559 net::URLRequestContext
* request_context
,
560 const scoped_refptr
<storage::QuotaManagerProxy
>& quota_manager_proxy
,
561 base::WeakPtr
<storage::BlobStorageContext
> blob_context
)
564 request_context_(request_context
),
565 quota_manager_proxy_(quota_manager_proxy
),
566 blob_storage_context_(blob_context
),
567 backend_state_(BACKEND_UNINITIALIZED
),
568 scheduler_(new CacheStorageScheduler()),
569 initializing_(false),
570 memory_only_(path
.empty()),
571 weak_ptr_factory_(this) {
574 void CacheStorageCache::MatchImpl(scoped_ptr
<ServiceWorkerFetchRequest
> request
,
575 const ResponseCallback
& callback
) {
576 DCHECK(backend_state_
!= BACKEND_UNINITIALIZED
);
577 if (backend_state_
!= BACKEND_OPEN
) {
578 callback
.Run(ERROR_TYPE_STORAGE
, scoped_ptr
<ServiceWorkerResponse
>(),
579 scoped_ptr
<storage::BlobDataHandle
>());
583 scoped_ptr
<MatchContext
> match_context(
584 new MatchContext(request
.Pass(), callback
, blob_storage_context_
));
586 disk_cache::Entry
** entry_ptr
= &match_context
->entry
;
587 ServiceWorkerFetchRequest
* request_ptr
= match_context
->request
.get();
589 net::CompletionCallback open_entry_callback
= base::Bind(
590 &CacheStorageCache::MatchDidOpenEntry
, weak_ptr_factory_
.GetWeakPtr(),
591 base::Passed(match_context
.Pass()));
593 int rv
= backend_
->OpenEntry(request_ptr
->url
.spec(), entry_ptr
,
594 open_entry_callback
);
595 if (rv
!= net::ERR_IO_PENDING
)
596 open_entry_callback
.Run(rv
);
599 void CacheStorageCache::MatchDidOpenEntry(
600 scoped_ptr
<MatchContext
> match_context
,
603 match_context
->original_callback
.Run(
604 CacheStorageCache::ERROR_TYPE_NOT_FOUND
,
605 scoped_ptr
<ServiceWorkerResponse
>(),
606 scoped_ptr
<storage::BlobDataHandle
>());
610 // Copy the entry pointer before passing it in base::Bind.
611 disk_cache::Entry
* tmp_entry_ptr
= match_context
->entry
;
612 DCHECK(tmp_entry_ptr
);
614 MetadataCallback headers_callback
= base::Bind(
615 &CacheStorageCache::MatchDidReadMetadata
, weak_ptr_factory_
.GetWeakPtr(),
616 base::Passed(match_context
.Pass()));
618 ReadMetadata(tmp_entry_ptr
, headers_callback
);
621 void CacheStorageCache::MatchDidReadMetadata(
622 scoped_ptr
<MatchContext
> match_context
,
623 scoped_ptr
<CacheMetadata
> metadata
) {
625 match_context
->original_callback
.Run(CacheStorageCache::ERROR_TYPE_STORAGE
,
626 scoped_ptr
<ServiceWorkerResponse
>(),
627 scoped_ptr
<storage::BlobDataHandle
>());
631 match_context
->response
.reset(new ServiceWorkerResponse(
632 match_context
->request
->url
, metadata
->response().status_code(),
633 metadata
->response().status_text(),
634 ProtoResponseTypeToWebResponseType(metadata
->response().response_type()),
635 ServiceWorkerHeaderMap(), "", 0, GURL()));
637 ServiceWorkerResponse
* response
= match_context
->response
.get();
639 if (metadata
->response().has_url())
640 response
->url
= GURL(metadata
->response().url());
642 for (int i
= 0; i
< metadata
->response().headers_size(); ++i
) {
643 const CacheHeaderMap header
= metadata
->response().headers(i
);
644 DCHECK(header
.name().find('\0') == std::string::npos
);
645 DCHECK(header
.value().find('\0') == std::string::npos
);
646 response
->headers
.insert(std::make_pair(header
.name(), header
.value()));
649 ServiceWorkerHeaderMap cached_request_headers
;
650 for (int i
= 0; i
< metadata
->request().headers_size(); ++i
) {
651 const CacheHeaderMap header
= metadata
->request().headers(i
);
652 DCHECK(header
.name().find('\0') == std::string::npos
);
653 DCHECK(header
.value().find('\0') == std::string::npos
);
654 cached_request_headers
[header
.name()] = header
.value();
657 if (!VaryMatches(match_context
->request
->headers
, cached_request_headers
,
658 response
->headers
)) {
659 match_context
->original_callback
.Run(
660 CacheStorageCache::ERROR_TYPE_NOT_FOUND
,
661 scoped_ptr
<ServiceWorkerResponse
>(),
662 scoped_ptr
<storage::BlobDataHandle
>());
666 if (match_context
->entry
->GetDataSize(INDEX_RESPONSE_BODY
) == 0) {
667 match_context
->original_callback
.Run(CacheStorageCache::ERROR_TYPE_OK
,
668 match_context
->response
.Pass(),
669 scoped_ptr
<storage::BlobDataHandle
>());
673 // Stream the response body into a blob.
674 if (!match_context
->blob_storage_context
) {
675 match_context
->original_callback
.Run(CacheStorageCache::ERROR_TYPE_STORAGE
,
676 scoped_ptr
<ServiceWorkerResponse
>(),
677 scoped_ptr
<storage::BlobDataHandle
>());
681 response
->blob_uuid
= base::GenerateGUID();
683 match_context
->blob_data
.reset(
684 new storage::BlobDataBuilder(response
->blob_uuid
));
685 match_context
->response_body_buffer
= new net::IOBufferWithSize(kBufferSize
);
687 disk_cache::Entry
* tmp_entry_ptr
= match_context
->entry
;
688 net::IOBufferWithSize
* response_body_buffer
=
689 match_context
->response_body_buffer
.get();
691 net::CompletionCallback read_callback
= base::Bind(
692 &CacheStorageCache::MatchDidReadResponseBodyData
,
693 weak_ptr_factory_
.GetWeakPtr(), base::Passed(match_context
.Pass()));
696 tmp_entry_ptr
->ReadData(INDEX_RESPONSE_BODY
, 0, response_body_buffer
,
697 response_body_buffer
->size(), read_callback
);
699 if (read_rv
!= net::ERR_IO_PENDING
)
700 read_callback
.Run(read_rv
);
703 void CacheStorageCache::MatchDidReadResponseBodyData(
704 scoped_ptr
<MatchContext
> match_context
,
707 match_context
->original_callback
.Run(CacheStorageCache::ERROR_TYPE_STORAGE
,
708 scoped_ptr
<ServiceWorkerResponse
>(),
709 scoped_ptr
<storage::BlobDataHandle
>());
714 match_context
->response
->blob_uuid
= match_context
->blob_data
->uuid();
715 match_context
->response
->blob_size
= match_context
->total_bytes_read
;
716 MatchDoneWithBody(match_context
.Pass());
720 // TODO(jkarlin): This copying of the the entire cache response into memory is
721 // awful. Create a new interface around SimpleCache that provides access the
722 // data directly from the file. See bug http://crbug.com/403493.
723 match_context
->blob_data
->AppendData(
724 match_context
->response_body_buffer
->data(), rv
);
725 match_context
->total_bytes_read
+= rv
;
726 int total_bytes_read
= match_context
->total_bytes_read
;
728 // Grab some pointers before passing match_context in bind.
729 net::IOBufferWithSize
* buffer
= match_context
->response_body_buffer
.get();
730 disk_cache::Entry
* tmp_entry_ptr
= match_context
->entry
;
732 net::CompletionCallback read_callback
= base::Bind(
733 &CacheStorageCache::MatchDidReadResponseBodyData
,
734 weak_ptr_factory_
.GetWeakPtr(), base::Passed(match_context
.Pass()));
736 int read_rv
= tmp_entry_ptr
->ReadData(INDEX_RESPONSE_BODY
, total_bytes_read
,
737 buffer
, buffer
->size(), read_callback
);
739 if (read_rv
!= net::ERR_IO_PENDING
)
740 read_callback
.Run(read_rv
);
743 void CacheStorageCache::MatchDoneWithBody(
744 scoped_ptr
<MatchContext
> match_context
) {
745 if (!match_context
->blob_storage_context
) {
746 match_context
->original_callback
.Run(CacheStorageCache::ERROR_TYPE_STORAGE
,
747 scoped_ptr
<ServiceWorkerResponse
>(),
748 scoped_ptr
<storage::BlobDataHandle
>());
752 scoped_ptr
<storage::BlobDataHandle
> blob_data_handle(
753 match_context
->blob_storage_context
->AddFinishedBlob(
754 match_context
->blob_data
.get()));
756 match_context
->original_callback
.Run(CacheStorageCache::ERROR_TYPE_OK
,
757 match_context
->response
.Pass(),
758 blob_data_handle
.Pass());
761 void CacheStorageCache::PutImpl(scoped_ptr
<PutContext
> put_context
) {
762 DCHECK(backend_state_
!= BACKEND_UNINITIALIZED
);
763 if (backend_state_
!= BACKEND_OPEN
) {
764 put_context
->callback
.Run(ERROR_TYPE_STORAGE
,
765 scoped_ptr
<ServiceWorkerResponse
>(),
766 scoped_ptr
<storage::BlobDataHandle
>());
770 scoped_ptr
<ServiceWorkerFetchRequest
> request_copy(
771 new ServiceWorkerFetchRequest(*put_context
->request
));
773 DeleteImpl(request_copy
.Pass(), base::Bind(&CacheStorageCache::PutDidDelete
,
774 weak_ptr_factory_
.GetWeakPtr(),
775 base::Passed(put_context
.Pass())));
778 void CacheStorageCache::PutDidDelete(scoped_ptr
<PutContext
> put_context
,
779 ErrorType delete_error
) {
780 if (backend_state_
!= BACKEND_OPEN
) {
781 put_context
->callback
.Run(ERROR_TYPE_STORAGE
,
782 scoped_ptr
<ServiceWorkerResponse
>(),
783 scoped_ptr
<storage::BlobDataHandle
>());
787 disk_cache::Entry
** entry_ptr
= &put_context
->cache_entry
;
788 ServiceWorkerFetchRequest
* request_ptr
= put_context
->request
.get();
789 disk_cache::Backend
* backend_ptr
= backend_
.get();
791 net::CompletionCallback create_entry_callback
= base::Bind(
792 &CacheStorageCache::PutDidCreateEntry
, weak_ptr_factory_
.GetWeakPtr(),
793 base::Passed(put_context
.Pass()));
795 int create_rv
= backend_ptr
->CreateEntry(request_ptr
->url
.spec(), entry_ptr
,
796 create_entry_callback
);
798 if (create_rv
!= net::ERR_IO_PENDING
)
799 create_entry_callback
.Run(create_rv
);
802 void CacheStorageCache::PutDidCreateEntry(scoped_ptr
<PutContext
> put_context
,
805 put_context
->callback
.Run(CacheStorageCache::ERROR_TYPE_EXISTS
,
806 scoped_ptr
<ServiceWorkerResponse
>(),
807 scoped_ptr
<storage::BlobDataHandle
>());
811 DCHECK(put_context
->cache_entry
);
813 CacheMetadata metadata
;
814 CacheRequest
* request_metadata
= metadata
.mutable_request();
815 request_metadata
->set_method(put_context
->request
->method
);
816 for (ServiceWorkerHeaderMap::const_iterator it
=
817 put_context
->request
->headers
.begin();
818 it
!= put_context
->request
->headers
.end(); ++it
) {
819 DCHECK(it
->first
.find('\0') == std::string::npos
);
820 DCHECK(it
->second
.find('\0') == std::string::npos
);
821 CacheHeaderMap
* header_map
= request_metadata
->add_headers();
822 header_map
->set_name(it
->first
);
823 header_map
->set_value(it
->second
);
826 CacheResponse
* response_metadata
= metadata
.mutable_response();
827 response_metadata
->set_status_code(put_context
->response
->status_code
);
828 response_metadata
->set_status_text(put_context
->response
->status_text
);
829 response_metadata
->set_response_type(
830 WebResponseTypeToProtoResponseType(put_context
->response
->response_type
));
831 response_metadata
->set_url(put_context
->response
->url
.spec());
832 for (ServiceWorkerHeaderMap::const_iterator it
=
833 put_context
->response
->headers
.begin();
834 it
!= put_context
->response
->headers
.end(); ++it
) {
835 DCHECK(it
->first
.find('\0') == std::string::npos
);
836 DCHECK(it
->second
.find('\0') == std::string::npos
);
837 CacheHeaderMap
* header_map
= response_metadata
->add_headers();
838 header_map
->set_name(it
->first
);
839 header_map
->set_value(it
->second
);
842 scoped_ptr
<std::string
> serialized(new std::string());
843 if (!metadata
.SerializeToString(serialized
.get())) {
844 put_context
->callback
.Run(CacheStorageCache::ERROR_TYPE_STORAGE
,
845 scoped_ptr
<ServiceWorkerResponse
>(),
846 scoped_ptr
<storage::BlobDataHandle
>());
850 scoped_refptr
<net::StringIOBuffer
> buffer(
851 new net::StringIOBuffer(serialized
.Pass()));
853 // Get a temporary copy of the entry pointer before passing it in base::Bind.
854 disk_cache::Entry
* tmp_entry_ptr
= put_context
->cache_entry
;
856 net::CompletionCallback write_headers_callback
= base::Bind(
857 &CacheStorageCache::PutDidWriteHeaders
, weak_ptr_factory_
.GetWeakPtr(),
858 base::Passed(put_context
.Pass()), buffer
->size());
860 rv
= tmp_entry_ptr
->WriteData(INDEX_HEADERS
, 0 /* offset */, buffer
.get(),
861 buffer
->size(), write_headers_callback
,
862 true /* truncate */);
864 if (rv
!= net::ERR_IO_PENDING
)
865 write_headers_callback
.Run(rv
);
868 void CacheStorageCache::PutDidWriteHeaders(scoped_ptr
<PutContext
> put_context
,
871 if (rv
!= expected_bytes
) {
872 put_context
->cache_entry
->Doom();
873 put_context
->callback
.Run(CacheStorageCache::ERROR_TYPE_STORAGE
,
874 scoped_ptr
<ServiceWorkerResponse
>(),
875 scoped_ptr
<storage::BlobDataHandle
>());
879 // The metadata is written, now for the response content. The data is streamed
880 // from the blob into the cache entry.
882 if (put_context
->response
->blob_uuid
.empty()) {
883 if (put_context
->quota_manager_proxy
.get()) {
884 put_context
->quota_manager_proxy
->NotifyStorageModified(
885 storage::QuotaClient::kServiceWorkerCache
, put_context
->origin
,
886 storage::kStorageTypeTemporary
,
887 put_context
->cache_entry
->GetDataSize(INDEX_HEADERS
));
890 put_context
->callback
.Run(CacheStorageCache::ERROR_TYPE_OK
,
891 put_context
->response
.Pass(),
892 scoped_ptr
<storage::BlobDataHandle
>());
896 DCHECK(put_context
->blob_data_handle
);
898 disk_cache::ScopedEntryPtr
entry(put_context
->cache_entry
);
899 put_context
->cache_entry
= NULL
;
900 scoped_ptr
<BlobReader
> reader(new BlobReader());
901 BlobReader
* reader_ptr
= reader
.get();
903 // Grab some pointers before passing put_context in Bind.
904 net::URLRequestContext
* request_context
= put_context
->request_context
;
905 scoped_ptr
<storage::BlobDataHandle
> blob_data_handle
=
906 put_context
->blob_data_handle
.Pass();
908 reader_ptr
->StreamBlobToCache(
909 entry
.Pass(), request_context
, blob_data_handle
.Pass(),
910 base::Bind(&CacheStorageCache::PutDidWriteBlobToCache
,
911 weak_ptr_factory_
.GetWeakPtr(),
912 base::Passed(put_context
.Pass()),
913 base::Passed(reader
.Pass())));
916 void CacheStorageCache::PutDidWriteBlobToCache(
917 scoped_ptr
<PutContext
> put_context
,
918 scoped_ptr
<BlobReader
> blob_reader
,
919 disk_cache::ScopedEntryPtr entry
,
922 put_context
->cache_entry
= entry
.release();
925 put_context
->cache_entry
->Doom();
926 put_context
->callback
.Run(CacheStorageCache::ERROR_TYPE_STORAGE
,
927 scoped_ptr
<ServiceWorkerResponse
>(),
928 scoped_ptr
<storage::BlobDataHandle
>());
932 if (put_context
->quota_manager_proxy
.get()) {
933 put_context
->quota_manager_proxy
->NotifyStorageModified(
934 storage::QuotaClient::kServiceWorkerCache
, put_context
->origin
,
935 storage::kStorageTypeTemporary
,
936 put_context
->cache_entry
->GetDataSize(INDEX_HEADERS
) +
937 put_context
->cache_entry
->GetDataSize(INDEX_RESPONSE_BODY
));
940 put_context
->callback
.Run(CacheStorageCache::ERROR_TYPE_OK
,
941 put_context
->response
.Pass(),
942 put_context
->out_blob_data_handle
.Pass());
945 void CacheStorageCache::DeleteImpl(
946 scoped_ptr
<ServiceWorkerFetchRequest
> request
,
947 const ErrorCallback
& callback
) {
948 DCHECK(backend_state_
!= BACKEND_UNINITIALIZED
);
949 if (backend_state_
!= BACKEND_OPEN
) {
950 callback
.Run(ERROR_TYPE_STORAGE
);
953 scoped_ptr
<disk_cache::Entry
*> entry(new disk_cache::Entry
*);
955 disk_cache::Entry
** entry_ptr
= entry
.get();
957 ServiceWorkerFetchRequest
* request_ptr
= request
.get();
959 net::CompletionCallback open_entry_callback
= base::Bind(
960 &CacheStorageCache::DeleteDidOpenEntry
, weak_ptr_factory_
.GetWeakPtr(),
961 origin_
, base::Passed(request
.Pass()), callback
,
962 base::Passed(entry
.Pass()), quota_manager_proxy_
);
964 int rv
= backend_
->OpenEntry(request_ptr
->url
.spec(), entry_ptr
,
965 open_entry_callback
);
966 if (rv
!= net::ERR_IO_PENDING
)
967 open_entry_callback
.Run(rv
);
970 void CacheStorageCache::DeleteDidOpenEntry(
972 scoped_ptr
<ServiceWorkerFetchRequest
> request
,
973 const CacheStorageCache::ErrorCallback
& callback
,
974 scoped_ptr
<disk_cache::Entry
*> entry_ptr
,
975 const scoped_refptr
<storage::QuotaManagerProxy
>& quota_manager_proxy
,
978 callback
.Run(CacheStorageCache::ERROR_TYPE_NOT_FOUND
);
983 disk_cache::ScopedEntryPtr
entry(*entry_ptr
);
985 if (quota_manager_proxy
.get()) {
986 quota_manager_proxy
->NotifyStorageModified(
987 storage::QuotaClient::kServiceWorkerCache
, origin
,
988 storage::kStorageTypeTemporary
,
989 -1 * (entry
->GetDataSize(INDEX_HEADERS
) +
990 entry
->GetDataSize(INDEX_RESPONSE_BODY
)));
994 callback
.Run(CacheStorageCache::ERROR_TYPE_OK
);
997 void CacheStorageCache::KeysImpl(const RequestsCallback
& callback
) {
998 DCHECK(backend_state_
!= BACKEND_UNINITIALIZED
);
999 if (backend_state_
!= BACKEND_OPEN
) {
1000 callback
.Run(ERROR_TYPE_STORAGE
, scoped_ptr
<Requests
>());
1004 // 1. Iterate through all of the entries, open them, and add them to a vector.
1005 // 2. For each open entry:
1006 // 2.1. Read the headers into a protobuf.
1007 // 2.2. Copy the protobuf into a ServiceWorkerFetchRequest (a "key").
1008 // 2.3. Push the response into a vector of requests to be returned.
1009 // 3. Return the vector of requests (keys).
1011 // The entries have to be loaded into a vector first because enumeration loops
1012 // forever if you read data from a cache entry while enumerating.
1014 scoped_ptr
<KeysContext
> keys_context(new KeysContext(callback
));
1016 keys_context
->backend_iterator
= backend_
->CreateIterator();
1017 disk_cache::Backend::Iterator
& iterator
= *keys_context
->backend_iterator
;
1018 disk_cache::Entry
** enumerated_entry
= &keys_context
->enumerated_entry
;
1020 net::CompletionCallback open_entry_callback
= base::Bind(
1021 &CacheStorageCache::KeysDidOpenNextEntry
, weak_ptr_factory_
.GetWeakPtr(),
1022 base::Passed(keys_context
.Pass()));
1024 int rv
= iterator
.OpenNextEntry(enumerated_entry
, open_entry_callback
);
1026 if (rv
!= net::ERR_IO_PENDING
)
1027 open_entry_callback
.Run(rv
);
1030 void CacheStorageCache::KeysDidOpenNextEntry(
1031 scoped_ptr
<KeysContext
> keys_context
,
1033 if (rv
== net::ERR_FAILED
) {
1034 DCHECK(!keys_context
->enumerated_entry
);
1035 // Enumeration is complete, extract the requests from the entries.
1036 Entries::iterator iter
= keys_context
->entries
.begin();
1037 KeysProcessNextEntry(keys_context
.Pass(), iter
);
1042 keys_context
->original_callback
.Run(ERROR_TYPE_STORAGE
,
1043 scoped_ptr
<Requests
>());
1047 if (backend_state_
!= BACKEND_OPEN
) {
1048 keys_context
->original_callback
.Run(ERROR_TYPE_NOT_FOUND
,
1049 scoped_ptr
<Requests
>());
1054 keys_context
->entries
.push_back(keys_context
->enumerated_entry
);
1055 keys_context
->enumerated_entry
= NULL
;
1057 // Enumerate the next entry.
1058 disk_cache::Backend::Iterator
& iterator
= *keys_context
->backend_iterator
;
1059 disk_cache::Entry
** enumerated_entry
= &keys_context
->enumerated_entry
;
1060 net::CompletionCallback open_entry_callback
= base::Bind(
1061 &CacheStorageCache::KeysDidOpenNextEntry
, weak_ptr_factory_
.GetWeakPtr(),
1062 base::Passed(keys_context
.Pass()));
1064 rv
= iterator
.OpenNextEntry(enumerated_entry
, open_entry_callback
);
1066 if (rv
!= net::ERR_IO_PENDING
)
1067 open_entry_callback
.Run(rv
);
1070 void CacheStorageCache::KeysProcessNextEntry(
1071 scoped_ptr
<KeysContext
> keys_context
,
1072 const Entries::iterator
& iter
) {
1073 if (iter
== keys_context
->entries
.end()) {
1074 // All done. Return all of the keys.
1075 keys_context
->original_callback
.Run(ERROR_TYPE_OK
,
1076 keys_context
->out_keys
.Pass());
1080 ReadMetadata(*iter
, base::Bind(&CacheStorageCache::KeysDidReadMetadata
,
1081 weak_ptr_factory_
.GetWeakPtr(),
1082 base::Passed(keys_context
.Pass()), iter
));
1085 void CacheStorageCache::KeysDidReadMetadata(
1086 scoped_ptr
<KeysContext
> keys_context
,
1087 const Entries::iterator
& iter
,
1088 scoped_ptr
<CacheMetadata
> metadata
) {
1089 disk_cache::Entry
* entry
= *iter
;
1092 keys_context
->out_keys
->push_back(ServiceWorkerFetchRequest(
1093 GURL(entry
->GetKey()), metadata
->request().method(),
1094 ServiceWorkerHeaderMap(), Referrer(), false));
1096 ServiceWorkerHeaderMap
& req_headers
=
1097 keys_context
->out_keys
->back().headers
;
1099 for (int i
= 0; i
< metadata
->request().headers_size(); ++i
) {
1100 const CacheHeaderMap header
= metadata
->request().headers(i
);
1101 DCHECK(header
.name().find('\0') == std::string::npos
);
1102 DCHECK(header
.value().find('\0') == std::string::npos
);
1103 req_headers
.insert(std::make_pair(header
.name(), header
.value()));
1109 KeysProcessNextEntry(keys_context
.Pass(), iter
+ 1);
1112 void CacheStorageCache::CloseImpl(const base::Closure
& callback
) {
1113 DCHECK(backend_state_
!= BACKEND_CLOSED
);
1115 backend_state_
= BACKEND_CLOSED
;
1120 void CacheStorageCache::CreateBackend(const ErrorCallback
& callback
) {
1123 // Use APP_CACHE as opposed to DISK_CACHE to prevent cache eviction.
1124 net::CacheType cache_type
= memory_only_
? net::MEMORY_CACHE
: net::APP_CACHE
;
1126 scoped_ptr
<ScopedBackendPtr
> backend_ptr(new ScopedBackendPtr());
1128 // Temporary pointer so that backend_ptr can be Pass()'d in Bind below.
1129 ScopedBackendPtr
* backend
= backend_ptr
.get();
1131 net::CompletionCallback create_cache_callback
=
1132 base::Bind(&CacheStorageCache::CreateBackendDidCreate
,
1133 weak_ptr_factory_
.GetWeakPtr(), callback
,
1134 base::Passed(backend_ptr
.Pass()));
1136 // TODO(jkarlin): Use the cache MessageLoopProxy that ServiceWorkerCacheCore
1137 // has for disk caches.
1138 int rv
= disk_cache::CreateCacheBackend(
1139 cache_type
, net::CACHE_BACKEND_SIMPLE
, path_
, kMaxCacheBytes
,
1141 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::CACHE
).get(),
1142 NULL
, backend
, create_cache_callback
);
1143 if (rv
!= net::ERR_IO_PENDING
)
1144 create_cache_callback
.Run(rv
);
1147 void CacheStorageCache::CreateBackendDidCreate(
1148 const CacheStorageCache::ErrorCallback
& callback
,
1149 scoped_ptr
<ScopedBackendPtr
> backend_ptr
,
1151 if (rv
!= net::OK
) {
1152 callback
.Run(CacheStorageCache::ERROR_TYPE_STORAGE
);
1156 backend_
= backend_ptr
->Pass();
1157 callback
.Run(CacheStorageCache::ERROR_TYPE_OK
);
1160 void CacheStorageCache::InitBackend() {
1161 DCHECK(backend_state_
== BACKEND_UNINITIALIZED
);
1166 DCHECK(!scheduler_
->ScheduledOperations());
1167 initializing_
= true;
1169 scheduler_
->ScheduleOperation(base::Bind(
1170 &CacheStorageCache::CreateBackend
, weak_ptr_factory_
.GetWeakPtr(),
1171 base::Bind(&CacheStorageCache::InitDone
,
1172 weak_ptr_factory_
.GetWeakPtr())));
1175 void CacheStorageCache::InitDone(ErrorType error
) {
1176 initializing_
= false;
1177 backend_state_
= (error
== ERROR_TYPE_OK
&& backend_
&&
1178 backend_state_
== BACKEND_UNINITIALIZED
)
1182 UMA_HISTOGRAM_ENUMERATION("ServiceWorkerCache.InitBackendResult", error
,
1183 ErrorType::ERROR_TYPE_LAST
+ 1);
1185 scheduler_
->CompleteOperationAndRunNext();
1188 void CacheStorageCache::PendingClosure(const base::Closure
& callback
) {
1189 base::WeakPtr
<CacheStorageCache
> cache
= weak_ptr_factory_
.GetWeakPtr();
1193 scheduler_
->CompleteOperationAndRunNext();
1196 void CacheStorageCache::PendingErrorCallback(const ErrorCallback
& callback
,
1198 base::WeakPtr
<CacheStorageCache
> cache
= weak_ptr_factory_
.GetWeakPtr();
1200 callback
.Run(error
);
1202 scheduler_
->CompleteOperationAndRunNext();
1205 void CacheStorageCache::PendingResponseCallback(
1206 const ResponseCallback
& callback
,
1208 scoped_ptr
<ServiceWorkerResponse
> response
,
1209 scoped_ptr
<storage::BlobDataHandle
> blob_data_handle
) {
1210 base::WeakPtr
<CacheStorageCache
> cache
= weak_ptr_factory_
.GetWeakPtr();
1212 callback
.Run(error
, response
.Pass(), blob_data_handle
.Pass());
1214 scheduler_
->CompleteOperationAndRunNext();
1217 void CacheStorageCache::PendingRequestsCallback(
1218 const RequestsCallback
& callback
,
1220 scoped_ptr
<Requests
> requests
) {
1221 base::WeakPtr
<CacheStorageCache
> cache
= weak_ptr_factory_
.GetWeakPtr();
1223 callback
.Run(error
, requests
.Pass());
1225 scheduler_
->CompleteOperationAndRunNext();
1228 } // namespace content