Home | History | Annotate | Download | only in service_worker
      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
   1026