1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/browser/service_worker/service_worker_write_to_cache_job.h"
7 #include "content/browser/service_worker/service_worker_context_core.h"
8 #include "content/browser/service_worker/service_worker_disk_cache.h"
9 #include "content/browser/service_worker/service_worker_histograms.h"
10 #include "net/base/io_buffer.h"
11 #include "net/base/net_errors.h"
12 #include "net/http/http_request_headers.h"
13 #include "net/http/http_response_headers.h"
14 #include "net/http/http_util.h"
15 #include "net/url_request/url_request.h"
16 #include "net/url_request/url_request_context.h"
17 #include "net/url_request/url_request_status.h"
21 ServiceWorkerWriteToCacheJob::ServiceWorkerWriteToCacheJob(
22 net::URLRequest* request,
23 net::NetworkDelegate* network_delegate,
24 base::WeakPtr<ServiceWorkerContextCore> context,
25 ServiceWorkerVersion* version,
27 : net::URLRequestJob(request, network_delegate),
30 response_id_(response_id),
32 has_been_killed_(false),
33 did_notify_started_(false),
34 did_notify_finished_(false),
39 ServiceWorkerWriteToCacheJob::~ServiceWorkerWriteToCacheJob() {
40 DCHECK_EQ(did_notify_started_, did_notify_finished_);
43 void ServiceWorkerWriteToCacheJob::Start() {
45 NotifyStartError(net::URLRequestStatus(
46 net::URLRequestStatus::FAILED, net::ERR_FAILED));
49 version_->script_cache_map()->NotifyStartedCaching(
51 did_notify_started_ = true;
55 void ServiceWorkerWriteToCacheJob::Kill() {
58 weak_factory_.InvalidateWeakPtrs();
59 has_been_killed_ = true;
61 if (did_notify_started_ && !did_notify_finished_) {
62 version_->script_cache_map()->NotifyFinishedCaching(
64 did_notify_finished_ = true;
68 net::URLRequestJob::Kill();
71 net::LoadState ServiceWorkerWriteToCacheJob::GetLoadState() const {
72 if (writer_ && writer_->IsWritePending())
73 return net::LOAD_STATE_WAITING_FOR_APPCACHE;
75 return net_request_->GetLoadState().state;
76 return net::LOAD_STATE_IDLE;
79 bool ServiceWorkerWriteToCacheJob::GetCharset(std::string* charset) {
82 return http_info()->headers->GetCharset(charset);
85 bool ServiceWorkerWriteToCacheJob::GetMimeType(std::string* mime_type) const {
88 return http_info()->headers->GetMimeType(mime_type);
91 void ServiceWorkerWriteToCacheJob::GetResponseInfo(
92 net::HttpResponseInfo* info) {
98 int ServiceWorkerWriteToCacheJob::GetResponseCode() const {
101 return http_info()->headers->response_code();
104 void ServiceWorkerWriteToCacheJob::SetExtraRequestHeaders(
105 const net::HttpRequestHeaders& headers) {
107 DCHECK(!headers.GetHeader(net::HttpRequestHeaders::kRange, &value));
108 net_request_->SetExtraRequestHeaders(headers);
111 bool ServiceWorkerWriteToCacheJob::ReadRawData(
115 net::URLRequestStatus status = ReadNetData(buf, buf_size, bytes_read);
117 if (status.is_io_pending())
120 // No more data to process, the job is complete.
122 version_->script_cache_map()->NotifyFinishedCaching(
123 url_, status.is_success());
124 did_notify_finished_ = true;
125 return status.is_success();
128 const net::HttpResponseInfo* ServiceWorkerWriteToCacheJob::http_info() const {
129 return http_info_.get();
132 void ServiceWorkerWriteToCacheJob::InitNetRequest() {
134 net_request_ = request()->context()->CreateRequest(
136 request()->priority(),
138 this->GetCookieStore());
139 net_request_->set_first_party_for_cookies(
140 request()->first_party_for_cookies());
141 net_request_->SetReferrer(request()->referrer());
142 net_request_->SetExtraRequestHeaders(request()->extra_request_headers());
145 void ServiceWorkerWriteToCacheJob::StartNetRequest() {
146 net_request_->Start(); // We'll continue in OnResponseStarted.
149 net::URLRequestStatus ServiceWorkerWriteToCacheJob::ReadNetData(
153 DCHECK_GT(buf_size, 0);
158 int net_bytes_read = 0;
159 if (!net_request_->Read(buf, buf_size, &net_bytes_read)) {
160 if (net_request_->status().is_io_pending())
161 return net_request_->status();
162 DCHECK(!net_request_->status().is_success());
163 return net_request_->status();
166 if (net_bytes_read != 0) {
167 WriteDataToCache(net_bytes_read);
168 DCHECK(GetStatus().is_io_pending());
172 DCHECK(net_request_->status().is_success());
173 return net_request_->status();
176 void ServiceWorkerWriteToCacheJob::WriteHeadersToCache() {
178 AsyncNotifyDoneHelper(net::URLRequestStatus(
179 net::URLRequestStatus::FAILED, net::ERR_FAILED));
182 writer_ = context_->storage()->CreateResponseWriter(response_id_);
183 info_buffer_ = new HttpResponseInfoIOBuffer(
184 new net::HttpResponseInfo(net_request_->response_info()));
187 base::Bind(&ServiceWorkerWriteToCacheJob::OnWriteHeadersComplete,
188 weak_factory_.GetWeakPtr()));
189 SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0));
192 void ServiceWorkerWriteToCacheJob::OnWriteHeadersComplete(int result) {
193 SetStatus(net::URLRequestStatus()); // Clear the IO_PENDING status
195 ServiceWorkerHistograms::CountWriteResponseResult(
196 ServiceWorkerHistograms::WRITE_HEADERS_ERROR);
197 AsyncNotifyDoneHelper(net::URLRequestStatus(
198 net::URLRequestStatus::FAILED, result));
201 http_info_.reset(info_buffer_->http_info.release());
203 NotifyHeadersComplete();
206 void ServiceWorkerWriteToCacheJob::WriteDataToCache(int amount_to_write) {
207 DCHECK_NE(0, amount_to_write);
208 SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0));
210 io_buffer_, amount_to_write,
211 base::Bind(&ServiceWorkerWriteToCacheJob::OnWriteDataComplete,
212 weak_factory_.GetWeakPtr()));
215 void ServiceWorkerWriteToCacheJob::OnWriteDataComplete(int result) {
216 DCHECK_NE(0, result);
219 AsyncNotifyDoneHelper(net::URLRequestStatus(
220 net::URLRequestStatus::FAILED, net::ERR_FAILED));
224 ServiceWorkerHistograms::CountWriteResponseResult(
225 ServiceWorkerHistograms::WRITE_DATA_ERROR);
226 AsyncNotifyDoneHelper(net::URLRequestStatus(
227 net::URLRequestStatus::FAILED, result));
230 ServiceWorkerHistograms::CountWriteResponseResult(
231 ServiceWorkerHistograms::WRITE_OK);
232 SetStatus(net::URLRequestStatus()); // Clear the IO_PENDING status
233 NotifyReadComplete(result);
236 void ServiceWorkerWriteToCacheJob::OnReceivedRedirect(
237 net::URLRequest* request,
239 bool* defer_redirect) {
240 DCHECK_EQ(net_request_, request);
241 // Script resources can't redirect.
242 AsyncNotifyDoneHelper(net::URLRequestStatus(
243 net::URLRequestStatus::FAILED, net::ERR_FAILED));
246 void ServiceWorkerWriteToCacheJob::OnAuthRequired(
247 net::URLRequest* request,
248 net::AuthChallengeInfo* auth_info) {
249 DCHECK_EQ(net_request_, request);
250 // TODO(michaeln): Pass this thru to our jobs client.
251 AsyncNotifyDoneHelper(net::URLRequestStatus(
252 net::URLRequestStatus::FAILED, net::ERR_FAILED));
255 void ServiceWorkerWriteToCacheJob::OnCertificateRequested(
256 net::URLRequest* request,
257 net::SSLCertRequestInfo* cert_request_info) {
258 DCHECK_EQ(net_request_, request);
259 // TODO(michaeln): Pass this thru to our jobs client.
260 // see NotifyCertificateRequested.
261 AsyncNotifyDoneHelper(net::URLRequestStatus(
262 net::URLRequestStatus::FAILED, net::ERR_FAILED));
265 void ServiceWorkerWriteToCacheJob:: OnSSLCertificateError(
266 net::URLRequest* request,
267 const net::SSLInfo& ssl_info,
269 DCHECK_EQ(net_request_, request);
270 // TODO(michaeln): Pass this thru to our jobs client,
271 // see NotifySSLCertificateError.
272 AsyncNotifyDoneHelper(net::URLRequestStatus(
273 net::URLRequestStatus::FAILED, net::ERR_FAILED));
276 void ServiceWorkerWriteToCacheJob::OnBeforeNetworkStart(
277 net::URLRequest* request,
279 DCHECK_EQ(net_request_, request);
280 NotifyBeforeNetworkStart(defer);
283 void ServiceWorkerWriteToCacheJob::OnResponseStarted(
284 net::URLRequest* request) {
285 DCHECK_EQ(net_request_, request);
286 if (!request->status().is_success()) {
287 AsyncNotifyDoneHelper(request->status());
290 if (request->GetResponseCode() / 100 != 2) {
291 AsyncNotifyDoneHelper(net::URLRequestStatus(
292 net::URLRequestStatus::FAILED, net::ERR_FAILED));
293 // TODO(michaeln): Instead of error'ing immediately, send the net
294 // response to our consumer, just don't cache it?
297 WriteHeadersToCache();
300 void ServiceWorkerWriteToCacheJob::OnReadCompleted(
301 net::URLRequest* request,
303 DCHECK_EQ(net_request_, request);
304 if (!request->status().is_success()) {
305 AsyncNotifyDoneHelper(request->status());
308 if (bytes_read > 0) {
309 WriteDataToCache(bytes_read);
312 // We're done with all.
313 AsyncNotifyDoneHelper(request->status());
317 void ServiceWorkerWriteToCacheJob::AsyncNotifyDoneHelper(
318 const net::URLRequestStatus& status) {
319 DCHECK(!status.is_io_pending());
320 version_->script_cache_map()->NotifyFinishedCaching(
321 url_, status.is_success());
322 did_notify_finished_ = true;
327 } // namespace content