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_metrics.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 ResourceType resource_type,
25 base::WeakPtr<ServiceWorkerContextCore> context,
26 ServiceWorkerVersion* version,
29 : net::URLRequestJob(request, network_delegate),
30 resource_type_(resource_type),
33 response_id_(response_id),
35 has_been_killed_(false),
36 did_notify_started_(false),
37 did_notify_finished_(false),
39 InitNetRequest(extra_load_flags);
42 ServiceWorkerWriteToCacheJob::~ServiceWorkerWriteToCacheJob() {
43 DCHECK_EQ(did_notify_started_, did_notify_finished_);
46 void ServiceWorkerWriteToCacheJob::Start() {
48 NotifyStartError(net::URLRequestStatus(
49 net::URLRequestStatus::FAILED, net::ERR_FAILED));
52 version_->script_cache_map()->NotifyStartedCaching(
54 did_notify_started_ = true;
58 void ServiceWorkerWriteToCacheJob::Kill() {
61 weak_factory_.InvalidateWeakPtrs();
62 has_been_killed_ = true;
64 if (did_notify_started_ && !did_notify_finished_) {
65 version_->script_cache_map()->NotifyFinishedCaching(
67 did_notify_finished_ = true;
71 net::URLRequestJob::Kill();
74 net::LoadState ServiceWorkerWriteToCacheJob::GetLoadState() const {
75 if (writer_ && writer_->IsWritePending())
76 return net::LOAD_STATE_WAITING_FOR_APPCACHE;
78 return net_request_->GetLoadState().state;
79 return net::LOAD_STATE_IDLE;
82 bool ServiceWorkerWriteToCacheJob::GetCharset(std::string* charset) {
85 return http_info()->headers->GetCharset(charset);
88 bool ServiceWorkerWriteToCacheJob::GetMimeType(std::string* mime_type) const {
91 return http_info()->headers->GetMimeType(mime_type);
94 void ServiceWorkerWriteToCacheJob::GetResponseInfo(
95 net::HttpResponseInfo* info) {
101 int ServiceWorkerWriteToCacheJob::GetResponseCode() const {
104 return http_info()->headers->response_code();
107 void ServiceWorkerWriteToCacheJob::SetExtraRequestHeaders(
108 const net::HttpRequestHeaders& headers) {
110 DCHECK(!headers.GetHeader(net::HttpRequestHeaders::kRange, &value));
111 net_request_->SetExtraRequestHeaders(headers);
114 bool ServiceWorkerWriteToCacheJob::ReadRawData(
118 net::URLRequestStatus status = ReadNetData(buf, buf_size, bytes_read);
120 if (status.is_io_pending())
123 // No more data to process, the job is complete.
125 version_->script_cache_map()->NotifyFinishedCaching(
126 url_, status.is_success());
127 did_notify_finished_ = true;
128 return status.is_success();
131 const net::HttpResponseInfo* ServiceWorkerWriteToCacheJob::http_info() const {
132 return http_info_.get();
135 void ServiceWorkerWriteToCacheJob::InitNetRequest(
136 int extra_load_flags) {
138 net_request_ = request()->context()->CreateRequest(
140 request()->priority(),
142 this->GetCookieStore());
143 net_request_->set_first_party_for_cookies(
144 request()->first_party_for_cookies());
145 net_request_->SetReferrer(request()->referrer());
146 if (extra_load_flags)
147 net_request_->SetLoadFlags(net_request_->load_flags() | extra_load_flags);
149 if (resource_type_ == RESOURCE_TYPE_SERVICE_WORKER) {
150 // This will get copied into net_request_ when URLRequest::StartJob calls
151 // ServiceWorkerWriteToCacheJob::SetExtraRequestHeaders.
152 request()->SetExtraRequestHeaderByName("Service-Worker", "script", true);
156 void ServiceWorkerWriteToCacheJob::StartNetRequest() {
157 net_request_->Start(); // We'll continue in OnResponseStarted.
160 net::URLRequestStatus ServiceWorkerWriteToCacheJob::ReadNetData(
164 DCHECK_GT(buf_size, 0);
169 int net_bytes_read = 0;
170 if (!net_request_->Read(buf, buf_size, &net_bytes_read)) {
171 if (net_request_->status().is_io_pending())
172 return net_request_->status();
173 DCHECK(!net_request_->status().is_success());
174 return net_request_->status();
177 if (net_bytes_read != 0) {
178 WriteDataToCache(net_bytes_read);
179 DCHECK(GetStatus().is_io_pending());
183 DCHECK(net_request_->status().is_success());
184 return net_request_->status();
187 void ServiceWorkerWriteToCacheJob::WriteHeadersToCache() {
189 AsyncNotifyDoneHelper(net::URLRequestStatus(
190 net::URLRequestStatus::FAILED, net::ERR_FAILED));
193 writer_ = context_->storage()->CreateResponseWriter(response_id_);
194 info_buffer_ = new HttpResponseInfoIOBuffer(
195 new net::HttpResponseInfo(net_request_->response_info()));
198 base::Bind(&ServiceWorkerWriteToCacheJob::OnWriteHeadersComplete,
199 weak_factory_.GetWeakPtr()));
200 SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0));
203 void ServiceWorkerWriteToCacheJob::OnWriteHeadersComplete(int result) {
204 SetStatus(net::URLRequestStatus()); // Clear the IO_PENDING status
206 ServiceWorkerMetrics::CountWriteResponseResult(
207 ServiceWorkerMetrics::WRITE_HEADERS_ERROR);
208 AsyncNotifyDoneHelper(net::URLRequestStatus(
209 net::URLRequestStatus::FAILED, result));
212 http_info_.reset(info_buffer_->http_info.release());
214 NotifyHeadersComplete();
217 void ServiceWorkerWriteToCacheJob::WriteDataToCache(int amount_to_write) {
218 DCHECK_NE(0, amount_to_write);
219 SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0));
221 io_buffer_, amount_to_write,
222 base::Bind(&ServiceWorkerWriteToCacheJob::OnWriteDataComplete,
223 weak_factory_.GetWeakPtr()));
226 void ServiceWorkerWriteToCacheJob::OnWriteDataComplete(int result) {
227 DCHECK_NE(0, result);
230 AsyncNotifyDoneHelper(net::URLRequestStatus(
231 net::URLRequestStatus::FAILED, net::ERR_FAILED));
235 ServiceWorkerMetrics::CountWriteResponseResult(
236 ServiceWorkerMetrics::WRITE_DATA_ERROR);
237 AsyncNotifyDoneHelper(net::URLRequestStatus(
238 net::URLRequestStatus::FAILED, result));
241 ServiceWorkerMetrics::CountWriteResponseResult(
242 ServiceWorkerMetrics::WRITE_OK);
243 SetStatus(net::URLRequestStatus()); // Clear the IO_PENDING status
244 NotifyReadComplete(result);
247 void ServiceWorkerWriteToCacheJob::OnReceivedRedirect(
248 net::URLRequest* request,
249 const net::RedirectInfo& redirect_info,
250 bool* defer_redirect) {
251 DCHECK_EQ(net_request_, request);
252 // Script resources can't redirect.
253 AsyncNotifyDoneHelper(net::URLRequestStatus(
254 net::URLRequestStatus::FAILED, net::ERR_FAILED));
257 void ServiceWorkerWriteToCacheJob::OnAuthRequired(
258 net::URLRequest* request,
259 net::AuthChallengeInfo* auth_info) {
260 DCHECK_EQ(net_request_, request);
261 // TODO(michaeln): Pass this thru to our jobs client.
262 AsyncNotifyDoneHelper(net::URLRequestStatus(
263 net::URLRequestStatus::FAILED, net::ERR_FAILED));
266 void ServiceWorkerWriteToCacheJob::OnCertificateRequested(
267 net::URLRequest* request,
268 net::SSLCertRequestInfo* cert_request_info) {
269 DCHECK_EQ(net_request_, request);
270 // TODO(michaeln): Pass this thru to our jobs client.
271 // see NotifyCertificateRequested.
272 AsyncNotifyDoneHelper(net::URLRequestStatus(
273 net::URLRequestStatus::FAILED, net::ERR_FAILED));
276 void ServiceWorkerWriteToCacheJob:: OnSSLCertificateError(
277 net::URLRequest* request,
278 const net::SSLInfo& ssl_info,
280 DCHECK_EQ(net_request_, request);
281 // TODO(michaeln): Pass this thru to our jobs client,
282 // see NotifySSLCertificateError.
283 AsyncNotifyDoneHelper(net::URLRequestStatus(
284 net::URLRequestStatus::FAILED, net::ERR_FAILED));
287 void ServiceWorkerWriteToCacheJob::OnBeforeNetworkStart(
288 net::URLRequest* request,
290 DCHECK_EQ(net_request_, request);
291 NotifyBeforeNetworkStart(defer);
294 void ServiceWorkerWriteToCacheJob::OnResponseStarted(
295 net::URLRequest* request) {
296 DCHECK_EQ(net_request_, request);
297 if (!request->status().is_success()) {
298 AsyncNotifyDoneHelper(request->status());
301 if (request->GetResponseCode() / 100 != 2) {
302 AsyncNotifyDoneHelper(net::URLRequestStatus(
303 net::URLRequestStatus::FAILED, net::ERR_FAILED));
304 // TODO(michaeln): Instead of error'ing immediately, send the net
305 // response to our consumer, just don't cache it?
308 // To prevent most user-uploaded content from being used as a serviceworker.
309 if (version_->script_url() == url_) {
310 std::string mime_type;
311 request->GetMimeType(&mime_type);
312 if (mime_type != "application/x-javascript" &&
313 mime_type != "text/javascript" &&
314 mime_type != "application/javascript") {
315 AsyncNotifyDoneHelper(net::URLRequestStatus(
316 net::URLRequestStatus::FAILED, net::ERR_FAILED));
320 WriteHeadersToCache();
323 void ServiceWorkerWriteToCacheJob::OnReadCompleted(
324 net::URLRequest* request,
326 DCHECK_EQ(net_request_, request);
327 if (!request->status().is_success()) {
328 AsyncNotifyDoneHelper(request->status());
331 if (bytes_read > 0) {
332 WriteDataToCache(bytes_read);
335 // We're done with all.
336 AsyncNotifyDoneHelper(request->status());
340 void ServiceWorkerWriteToCacheJob::AsyncNotifyDoneHelper(
341 const net::URLRequestStatus& status) {
342 DCHECK(!status.is_io_pending());
343 version_->script_cache_map()->NotifyFinishedCaching(
344 url_, status.is_success());
345 did_notify_finished_ = true;
350 } // namespace content