Implement SSLKEYLOGFILE for OpenSSL.
[chromium-blink-merge.git] / content / browser / service_worker / service_worker_cache.cc
blob2fb5216d8f19a7c1ccd4ea561e894b3648adc446
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 "content/browser/service_worker/service_worker_cache.pb.h"
13 #include "content/public/browser/browser_thread.h"
14 #include "net/base/io_buffer.h"
15 #include "net/base/net_errors.h"
16 #include "net/disk_cache/disk_cache.h"
17 #include "net/url_request/url_request_context.h"
18 #include "storage/browser/blob/blob_data_handle.h"
19 #include "storage/browser/blob/blob_storage_context.h"
20 #include "storage/browser/blob/blob_url_request_job_factory.h"
22 namespace content {
24 namespace {
26 typedef scoped_ptr<disk_cache::Backend> ScopedBackendPtr;
27 typedef base::Callback<void(bool)> BoolCallback;
28 typedef base::Callback<void(disk_cache::ScopedEntryPtr, bool)>
29 EntryBoolCallback;
30 typedef base::Callback<void(scoped_ptr<ServiceWorkerRequestResponseHeaders>)>
31 HeadersCallback;
33 enum EntryIndex { INDEX_HEADERS = 0, INDEX_RESPONSE_BODY };
35 // The maximum size of an individual cache. Ultimately cache size is controlled
36 // per-origin.
37 const int kMaxCacheBytes = 512 * 1024 * 1024;
39 // Buffer size for cache and blob reading/writing.
40 const int kBufferSize = 1024 * 512;
42 struct ResponseReadContext {
43 ResponseReadContext(scoped_refptr<net::IOBufferWithSize> buff,
44 scoped_refptr<storage::BlobData> blob)
45 : buffer(buff), blob_data(blob), total_bytes_read(0) {}
47 scoped_refptr<net::IOBufferWithSize> buffer;
48 scoped_refptr<storage::BlobData> blob_data;
49 int total_bytes_read;
51 DISALLOW_COPY_AND_ASSIGN(ResponseReadContext);
54 // Streams data from a blob and writes it to a given disk_cache::Entry.
55 class BlobReader : public net::URLRequest::Delegate {
56 public:
57 typedef base::Callback<void(disk_cache::ScopedEntryPtr, bool)>
58 EntryBoolCallback;
60 BlobReader(disk_cache::ScopedEntryPtr entry)
61 : cache_entry_offset_(0),
62 buffer_(new net::IOBufferWithSize(kBufferSize)),
63 weak_ptr_factory_(this) {
64 DCHECK(entry);
65 entry_ = entry.Pass();
68 void StreamBlobToCache(net::URLRequestContext* request_context,
69 scoped_ptr<storage::BlobDataHandle> blob_data_handle,
70 const EntryBoolCallback& callback) {
71 callback_ = callback;
72 blob_request_ = storage::BlobProtocolHandler::CreateBlobRequest(
73 blob_data_handle.Pass(), request_context, this);
74 blob_request_->Start();
77 // net::URLRequest::Delegate overrides for reading blobs.
78 virtual void OnReceivedRedirect(net::URLRequest* request,
79 const net::RedirectInfo& redirect_info,
80 bool* defer_redirect) OVERRIDE {
81 NOTREACHED();
83 virtual void OnAuthRequired(net::URLRequest* request,
84 net::AuthChallengeInfo* auth_info) OVERRIDE {
85 NOTREACHED();
87 virtual void OnCertificateRequested(
88 net::URLRequest* request,
89 net::SSLCertRequestInfo* cert_request_info) OVERRIDE {
90 NOTREACHED();
92 virtual void OnSSLCertificateError(net::URLRequest* request,
93 const net::SSLInfo& ssl_info,
94 bool fatal) OVERRIDE {
95 NOTREACHED();
97 virtual void OnBeforeNetworkStart(net::URLRequest* request,
98 bool* defer) OVERRIDE {
99 NOTREACHED();
102 virtual void OnResponseStarted(net::URLRequest* request) OVERRIDE {
103 if (!request->status().is_success()) {
104 callback_.Run(entry_.Pass(), false);
105 return;
107 ReadFromBlob();
110 virtual void ReadFromBlob() {
111 int bytes_read = 0;
112 bool done =
113 blob_request_->Read(buffer_.get(), buffer_->size(), &bytes_read);
114 if (done)
115 OnReadCompleted(blob_request_.get(), bytes_read);
118 virtual void OnReadCompleted(net::URLRequest* request,
119 int bytes_read) OVERRIDE {
120 if (!request->status().is_success()) {
121 callback_.Run(entry_.Pass(), false);
122 return;
125 if (bytes_read == 0) {
126 callback_.Run(entry_.Pass(), true);
127 return;
130 net::CompletionCallback cache_write_callback =
131 base::Bind(&BlobReader::DidWriteDataToEntry,
132 weak_ptr_factory_.GetWeakPtr(),
133 bytes_read);
135 int rv = entry_->WriteData(INDEX_RESPONSE_BODY,
136 cache_entry_offset_,
137 buffer_.get(),
138 bytes_read,
139 cache_write_callback,
140 true /* truncate */);
141 if (rv != net::ERR_IO_PENDING)
142 cache_write_callback.Run(rv);
145 void DidWriteDataToEntry(int expected_bytes, int rv) {
146 if (rv != expected_bytes) {
147 callback_.Run(entry_.Pass(), false);
148 return;
151 cache_entry_offset_ += rv;
152 ReadFromBlob();
155 private:
156 int cache_entry_offset_;
157 disk_cache::ScopedEntryPtr entry_;
158 scoped_ptr<net::URLRequest> blob_request_;
159 EntryBoolCallback callback_;
160 scoped_refptr<net::IOBufferWithSize> buffer_;
161 base::WeakPtrFactory<BlobReader> weak_ptr_factory_;
164 // Put callbacks
165 void PutDidCreateEntry(scoped_ptr<ServiceWorkerFetchRequest> request,
166 scoped_ptr<ServiceWorkerResponse> response,
167 const ServiceWorkerCache::ErrorCallback& callback,
168 scoped_ptr<disk_cache::Entry*> entryptr,
169 scoped_ptr<storage::BlobDataHandle> blob_data_handle,
170 net::URLRequestContext* request_context,
171 int rv);
172 void PutDidWriteHeaders(scoped_ptr<ServiceWorkerResponse> response,
173 const ServiceWorkerCache::ErrorCallback& callback,
174 disk_cache::ScopedEntryPtr entry,
175 scoped_ptr<storage::BlobDataHandle> blob_data_handle,
176 net::URLRequestContext* request_context,
177 int expected_bytes,
178 int rv);
179 void PutDidWriteBlobToCache(const ServiceWorkerCache::ErrorCallback& callback,
180 scoped_ptr<BlobReader> blob_reader,
181 disk_cache::ScopedEntryPtr entry,
182 bool success);
184 // Match callbacks
185 void MatchDidOpenEntry(scoped_ptr<ServiceWorkerFetchRequest> request,
186 const ServiceWorkerCache::ResponseCallback& callback,
187 base::WeakPtr<storage::BlobStorageContext> blob_storage,
188 scoped_ptr<disk_cache::Entry*> entryptr,
189 int rv);
190 void MatchDidReadHeaderData(
191 scoped_ptr<ServiceWorkerFetchRequest> request,
192 const ServiceWorkerCache::ResponseCallback& callback,
193 base::WeakPtr<storage::BlobStorageContext> blob_storage,
194 disk_cache::ScopedEntryPtr entry,
195 scoped_ptr<ServiceWorkerRequestResponseHeaders> headers);
196 void MatchDidReadResponseBodyData(
197 scoped_ptr<ServiceWorkerFetchRequest> request,
198 const ServiceWorkerCache::ResponseCallback& callback,
199 base::WeakPtr<storage::BlobStorageContext> blob_storage,
200 disk_cache::ScopedEntryPtr entry,
201 scoped_ptr<ServiceWorkerResponse> response,
202 scoped_ptr<ResponseReadContext> response_context,
203 int rv);
204 void MatchDoneWithBody(scoped_ptr<ServiceWorkerFetchRequest> request,
205 const ServiceWorkerCache::ResponseCallback& callback,
206 base::WeakPtr<storage::BlobStorageContext> blob_storage,
207 scoped_ptr<ServiceWorkerResponse> response,
208 scoped_ptr<ResponseReadContext> response_context);
210 // Delete callbacks
211 void DeleteDidOpenEntry(scoped_ptr<ServiceWorkerFetchRequest> request,
212 const ServiceWorkerCache::ErrorCallback& callback,
213 scoped_ptr<disk_cache::Entry*> entryptr,
214 int rv);
216 // Copy headers out of a cache entry and into a protobuf. The callback is
217 // guaranteed to be run.
218 void ReadHeaders(disk_cache::Entry* entry, const HeadersCallback& callback);
219 void ReadHeadersDidReadHeaderData(
220 disk_cache::Entry* entry,
221 const HeadersCallback& callback,
222 const scoped_refptr<net::IOBufferWithSize>& buffer,
223 int rv);
225 // CreateBackend callbacks
226 void CreateBackendDidCreate(const ServiceWorkerCache::ErrorCallback& callback,
227 scoped_ptr<ScopedBackendPtr> backend_ptr,
228 base::WeakPtr<ServiceWorkerCache> cache,
229 int rv);
231 void PutDidCreateEntry(scoped_ptr<ServiceWorkerFetchRequest> request,
232 scoped_ptr<ServiceWorkerResponse> response,
233 const ServiceWorkerCache::ErrorCallback& callback,
234 scoped_ptr<disk_cache::Entry*> entryptr,
235 scoped_ptr<storage::BlobDataHandle> blob_data_handle,
236 net::URLRequestContext* request_context,
237 int rv) {
238 if (rv != net::OK) {
239 callback.Run(ServiceWorkerCache::ErrorTypeExists);
240 return;
243 DCHECK(entryptr);
244 disk_cache::ScopedEntryPtr entry(*entryptr);
246 ServiceWorkerRequestResponseHeaders headers;
247 headers.set_method(request->method);
249 headers.set_status_code(response->status_code);
250 headers.set_status_text(response->status_text);
251 for (std::map<std::string, std::string>::const_iterator it =
252 request->headers.begin();
253 it != request->headers.end();
254 ++it) {
255 ServiceWorkerRequestResponseHeaders::HeaderMap* header_map =
256 headers.add_request_headers();
257 header_map->set_name(it->first);
258 header_map->set_value(it->second);
261 for (std::map<std::string, std::string>::const_iterator it =
262 response->headers.begin();
263 it != response->headers.end();
264 ++it) {
265 ServiceWorkerRequestResponseHeaders::HeaderMap* header_map =
266 headers.add_response_headers();
267 header_map->set_name(it->first);
268 header_map->set_value(it->second);
271 scoped_ptr<std::string> serialized(new std::string());
272 if (!headers.SerializeToString(serialized.get())) {
273 callback.Run(ServiceWorkerCache::ErrorTypeStorage);
274 return;
277 scoped_refptr<net::StringIOBuffer> buffer(
278 new net::StringIOBuffer(serialized.Pass()));
280 // Get a temporary copy of the entry pointer before passing it in base::Bind.
281 disk_cache::Entry* tmp_entry_ptr = entry.get();
283 net::CompletionCallback write_headers_callback =
284 base::Bind(PutDidWriteHeaders,
285 base::Passed(response.Pass()),
286 callback,
287 base::Passed(entry.Pass()),
288 base::Passed(blob_data_handle.Pass()),
289 request_context,
290 buffer->size());
292 rv = tmp_entry_ptr->WriteData(INDEX_HEADERS,
293 0 /* offset */,
294 buffer.get(),
295 buffer->size(),
296 write_headers_callback,
297 true /* truncate */);
299 if (rv != net::ERR_IO_PENDING)
300 write_headers_callback.Run(rv);
303 void PutDidWriteHeaders(scoped_ptr<ServiceWorkerResponse> response,
304 const ServiceWorkerCache::ErrorCallback& callback,
305 disk_cache::ScopedEntryPtr entry,
306 scoped_ptr<storage::BlobDataHandle> blob_data_handle,
307 net::URLRequestContext* request_context,
308 int expected_bytes,
309 int rv) {
310 if (rv != expected_bytes) {
311 entry->Doom();
312 callback.Run(ServiceWorkerCache::ErrorTypeStorage);
313 return;
316 // The metadata is written, now for the response content. The data is streamed
317 // from the blob into the cache entry.
319 if (response->blob_uuid.empty()) {
320 callback.Run(ServiceWorkerCache::ErrorTypeOK);
321 return;
324 DCHECK(blob_data_handle);
326 scoped_ptr<BlobReader> reader(new BlobReader(entry.Pass()));
327 BlobReader* reader_ptr = reader.get();
329 reader_ptr->StreamBlobToCache(
330 request_context,
331 blob_data_handle.Pass(),
332 base::Bind(
333 PutDidWriteBlobToCache, callback, base::Passed(reader.Pass())));
336 void PutDidWriteBlobToCache(const ServiceWorkerCache::ErrorCallback& callback,
337 scoped_ptr<BlobReader> blob_reader,
338 disk_cache::ScopedEntryPtr entry,
339 bool success) {
340 if (!success) {
341 entry->Doom();
342 callback.Run(ServiceWorkerCache::ErrorTypeStorage);
343 return;
346 callback.Run(ServiceWorkerCache::ErrorTypeOK);
349 void MatchDidOpenEntry(scoped_ptr<ServiceWorkerFetchRequest> request,
350 const ServiceWorkerCache::ResponseCallback& callback,
351 base::WeakPtr<storage::BlobStorageContext> blob_storage,
352 scoped_ptr<disk_cache::Entry*> entryptr,
353 int rv) {
354 if (rv != net::OK) {
355 callback.Run(ServiceWorkerCache::ErrorTypeNotFound,
356 scoped_ptr<ServiceWorkerResponse>(),
357 scoped_ptr<storage::BlobDataHandle>());
358 return;
361 DCHECK(entryptr);
362 disk_cache::ScopedEntryPtr entry(*entryptr);
364 // Copy the entry pointer before passing it in base::Bind.
365 disk_cache::Entry* tmp_entry_ptr = entry.get();
367 HeadersCallback headers_callback = base::Bind(MatchDidReadHeaderData,
368 base::Passed(request.Pass()),
369 callback,
370 blob_storage,
371 base::Passed(entry.Pass()));
373 ReadHeaders(tmp_entry_ptr, headers_callback);
376 void MatchDidReadHeaderData(
377 scoped_ptr<ServiceWorkerFetchRequest> request,
378 const ServiceWorkerCache::ResponseCallback& callback,
379 base::WeakPtr<storage::BlobStorageContext> blob_storage,
380 disk_cache::ScopedEntryPtr entry,
381 scoped_ptr<ServiceWorkerRequestResponseHeaders> headers) {
382 if (!headers) {
383 callback.Run(ServiceWorkerCache::ErrorTypeStorage,
384 scoped_ptr<ServiceWorkerResponse>(),
385 scoped_ptr<storage::BlobDataHandle>());
386 return;
389 scoped_ptr<ServiceWorkerResponse> response(
390 new ServiceWorkerResponse(request->url,
391 headers->status_code(),
392 headers->status_text(),
393 std::map<std::string, std::string>(),
394 ""));
396 for (int i = 0; i < headers->response_headers_size(); ++i) {
397 const ServiceWorkerRequestResponseHeaders::HeaderMap header =
398 headers->response_headers(i);
399 response->headers.insert(std::make_pair(header.name(), header.value()));
402 // TODO(jkarlin): Insert vary validation here.
404 if (entry->GetDataSize(INDEX_RESPONSE_BODY) == 0) {
405 callback.Run(ServiceWorkerCache::ErrorTypeOK,
406 response.Pass(),
407 scoped_ptr<storage::BlobDataHandle>());
408 return;
411 // Stream the response body into a blob.
412 if (!blob_storage) {
413 callback.Run(ServiceWorkerCache::ErrorTypeStorage,
414 scoped_ptr<ServiceWorkerResponse>(),
415 scoped_ptr<storage::BlobDataHandle>());
416 return;
419 response->blob_uuid = base::GenerateGUID();
421 scoped_refptr<storage::BlobData> blob_data =
422 new storage::BlobData(response->blob_uuid);
423 scoped_refptr<net::IOBufferWithSize> response_body_buffer(
424 new net::IOBufferWithSize(kBufferSize));
426 scoped_ptr<ResponseReadContext> read_context(
427 new ResponseReadContext(response_body_buffer, blob_data));
429 // Copy the entry pointer before passing it in base::Bind.
430 disk_cache::Entry* tmp_entry_ptr = entry.get();
432 net::CompletionCallback read_callback =
433 base::Bind(MatchDidReadResponseBodyData,
434 base::Passed(request.Pass()),
435 callback,
436 blob_storage,
437 base::Passed(entry.Pass()),
438 base::Passed(response.Pass()),
439 base::Passed(read_context.Pass()));
441 int read_rv = tmp_entry_ptr->ReadData(INDEX_RESPONSE_BODY,
443 response_body_buffer.get(),
444 response_body_buffer->size(),
445 read_callback);
447 if (read_rv != net::ERR_IO_PENDING)
448 read_callback.Run(read_rv);
451 void MatchDidReadResponseBodyData(
452 scoped_ptr<ServiceWorkerFetchRequest> request,
453 const ServiceWorkerCache::ResponseCallback& callback,
454 base::WeakPtr<storage::BlobStorageContext> blob_storage,
455 disk_cache::ScopedEntryPtr entry,
456 scoped_ptr<ServiceWorkerResponse> response,
457 scoped_ptr<ResponseReadContext> response_context,
458 int rv) {
459 if (rv < 0) {
460 callback.Run(ServiceWorkerCache::ErrorTypeStorage,
461 scoped_ptr<ServiceWorkerResponse>(),
462 scoped_ptr<storage::BlobDataHandle>());
463 return;
466 if (rv == 0) {
467 MatchDoneWithBody(request.Pass(),
468 callback,
469 blob_storage,
470 response.Pass(),
471 response_context.Pass());
472 return;
475 // TODO(jkarlin): This copying of the the entire cache response into memory is
476 // awful. Create a new interface around SimpleCache that provides access the
477 // data directly from the file. See bug http://crbug.com/403493.
478 response_context->blob_data->AppendData(response_context->buffer->data(), rv);
479 response_context->total_bytes_read += rv;
480 int total_bytes_read = response_context->total_bytes_read;
482 // Grab some pointers before passing them in bind.
483 net::IOBufferWithSize* buffer = response_context->buffer.get();
484 disk_cache::Entry* tmp_entry_ptr = entry.get();
486 net::CompletionCallback read_callback =
487 base::Bind(MatchDidReadResponseBodyData,
488 base::Passed(request.Pass()),
489 callback,
490 blob_storage,
491 base::Passed(entry.Pass()),
492 base::Passed(response.Pass()),
493 base::Passed(response_context.Pass()));
495 int read_rv = tmp_entry_ptr->ReadData(INDEX_RESPONSE_BODY,
496 total_bytes_read,
497 buffer,
498 buffer->size(),
499 read_callback);
501 if (read_rv != net::ERR_IO_PENDING)
502 read_callback.Run(read_rv);
505 void MatchDoneWithBody(scoped_ptr<ServiceWorkerFetchRequest> request,
506 const ServiceWorkerCache::ResponseCallback& callback,
507 base::WeakPtr<storage::BlobStorageContext> blob_storage,
508 scoped_ptr<ServiceWorkerResponse> response,
509 scoped_ptr<ResponseReadContext> response_context) {
510 if (!blob_storage) {
511 callback.Run(ServiceWorkerCache::ErrorTypeStorage,
512 scoped_ptr<ServiceWorkerResponse>(),
513 scoped_ptr<storage::BlobDataHandle>());
514 return;
517 scoped_ptr<storage::BlobDataHandle> blob_data_handle(
518 blob_storage->AddFinishedBlob(response_context->blob_data.get()));
520 callback.Run(ServiceWorkerCache::ErrorTypeOK,
521 response.Pass(),
522 blob_data_handle.Pass());
525 void DeleteDidOpenEntry(scoped_ptr<ServiceWorkerFetchRequest> request,
526 const ServiceWorkerCache::ErrorCallback& callback,
527 scoped_ptr<disk_cache::Entry*> entryptr,
528 int rv) {
529 if (rv != net::OK) {
530 callback.Run(ServiceWorkerCache::ErrorTypeNotFound);
531 return;
534 DCHECK(entryptr);
535 disk_cache::ScopedEntryPtr entry(*entryptr);
537 entry->Doom();
538 callback.Run(ServiceWorkerCache::ErrorTypeOK);
541 void ReadHeaders(disk_cache::Entry* entry, const HeadersCallback& callback) {
542 DCHECK(entry);
544 scoped_refptr<net::IOBufferWithSize> buffer(
545 new net::IOBufferWithSize(entry->GetDataSize(INDEX_HEADERS)));
547 net::CompletionCallback read_header_callback =
548 base::Bind(ReadHeadersDidReadHeaderData, entry, callback, buffer);
550 int read_rv = entry->ReadData(
551 INDEX_HEADERS, 0, buffer.get(), buffer->size(), read_header_callback);
553 if (read_rv != net::ERR_IO_PENDING)
554 read_header_callback.Run(read_rv);
557 void ReadHeadersDidReadHeaderData(
558 disk_cache::Entry* entry,
559 const HeadersCallback& callback,
560 const scoped_refptr<net::IOBufferWithSize>& buffer,
561 int rv) {
562 if (rv != buffer->size()) {
563 callback.Run(scoped_ptr<ServiceWorkerRequestResponseHeaders>());
564 return;
567 scoped_ptr<ServiceWorkerRequestResponseHeaders> headers(
568 new ServiceWorkerRequestResponseHeaders());
570 if (!headers->ParseFromArray(buffer->data(), buffer->size())) {
571 callback.Run(scoped_ptr<ServiceWorkerRequestResponseHeaders>());
572 return;
575 callback.Run(headers.Pass());
578 void CreateBackendDidCreate(const ServiceWorkerCache::ErrorCallback& callback,
579 scoped_ptr<ScopedBackendPtr> backend_ptr,
580 base::WeakPtr<ServiceWorkerCache> cache,
581 int rv) {
582 if (rv != net::OK || !cache) {
583 callback.Run(ServiceWorkerCache::ErrorTypeStorage);
584 return;
587 cache->set_backend(backend_ptr->Pass());
588 callback.Run(ServiceWorkerCache::ErrorTypeOK);
591 } // namespace
593 // The state needed to pass between ServiceWorkerCache::Keys callbacks.
594 struct ServiceWorkerCache::KeysContext {
595 KeysContext(const ServiceWorkerCache::RequestsCallback& callback,
596 base::WeakPtr<ServiceWorkerCache> cache)
597 : original_callback(callback),
598 cache(cache),
599 out_keys(new ServiceWorkerCache::Requests()),
600 backend_iterator(NULL),
601 enumerated_entry(NULL) {}
603 ~KeysContext() {
604 for (size_t i = 0, max = entries.size(); i < max; ++i)
605 entries[i]->Close();
606 if (enumerated_entry)
607 enumerated_entry->Close();
608 if (cache && backend_iterator && cache->backend_)
609 cache->backend_->EndEnumeration(&backend_iterator);
612 // The callback passed to the Keys() function.
613 ServiceWorkerCache::RequestsCallback original_callback;
615 // The ServiceWorkerCache that Keys was called on.
616 base::WeakPtr<ServiceWorkerCache> cache;
618 // The vector of open entries in the backend.
619 Entries entries;
621 // The output of the Keys function.
622 scoped_ptr<ServiceWorkerCache::Requests> out_keys;
624 // Used for enumerating cache entries.
625 void* backend_iterator;
626 disk_cache::Entry* enumerated_entry;
629 // static
630 scoped_refptr<ServiceWorkerCache> ServiceWorkerCache::CreateMemoryCache(
631 net::URLRequestContext* request_context,
632 base::WeakPtr<storage::BlobStorageContext> blob_context) {
633 return make_scoped_refptr(
634 new ServiceWorkerCache(base::FilePath(), request_context, blob_context));
637 // static
638 scoped_refptr<ServiceWorkerCache> ServiceWorkerCache::CreatePersistentCache(
639 const base::FilePath& path,
640 net::URLRequestContext* request_context,
641 base::WeakPtr<storage::BlobStorageContext> blob_context) {
642 return make_scoped_refptr(
643 new ServiceWorkerCache(path, request_context, blob_context));
646 ServiceWorkerCache::~ServiceWorkerCache() {
649 base::WeakPtr<ServiceWorkerCache> ServiceWorkerCache::AsWeakPtr() {
650 return weak_ptr_factory_.GetWeakPtr();
653 void ServiceWorkerCache::Put(scoped_ptr<ServiceWorkerFetchRequest> request,
654 scoped_ptr<ServiceWorkerResponse> response,
655 const ErrorCallback& callback) {
656 scoped_ptr<storage::BlobDataHandle> blob_data_handle;
658 if (!response->blob_uuid.empty()) {
659 if (!blob_storage_context_) {
660 callback.Run(ErrorTypeStorage);
661 return;
663 blob_data_handle =
664 blob_storage_context_->GetBlobDataFromUUID(response->blob_uuid);
665 if (!blob_data_handle) {
666 callback.Run(ErrorTypeStorage);
667 return;
671 base::Closure continuation = base::Bind(&ServiceWorkerCache::PutImpl,
672 weak_ptr_factory_.GetWeakPtr(),
673 base::Passed(request.Pass()),
674 base::Passed(response.Pass()),
675 base::Passed(blob_data_handle.Pass()),
676 callback);
678 if (!initialized_) {
679 Init(continuation);
680 return;
683 continuation.Run();
686 void ServiceWorkerCache::Match(scoped_ptr<ServiceWorkerFetchRequest> request,
687 const ResponseCallback& callback) {
688 if (!initialized_) {
689 Init(base::Bind(&ServiceWorkerCache::Match,
690 weak_ptr_factory_.GetWeakPtr(),
691 base::Passed(request.Pass()),
692 callback));
693 return;
695 if (!backend_) {
696 callback.Run(ErrorTypeStorage,
697 scoped_ptr<ServiceWorkerResponse>(),
698 scoped_ptr<storage::BlobDataHandle>());
699 return;
702 scoped_ptr<disk_cache::Entry*> entry(new disk_cache::Entry*);
704 disk_cache::Entry** entry_ptr = entry.get();
706 ServiceWorkerFetchRequest* request_ptr = request.get();
708 net::CompletionCallback open_entry_callback =
709 base::Bind(MatchDidOpenEntry,
710 base::Passed(request.Pass()),
711 callback,
712 blob_storage_context_,
713 base::Passed(entry.Pass()));
715 int rv = backend_->OpenEntry(
716 request_ptr->url.spec(), entry_ptr, open_entry_callback);
717 if (rv != net::ERR_IO_PENDING)
718 open_entry_callback.Run(rv);
721 void ServiceWorkerCache::Delete(scoped_ptr<ServiceWorkerFetchRequest> request,
722 const ErrorCallback& callback) {
723 if (!initialized_) {
724 Init(base::Bind(&ServiceWorkerCache::Delete,
725 weak_ptr_factory_.GetWeakPtr(),
726 base::Passed(request.Pass()),
727 callback));
728 return;
730 if (!backend_) {
731 callback.Run(ErrorTypeStorage);
732 return;
735 scoped_ptr<disk_cache::Entry*> entry(new disk_cache::Entry*);
737 disk_cache::Entry** entry_ptr = entry.get();
739 ServiceWorkerFetchRequest* request_ptr = request.get();
741 net::CompletionCallback open_entry_callback =
742 base::Bind(DeleteDidOpenEntry,
743 base::Passed(request.Pass()),
744 callback,
745 base::Passed(entry.Pass()));
747 int rv = backend_->OpenEntry(
748 request_ptr->url.spec(), entry_ptr, open_entry_callback);
749 if (rv != net::ERR_IO_PENDING)
750 open_entry_callback.Run(rv);
753 void ServiceWorkerCache::Keys(const RequestsCallback& callback) {
754 if (!initialized_) {
755 Init(base::Bind(
756 &ServiceWorkerCache::Keys, weak_ptr_factory_.GetWeakPtr(), callback));
757 return;
759 if (!backend_) {
760 callback.Run(ErrorTypeStorage, scoped_ptr<Requests>());
761 return;
764 // 1. Iterate through all of the entries, open them, and add them to a vector.
765 // 2. For each open entry:
766 // 2.1. Read the headers into a protobuf.
767 // 2.2. Copy the protobuf into a ServiceWorkerFetchRequest (a "key").
768 // 2.3. Push the response into a vector of requests to be returned.
769 // 3. Return the vector of requests (keys).
771 // The entries have to be loaded into a vector first because enumeration loops
772 // forever if you read data from a cache entry while enumerating.
774 scoped_ptr<KeysContext> keys_context(
775 new KeysContext(callback, weak_ptr_factory_.GetWeakPtr()));
777 void** backend_iterator = &keys_context->backend_iterator;
778 disk_cache::Entry** enumerated_entry = &keys_context->enumerated_entry;
780 net::CompletionCallback open_entry_callback =
781 base::Bind(KeysDidOpenNextEntry, base::Passed(keys_context.Pass()));
783 int rv = backend_->OpenNextEntry(
784 backend_iterator, enumerated_entry, open_entry_callback);
786 if (rv != net::ERR_IO_PENDING)
787 open_entry_callback.Run(rv);
790 void ServiceWorkerCache::Close() {
791 backend_.reset();
794 ServiceWorkerCache::ServiceWorkerCache(
795 const base::FilePath& path,
796 net::URLRequestContext* request_context,
797 base::WeakPtr<storage::BlobStorageContext> blob_context)
798 : path_(path),
799 request_context_(request_context),
800 blob_storage_context_(blob_context),
801 initialized_(false),
802 weak_ptr_factory_(this) {
805 void ServiceWorkerCache::PutImpl(
806 scoped_ptr<ServiceWorkerFetchRequest> request,
807 scoped_ptr<ServiceWorkerResponse> response,
808 scoped_ptr<storage::BlobDataHandle> blob_data_handle,
809 const ErrorCallback& callback) {
810 if (!backend_) {
811 callback.Run(ErrorTypeStorage);
812 return;
815 scoped_ptr<disk_cache::Entry*> entry(new disk_cache::Entry*);
817 disk_cache::Entry** entry_ptr = entry.get();
819 ServiceWorkerFetchRequest* request_ptr = request.get();
821 net::CompletionCallback create_entry_callback =
822 base::Bind(PutDidCreateEntry,
823 base::Passed(request.Pass()),
824 base::Passed(response.Pass()),
825 callback,
826 base::Passed(entry.Pass()),
827 base::Passed(blob_data_handle.Pass()),
828 request_context_);
830 int rv = backend_->CreateEntry(
831 request_ptr->url.spec(), entry_ptr, create_entry_callback);
833 if (rv != net::ERR_IO_PENDING)
834 create_entry_callback.Run(rv);
837 // static
838 void ServiceWorkerCache::KeysDidOpenNextEntry(
839 scoped_ptr<KeysContext> keys_context,
840 int rv) {
841 if (rv == net::ERR_FAILED) {
842 DCHECK(!keys_context->enumerated_entry);
843 // Enumeration is complete, extract the requests from the entries.
844 Entries::iterator iter = keys_context->entries.begin();
845 KeysProcessNextEntry(keys_context.Pass(), iter);
846 return;
849 base::WeakPtr<ServiceWorkerCache> cache = keys_context->cache;
850 if (rv < 0 || !cache) {
851 keys_context->original_callback.Run(ErrorTypeStorage,
852 scoped_ptr<Requests>());
853 return;
856 if (!cache->backend_) {
857 keys_context->original_callback.Run(ErrorTypeNotFound,
858 scoped_ptr<Requests>());
859 return;
862 // Store the entry.
863 keys_context->entries.push_back(keys_context->enumerated_entry);
864 keys_context->enumerated_entry = NULL;
866 // Enumerate the next entry.
867 void** backend_iterator = &keys_context->backend_iterator;
868 disk_cache::Entry** enumerated_entry = &keys_context->enumerated_entry;
870 net::CompletionCallback open_entry_callback =
871 base::Bind(KeysDidOpenNextEntry, base::Passed(keys_context.Pass()));
873 rv = cache->backend_->OpenNextEntry(
874 backend_iterator, enumerated_entry, open_entry_callback);
876 if (rv != net::ERR_IO_PENDING)
877 open_entry_callback.Run(rv);
880 // static
881 void ServiceWorkerCache::KeysProcessNextEntry(
882 scoped_ptr<KeysContext> keys_context,
883 const Entries::iterator& iter) {
884 if (iter == keys_context->entries.end()) {
885 // All done. Return all of the keys.
886 keys_context->original_callback.Run(ErrorTypeOK,
887 keys_context->out_keys.Pass());
888 return;
891 ReadHeaders(
892 *iter,
893 base::Bind(KeysDidReadHeaders, base::Passed(keys_context.Pass()), iter));
896 // static
897 void ServiceWorkerCache::KeysDidReadHeaders(
898 scoped_ptr<KeysContext> keys_context,
899 const Entries::iterator& iter,
900 scoped_ptr<ServiceWorkerRequestResponseHeaders> headers) {
901 disk_cache::Entry* entry = *iter;
903 if (headers) {
904 keys_context->out_keys->push_back(
905 ServiceWorkerFetchRequest(GURL(entry->GetKey()),
906 headers->method(),
907 std::map<std::string, std::string>(),
908 GURL(),
909 false));
911 std::map<std::string, std::string>& req_headers =
912 keys_context->out_keys->back().headers;
914 for (int i = 0; i < headers->request_headers_size(); ++i) {
915 const ServiceWorkerRequestResponseHeaders::HeaderMap header =
916 headers->request_headers(i);
917 req_headers.insert(std::make_pair(header.name(), header.value()));
919 } else {
920 entry->Doom();
923 KeysProcessNextEntry(keys_context.Pass(), iter + 1);
926 void ServiceWorkerCache::CreateBackend(const ErrorCallback& callback) {
927 DCHECK(!backend_);
929 // Use APP_CACHE as opposed to DISK_CACHE to prevent cache eviction.
930 net::CacheType cache_type =
931 path_.empty() ? net::MEMORY_CACHE : net::APP_CACHE;
933 scoped_ptr<ScopedBackendPtr> backend_ptr(new ScopedBackendPtr());
935 // Temporary pointer so that backend_ptr can be Pass()'d in Bind below.
936 ScopedBackendPtr* backend = backend_ptr.get();
938 net::CompletionCallback create_cache_callback =
939 base::Bind(CreateBackendDidCreate,
940 callback,
941 base::Passed(backend_ptr.Pass()),
942 weak_ptr_factory_.GetWeakPtr());
944 // TODO(jkarlin): Use the cache MessageLoopProxy that ServiceWorkerCacheCore
945 // has for disk caches.
946 int rv = disk_cache::CreateCacheBackend(
947 cache_type,
948 net::CACHE_BACKEND_SIMPLE,
949 path_,
950 kMaxCacheBytes,
951 false, /* force */
952 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::CACHE).get(),
953 NULL,
954 backend,
955 create_cache_callback);
956 if (rv != net::ERR_IO_PENDING)
957 create_cache_callback.Run(rv);
960 void ServiceWorkerCache::Init(const base::Closure& callback) {
961 init_callbacks_.push_back(callback);
963 // If this isn't the first call to Init then return as the initialization
964 // has already started.
965 if (init_callbacks_.size() > 1u)
966 return;
968 CreateBackend(base::Bind(&ServiceWorkerCache::InitDone,
969 weak_ptr_factory_.GetWeakPtr()));
972 void ServiceWorkerCache::InitDone(ErrorType error) {
973 initialized_ = true;
974 for (std::vector<base::Closure>::iterator it = init_callbacks_.begin();
975 it != init_callbacks_.end();
976 ++it) {
977 it->Run();
979 init_callbacks_.clear();
982 } // namespace content