Index: content/browser/service_worker/service_worker_write_to_cache_job.cc |
diff --git a/content/browser/service_worker/service_worker_write_to_cache_job.cc b/content/browser/service_worker/service_worker_write_to_cache_job.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..313e3a4796682e69c0c605d30d3ee13fd4c94363 |
--- /dev/null |
+++ b/content/browser/service_worker/service_worker_write_to_cache_job.cc |
@@ -0,0 +1,320 @@ |
+// Copyright 2014 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "content/browser/service_worker/service_worker_write_to_cache_job.h" |
+ |
+#include "content/browser/service_worker/service_worker_context_core.h" |
+#include "content/browser/service_worker/service_worker_disk_cache.h" |
+#include "net/base/io_buffer.h" |
+#include "net/base/net_errors.h" |
+#include "net/http/http_request_headers.h" |
+#include "net/http/http_response_headers.h" |
+#include "net/http/http_util.h" |
+#include "net/url_request/url_request.h" |
+#include "net/url_request/url_request_context.h" |
+#include "net/url_request/url_request_status.h" |
+ |
+namespace content { |
+ |
+ServiceWorkerWriteToCacheJob::ServiceWorkerWriteToCacheJob( |
+ net::URLRequest* request, |
+ net::NetworkDelegate* network_delegate, |
+ base::WeakPtr<ServiceWorkerContextCore> context, |
+ ServiceWorkerVersion* version, |
+ int64 response_id) |
+ : net::URLRequestJob(request, network_delegate), |
+ context_(context), |
+ url_(request->url()), |
+ response_id_(response_id), |
+ version_(version), |
+ has_been_killed_(false), |
+ did_notify_started_(false), |
+ did_notify_finished_(false), |
+ weak_factory_(this) { |
+ InitNetRequest(); |
+} |
+ |
+ServiceWorkerWriteToCacheJob::~ServiceWorkerWriteToCacheJob() { |
+ DCHECK_EQ(did_notify_started_, did_notify_finished_); |
+} |
+ |
+void ServiceWorkerWriteToCacheJob::Start() { |
+ if (!context_) { |
+ NotifyStartError(net::URLRequestStatus( |
+ net::URLRequestStatus::FAILED, net::ERR_FAILED)); |
+ return; |
+ } |
+ version_->script_cache_map()->NotifyStartedCaching( |
+ url_, response_id_); |
+ did_notify_started_ = true; |
+ StartNetRequest(); |
+} |
+ |
+void ServiceWorkerWriteToCacheJob::Kill() { |
+ if (has_been_killed_) |
+ return; |
+ weak_factory_.InvalidateWeakPtrs(); |
+ has_been_killed_ = true; |
+ net_request_.reset(); |
+ if (did_notify_started_ && !did_notify_finished_) { |
+ version_->script_cache_map()->NotifyFinishedCaching( |
+ url_, false); |
+ did_notify_finished_ = true; |
+ } |
+ writer_.reset(); |
+ context_.reset(); |
+ net::URLRequestJob::Kill(); |
+} |
+ |
+net::LoadState ServiceWorkerWriteToCacheJob::GetLoadState() const { |
+ if (writer_ && writer_->IsWritePending()) |
+ return net::LOAD_STATE_WAITING_FOR_APPCACHE; |
+ if (net_request_) |
+ return net_request_->GetLoadState().state; |
+ return net::LOAD_STATE_IDLE; |
+} |
+ |
+bool ServiceWorkerWriteToCacheJob::GetCharset(std::string* charset) { |
+ if (!http_info()) |
+ return false; |
+ return http_info()->headers->GetCharset(charset); |
+} |
+ |
+bool ServiceWorkerWriteToCacheJob::GetMimeType(std::string* mime_type) const { |
+ if (!http_info()) |
+ return false; |
+ return http_info()->headers->GetMimeType(mime_type); |
+} |
+ |
+void ServiceWorkerWriteToCacheJob::GetResponseInfo( |
+ net::HttpResponseInfo* info) { |
+ if (!http_info()) |
+ return; |
+ *info = *http_info(); |
+} |
+ |
+int ServiceWorkerWriteToCacheJob::GetResponseCode() const { |
+ if (!http_info()) |
+ return -1; |
+ return http_info()->headers->response_code(); |
+} |
+ |
+void ServiceWorkerWriteToCacheJob::SetExtraRequestHeaders( |
+ const net::HttpRequestHeaders& headers) { |
+ std::string value; |
+ DCHECK(!headers.GetHeader(net::HttpRequestHeaders::kRange, &value)); |
+ net_request_->SetExtraRequestHeaders(headers); |
+} |
+ |
+bool ServiceWorkerWriteToCacheJob::ReadRawData( |
+ net::IOBuffer* buf, |
+ int buf_size, |
+ int *bytes_read) { |
+ net::URLRequestStatus status = ReadNetData(buf, buf_size, bytes_read); |
+ SetStatus(status); |
+ if (status.is_io_pending()) |
+ return false; |
+ |
+ // No more data to process, the job is complete. |
+ io_buffer_ = NULL; |
+ version_->script_cache_map()->NotifyFinishedCaching( |
+ url_, status.is_success()); |
+ did_notify_finished_ = true; |
+ return status.is_success(); |
+} |
+ |
+const net::HttpResponseInfo* ServiceWorkerWriteToCacheJob::http_info() const { |
+ return http_info_.get(); |
+} |
+ |
+void ServiceWorkerWriteToCacheJob::InitNetRequest() { |
+ DCHECK(request()); |
+ net_request_ = request()->context()->CreateRequest( |
+ request()->url(), |
+ request()->priority(), |
+ this, |
+ this->GetCookieStore()); |
+ net_request_->set_first_party_for_cookies( |
+ request()->first_party_for_cookies()); |
+ net_request_->SetReferrer(request()->referrer()); |
+ net_request_->SetExtraRequestHeaders(request()->extra_request_headers()); |
+} |
+ |
+void ServiceWorkerWriteToCacheJob::StartNetRequest() { |
+ net_request_->Start(); // We'll continue in OnResponseStarted. |
+} |
+ |
+net::URLRequestStatus ServiceWorkerWriteToCacheJob::ReadNetData( |
+ net::IOBuffer* buf, |
+ int buf_size, |
+ int *bytes_read) { |
+ DCHECK_GT(buf_size, 0); |
+ DCHECK(bytes_read); |
+ |
+ *bytes_read = 0; |
+ io_buffer_ = buf; |
+ int net_bytes_read = 0; |
+ if (!net_request_->Read(buf, buf_size, &net_bytes_read)) { |
+ if (net_request_->status().is_io_pending()) |
+ return net_request_->status(); |
+ DCHECK(!net_request_->status().is_success()); |
+ return net_request_->status(); |
+ } |
+ |
+ if (net_bytes_read != 0) { |
+ WriteDataToCache(net_bytes_read); |
+ DCHECK(GetStatus().is_io_pending()); |
+ return GetStatus(); |
+ } |
+ |
+ DCHECK(net_request_->status().is_success()); |
+ return net_request_->status(); |
+} |
+ |
+void ServiceWorkerWriteToCacheJob::WriteHeadersToCache() { |
+ if (!context_) { |
+ AsyncNotifyDoneHelper(net::URLRequestStatus( |
+ net::URLRequestStatus::FAILED, net::ERR_FAILED)); |
+ return; |
+ } |
+ writer_ = context_->storage()->CreateResponseWriter(response_id_); |
+ info_buffer_ = new HttpResponseInfoIOBuffer( |
+ new net::HttpResponseInfo(net_request_->response_info())); |
+ writer_->WriteInfo( |
+ info_buffer_, |
+ base::Bind(&ServiceWorkerWriteToCacheJob::OnWriteHeadersComplete, |
+ weak_factory_.GetWeakPtr())); |
+ SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0)); |
+} |
+ |
+void ServiceWorkerWriteToCacheJob::OnWriteHeadersComplete(int result) { |
+ SetStatus(net::URLRequestStatus()); // Clear the IO_PENDING status |
+ if (result < 0) { |
+ AsyncNotifyDoneHelper(net::URLRequestStatus( |
+ net::URLRequestStatus::FAILED, result)); |
+ return; |
+ } |
+ http_info_.reset(info_buffer_->http_info.release()); |
+ info_buffer_ = NULL; |
+ NotifyHeadersComplete(); |
+} |
+ |
+void ServiceWorkerWriteToCacheJob::WriteDataToCache(int amount_to_write) { |
+ DCHECK_NE(0, amount_to_write); |
+ SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0)); |
+ writer_->WriteData( |
+ io_buffer_, amount_to_write, |
+ base::Bind(&ServiceWorkerWriteToCacheJob::OnWriteDataComplete, |
+ weak_factory_.GetWeakPtr())); |
+} |
+ |
+void ServiceWorkerWriteToCacheJob::OnWriteDataComplete(int result) { |
+ DCHECK_NE(0, result); |
+ io_buffer_ = NULL; |
+ if (!context_) { |
+ AsyncNotifyDoneHelper(net::URLRequestStatus( |
+ net::URLRequestStatus::FAILED, net::ERR_FAILED)); |
+ return; |
+ } |
+ if (result < 0) { |
+ AsyncNotifyDoneHelper(net::URLRequestStatus( |
+ net::URLRequestStatus::FAILED, result)); |
+ return; |
+ } |
+ SetStatus(net::URLRequestStatus()); // Clear the IO_PENDING status |
+ NotifyReadComplete(result); |
+} |
+ |
+void ServiceWorkerWriteToCacheJob::OnReceivedRedirect( |
+ net::URLRequest* request, |
+ const GURL& new_url, |
+ bool* defer_redirect) { |
+ DCHECK_EQ(net_request_, request); |
+ // Script resources can't redirect. |
+ AsyncNotifyDoneHelper(net::URLRequestStatus( |
+ net::URLRequestStatus::FAILED, net::ERR_FAILED)); |
+} |
+ |
+void ServiceWorkerWriteToCacheJob::OnAuthRequired( |
+ net::URLRequest* request, |
+ net::AuthChallengeInfo* auth_info) { |
+ DCHECK_EQ(net_request_, request); |
+ // TODO(michaeln): Pass this thru to our jobs client. |
+ AsyncNotifyDoneHelper(net::URLRequestStatus( |
+ net::URLRequestStatus::FAILED, net::ERR_FAILED)); |
+} |
+ |
+void ServiceWorkerWriteToCacheJob::OnCertificateRequested( |
+ net::URLRequest* request, |
+ net::SSLCertRequestInfo* cert_request_info) { |
+ DCHECK_EQ(net_request_, request); |
+ // TODO(michaeln): Pass this thru to our jobs client. |
+ // see NotifyCertificateRequested. |
+ AsyncNotifyDoneHelper(net::URLRequestStatus( |
+ net::URLRequestStatus::FAILED, net::ERR_FAILED)); |
+} |
+ |
+void ServiceWorkerWriteToCacheJob:: OnSSLCertificateError( |
+ net::URLRequest* request, |
+ const net::SSLInfo& ssl_info, |
+ bool fatal) { |
+ DCHECK_EQ(net_request_, request); |
+ // TODO(michaeln): Pass this thru to our jobs client, |
+ // see NotifySSLCertificateError. |
+ AsyncNotifyDoneHelper(net::URLRequestStatus( |
+ net::URLRequestStatus::FAILED, net::ERR_FAILED)); |
+} |
+ |
+void ServiceWorkerWriteToCacheJob::OnBeforeNetworkStart( |
+ net::URLRequest* request, |
+ bool* defer) { |
+ DCHECK_EQ(net_request_, request); |
+ NotifyBeforeNetworkStart(defer); |
+} |
+ |
+void ServiceWorkerWriteToCacheJob::OnResponseStarted( |
+ net::URLRequest* request) { |
+ DCHECK_EQ(net_request_, request); |
+ if (!request->status().is_success()) { |
+ AsyncNotifyDoneHelper(request->status()); |
+ return; |
+ } |
+ if (request->GetResponseCode() / 100 != 2) { |
+ AsyncNotifyDoneHelper(net::URLRequestStatus( |
+ net::URLRequestStatus::FAILED, net::ERR_FAILED)); |
+ // TODO(michaeln): Instead of error'ing immediately, send the net |
+ // response to our consumer, just don't cache it? |
+ return; |
+ } |
+ WriteHeadersToCache(); |
+} |
+ |
+void ServiceWorkerWriteToCacheJob::OnReadCompleted( |
+ net::URLRequest* request, |
+ int bytes_read) { |
+ DCHECK_EQ(net_request_, request); |
+ if (!request->status().is_success()) { |
+ AsyncNotifyDoneHelper(request->status()); |
+ return; |
+ } |
+ if (bytes_read > 0) { |
+ WriteDataToCache(bytes_read); |
+ return; |
+ } |
+ // We're done with all. |
+ AsyncNotifyDoneHelper(request->status()); |
+ return; |
+} |
+ |
+void ServiceWorkerWriteToCacheJob::AsyncNotifyDoneHelper( |
+ const net::URLRequestStatus& status) { |
+ DCHECK(!status.is_io_pending()); |
+ version_->script_cache_map()->NotifyFinishedCaching( |
+ url_, status.is_success()); |
+ did_notify_finished_ = true; |
+ SetStatus(status); |
+ NotifyDone(status); |
+} |
+ |
+} // namespace content |