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 "base/debug/trace_event.h"
8 #include "content/browser/service_worker/service_worker_context_core.h"
9 #include "content/browser/service_worker/service_worker_disk_cache.h"
10 #include "content/browser/service_worker/service_worker_metrics.h"
11 #include "net/base/io_buffer.h"
12 #include "net/base/net_errors.h"
13 #include "net/http/http_network_session.h"
14 #include "net/http/http_request_headers.h"
15 #include "net/http/http_response_headers.h"
16 #include "net/http/http_util.h"
17 #include "net/url_request/url_request.h"
18 #include "net/url_request/url_request_context.h"
19 #include "net/url_request/url_request_status.h"
23 ServiceWorkerWriteToCacheJob::ServiceWorkerWriteToCacheJob(
24 net::URLRequest* request,
25 net::NetworkDelegate* network_delegate,
26 ResourceType resource_type,
27 base::WeakPtr<ServiceWorkerContextCore> context,
28 ServiceWorkerVersion* version,
31 : net::URLRequestJob(request, network_delegate),
32 resource_type_(resource_type),
35 response_id_(response_id),
37 has_been_killed_(false),
38 did_notify_started_(false),
39 did_notify_finished_(false),
41 InitNetRequest(extra_load_flags);
44 ServiceWorkerWriteToCacheJob::~ServiceWorkerWriteToCacheJob() {
45 DCHECK_EQ(did_notify_started_, did_notify_finished_);
48 void ServiceWorkerWriteToCacheJob::Start() {
49 TRACE_EVENT_ASYNC_BEGIN1("ServiceWorker",
50 "ServiceWorkerWriteToCacheJob::ExecutingJob",
52 "URL", request_->url().spec());
54 NotifyStartError(net::URLRequestStatus(
55 net::URLRequestStatus::FAILED, net::ERR_FAILED));
58 version_->script_cache_map()->NotifyStartedCaching(
60 did_notify_started_ = true;
64 void ServiceWorkerWriteToCacheJob::Kill() {
67 weak_factory_.InvalidateWeakPtrs();
68 has_been_killed_ = true;
70 if (did_notify_started_ && !did_notify_finished_) {
71 version_->script_cache_map()->NotifyFinishedCaching(
74 net::URLRequestStatus(net::URLRequestStatus::FAILED, net::ERR_ABORTED));
75 did_notify_finished_ = true;
79 net::URLRequestJob::Kill();
82 net::LoadState ServiceWorkerWriteToCacheJob::GetLoadState() const {
83 if (writer_ && writer_->IsWritePending())
84 return net::LOAD_STATE_WAITING_FOR_APPCACHE;
86 return net_request_->GetLoadState().state;
87 return net::LOAD_STATE_IDLE;
90 bool ServiceWorkerWriteToCacheJob::GetCharset(std::string* charset) {
93 return http_info()->headers->GetCharset(charset);
96 bool ServiceWorkerWriteToCacheJob::GetMimeType(std::string* mime_type) const {
99 return http_info()->headers->GetMimeType(mime_type);
102 void ServiceWorkerWriteToCacheJob::GetResponseInfo(
103 net::HttpResponseInfo* info) {
106 *info = *http_info();
109 int ServiceWorkerWriteToCacheJob::GetResponseCode() const {
112 return http_info()->headers->response_code();
115 void ServiceWorkerWriteToCacheJob::SetExtraRequestHeaders(
116 const net::HttpRequestHeaders& headers) {
118 DCHECK(!headers.GetHeader(net::HttpRequestHeaders::kRange, &value));
119 net_request_->SetExtraRequestHeaders(headers);
122 bool ServiceWorkerWriteToCacheJob::ReadRawData(
126 net::URLRequestStatus status = ReadNetData(buf, buf_size, bytes_read);
128 if (status.is_io_pending())
131 // No more data to process, the job is complete.
133 version_->script_cache_map()->NotifyFinishedCaching(
134 url_, writer_->amount_written(), status);
135 did_notify_finished_ = true;
136 return status.is_success();
139 const net::HttpResponseInfo* ServiceWorkerWriteToCacheJob::http_info() const {
140 return http_info_.get();
143 void ServiceWorkerWriteToCacheJob::InitNetRequest(
144 int extra_load_flags) {
146 net_request_ = request()->context()->CreateRequest(
148 request()->priority(),
150 this->GetCookieStore());
151 net_request_->set_first_party_for_cookies(
152 request()->first_party_for_cookies());
153 net_request_->SetReferrer(request()->referrer());
154 if (extra_load_flags)
155 net_request_->SetLoadFlags(net_request_->load_flags() | extra_load_flags);
157 if (resource_type_ == RESOURCE_TYPE_SERVICE_WORKER) {
158 // This will get copied into net_request_ when URLRequest::StartJob calls
159 // ServiceWorkerWriteToCacheJob::SetExtraRequestHeaders.
160 request()->SetExtraRequestHeaderByName("Service-Worker", "script", true);
164 void ServiceWorkerWriteToCacheJob::StartNetRequest() {
165 TRACE_EVENT_ASYNC_STEP_INTO0("ServiceWorker",
166 "ServiceWorkerWriteToCacheJob::ExecutingJob",
169 net_request_->Start(); // We'll continue in OnResponseStarted.
172 net::URLRequestStatus ServiceWorkerWriteToCacheJob::ReadNetData(
176 DCHECK_GT(buf_size, 0);
181 int net_bytes_read = 0;
182 if (!net_request_->Read(buf, buf_size, &net_bytes_read)) {
183 if (net_request_->status().is_io_pending())
184 return net_request_->status();
185 DCHECK(!net_request_->status().is_success());
186 return net_request_->status();
189 if (net_bytes_read != 0) {
190 WriteDataToCache(net_bytes_read);
191 DCHECK(GetStatus().is_io_pending());
195 DCHECK(net_request_->status().is_success());
196 return net_request_->status();
199 void ServiceWorkerWriteToCacheJob::WriteHeadersToCache() {
201 AsyncNotifyDoneHelper(net::URLRequestStatus(
202 net::URLRequestStatus::FAILED, net::ERR_FAILED));
205 TRACE_EVENT_ASYNC_STEP_INTO0("ServiceWorker",
206 "ServiceWorkerWriteToCacheJob::ExecutingJob",
208 "WriteHeadersToCache");
209 writer_ = context_->storage()->CreateResponseWriter(response_id_);
210 info_buffer_ = new HttpResponseInfoIOBuffer(
211 new net::HttpResponseInfo(net_request_->response_info()));
214 base::Bind(&ServiceWorkerWriteToCacheJob::OnWriteHeadersComplete,
215 weak_factory_.GetWeakPtr()));
216 SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0));
219 void ServiceWorkerWriteToCacheJob::OnWriteHeadersComplete(int result) {
220 SetStatus(net::URLRequestStatus()); // Clear the IO_PENDING status
222 ServiceWorkerMetrics::CountWriteResponseResult(
223 ServiceWorkerMetrics::WRITE_HEADERS_ERROR);
224 AsyncNotifyDoneHelper(net::URLRequestStatus(
225 net::URLRequestStatus::FAILED, result));
228 http_info_.reset(info_buffer_->http_info.release());
230 TRACE_EVENT_ASYNC_STEP_INTO0("ServiceWorker",
231 "ServiceWorkerWriteToCacheJob::ExecutingJob",
233 "WriteHeadersToCacheCompleted");
234 NotifyHeadersComplete();
237 void ServiceWorkerWriteToCacheJob::WriteDataToCache(int amount_to_write) {
238 DCHECK_NE(0, amount_to_write);
239 TRACE_EVENT_ASYNC_STEP_INTO1("ServiceWorker",
240 "ServiceWorkerWriteToCacheJob::ExecutingJob",
243 "Amount to write", amount_to_write);
244 SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0));
248 base::Bind(&ServiceWorkerWriteToCacheJob::OnWriteDataComplete,
249 weak_factory_.GetWeakPtr()));
252 void ServiceWorkerWriteToCacheJob::OnWriteDataComplete(int result) {
253 DCHECK_NE(0, result);
256 AsyncNotifyDoneHelper(net::URLRequestStatus(
257 net::URLRequestStatus::FAILED, net::ERR_FAILED));
261 ServiceWorkerMetrics::CountWriteResponseResult(
262 ServiceWorkerMetrics::WRITE_DATA_ERROR);
263 AsyncNotifyDoneHelper(net::URLRequestStatus(
264 net::URLRequestStatus::FAILED, result));
267 ServiceWorkerMetrics::CountWriteResponseResult(
268 ServiceWorkerMetrics::WRITE_OK);
269 SetStatus(net::URLRequestStatus()); // Clear the IO_PENDING status
270 NotifyReadComplete(result);
271 TRACE_EVENT_ASYNC_END0("ServiceWorker",
272 "ServiceWorkerWriteToCacheJob::ExecutingJob",
276 void ServiceWorkerWriteToCacheJob::OnReceivedRedirect(
277 net::URLRequest* request,
278 const net::RedirectInfo& redirect_info,
279 bool* defer_redirect) {
280 DCHECK_EQ(net_request_, request);
281 TRACE_EVENT0("ServiceWorker",
282 "ServiceWorkerWriteToCacheJob::OnReceivedRedirect");
283 // Script resources can't redirect.
284 AsyncNotifyDoneHelper(net::URLRequestStatus(
285 net::URLRequestStatus::FAILED, net::ERR_UNSAFE_REDIRECT));
288 void ServiceWorkerWriteToCacheJob::OnAuthRequired(
289 net::URLRequest* request,
290 net::AuthChallengeInfo* auth_info) {
291 DCHECK_EQ(net_request_, request);
292 TRACE_EVENT0("ServiceWorker",
293 "ServiceWorkerWriteToCacheJob::OnAuthRequired");
294 // TODO(michaeln): Pass this thru to our jobs client.
295 AsyncNotifyDoneHelper(net::URLRequestStatus(
296 net::URLRequestStatus::FAILED, net::ERR_FAILED));
299 void ServiceWorkerWriteToCacheJob::OnCertificateRequested(
300 net::URLRequest* request,
301 net::SSLCertRequestInfo* cert_request_info) {
302 DCHECK_EQ(net_request_, request);
303 TRACE_EVENT0("ServiceWorker",
304 "ServiceWorkerWriteToCacheJob::OnCertificateRequested");
305 // TODO(michaeln): Pass this thru to our jobs client.
306 // see NotifyCertificateRequested.
307 AsyncNotifyDoneHelper(net::URLRequestStatus(
308 net::URLRequestStatus::FAILED, net::ERR_FAILED));
311 void ServiceWorkerWriteToCacheJob::OnSSLCertificateError(
312 net::URLRequest* request,
313 const net::SSLInfo& ssl_info,
315 DCHECK_EQ(net_request_, request);
316 TRACE_EVENT0("ServiceWorker",
317 "ServiceWorkerWriteToCacheJob::OnSSLCertificateError");
318 // TODO(michaeln): Pass this thru to our jobs client,
319 // see NotifySSLCertificateError.
320 AsyncNotifyDoneHelper(net::URLRequestStatus(
321 net::URLRequestStatus::FAILED, net::ERR_INSECURE_RESPONSE));
324 void ServiceWorkerWriteToCacheJob::OnBeforeNetworkStart(
325 net::URLRequest* request,
327 DCHECK_EQ(net_request_, request);
328 TRACE_EVENT0("ServiceWorker",
329 "ServiceWorkerWriteToCacheJob::OnBeforeNetworkStart");
330 NotifyBeforeNetworkStart(defer);
333 void ServiceWorkerWriteToCacheJob::OnResponseStarted(
334 net::URLRequest* request) {
335 DCHECK_EQ(net_request_, request);
336 if (!request->status().is_success()) {
337 AsyncNotifyDoneHelper(request->status());
340 if (request->GetResponseCode() / 100 != 2) {
341 AsyncNotifyDoneHelper(net::URLRequestStatus(
342 net::URLRequestStatus::FAILED, net::ERR_FAILED));
343 // TODO(michaeln): Instead of error'ing immediately, send the net
344 // response to our consumer, just don't cache it?
347 // OnSSLCertificateError is not called when the HTTPS connection is reused.
348 // So we check cert_status here.
349 if (net::IsCertStatusError(request->ssl_info().cert_status)) {
350 const net::HttpNetworkSession::Params* session_params =
351 request->context()->GetNetworkSessionParams();
352 if (!session_params || !session_params->ignore_certificate_errors) {
353 AsyncNotifyDoneHelper(net::URLRequestStatus(net::URLRequestStatus::FAILED,
354 net::ERR_INSECURE_RESPONSE));
358 // To prevent most user-uploaded content from being used as a serviceworker.
359 if (version_->script_url() == url_) {
360 std::string mime_type;
361 request->GetMimeType(&mime_type);
362 if (mime_type != "application/x-javascript" &&
363 mime_type != "text/javascript" &&
364 mime_type != "application/javascript") {
365 AsyncNotifyDoneHelper(net::URLRequestStatus(
366 net::URLRequestStatus::FAILED, net::ERR_INSECURE_RESPONSE));
370 WriteHeadersToCache();
373 void ServiceWorkerWriteToCacheJob::OnReadCompleted(
374 net::URLRequest* request,
376 DCHECK_EQ(net_request_, request);
377 if (bytes_read < 0) {
378 DCHECK(!request->status().is_success());
379 AsyncNotifyDoneHelper(request->status());
382 if (bytes_read > 0) {
383 WriteDataToCache(bytes_read);
386 // No more data to process, the job is complete.
387 DCHECK(request->status().is_success());
389 version_->script_cache_map()->NotifyFinishedCaching(
390 url_, writer_->amount_written(), net::URLRequestStatus());
391 did_notify_finished_ = true;
392 SetStatus(net::URLRequestStatus()); // Clear the IO_PENDING status
393 NotifyReadComplete(0);
396 void ServiceWorkerWriteToCacheJob::AsyncNotifyDoneHelper(
397 const net::URLRequestStatus& status) {
398 DCHECK(!status.is_io_pending());
399 DCHECK(!did_notify_finished_);
402 size = writer_->amount_written();
404 version_->script_cache_map()->NotifyFinishedCaching(url_, size, status);
405 did_notify_finished_ = true;
410 } // namespace content