Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / content / browser / service_worker / service_worker_write_to_cache_job.cc
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.
4
5 #include "content/browser/service_worker/service_worker_write_to_cache_job.h"
6
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"
20
21 namespace content {
22
23 ServiceWorkerWriteToCacheJob::ServiceWorkerWriteToCacheJob(
24     net::URLRequest* request,
25     net::NetworkDelegate* network_delegate,
26     ResourceType resource_type,
27     base::WeakPtr<ServiceWorkerContextCore> context,
28     ServiceWorkerVersion* version,
29     int extra_load_flags,
30     int64 response_id)
31     : net::URLRequestJob(request, network_delegate),
32       resource_type_(resource_type),
33       context_(context),
34       url_(request->url()),
35       response_id_(response_id),
36       version_(version),
37       has_been_killed_(false),
38       did_notify_started_(false),
39       did_notify_finished_(false),
40       weak_factory_(this) {
41   InitNetRequest(extra_load_flags);
42 }
43
44 ServiceWorkerWriteToCacheJob::~ServiceWorkerWriteToCacheJob() {
45   DCHECK_EQ(did_notify_started_, did_notify_finished_);
46 }
47
48 void ServiceWorkerWriteToCacheJob::Start() {
49   TRACE_EVENT_ASYNC_BEGIN1("ServiceWorker",
50                            "ServiceWorkerWriteToCacheJob::ExecutingJob",
51                            this,
52                            "URL", request_->url().spec());
53   if (!context_) {
54     NotifyStartError(net::URLRequestStatus(
55         net::URLRequestStatus::FAILED, net::ERR_FAILED));
56     return;
57   }
58   version_->script_cache_map()->NotifyStartedCaching(
59       url_, response_id_);
60   did_notify_started_ = true;
61   StartNetRequest();
62 }
63
64 void ServiceWorkerWriteToCacheJob::Kill() {
65   if (has_been_killed_)
66     return;
67   weak_factory_.InvalidateWeakPtrs();
68   has_been_killed_ = true;
69   net_request_.reset();
70   if (did_notify_started_ && !did_notify_finished_) {
71     version_->script_cache_map()->NotifyFinishedCaching(
72         url_,
73         -1,
74         net::URLRequestStatus(net::URLRequestStatus::FAILED, net::ERR_ABORTED));
75     did_notify_finished_ = true;
76   }
77   writer_.reset();
78   context_.reset();
79   net::URLRequestJob::Kill();
80 }
81
82 net::LoadState ServiceWorkerWriteToCacheJob::GetLoadState() const {
83   if (writer_ && writer_->IsWritePending())
84     return net::LOAD_STATE_WAITING_FOR_APPCACHE;
85   if (net_request_)
86     return net_request_->GetLoadState().state;
87   return net::LOAD_STATE_IDLE;
88 }
89
90 bool ServiceWorkerWriteToCacheJob::GetCharset(std::string* charset) {
91   if (!http_info())
92     return false;
93   return http_info()->headers->GetCharset(charset);
94 }
95
96 bool ServiceWorkerWriteToCacheJob::GetMimeType(std::string* mime_type) const {
97   if (!http_info())
98     return false;
99   return http_info()->headers->GetMimeType(mime_type);
100 }
101
102 void ServiceWorkerWriteToCacheJob::GetResponseInfo(
103     net::HttpResponseInfo* info) {
104   if (!http_info())
105     return;
106   *info = *http_info();
107 }
108
109 int ServiceWorkerWriteToCacheJob::GetResponseCode() const {
110   if (!http_info())
111     return -1;
112   return http_info()->headers->response_code();
113 }
114
115 void ServiceWorkerWriteToCacheJob::SetExtraRequestHeaders(
116       const net::HttpRequestHeaders& headers) {
117   std::string value;
118   DCHECK(!headers.GetHeader(net::HttpRequestHeaders::kRange, &value));
119   net_request_->SetExtraRequestHeaders(headers);
120 }
121
122 bool ServiceWorkerWriteToCacheJob::ReadRawData(
123     net::IOBuffer* buf,
124     int buf_size,
125     int *bytes_read) {
126   net::URLRequestStatus status = ReadNetData(buf, buf_size, bytes_read);
127   SetStatus(status);
128   if (status.is_io_pending())
129     return false;
130
131   // No more data to process, the job is complete.
132   io_buffer_ = NULL;
133   version_->script_cache_map()->NotifyFinishedCaching(
134       url_, writer_->amount_written(), status);
135   did_notify_finished_ = true;
136   return status.is_success();
137 }
138
139 const net::HttpResponseInfo* ServiceWorkerWriteToCacheJob::http_info() const {
140   return http_info_.get();
141 }
142
143 void ServiceWorkerWriteToCacheJob::InitNetRequest(
144     int extra_load_flags) {
145   DCHECK(request());
146   net_request_ = request()->context()->CreateRequest(
147       request()->url(),
148       request()->priority(),
149       this,
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);
156
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);
161   }
162 }
163
164 void ServiceWorkerWriteToCacheJob::StartNetRequest() {
165   TRACE_EVENT_ASYNC_STEP_INTO0("ServiceWorker",
166                                "ServiceWorkerWriteToCacheJob::ExecutingJob",
167                                this,
168                                "NetRequest");
169   net_request_->Start();  // We'll continue in OnResponseStarted.
170 }
171
172 net::URLRequestStatus ServiceWorkerWriteToCacheJob::ReadNetData(
173     net::IOBuffer* buf,
174     int buf_size,
175     int *bytes_read) {
176   DCHECK_GT(buf_size, 0);
177   DCHECK(bytes_read);
178
179   *bytes_read = 0;
180   io_buffer_ = buf;
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();
187   }
188
189   if (net_bytes_read != 0) {
190     WriteDataToCache(net_bytes_read);
191     DCHECK(GetStatus().is_io_pending());
192     return GetStatus();
193   }
194
195   DCHECK(net_request_->status().is_success());
196   return net_request_->status();
197 }
198
199 void ServiceWorkerWriteToCacheJob::WriteHeadersToCache() {
200   if (!context_) {
201     AsyncNotifyDoneHelper(net::URLRequestStatus(
202         net::URLRequestStatus::FAILED, net::ERR_FAILED));
203     return;
204   }
205   TRACE_EVENT_ASYNC_STEP_INTO0("ServiceWorker",
206                                "ServiceWorkerWriteToCacheJob::ExecutingJob",
207                                this,
208                                "WriteHeadersToCache");
209   writer_ = context_->storage()->CreateResponseWriter(response_id_);
210   info_buffer_ = new HttpResponseInfoIOBuffer(
211       new net::HttpResponseInfo(net_request_->response_info()));
212   writer_->WriteInfo(
213       info_buffer_.get(),
214       base::Bind(&ServiceWorkerWriteToCacheJob::OnWriteHeadersComplete,
215                  weak_factory_.GetWeakPtr()));
216   SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0));
217 }
218
219 void ServiceWorkerWriteToCacheJob::OnWriteHeadersComplete(int result) {
220   SetStatus(net::URLRequestStatus());  // Clear the IO_PENDING status
221   if (result < 0) {
222     ServiceWorkerMetrics::CountWriteResponseResult(
223         ServiceWorkerMetrics::WRITE_HEADERS_ERROR);
224     AsyncNotifyDoneHelper(net::URLRequestStatus(
225         net::URLRequestStatus::FAILED, result));
226     return;
227   }
228   http_info_.reset(info_buffer_->http_info.release());
229   info_buffer_ = NULL;
230   TRACE_EVENT_ASYNC_STEP_INTO0("ServiceWorker",
231                                "ServiceWorkerWriteToCacheJob::ExecutingJob",
232                                this,
233                                "WriteHeadersToCacheCompleted");
234   NotifyHeadersComplete();
235 }
236
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",
241                                this,
242                                "WriteDataToCache",
243                                "Amount to write", amount_to_write);
244   SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0));
245   writer_->WriteData(
246       io_buffer_.get(),
247       amount_to_write,
248       base::Bind(&ServiceWorkerWriteToCacheJob::OnWriteDataComplete,
249                  weak_factory_.GetWeakPtr()));
250 }
251
252 void ServiceWorkerWriteToCacheJob::OnWriteDataComplete(int result) {
253   DCHECK_NE(0, result);
254   io_buffer_ = NULL;
255   if (!context_) {
256     AsyncNotifyDoneHelper(net::URLRequestStatus(
257         net::URLRequestStatus::FAILED, net::ERR_FAILED));
258     return;
259   }
260   if (result < 0) {
261     ServiceWorkerMetrics::CountWriteResponseResult(
262         ServiceWorkerMetrics::WRITE_DATA_ERROR);
263     AsyncNotifyDoneHelper(net::URLRequestStatus(
264         net::URLRequestStatus::FAILED, result));
265     return;
266   }
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",
273                          this);
274 }
275
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));
286 }
287
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));
297 }
298
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));
309 }
310
311 void ServiceWorkerWriteToCacheJob::OnSSLCertificateError(
312     net::URLRequest* request,
313     const net::SSLInfo& ssl_info,
314     bool fatal) {
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));
322 }
323
324 void ServiceWorkerWriteToCacheJob::OnBeforeNetworkStart(
325     net::URLRequest* request,
326     bool* defer) {
327   DCHECK_EQ(net_request_, request);
328   TRACE_EVENT0("ServiceWorker",
329                "ServiceWorkerWriteToCacheJob::OnBeforeNetworkStart");
330   NotifyBeforeNetworkStart(defer);
331 }
332
333 void ServiceWorkerWriteToCacheJob::OnResponseStarted(
334     net::URLRequest* request) {
335   DCHECK_EQ(net_request_, request);
336   if (!request->status().is_success()) {
337     AsyncNotifyDoneHelper(request->status());
338     return;
339   }
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?
345     return;
346   }
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));
355       return;
356     }
357   }
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));
367       return;
368     }
369   }
370   WriteHeadersToCache();
371 }
372
373 void ServiceWorkerWriteToCacheJob::OnReadCompleted(
374     net::URLRequest* request,
375     int bytes_read) {
376   DCHECK_EQ(net_request_, request);
377   if (bytes_read < 0) {
378     DCHECK(!request->status().is_success());
379     AsyncNotifyDoneHelper(request->status());
380     return;
381   }
382   if (bytes_read > 0) {
383     WriteDataToCache(bytes_read);
384     return;
385   }
386   // No more data to process, the job is complete.
387   DCHECK(request->status().is_success());
388   io_buffer_ = NULL;
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);
394 }
395
396 void ServiceWorkerWriteToCacheJob::AsyncNotifyDoneHelper(
397     const net::URLRequestStatus& status) {
398   DCHECK(!status.is_io_pending());
399   DCHECK(!did_notify_finished_);
400   int size = -1;
401   if (writer_.get()) {
402     size = writer_->amount_written();
403   }
404   version_->script_cache_map()->NotifyFinishedCaching(url_, size, status);
405   did_notify_finished_ = true;
406   SetStatus(status);
407   NotifyDone(status);
408 }
409
410 }  // namespace content