Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / content / browser / service_worker / service_worker_cache.cc
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.
4
5 #include "content/browser/service_worker/service_worker_cache.h"
6
7 #include <string>
8
9 #include "base/files/file_path.h"
10 #include "base/guid.h"
11 #include "base/message_loop/message_loop_proxy.h"
12 #include "base/strings/string_util.h"
13 #include "content/browser/service_worker/service_worker_cache.pb.h"
14 #include "content/public/browser/browser_thread.h"
15 #include "net/base/io_buffer.h"
16 #include "net/base/net_errors.h"
17 #include "net/disk_cache/disk_cache.h"
18 #include "net/url_request/url_request_context.h"
19 #include "storage/browser/blob/blob_data_handle.h"
20 #include "storage/browser/blob/blob_storage_context.h"
21 #include "storage/browser/blob/blob_url_request_job_factory.h"
22
23 namespace content {
24
25 namespace {
26
27 typedef scoped_ptr<disk_cache::Backend> ScopedBackendPtr;
28 typedef base::Callback<void(bool)> BoolCallback;
29 typedef base::Callback<void(disk_cache::ScopedEntryPtr, bool)>
30     EntryBoolCallback;
31 typedef base::Callback<void(scoped_ptr<ServiceWorkerRequestResponseHeaders>)>
32     HeadersCallback;
33
34 enum EntryIndex { INDEX_HEADERS = 0, INDEX_RESPONSE_BODY };
35
36 // The maximum size of an individual cache. Ultimately cache size is controlled
37 // per-origin.
38 const int kMaxCacheBytes = 512 * 1024 * 1024;
39
40 // Buffer size for cache and blob reading/writing.
41 const int kBufferSize = 1024 * 512;
42
43 struct ResponseReadContext {
44   ResponseReadContext(scoped_refptr<net::IOBufferWithSize> buff,
45                       scoped_refptr<storage::BlobData> blob)
46       : buffer(buff), blob_data(blob), total_bytes_read(0) {}
47
48   scoped_refptr<net::IOBufferWithSize> buffer;
49   scoped_refptr<storage::BlobData> blob_data;
50   int total_bytes_read;
51
52   DISALLOW_COPY_AND_ASSIGN(ResponseReadContext);
53 };
54
55 // Streams data from a blob and writes it to a given disk_cache::Entry.
56 class BlobReader : public net::URLRequest::Delegate {
57  public:
58   typedef base::Callback<void(disk_cache::ScopedEntryPtr, bool)>
59       EntryBoolCallback;
60
61   BlobReader(disk_cache::ScopedEntryPtr entry)
62       : cache_entry_offset_(0),
63         buffer_(new net::IOBufferWithSize(kBufferSize)),
64         weak_ptr_factory_(this) {
65     DCHECK(entry);
66     entry_ = entry.Pass();
67   }
68
69   void StreamBlobToCache(net::URLRequestContext* request_context,
70                          scoped_ptr<storage::BlobDataHandle> blob_data_handle,
71                          const EntryBoolCallback& callback) {
72     callback_ = callback;
73     blob_request_ = storage::BlobProtocolHandler::CreateBlobRequest(
74         blob_data_handle.Pass(), request_context, this);
75     blob_request_->Start();
76   }
77
78   // net::URLRequest::Delegate overrides for reading blobs.
79   virtual void OnReceivedRedirect(net::URLRequest* request,
80                                   const net::RedirectInfo& redirect_info,
81                                   bool* defer_redirect) OVERRIDE {
82     NOTREACHED();
83   }
84   virtual void OnAuthRequired(net::URLRequest* request,
85                               net::AuthChallengeInfo* auth_info) OVERRIDE {
86     NOTREACHED();
87   }
88   virtual void OnCertificateRequested(
89       net::URLRequest* request,
90       net::SSLCertRequestInfo* cert_request_info) OVERRIDE {
91     NOTREACHED();
92   }
93   virtual void OnSSLCertificateError(net::URLRequest* request,
94                                      const net::SSLInfo& ssl_info,
95                                      bool fatal) OVERRIDE {
96     NOTREACHED();
97   }
98   virtual void OnBeforeNetworkStart(net::URLRequest* request,
99                                     bool* defer) OVERRIDE {
100     NOTREACHED();
101   }
102
103   virtual void OnResponseStarted(net::URLRequest* request) OVERRIDE {
104     if (!request->status().is_success()) {
105       callback_.Run(entry_.Pass(), false);
106       return;
107     }
108     ReadFromBlob();
109   }
110
111   virtual void ReadFromBlob() {
112     int bytes_read = 0;
113     bool done =
114         blob_request_->Read(buffer_.get(), buffer_->size(), &bytes_read);
115     if (done)
116       OnReadCompleted(blob_request_.get(), bytes_read);
117   }
118
119   virtual void OnReadCompleted(net::URLRequest* request,
120                                int bytes_read) OVERRIDE {
121     if (!request->status().is_success()) {
122       callback_.Run(entry_.Pass(), false);
123       return;
124     }
125
126     if (bytes_read == 0) {
127       callback_.Run(entry_.Pass(), true);
128       return;
129     }
130
131     net::CompletionCallback cache_write_callback =
132         base::Bind(&BlobReader::DidWriteDataToEntry,
133                    weak_ptr_factory_.GetWeakPtr(),
134                    bytes_read);
135
136     int rv = entry_->WriteData(INDEX_RESPONSE_BODY,
137                                cache_entry_offset_,
138                                buffer_.get(),
139                                bytes_read,
140                                cache_write_callback,
141                                true /* truncate */);
142     if (rv != net::ERR_IO_PENDING)
143       cache_write_callback.Run(rv);
144   }
145
146   void DidWriteDataToEntry(int expected_bytes, int rv) {
147     if (rv != expected_bytes) {
148       callback_.Run(entry_.Pass(), false);
149       return;
150     }
151
152     cache_entry_offset_ += rv;
153     ReadFromBlob();
154   }
155
156  private:
157   int cache_entry_offset_;
158   disk_cache::ScopedEntryPtr entry_;
159   scoped_ptr<net::URLRequest> blob_request_;
160   EntryBoolCallback callback_;
161   scoped_refptr<net::IOBufferWithSize> buffer_;
162   base::WeakPtrFactory<BlobReader> weak_ptr_factory_;
163 };
164
165 // Put callbacks
166 void PutDidCreateEntry(scoped_ptr<ServiceWorkerFetchRequest> request,
167                        scoped_ptr<ServiceWorkerResponse> response,
168                        const ServiceWorkerCache::ErrorCallback& callback,
169                        scoped_ptr<disk_cache::Entry*> entryptr,
170                        scoped_ptr<storage::BlobDataHandle> blob_data_handle,
171                        net::URLRequestContext* request_context,
172                        int rv);
173 void PutDidWriteHeaders(scoped_ptr<ServiceWorkerResponse> response,
174                         const ServiceWorkerCache::ErrorCallback& callback,
175                         disk_cache::ScopedEntryPtr entry,
176                         scoped_ptr<storage::BlobDataHandle> blob_data_handle,
177                         net::URLRequestContext* request_context,
178                         int expected_bytes,
179                         int rv);
180 void PutDidWriteBlobToCache(const ServiceWorkerCache::ErrorCallback& callback,
181                             scoped_ptr<BlobReader> blob_reader,
182                             disk_cache::ScopedEntryPtr entry,
183                             bool success);
184
185 // Match callbacks
186 void MatchDidOpenEntry(scoped_ptr<ServiceWorkerFetchRequest> request,
187                        const ServiceWorkerCache::ResponseCallback& callback,
188                        base::WeakPtr<storage::BlobStorageContext> blob_storage,
189                        scoped_ptr<disk_cache::Entry*> entryptr,
190                        int rv);
191 void MatchDidReadHeaderData(
192     scoped_ptr<ServiceWorkerFetchRequest> request,
193     const ServiceWorkerCache::ResponseCallback& callback,
194     base::WeakPtr<storage::BlobStorageContext> blob_storage,
195     disk_cache::ScopedEntryPtr entry,
196     scoped_ptr<ServiceWorkerRequestResponseHeaders> headers);
197 void MatchDidReadResponseBodyData(
198     scoped_ptr<ServiceWorkerFetchRequest> request,
199     const ServiceWorkerCache::ResponseCallback& callback,
200     base::WeakPtr<storage::BlobStorageContext> blob_storage,
201     disk_cache::ScopedEntryPtr entry,
202     scoped_ptr<ServiceWorkerResponse> response,
203     scoped_ptr<ResponseReadContext> response_context,
204     int rv);
205 void MatchDoneWithBody(scoped_ptr<ServiceWorkerFetchRequest> request,
206                        const ServiceWorkerCache::ResponseCallback& callback,
207                        base::WeakPtr<storage::BlobStorageContext> blob_storage,
208                        scoped_ptr<ServiceWorkerResponse> response,
209                        scoped_ptr<ResponseReadContext> response_context);
210
211 // Delete callbacks
212 void DeleteDidOpenEntry(scoped_ptr<ServiceWorkerFetchRequest> request,
213                         const ServiceWorkerCache::ErrorCallback& callback,
214                         scoped_ptr<disk_cache::Entry*> entryptr,
215                         int rv);
216
217 // Copy headers out of a cache entry and into a protobuf. The callback is
218 // guaranteed to be run.
219 void ReadHeaders(disk_cache::Entry* entry, const HeadersCallback& callback);
220 void ReadHeadersDidReadHeaderData(
221     disk_cache::Entry* entry,
222     const HeadersCallback& callback,
223     const scoped_refptr<net::IOBufferWithSize>& buffer,
224     int rv);
225
226 // CreateBackend callbacks
227 void CreateBackendDidCreate(const ServiceWorkerCache::ErrorCallback& callback,
228                             scoped_ptr<ScopedBackendPtr> backend_ptr,
229                             base::WeakPtr<ServiceWorkerCache> cache,
230                             int rv);
231
232 void PutDidCreateEntry(scoped_ptr<ServiceWorkerFetchRequest> request,
233                        scoped_ptr<ServiceWorkerResponse> response,
234                        const ServiceWorkerCache::ErrorCallback& callback,
235                        scoped_ptr<disk_cache::Entry*> entryptr,
236                        scoped_ptr<storage::BlobDataHandle> blob_data_handle,
237                        net::URLRequestContext* request_context,
238                        int rv) {
239   if (rv != net::OK) {
240     callback.Run(ServiceWorkerCache::ErrorTypeExists);
241     return;
242   }
243
244   DCHECK(entryptr);
245   disk_cache::ScopedEntryPtr entry(*entryptr);
246
247   ServiceWorkerRequestResponseHeaders headers;
248   headers.set_method(request->method);
249
250   headers.set_status_code(response->status_code);
251   headers.set_status_text(response->status_text);
252   for (ServiceWorkerHeaderMap::const_iterator it = request->headers.begin();
253        it != request->headers.end();
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);
259   }
260
261   for (ServiceWorkerHeaderMap::const_iterator it = response->headers.begin();
262        it != response->headers.end();
263        ++it) {
264     ServiceWorkerRequestResponseHeaders::HeaderMap* header_map =
265         headers.add_response_headers();
266     header_map->set_name(it->first);
267     header_map->set_value(it->second);
268   }
269
270   scoped_ptr<std::string> serialized(new std::string());
271   if (!headers.SerializeToString(serialized.get())) {
272     callback.Run(ServiceWorkerCache::ErrorTypeStorage);
273     return;
274   }
275
276   scoped_refptr<net::StringIOBuffer> buffer(
277       new net::StringIOBuffer(serialized.Pass()));
278
279   // Get a temporary copy of the entry pointer before passing it in base::Bind.
280   disk_cache::Entry* tmp_entry_ptr = entry.get();
281
282   net::CompletionCallback write_headers_callback =
283       base::Bind(PutDidWriteHeaders,
284                  base::Passed(response.Pass()),
285                  callback,
286                  base::Passed(entry.Pass()),
287                  base::Passed(blob_data_handle.Pass()),
288                  request_context,
289                  buffer->size());
290
291   rv = tmp_entry_ptr->WriteData(INDEX_HEADERS,
292                                 0 /* offset */,
293                                 buffer.get(),
294                                 buffer->size(),
295                                 write_headers_callback,
296                                 true /* truncate */);
297
298   if (rv != net::ERR_IO_PENDING)
299     write_headers_callback.Run(rv);
300 }
301
302 void PutDidWriteHeaders(scoped_ptr<ServiceWorkerResponse> response,
303                         const ServiceWorkerCache::ErrorCallback& callback,
304                         disk_cache::ScopedEntryPtr entry,
305                         scoped_ptr<storage::BlobDataHandle> blob_data_handle,
306                         net::URLRequestContext* request_context,
307                         int expected_bytes,
308                         int rv) {
309   if (rv != expected_bytes) {
310     entry->Doom();
311     callback.Run(ServiceWorkerCache::ErrorTypeStorage);
312     return;
313   }
314
315   // The metadata is written, now for the response content. The data is streamed
316   // from the blob into the cache entry.
317
318   if (response->blob_uuid.empty()) {
319     callback.Run(ServiceWorkerCache::ErrorTypeOK);
320     return;
321   }
322
323   DCHECK(blob_data_handle);
324
325   scoped_ptr<BlobReader> reader(new BlobReader(entry.Pass()));
326   BlobReader* reader_ptr = reader.get();
327
328   reader_ptr->StreamBlobToCache(
329       request_context,
330       blob_data_handle.Pass(),
331       base::Bind(
332           PutDidWriteBlobToCache, callback, base::Passed(reader.Pass())));
333 }
334
335 void PutDidWriteBlobToCache(const ServiceWorkerCache::ErrorCallback& callback,
336                             scoped_ptr<BlobReader> blob_reader,
337                             disk_cache::ScopedEntryPtr entry,
338                             bool success) {
339   if (!success) {
340     entry->Doom();
341     callback.Run(ServiceWorkerCache::ErrorTypeStorage);
342     return;
343   }
344
345   callback.Run(ServiceWorkerCache::ErrorTypeOK);
346 }
347
348 void MatchDidOpenEntry(scoped_ptr<ServiceWorkerFetchRequest> request,
349                        const ServiceWorkerCache::ResponseCallback& callback,
350                        base::WeakPtr<storage::BlobStorageContext> blob_storage,
351                        scoped_ptr<disk_cache::Entry*> entryptr,
352                        int rv) {
353   if (rv != net::OK) {
354     callback.Run(ServiceWorkerCache::ErrorTypeNotFound,
355                  scoped_ptr<ServiceWorkerResponse>(),
356                  scoped_ptr<storage::BlobDataHandle>());
357     return;
358   }
359
360   DCHECK(entryptr);
361   disk_cache::ScopedEntryPtr entry(*entryptr);
362
363   // Copy the entry pointer before passing it in base::Bind.
364   disk_cache::Entry* tmp_entry_ptr = entry.get();
365
366   HeadersCallback headers_callback = base::Bind(MatchDidReadHeaderData,
367                                                 base::Passed(request.Pass()),
368                                                 callback,
369                                                 blob_storage,
370                                                 base::Passed(entry.Pass()));
371
372   ReadHeaders(tmp_entry_ptr, headers_callback);
373 }
374
375 bool VaryMatches(const ServiceWorkerHeaderMap& request,
376                  const ServiceWorkerHeaderMap& cached_request,
377                  const ServiceWorkerHeaderMap& response) {
378   ServiceWorkerHeaderMap::const_iterator vary_iter = response.find("vary");
379   if (vary_iter == response.end())
380     return true;
381
382   std::vector<std::string> vary_keys;
383   Tokenize(vary_iter->second, ",", &vary_keys);
384   for (std::vector<std::string>::const_iterator it = vary_keys.begin();
385        it != vary_keys.end();
386        ++it) {
387     std::string trimmed;
388     base::TrimWhitespaceASCII(*it, base::TRIM_ALL, &trimmed);
389     if (trimmed == "*")
390       return false;
391
392     ServiceWorkerHeaderMap::const_iterator request_iter = request.find(trimmed);
393     ServiceWorkerHeaderMap::const_iterator cached_request_iter =
394         cached_request.find(trimmed);
395
396     // If the header exists in one but not the other, no match.
397     if ((request_iter == request.end()) !=
398         (cached_request_iter == cached_request.end()))
399       return false;
400
401     // If the header exists in one, it exists in both. Verify that the values
402     // are equal.
403     if (request_iter != request.end() &&
404         request_iter->second != cached_request_iter->second)
405       return false;
406   }
407
408   return true;
409 }
410
411 void MatchDidReadHeaderData(
412     scoped_ptr<ServiceWorkerFetchRequest> request,
413     const ServiceWorkerCache::ResponseCallback& callback,
414     base::WeakPtr<storage::BlobStorageContext> blob_storage,
415     disk_cache::ScopedEntryPtr entry,
416     scoped_ptr<ServiceWorkerRequestResponseHeaders> headers) {
417   if (!headers) {
418     callback.Run(ServiceWorkerCache::ErrorTypeStorage,
419                  scoped_ptr<ServiceWorkerResponse>(),
420                  scoped_ptr<storage::BlobDataHandle>());
421     return;
422   }
423
424   scoped_ptr<ServiceWorkerResponse> response(
425       new ServiceWorkerResponse(request->url,
426                                 headers->status_code(),
427                                 headers->status_text(),
428                                 ServiceWorkerHeaderMap(),
429                                 ""));
430
431   for (int i = 0; i < headers->response_headers_size(); ++i) {
432     const ServiceWorkerRequestResponseHeaders::HeaderMap header =
433         headers->response_headers(i);
434     response->headers.insert(std::make_pair(header.name(), header.value()));
435   }
436
437   ServiceWorkerHeaderMap cached_request_headers;
438   for (int i = 0; i < headers->request_headers_size(); ++i) {
439     const ServiceWorkerRequestResponseHeaders::HeaderMap header =
440         headers->request_headers(i);
441     cached_request_headers[header.name()] = header.value();
442   }
443
444   if (!VaryMatches(
445           request->headers, cached_request_headers, response->headers)) {
446     callback.Run(ServiceWorkerCache::ErrorTypeNotFound,
447                  scoped_ptr<ServiceWorkerResponse>(),
448                  scoped_ptr<storage::BlobDataHandle>());
449     return;
450   }
451
452   if (entry->GetDataSize(INDEX_RESPONSE_BODY) == 0) {
453     callback.Run(ServiceWorkerCache::ErrorTypeOK,
454                  response.Pass(),
455                  scoped_ptr<storage::BlobDataHandle>());
456     return;
457   }
458
459   // Stream the response body into a blob.
460   if (!blob_storage) {
461     callback.Run(ServiceWorkerCache::ErrorTypeStorage,
462                  scoped_ptr<ServiceWorkerResponse>(),
463                  scoped_ptr<storage::BlobDataHandle>());
464     return;
465   }
466
467   response->blob_uuid = base::GenerateGUID();
468
469   scoped_refptr<storage::BlobData> blob_data =
470       new storage::BlobData(response->blob_uuid);
471   scoped_refptr<net::IOBufferWithSize> response_body_buffer(
472       new net::IOBufferWithSize(kBufferSize));
473
474   scoped_ptr<ResponseReadContext> read_context(
475       new ResponseReadContext(response_body_buffer, blob_data));
476
477   // Copy the entry pointer before passing it in base::Bind.
478   disk_cache::Entry* tmp_entry_ptr = entry.get();
479
480   net::CompletionCallback read_callback =
481       base::Bind(MatchDidReadResponseBodyData,
482                  base::Passed(request.Pass()),
483                  callback,
484                  blob_storage,
485                  base::Passed(entry.Pass()),
486                  base::Passed(response.Pass()),
487                  base::Passed(read_context.Pass()));
488
489   int read_rv = tmp_entry_ptr->ReadData(INDEX_RESPONSE_BODY,
490                                         0,
491                                         response_body_buffer.get(),
492                                         response_body_buffer->size(),
493                                         read_callback);
494
495   if (read_rv != net::ERR_IO_PENDING)
496     read_callback.Run(read_rv);
497 }
498
499 void MatchDidReadResponseBodyData(
500     scoped_ptr<ServiceWorkerFetchRequest> request,
501     const ServiceWorkerCache::ResponseCallback& callback,
502     base::WeakPtr<storage::BlobStorageContext> blob_storage,
503     disk_cache::ScopedEntryPtr entry,
504     scoped_ptr<ServiceWorkerResponse> response,
505     scoped_ptr<ResponseReadContext> response_context,
506     int rv) {
507   if (rv < 0) {
508     callback.Run(ServiceWorkerCache::ErrorTypeStorage,
509                  scoped_ptr<ServiceWorkerResponse>(),
510                  scoped_ptr<storage::BlobDataHandle>());
511     return;
512   }
513
514   if (rv == 0) {
515     MatchDoneWithBody(request.Pass(),
516                       callback,
517                       blob_storage,
518                       response.Pass(),
519                       response_context.Pass());
520     return;
521   }
522
523   // TODO(jkarlin): This copying of the the entire cache response into memory is
524   // awful. Create a new interface around SimpleCache that provides access the
525   // data directly from the file. See bug http://crbug.com/403493.
526   response_context->blob_data->AppendData(response_context->buffer->data(), rv);
527   response_context->total_bytes_read += rv;
528   int total_bytes_read = response_context->total_bytes_read;
529
530   // Grab some pointers before passing them in bind.
531   net::IOBufferWithSize* buffer = response_context->buffer.get();
532   disk_cache::Entry* tmp_entry_ptr = entry.get();
533
534   net::CompletionCallback read_callback =
535       base::Bind(MatchDidReadResponseBodyData,
536                  base::Passed(request.Pass()),
537                  callback,
538                  blob_storage,
539                  base::Passed(entry.Pass()),
540                  base::Passed(response.Pass()),
541                  base::Passed(response_context.Pass()));
542
543   int read_rv = tmp_entry_ptr->ReadData(INDEX_RESPONSE_BODY,
544                                         total_bytes_read,
545                                         buffer,
546                                         buffer->size(),
547                                         read_callback);
548
549   if (read_rv != net::ERR_IO_PENDING)
550     read_callback.Run(read_rv);
551 }
552
553 void MatchDoneWithBody(scoped_ptr<ServiceWorkerFetchRequest> request,
554                        const ServiceWorkerCache::ResponseCallback& callback,
555                        base::WeakPtr<storage::BlobStorageContext> blob_storage,
556                        scoped_ptr<ServiceWorkerResponse> response,
557                        scoped_ptr<ResponseReadContext> response_context) {
558   if (!blob_storage) {
559     callback.Run(ServiceWorkerCache::ErrorTypeStorage,
560                  scoped_ptr<ServiceWorkerResponse>(),
561                  scoped_ptr<storage::BlobDataHandle>());
562     return;
563   }
564
565   scoped_ptr<storage::BlobDataHandle> blob_data_handle(
566       blob_storage->AddFinishedBlob(response_context->blob_data.get()));
567
568   callback.Run(ServiceWorkerCache::ErrorTypeOK,
569                response.Pass(),
570                blob_data_handle.Pass());
571 }
572
573 void DeleteDidOpenEntry(scoped_ptr<ServiceWorkerFetchRequest> request,
574                         const ServiceWorkerCache::ErrorCallback& callback,
575                         scoped_ptr<disk_cache::Entry*> entryptr,
576                         int rv) {
577   if (rv != net::OK) {
578     callback.Run(ServiceWorkerCache::ErrorTypeNotFound);
579     return;
580   }
581
582   DCHECK(entryptr);
583   disk_cache::ScopedEntryPtr entry(*entryptr);
584
585   entry->Doom();
586   callback.Run(ServiceWorkerCache::ErrorTypeOK);
587 }
588
589 void ReadHeaders(disk_cache::Entry* entry, const HeadersCallback& callback) {
590   DCHECK(entry);
591
592   scoped_refptr<net::IOBufferWithSize> buffer(
593       new net::IOBufferWithSize(entry->GetDataSize(INDEX_HEADERS)));
594
595   net::CompletionCallback read_header_callback =
596       base::Bind(ReadHeadersDidReadHeaderData, entry, callback, buffer);
597
598   int read_rv = entry->ReadData(
599       INDEX_HEADERS, 0, buffer.get(), buffer->size(), read_header_callback);
600
601   if (read_rv != net::ERR_IO_PENDING)
602     read_header_callback.Run(read_rv);
603 }
604
605 void ReadHeadersDidReadHeaderData(
606     disk_cache::Entry* entry,
607     const HeadersCallback& callback,
608     const scoped_refptr<net::IOBufferWithSize>& buffer,
609     int rv) {
610   if (rv != buffer->size()) {
611     callback.Run(scoped_ptr<ServiceWorkerRequestResponseHeaders>());
612     return;
613   }
614
615   scoped_ptr<ServiceWorkerRequestResponseHeaders> headers(
616       new ServiceWorkerRequestResponseHeaders());
617
618   if (!headers->ParseFromArray(buffer->data(), buffer->size())) {
619     callback.Run(scoped_ptr<ServiceWorkerRequestResponseHeaders>());
620     return;
621   }
622
623   callback.Run(headers.Pass());
624 }
625
626 void CreateBackendDidCreate(const ServiceWorkerCache::ErrorCallback& callback,
627                             scoped_ptr<ScopedBackendPtr> backend_ptr,
628                             base::WeakPtr<ServiceWorkerCache> cache,
629                             int rv) {
630   if (rv != net::OK || !cache) {
631     callback.Run(ServiceWorkerCache::ErrorTypeStorage);
632     return;
633   }
634
635   cache->set_backend(backend_ptr->Pass());
636   callback.Run(ServiceWorkerCache::ErrorTypeOK);
637 }
638
639 }  // namespace
640
641 // The state needed to pass between ServiceWorkerCache::Keys callbacks.
642 struct ServiceWorkerCache::KeysContext {
643   KeysContext(const ServiceWorkerCache::RequestsCallback& callback,
644               base::WeakPtr<ServiceWorkerCache> cache)
645       : original_callback(callback),
646         cache(cache),
647         out_keys(new ServiceWorkerCache::Requests()),
648         enumerated_entry(NULL) {}
649
650   ~KeysContext() {
651     for (size_t i = 0, max = entries.size(); i < max; ++i)
652       entries[i]->Close();
653     if (enumerated_entry)
654       enumerated_entry->Close();
655   }
656
657   // The callback passed to the Keys() function.
658   ServiceWorkerCache::RequestsCallback original_callback;
659
660   // The ServiceWorkerCache that Keys was called on.
661   base::WeakPtr<ServiceWorkerCache> cache;
662
663   // The vector of open entries in the backend.
664   Entries entries;
665
666   // The output of the Keys function.
667   scoped_ptr<ServiceWorkerCache::Requests> out_keys;
668
669   // Used for enumerating cache entries.
670   scoped_ptr<disk_cache::Backend::Iterator> backend_iterator;
671   disk_cache::Entry* enumerated_entry;
672 };
673
674 // static
675 scoped_refptr<ServiceWorkerCache> ServiceWorkerCache::CreateMemoryCache(
676     net::URLRequestContext* request_context,
677     base::WeakPtr<storage::BlobStorageContext> blob_context) {
678   return make_scoped_refptr(
679       new ServiceWorkerCache(base::FilePath(), request_context, blob_context));
680 }
681
682 // static
683 scoped_refptr<ServiceWorkerCache> ServiceWorkerCache::CreatePersistentCache(
684     const base::FilePath& path,
685     net::URLRequestContext* request_context,
686     base::WeakPtr<storage::BlobStorageContext> blob_context) {
687   return make_scoped_refptr(
688       new ServiceWorkerCache(path, request_context, blob_context));
689 }
690
691 ServiceWorkerCache::~ServiceWorkerCache() {
692 }
693
694 base::WeakPtr<ServiceWorkerCache> ServiceWorkerCache::AsWeakPtr() {
695   return weak_ptr_factory_.GetWeakPtr();
696 }
697
698 void ServiceWorkerCache::Put(scoped_ptr<ServiceWorkerFetchRequest> request,
699                              scoped_ptr<ServiceWorkerResponse> response,
700                              const ErrorCallback& callback) {
701   scoped_ptr<storage::BlobDataHandle> blob_data_handle;
702
703   if (!response->blob_uuid.empty()) {
704     if (!blob_storage_context_) {
705       callback.Run(ErrorTypeStorage);
706       return;
707     }
708     blob_data_handle =
709         blob_storage_context_->GetBlobDataFromUUID(response->blob_uuid);
710     if (!blob_data_handle) {
711       callback.Run(ErrorTypeStorage);
712       return;
713     }
714   }
715
716   base::Closure continuation = base::Bind(&ServiceWorkerCache::PutImpl,
717                                           weak_ptr_factory_.GetWeakPtr(),
718                                           base::Passed(request.Pass()),
719                                           base::Passed(response.Pass()),
720                                           base::Passed(blob_data_handle.Pass()),
721                                           callback);
722
723   if (!initialized_) {
724     Init(continuation);
725     return;
726   }
727
728   continuation.Run();
729 }
730
731 void ServiceWorkerCache::Match(scoped_ptr<ServiceWorkerFetchRequest> request,
732                                const ResponseCallback& callback) {
733   if (!initialized_) {
734     Init(base::Bind(&ServiceWorkerCache::Match,
735                     weak_ptr_factory_.GetWeakPtr(),
736                     base::Passed(request.Pass()),
737                     callback));
738     return;
739   }
740   if (!backend_) {
741     callback.Run(ErrorTypeStorage,
742                  scoped_ptr<ServiceWorkerResponse>(),
743                  scoped_ptr<storage::BlobDataHandle>());
744     return;
745   }
746
747   scoped_ptr<disk_cache::Entry*> entry(new disk_cache::Entry*);
748
749   disk_cache::Entry** entry_ptr = entry.get();
750
751   ServiceWorkerFetchRequest* request_ptr = request.get();
752
753   net::CompletionCallback open_entry_callback =
754       base::Bind(MatchDidOpenEntry,
755                  base::Passed(request.Pass()),
756                  callback,
757                  blob_storage_context_,
758                  base::Passed(entry.Pass()));
759
760   int rv = backend_->OpenEntry(
761       request_ptr->url.spec(), entry_ptr, open_entry_callback);
762   if (rv != net::ERR_IO_PENDING)
763     open_entry_callback.Run(rv);
764 }
765
766 void ServiceWorkerCache::Delete(scoped_ptr<ServiceWorkerFetchRequest> request,
767                                 const ErrorCallback& callback) {
768   if (!initialized_) {
769     Init(base::Bind(&ServiceWorkerCache::Delete,
770                     weak_ptr_factory_.GetWeakPtr(),
771                     base::Passed(request.Pass()),
772                     callback));
773     return;
774   }
775   if (!backend_) {
776     callback.Run(ErrorTypeStorage);
777     return;
778   }
779
780   scoped_ptr<disk_cache::Entry*> entry(new disk_cache::Entry*);
781
782   disk_cache::Entry** entry_ptr = entry.get();
783
784   ServiceWorkerFetchRequest* request_ptr = request.get();
785
786   net::CompletionCallback open_entry_callback =
787       base::Bind(DeleteDidOpenEntry,
788                  base::Passed(request.Pass()),
789                  callback,
790                  base::Passed(entry.Pass()));
791
792   int rv = backend_->OpenEntry(
793       request_ptr->url.spec(), entry_ptr, open_entry_callback);
794   if (rv != net::ERR_IO_PENDING)
795     open_entry_callback.Run(rv);
796 }
797
798 void ServiceWorkerCache::Keys(const RequestsCallback& callback) {
799   if (!initialized_) {
800     Init(base::Bind(
801         &ServiceWorkerCache::Keys, weak_ptr_factory_.GetWeakPtr(), callback));
802     return;
803   }
804   if (!backend_) {
805     callback.Run(ErrorTypeStorage, scoped_ptr<Requests>());
806     return;
807   }
808
809   // 1. Iterate through all of the entries, open them, and add them to a vector.
810   // 2. For each open entry:
811   //  2.1. Read the headers into a protobuf.
812   //  2.2. Copy the protobuf into a ServiceWorkerFetchRequest (a "key").
813   //  2.3. Push the response into a vector of requests to be returned.
814   // 3. Return the vector of requests (keys).
815
816   // The entries have to be loaded into a vector first because enumeration loops
817   // forever if you read data from a cache entry while enumerating.
818
819   scoped_ptr<KeysContext> keys_context(
820       new KeysContext(callback, weak_ptr_factory_.GetWeakPtr()));
821
822   keys_context->backend_iterator = backend_->CreateIterator();
823   disk_cache::Backend::Iterator& iterator = *keys_context->backend_iterator;
824   disk_cache::Entry** enumerated_entry = &keys_context->enumerated_entry;
825
826   net::CompletionCallback open_entry_callback =
827       base::Bind(KeysDidOpenNextEntry, base::Passed(keys_context.Pass()));
828
829   int rv = iterator.OpenNextEntry(enumerated_entry, open_entry_callback);
830
831   if (rv != net::ERR_IO_PENDING)
832     open_entry_callback.Run(rv);
833 }
834
835 void ServiceWorkerCache::Close() {
836   backend_.reset();
837 }
838
839 ServiceWorkerCache::ServiceWorkerCache(
840     const base::FilePath& path,
841     net::URLRequestContext* request_context,
842     base::WeakPtr<storage::BlobStorageContext> blob_context)
843     : path_(path),
844       request_context_(request_context),
845       blob_storage_context_(blob_context),
846       initialized_(false),
847       weak_ptr_factory_(this) {
848 }
849
850 void ServiceWorkerCache::PutImpl(
851     scoped_ptr<ServiceWorkerFetchRequest> request,
852     scoped_ptr<ServiceWorkerResponse> response,
853     scoped_ptr<storage::BlobDataHandle> blob_data_handle,
854     const ErrorCallback& callback) {
855   if (!backend_) {
856     callback.Run(ErrorTypeStorage);
857     return;
858   }
859
860   scoped_ptr<disk_cache::Entry*> entry(new disk_cache::Entry*);
861
862   disk_cache::Entry** entry_ptr = entry.get();
863
864   ServiceWorkerFetchRequest* request_ptr = request.get();
865
866   net::CompletionCallback create_entry_callback =
867       base::Bind(PutDidCreateEntry,
868                  base::Passed(request.Pass()),
869                  base::Passed(response.Pass()),
870                  callback,
871                  base::Passed(entry.Pass()),
872                  base::Passed(blob_data_handle.Pass()),
873                  request_context_);
874
875   int rv = backend_->CreateEntry(
876       request_ptr->url.spec(), entry_ptr, create_entry_callback);
877
878   if (rv != net::ERR_IO_PENDING)
879     create_entry_callback.Run(rv);
880 }
881
882 // static
883 void ServiceWorkerCache::KeysDidOpenNextEntry(
884     scoped_ptr<KeysContext> keys_context,
885     int rv) {
886   if (rv == net::ERR_FAILED) {
887     DCHECK(!keys_context->enumerated_entry);
888     // Enumeration is complete, extract the requests from the entries.
889     Entries::iterator iter = keys_context->entries.begin();
890     KeysProcessNextEntry(keys_context.Pass(), iter);
891     return;
892   }
893
894   base::WeakPtr<ServiceWorkerCache> cache = keys_context->cache;
895   if (rv < 0 || !cache) {
896     keys_context->original_callback.Run(ErrorTypeStorage,
897                                         scoped_ptr<Requests>());
898     return;
899   }
900
901   if (!cache->backend_) {
902     keys_context->original_callback.Run(ErrorTypeNotFound,
903                                         scoped_ptr<Requests>());
904     return;
905   }
906
907   // Store the entry.
908   keys_context->entries.push_back(keys_context->enumerated_entry);
909   keys_context->enumerated_entry = NULL;
910
911   // Enumerate the next entry.
912   disk_cache::Backend::Iterator& iterator = *keys_context->backend_iterator;
913   disk_cache::Entry** enumerated_entry = &keys_context->enumerated_entry;
914   net::CompletionCallback open_entry_callback =
915       base::Bind(KeysDidOpenNextEntry, base::Passed(keys_context.Pass()));
916
917   rv = iterator.OpenNextEntry(enumerated_entry, open_entry_callback);
918
919   if (rv != net::ERR_IO_PENDING)
920     open_entry_callback.Run(rv);
921 }
922
923 // static
924 void ServiceWorkerCache::KeysProcessNextEntry(
925     scoped_ptr<KeysContext> keys_context,
926     const Entries::iterator& iter) {
927   if (iter == keys_context->entries.end()) {
928     // All done. Return all of the keys.
929     keys_context->original_callback.Run(ErrorTypeOK,
930                                         keys_context->out_keys.Pass());
931     return;
932   }
933
934   ReadHeaders(
935       *iter,
936       base::Bind(KeysDidReadHeaders, base::Passed(keys_context.Pass()), iter));
937 }
938
939 // static
940 void ServiceWorkerCache::KeysDidReadHeaders(
941     scoped_ptr<KeysContext> keys_context,
942     const Entries::iterator& iter,
943     scoped_ptr<ServiceWorkerRequestResponseHeaders> headers) {
944   disk_cache::Entry* entry = *iter;
945
946   if (headers) {
947     keys_context->out_keys->push_back(
948         ServiceWorkerFetchRequest(GURL(entry->GetKey()),
949                                   headers->method(),
950                                   ServiceWorkerHeaderMap(),
951                                   GURL(),
952                                   false));
953
954     ServiceWorkerHeaderMap& req_headers =
955         keys_context->out_keys->back().headers;
956
957     for (int i = 0; i < headers->request_headers_size(); ++i) {
958       const ServiceWorkerRequestResponseHeaders::HeaderMap header =
959           headers->request_headers(i);
960       req_headers.insert(std::make_pair(header.name(), header.value()));
961     }
962   } else {
963     entry->Doom();
964   }
965
966   KeysProcessNextEntry(keys_context.Pass(), iter + 1);
967 }
968
969 void ServiceWorkerCache::CreateBackend(const ErrorCallback& callback) {
970   DCHECK(!backend_);
971
972   // Use APP_CACHE as opposed to DISK_CACHE to prevent cache eviction.
973   net::CacheType cache_type =
974       path_.empty() ? net::MEMORY_CACHE : net::APP_CACHE;
975
976   scoped_ptr<ScopedBackendPtr> backend_ptr(new ScopedBackendPtr());
977
978   // Temporary pointer so that backend_ptr can be Pass()'d in Bind below.
979   ScopedBackendPtr* backend = backend_ptr.get();
980
981   net::CompletionCallback create_cache_callback =
982       base::Bind(CreateBackendDidCreate,
983                  callback,
984                  base::Passed(backend_ptr.Pass()),
985                  weak_ptr_factory_.GetWeakPtr());
986
987   // TODO(jkarlin): Use the cache MessageLoopProxy that ServiceWorkerCacheCore
988   // has for disk caches.
989   int rv = disk_cache::CreateCacheBackend(
990       cache_type,
991       net::CACHE_BACKEND_SIMPLE,
992       path_,
993       kMaxCacheBytes,
994       false, /* force */
995       BrowserThread::GetMessageLoopProxyForThread(BrowserThread::CACHE).get(),
996       NULL,
997       backend,
998       create_cache_callback);
999   if (rv != net::ERR_IO_PENDING)
1000     create_cache_callback.Run(rv);
1001 }
1002
1003 void ServiceWorkerCache::Init(const base::Closure& callback) {
1004   init_callbacks_.push_back(callback);
1005
1006   // If this isn't the first call to Init then return as the initialization
1007   // has already started.
1008   if (init_callbacks_.size() > 1u)
1009     return;
1010
1011   CreateBackend(base::Bind(&ServiceWorkerCache::InitDone,
1012                            weak_ptr_factory_.GetWeakPtr()));
1013 }
1014
1015 void ServiceWorkerCache::InitDone(ErrorType error) {
1016   initialized_ = true;
1017   for (std::vector<base::Closure>::iterator it = init_callbacks_.begin();
1018        it != init_callbacks_.end();
1019        ++it) {
1020     it->Run();
1021   }
1022   init_callbacks_.clear();
1023 }
1024
1025 }  // namespace content