Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(957)

Unified Diff: content/browser/service_worker/service_worker_cache.cc

Issue 465463002: Initial implementation of ServiceWorkerCache. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@cache2
Patch Set: Put and Match mostly work with blobs but the first few bytes are screwy Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: content/browser/service_worker/service_worker_cache.cc
diff --git a/content/browser/service_worker/service_worker_cache.cc b/content/browser/service_worker/service_worker_cache.cc
index d5028f00854f1f2d1241105442ee5f1ec94bb289..c9d5c1f04c79d224f5c895aa99f73844232b502a 100644
--- a/content/browser/service_worker/service_worker_cache.cc
+++ b/content/browser/service_worker/service_worker_cache.cc
@@ -7,11 +7,149 @@
#include <string>
#include "base/files/file_path.h"
+#include "base/guid.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "content/browser/fileapi/chrome_blob_storage_context.h"
+#include "content/browser/service_worker/service_worker_cache.pb.h"
+#include "content/public/browser/browser_thread.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+#include "net/disk_cache/disk_cache.h"
#include "net/url_request/url_request_context.h"
+#include "webkit/browser/blob/blob_data_handle.h"
#include "webkit/browser/blob/blob_storage_context.h"
+#include "webkit/browser/blob/blob_url_request_job_factory.h"
namespace content {
+namespace {
+// The maximum size of an individual cache.
+const int kMaxCacheBytes = 5 * 1024 * 1024;
michaeln 2014/08/14 10:35:20 why so small?
jkarlin 2014/08/14 19:53:32 Great question. No idea what's proper. Upped to
+
+// Buffer size for cache and blob reading/writing.
+const int kBufferSize = 1024 * 512;
michaeln 2014/08/14 10:35:19 why so big :)
jkarlin 2014/08/14 19:53:33 This is the size that we read from the cache when
+}
+
+struct ServiceWorkerCache::ResponseReadContext {
+ explicit ResponseReadContext(scoped_refptr<net::IOBufferWithSize> buff,
michaeln 2014/08/14 10:35:20 is explicit needed with two args
jkarlin 2014/08/14 19:53:32 Done.
+ scoped_refptr<webkit_blob::BlobData> blob)
+ : buffer(buff), blob_data(blob), total_bytes_read(0) {}
+
+ scoped_refptr<net::IOBufferWithSize> buffer;
+ scoped_refptr<webkit_blob::BlobData> blob_data;
+ int total_bytes_read;
+
+ DISALLOW_COPY_AND_ASSIGN(ResponseReadContext);
+};
+
+// Streams data from a blob and writes it to a given disk_cache::Entry.
+// This class is owned by the callback function passed to Start().
+class ServiceWorkerCache::BlobReader : public net::URLRequest::Delegate {
michaeln 2014/08/14 10:35:20 this is very similar to what ServiceWorkerWriteToC
jkarlin 2014/08/14 19:53:33 True, but not a straight-forward adaptation.
+ public:
+ BlobReader(disk_cache::Entry* entry)
+ : cache_entry_offset_(0),
+ entry_(entry),
+ buffer_(new net::IOBufferWithSize(kBufferSize)) {
+ DCHECK(entry_);
+ }
+
+ void StreamBlobToCache(
+ net::URLRequestContext* request_context,
+ scoped_ptr<webkit_blob::BlobDataHandle> blob_data_handle,
+ const BoolCallback& callback) {
+ callback_ = callback;
+ blob_request_ = webkit_blob::BlobProtocolHandler::CreateBlobRequest(
+ blob_data_handle.Pass(), request_context, this);
+ blob_request_->Start();
+ }
+
+ // net::URLRequest::Delegate overrides for reading blobs.
+ virtual void OnReceivedRedirect(net::URLRequest* request,
+ const GURL& new_url,
+ bool* defer_redirect) OVERRIDE {
+ NOTREACHED();
+ }
+ virtual void OnAuthRequired(net::URLRequest* request,
+ net::AuthChallengeInfo* auth_info) OVERRIDE {
+ NOTREACHED();
+ }
+ virtual void OnCertificateRequested(
+ net::URLRequest* request,
+ net::SSLCertRequestInfo* cert_request_info) OVERRIDE {
+ NOTREACHED();
+ }
+ virtual void OnSSLCertificateError(net::URLRequest* request,
+ const net::SSLInfo& ssl_info,
+ bool fatal) OVERRIDE {
+ NOTREACHED();
+ }
+ virtual void OnBeforeNetworkStart(net::URLRequest* request,
+ bool* defer) OVERRIDE {
+ NOTREACHED();
+ }
+
+ virtual void OnResponseStarted(net::URLRequest* request) OVERRIDE {
+ if (!request->status().is_success()) {
+ callback_.Run(false);
+ return;
+ }
+ ReadFromBlob();
+ }
+
+ virtual void ReadFromBlob() {
+ int bytes_read;
+ bool done =
+ blob_request_->Read(buffer_.get(), buffer_->size(), &bytes_read);
+ if (done)
+ OnReadCompleted(blob_request_.get(), bytes_read);
+ }
+
+ virtual void OnReadCompleted(net::URLRequest* request,
+ int bytes_read) OVERRIDE {
+ if (!request->status().is_success()) {
+ callback_.Run(false);
+ return;
+ }
+
+ if (bytes_read == 0) {
+ callback_.Run(true); // what should we really return here?
+ return;
+ }
+
+ net::CompletionCallback cache_write_callback = base::Bind(
+ &BlobReader::DidWriteDataToEntry, base::Unretained(this), bytes_read);
+
+ LOG(ERROR) << "Writing: " << std::string(buffer_->data(), bytes_read);
+
+ int rv = entry_->WriteData(ServiceWorkerCache::INDEX_RESPONSE_BODY,
+ cache_entry_offset_,
+ buffer_,
+ bytes_read,
+ cache_write_callback,
+ false /* truncate */); // what should this be?
+ if (rv != net::ERR_IO_PENDING) {
+ DidWriteDataToEntry(bytes_read, rv);
+ }
+ }
+
+ void DidWriteDataToEntry(int expected_bytes, int rv) {
+ if (rv != expected_bytes) {
+ callback_.Run(false);
+ return;
+ }
+
+ cache_entry_offset_ += rv;
+ ReadFromBlob();
+ }
+
+ private:
+ int cache_entry_offset_;
+ disk_cache::Entry* entry_;
+ scoped_ptr<net::URLRequest> blob_request_;
+ BoolCallback callback_;
+ scoped_refptr<net::IOBufferWithSize> buffer_;
+};
+
// static
scoped_ptr<ServiceWorkerCache> ServiceWorkerCache::CreateMemoryCache(
const std::string& name,
@@ -31,15 +169,412 @@ scoped_ptr<ServiceWorkerCache> ServiceWorkerCache::CreatePersistentCache(
new ServiceWorkerCache(path, name, request_context, blob_context));
}
-void ServiceWorkerCache::CreateBackend(
- const base::Callback<void(bool)>& callback) {
- callback.Run(true);
+ServiceWorkerCache::~ServiceWorkerCache() {
}
base::WeakPtr<ServiceWorkerCache> ServiceWorkerCache::AsWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
}
+void ServiceWorkerCache::CreateBackend(const ErrorCallback& callback) {
+ net::CacheType cache_type =
+ path_.empty() ? net::MEMORY_CACHE : net::DISK_CACHE;
+
+ net::CompletionCallback create_cache_callback =
+ base::Bind(&ServiceWorkerCache::CreateBackendDidCreate,
+ base::Unretained(this),
+ callback);
+ int rv = disk_cache::CreateCacheBackend(
+ cache_type,
+ net::CACHE_BACKEND_SIMPLE,
+ path_,
+ kMaxCacheBytes,
+ true, /* force */
+ BrowserThread::GetMessageLoopProxyForThread(BrowserThread::CACHE).get(),
michaeln 2014/08/14 10:35:20 ServiceWorkerContextCore constructor has the cache
jkarlin 2014/08/14 19:53:32 Agree, added a TODO to do that in another CL.
+ NULL,
+ &backend_,
michaeln 2014/08/14 10:35:20 initialization is treacherous, i think if ServiceW
jkarlin 2014/08/14 19:53:33 The ServiceWorkerCacheStorage::Delete(cache) can't
michaeln1 2014/08/14 22:57:09 The callback won't get called but &backend_ is wri
jkarlin 2014/08/15 11:49:44 Done.
+ create_cache_callback);
+ if (rv != net::ERR_IO_PENDING)
+ CreateBackendDidCreate(callback, rv);
+}
+
+void ServiceWorkerCache::CreateBackendDidCreate(const ErrorCallback& callback,
+ int rv) {
+ if (rv != net::OK) {
+ callback.Run(ErrorTypeStorage);
+ return;
+ }
+ callback.Run(ErrorTypeOK);
+}
+
+void ServiceWorkerCache::Put(ServiceWorkerFetchRequest* request,
+ ServiceWorkerResponse* response,
+ const ErrorCallback& callback) {
+ DCHECK(backend_);
+
+ scoped_ptr<disk_cache::Entry*> entry(new disk_cache::Entry*);
+
+ disk_cache::Entry** entry_ptr = entry.get();
+
+ net::CompletionCallback create_entry_callback =
+ base::Bind(&ServiceWorkerCache::PutDidCreateEntry,
+ base::Unretained(this),
+ request,
+ response,
+ callback,
+ base::Passed(entry.Pass()));
+
+ int rv = backend_->CreateEntry(
+ request->url.spec(), entry_ptr, create_entry_callback);
+
+ if (rv != net::ERR_IO_PENDING)
+ create_entry_callback.Run(rv);
+}
+
+void ServiceWorkerCache::Match(ServiceWorkerFetchRequest* request,
+ const ResponseCallback& callback) {
+ DCHECK(backend_);
+
+ scoped_ptr<disk_cache::Entry*> entry(new disk_cache::Entry*);
+
+ disk_cache::Entry** entry_ptr = entry.get();
+
+ net::CompletionCallback open_entry_callback =
+ base::Bind(&ServiceWorkerCache::MatchDidOpenEntry,
+ weak_ptr_factory_.GetWeakPtr(),
+ request,
+ callback,
+ base::Passed(entry.Pass()));
+
+ int rv =
+ backend_->OpenEntry(request->url.spec(), entry_ptr, open_entry_callback);
+ if (rv != net::ERR_IO_PENDING)
+ open_entry_callback.Run(rv);
+}
+
+void ServiceWorkerCache::MatchDidOpenEntry(
+ ServiceWorkerFetchRequest* request,
+ const ResponseCallback& callback,
+ scoped_ptr<disk_cache::Entry*> entryptr,
+ int rv) {
+ if (rv != net::OK) {
+ callback.Run(ErrorNotFound,
+ scoped_ptr<ServiceWorkerResponse>(),
+ scoped_ptr<webkit_blob::BlobDataHandle>());
+ return;
+ }
+
+ DCHECK(entryptr);
+ disk_cache::Entry* entry = *entryptr;
+
+ scoped_refptr<net::IOBufferWithSize> buffer(
+ new net::IOBufferWithSize(entry->GetDataSize(INDEX_HEADERS)));
+
+ int read_rv =
+ entry->ReadData(INDEX_HEADERS,
+ 0,
+ buffer.get(),
+ buffer->size(),
+ base::Bind(&ServiceWorkerCache::MatchDidReadHeaderData,
+ weak_ptr_factory_.GetWeakPtr(),
+ request,
+ callback,
+ base::Unretained(entry),
michaeln 2014/08/14 10:35:20 if ServiceWorkerCache is deleted while in flight,
jkarlin 2014/08/14 19:53:32 Yes, thanks. Hadn't gotten to all of the pointers
+ buffer));
+ if (read_rv != net::ERR_IO_PENDING)
+ MatchDidReadHeaderData(request, callback, entry, buffer, read_rv);
+}
+
+void ServiceWorkerCache::MatchDidReadHeaderData(
+ ServiceWorkerFetchRequest* request,
+ const ResponseCallback& callback,
+ disk_cache::Entry* entry,
+ const scoped_refptr<net::IOBufferWithSize>& buffer,
+ int rv) {
+ if (rv != buffer->size()) {
+ entry->Close();
+ callback.Run(ErrorTypeStorage,
+ scoped_ptr<ServiceWorkerResponse>(),
+ scoped_ptr<webkit_blob::BlobDataHandle>());
+ return;
+ }
+
+ ServiceWorkerRequestResponseHeaders headers;
+
+ if (!headers.ParseFromArray(buffer->data(), buffer->size())) {
+ entry->Close();
+ callback.Run(ErrorTypeStorage,
+ scoped_ptr<ServiceWorkerResponse>(),
+ scoped_ptr<webkit_blob::BlobDataHandle>());
+ return;
+ }
+
+ scoped_ptr<ServiceWorkerResponse> response(
+ new ServiceWorkerResponse(request->url,
+ headers.status_code(),
+ headers.status_text(),
+ std::map<std::string, std::string>(),
+ ""));
+
+ for (int i = 0; i < headers.response_headers_size(); ++i) {
+ const ServiceWorkerRequestResponseHeaders::HeaderMap header =
+ headers.response_headers(i);
+ response->headers.insert(std::make_pair(header.name(), header.value()));
+ }
+
+ // TODO(jkarlin): Insert vary validation here.
+
+ if (entry->GetDataSize(INDEX_RESPONSE_BODY) == 0) {
+ entry->Close();
+ callback.Run(ErrorTypeOK,
+ response.Pass(),
+ scoped_ptr<webkit_blob::BlobDataHandle>());
+ return;
+ }
+
+ // Stream the response body into a blob.
+ if (!blob_storage_context_) {
+ entry->Close();
+ callback.Run(ErrorTypeStorage,
+ scoped_ptr<ServiceWorkerResponse>(),
+ scoped_ptr<webkit_blob::BlobDataHandle>());
+ return;
+ }
+
+ response->blob_uuid = base::GenerateGUID();
+
+ scoped_refptr<webkit_blob::BlobData> blob_data =
+ new webkit_blob::BlobData(response->blob_uuid);
+ scoped_refptr<net::IOBufferWithSize> response_body_buffer(
+ new net::IOBufferWithSize(kBufferSize));
+
+ scoped_ptr<ResponseReadContext> read_context(
+ new ResponseReadContext(response_body_buffer, blob_data));
+
+ net::CompletionCallback read_callback =
+ base::Bind(&ServiceWorkerCache::MatchDidReadResponseBodyData,
+ weak_ptr_factory_.GetWeakPtr(),
+ request,
+ callback,
+ base::Unretained(entry),
+ base::Passed(response.Pass()),
+ base::Passed(read_context.Pass()));
+
+ int read_rv = entry->ReadData(
+ INDEX_RESPONSE_BODY, 0, buffer.get(), buffer->size(), read_callback);
+
+ if (read_rv != net::ERR_IO_PENDING)
+ read_callback.Run(read_rv);
+}
+
+void ServiceWorkerCache::MatchDidReadResponseBodyData(
+ ServiceWorkerFetchRequest* request,
+ const ResponseCallback& callback,
+ disk_cache::Entry* entry,
+ scoped_ptr<ServiceWorkerResponse> response,
+ scoped_ptr<ResponseReadContext> response_context,
+ int rv) {
+ if (entry < 0) {
+ entry->Close();
+ callback.Run(ErrorTypeStorage,
+ scoped_ptr<ServiceWorkerResponse>(),
+ scoped_ptr<webkit_blob::BlobDataHandle>());
+ }
+
+ if (rv == 0) {
+ entry->Close();
+
+ MatchDoneWithBody(
+ request, callback, response.Pass(), response_context.Pass());
+ return;
+ }
+
+ // TODO(jkarlin): This copying of the the entire cache response into memory is
+ // awful. Create a new interface around SimpleCache that provides access the
+ // data directly from the file. See bug http://crbug.com/403493.
+ response_context->blob_data->AppendData(response_context->buffer->data(), rv);
+ response_context->total_bytes_read += rv;
+ int total_bytes_read = response_context->total_bytes_read;
+
+ LOG(ERROR) << "Reading: " << std::string(response_context->buffer->data(),
+ rv);
+ // Grab the pointer before response_context is Pass()ed.
+ net::IOBufferWithSize* buffer = response_context->buffer;
+
+ net::CompletionCallback read_callback =
+ base::Bind(&ServiceWorkerCache::MatchDidReadResponseBodyData,
+ weak_ptr_factory_.GetWeakPtr(),
+ request,
+ callback,
+ base::Unretained(entry),
+ base::Passed(response.Pass()),
+ base::Passed(response_context.Pass()));
+
+ int read_rv = entry->ReadData(INDEX_RESPONSE_BODY,
+ total_bytes_read,
+ buffer,
+ buffer->size(),
+ read_callback);
+
+ if (read_rv != net::ERR_IO_PENDING)
+ read_callback.Run(read_rv);
+}
+
+void ServiceWorkerCache::MatchDoneWithBody(
+ ServiceWorkerFetchRequest* request,
+ const ResponseCallback& callback,
+ scoped_ptr<ServiceWorkerResponse> response,
+ scoped_ptr<ResponseReadContext> response_context) {
+ // TODO(jkarlin): Create a blob and pass it back to the renderer. How do we
+ // reference count that?
michaeln 2014/08/14 10:35:20 1) the uuid is sent from browser->renderer in the
jkarlin 2014/08/14 19:53:32 Acknowledged.
+ if (!blob_storage_context_) {
+ callback.Run(ErrorTypeStorage,
+ scoped_ptr<ServiceWorkerResponse>(),
+ scoped_ptr<webkit_blob::BlobDataHandle>());
+ return;
+ }
+
+ scoped_ptr<webkit_blob::BlobDataHandle> blob_data_handle(
+ blob_storage_context_->AddFinishedBlob(
+ response_context->blob_data.get()));
+
+ callback.Run(ErrorTypeOK, response.Pass(), blob_data_handle.Pass());
+}
+
+void ServiceWorkerCache::PutDidCreateEntry(
+ ServiceWorkerFetchRequest* request,
+ ServiceWorkerResponse* response,
+ const ErrorCallback& callback,
+ scoped_ptr<disk_cache::Entry*> entryptr,
+ int rv) {
+ if (rv != net::OK) {
+ callback.Run(ErrorTypeExists);
+ return;
+ }
+
+ DCHECK(entryptr);
+ disk_cache::Entry* entry = *entryptr;
+
+ ServiceWorkerRequestResponseHeaders headers;
+ headers.set_method(request->method);
+
+ headers.set_status_code(response->status_code);
+ headers.set_status_text(response->status_text);
+ for (std::map<std::string, std::string>::const_iterator it =
+ request->headers.begin();
+ it != request->headers.end();
+ ++it) {
+ ServiceWorkerRequestResponseHeaders::HeaderMap* header_map =
+ headers.add_request_headers();
+ header_map->set_name(it->first);
+ header_map->set_value(it->second);
+ }
+
+ for (std::map<std::string, std::string>::const_iterator it =
+ response->headers.begin();
+ it != response->headers.end();
+ ++it) {
+ ServiceWorkerRequestResponseHeaders::HeaderMap* header_map =
+ headers.add_response_headers();
+ header_map->set_name(it->first);
+ header_map->set_value(it->second);
+ }
+
+ scoped_refptr<net::ZeroCopyStringIOBuffer> buffer(
michaeln 2014/08/14 10:35:20 would it make sense for the existing StringIOBuffe
jkarlin 2014/08/14 19:53:33 I like the idea of using std::string:swap. I chan
+ new net::ZeroCopyStringIOBuffer());
+ if (!headers.SerializeToString(buffer->string())) {
+ callback.Run(ErrorTypeStorage);
+ }
+
+ buffer->Done();
+
+ net::CompletionCallback write_headers_callback =
+ base::Bind(&ServiceWorkerCache::PutDidWriteHeaders,
+ weak_ptr_factory_.GetWeakPtr(),
+ request,
+ response,
+ callback,
+ entry,
+ buffer->string()->size());
+
+ rv = entry->WriteData(INDEX_HEADERS,
+ 0 /* offset */,
+ buffer,
+ buffer->string()->size(),
+ write_headers_callback,
+ true /* truncate */);
+
+ if (rv != net::ERR_IO_PENDING)
+ write_headers_callback.Run(rv);
+}
+
+void ServiceWorkerCache::PutDidWriteHeaders(ServiceWorkerFetchRequest* request,
+ ServiceWorkerResponse* response,
+ const ErrorCallback& callback,
+ disk_cache::Entry* entry,
+ int expected_bytes,
+ int rv) {
+ if (rv != expected_bytes) {
+ entry->Doom();
+ entry->Close();
+ callback.Run(ErrorTypeStorage);
+ return;
+ }
+
+ // The metadata is written, now for the response content. The data is streamed
+ // from the blob into the cache entry.
+
+ if (response->blob_uuid.empty()) {
+ entry->Close();
+ callback.Run(ErrorTypeOK);
+ return;
+ }
+
+ if (!blob_storage_context_) {
+ entry->Doom();
+ entry->Close();
+ callback.Run(ErrorTypeStorage);
+ return;
+ }
+
+ scoped_ptr<webkit_blob::BlobDataHandle> blob_data_handle =
+ blob_storage_context_->GetBlobDataFromUUID(response->blob_uuid);
michaeln 2014/08/14 10:35:20 you should get this handle up front in ServiceWork
jkarlin 2014/08/14 19:53:32 Thanks! Done and added test.
+
+ if (!blob_data_handle) {
+ entry->Doom();
+ entry->Close();
+ callback.Run(ErrorTypeStorage);
+ return;
+ }
+
+ scoped_ptr<BlobReader> reader(new BlobReader(entry));
+
+ reader->StreamBlobToCache(
+ request_context_,
+ blob_data_handle.Pass(),
+ base::Bind(&ServiceWorkerCache::PutDidWriteBlobToCache,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback,
+ entry,
michaeln 2014/08/14 10:35:20 if ServiceWorkerCache is deleted prior to completi
jkarlin 2014/08/14 19:53:33 Done.
+ base::Passed(reader.Pass())));
+}
+
+void ServiceWorkerCache::PutDidWriteBlobToCache(
+ const ErrorCallback& callback,
+ disk_cache::Entry* entry,
+ scoped_ptr<BlobReader> blob_reader,
+ bool success) {
+ if (!success) {
+ entry->Doom();
+ entry->Close();
+ callback.Run(ErrorTypeStorage);
+ return;
+ }
+
+ entry->Close();
+ callback.Run(ErrorTypeOK);
+}
+
ServiceWorkerCache::ServiceWorkerCache(
const base::FilePath& path,
const std::string& name,
@@ -53,7 +588,4 @@ ServiceWorkerCache::ServiceWorkerCache(
weak_ptr_factory_(this) {
}
-ServiceWorkerCache::~ServiceWorkerCache() {
-}
-
} // namespace content
« no previous file with comments | « content/browser/service_worker/service_worker_cache.h ('k') | content/browser/service_worker/service_worker_cache.proto » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698