Upstream version 10.39.225.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_request_headers.h"
14 #include "net/http/http_response_headers.h"
15 #include "net/http/http_util.h"
16 #include "net/url_request/url_request.h"
17 #include "net/url_request/url_request_context.h"
18 #include "net/url_request/url_request_status.h"
19
20 namespace content {
21
22 ServiceWorkerWriteToCacheJob::ServiceWorkerWriteToCacheJob(
23     net::URLRequest* request,
24     net::NetworkDelegate* network_delegate,
25     ResourceType resource_type,
26     base::WeakPtr<ServiceWorkerContextCore> context,
27     ServiceWorkerVersion* version,
28     int extra_load_flags,
29     int64 response_id)
30     : net::URLRequestJob(request, network_delegate),
31       resource_type_(resource_type),
32       context_(context),
33       url_(request->url()),
34       response_id_(response_id),
35       version_(version),
36       has_been_killed_(false),
37       did_notify_started_(false),
38       did_notify_finished_(false),
39       weak_factory_(this) {
40   InitNetRequest(extra_load_flags);
41 }
42
43 ServiceWorkerWriteToCacheJob::~ServiceWorkerWriteToCacheJob() {
44   DCHECK_EQ(did_notify_started_, did_notify_finished_);
45 }
46
47 void ServiceWorkerWriteToCacheJob::Start() {
48   TRACE_EVENT_ASYNC_BEGIN1("ServiceWorker",
49                            "ServiceWorkerWriteToCacheJob::ExecutingJob",
50                            this,
51                            "URL", request_->url().spec());
52   if (!context_) {
53     NotifyStartError(net::URLRequestStatus(
54         net::URLRequestStatus::FAILED, net::ERR_FAILED));
55     return;
56   }
57   version_->script_cache_map()->NotifyStartedCaching(
58       url_, response_id_);
59   did_notify_started_ = true;
60   StartNetRequest();
61 }
62
63 void ServiceWorkerWriteToCacheJob::Kill() {
64   if (has_been_killed_)
65     return;
66   weak_factory_.InvalidateWeakPtrs();
67   has_been_killed_ = true;
68   net_request_.reset();
69   if (did_notify_started_ && !did_notify_finished_) {
70     version_->script_cache_map()->NotifyFinishedCaching(
71         url_,
72         net::URLRequestStatus(net::URLRequestStatus::FAILED, net::ERR_ABORTED));
73     did_notify_finished_ = true;
74   }
75   writer_.reset();
76   context_.reset();
77   net::URLRequestJob::Kill();
78 }
79
80 net::LoadState ServiceWorkerWriteToCacheJob::GetLoadState() const {
81   if (writer_ && writer_->IsWritePending())
82     return net::LOAD_STATE_WAITING_FOR_APPCACHE;
83   if (net_request_)
84     return net_request_->GetLoadState().state;
85   return net::LOAD_STATE_IDLE;
86 }
87
88 bool ServiceWorkerWriteToCacheJob::GetCharset(std::string* charset) {
89   if (!http_info())
90     return false;
91   return http_info()->headers->GetCharset(charset);
92 }
93
94 bool ServiceWorkerWriteToCacheJob::GetMimeType(std::string* mime_type) const {
95   if (!http_info())
96     return false;
97   return http_info()->headers->GetMimeType(mime_type);
98 }
99
100 void ServiceWorkerWriteToCacheJob::GetResponseInfo(
101     net::HttpResponseInfo* info) {
102   if (!http_info())
103     return;
104   *info = *http_info();
105 }
106
107 int ServiceWorkerWriteToCacheJob::GetResponseCode() const {
108   if (!http_info())
109     return -1;
110   return http_info()->headers->response_code();
111 }
112
113 void ServiceWorkerWriteToCacheJob::SetExtraRequestHeaders(
114       const net::HttpRequestHeaders& headers) {
115   std::string value;
116   DCHECK(!headers.GetHeader(net::HttpRequestHeaders::kRange, &value));
117   net_request_->SetExtraRequestHeaders(headers);
118 }
119
120 bool ServiceWorkerWriteToCacheJob::ReadRawData(
121     net::IOBuffer* buf,
122     int buf_size,
123     int *bytes_read) {
124   net::URLRequestStatus status = ReadNetData(buf, buf_size, bytes_read);
125   SetStatus(status);
126   if (status.is_io_pending())
127     return false;
128
129   // No more data to process, the job is complete.
130   io_buffer_ = NULL;
131   version_->script_cache_map()->NotifyFinishedCaching(
132       url_, net::URLRequestStatus());
133   did_notify_finished_ = true;
134   return status.is_success();
135 }
136
137 const net::HttpResponseInfo* ServiceWorkerWriteToCacheJob::http_info() const {
138   return http_info_.get();
139 }
140
141 void ServiceWorkerWriteToCacheJob::InitNetRequest(
142     int extra_load_flags) {
143   DCHECK(request());
144   net_request_ = request()->context()->CreateRequest(
145       request()->url(),
146       request()->priority(),
147       this,
148       this->GetCookieStore());
149   net_request_->set_first_party_for_cookies(
150       request()->first_party_for_cookies());
151   net_request_->SetReferrer(request()->referrer());
152   if (extra_load_flags)
153     net_request_->SetLoadFlags(net_request_->load_flags() | extra_load_flags);
154
155   if (resource_type_ == RESOURCE_TYPE_SERVICE_WORKER) {
156     // This will get copied into net_request_ when URLRequest::StartJob calls
157     // ServiceWorkerWriteToCacheJob::SetExtraRequestHeaders.
158     request()->SetExtraRequestHeaderByName("Service-Worker", "script", true);
159   }
160 }
161
162 void ServiceWorkerWriteToCacheJob::StartNetRequest() {
163   TRACE_EVENT_ASYNC_STEP_INTO0("ServiceWorker",
164                                "ServiceWorkerWriteToCacheJob::ExecutingJob",
165                                this,
166                                "NetRequest");
167   net_request_->Start();  // We'll continue in OnResponseStarted.
168 }
169
170 net::URLRequestStatus ServiceWorkerWriteToCacheJob::ReadNetData(
171     net::IOBuffer* buf,
172     int buf_size,
173     int *bytes_read) {
174   DCHECK_GT(buf_size, 0);
175   DCHECK(bytes_read);
176
177   *bytes_read = 0;
178   io_buffer_ = buf;
179   int net_bytes_read = 0;
180   if (!net_request_->Read(buf, buf_size, &net_bytes_read)) {
181     if (net_request_->status().is_io_pending())
182       return net_request_->status();
183     DCHECK(!net_request_->status().is_success());
184     return net_request_->status();
185   }
186
187   if (net_bytes_read != 0) {
188     WriteDataToCache(net_bytes_read);
189     DCHECK(GetStatus().is_io_pending());
190     return GetStatus();
191   }
192
193   DCHECK(net_request_->status().is_success());
194   return net_request_->status();
195 }
196
197 void ServiceWorkerWriteToCacheJob::WriteHeadersToCache() {
198   if (!context_) {
199     AsyncNotifyDoneHelper(net::URLRequestStatus(
200         net::URLRequestStatus::FAILED, net::ERR_FAILED));
201     return;
202   }
203   TRACE_EVENT_ASYNC_STEP_INTO0("ServiceWorker",
204                                "ServiceWorkerWriteToCacheJob::ExecutingJob",
205                                this,
206                                "WriteHeadersToCache");
207   writer_ = context_->storage()->CreateResponseWriter(response_id_);
208   info_buffer_ = new HttpResponseInfoIOBuffer(
209       new net::HttpResponseInfo(net_request_->response_info()));
210   writer_->WriteInfo(
211       info_buffer_.get(),
212       base::Bind(&ServiceWorkerWriteToCacheJob::OnWriteHeadersComplete,
213                  weak_factory_.GetWeakPtr()));
214   SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0));
215 }
216
217 void ServiceWorkerWriteToCacheJob::OnWriteHeadersComplete(int result) {
218   SetStatus(net::URLRequestStatus());  // Clear the IO_PENDING status
219   if (result < 0) {
220     ServiceWorkerMetrics::CountWriteResponseResult(
221         ServiceWorkerMetrics::WRITE_HEADERS_ERROR);
222     AsyncNotifyDoneHelper(net::URLRequestStatus(
223         net::URLRequestStatus::FAILED, result));
224     return;
225   }
226   http_info_.reset(info_buffer_->http_info.release());
227   info_buffer_ = NULL;
228   TRACE_EVENT_ASYNC_STEP_INTO0("ServiceWorker",
229                                "ServiceWorkerWriteToCacheJob::ExecutingJob",
230                                this,
231                                "WriteHeadersToCacheCompleted");
232   NotifyHeadersComplete();
233 }
234
235 void ServiceWorkerWriteToCacheJob::WriteDataToCache(int amount_to_write) {
236   DCHECK_NE(0, amount_to_write);
237   TRACE_EVENT_ASYNC_STEP_INTO1("ServiceWorker",
238                                "ServiceWorkerWriteToCacheJob::ExecutingJob",
239                                this,
240                                "WriteDataToCache",
241                                "Amount to write", amount_to_write);
242   SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0));
243   writer_->WriteData(
244       io_buffer_.get(),
245       amount_to_write,
246       base::Bind(&ServiceWorkerWriteToCacheJob::OnWriteDataComplete,
247                  weak_factory_.GetWeakPtr()));
248 }
249
250 void ServiceWorkerWriteToCacheJob::OnWriteDataComplete(int result) {
251   DCHECK_NE(0, result);
252   io_buffer_ = NULL;
253   if (!context_) {
254     AsyncNotifyDoneHelper(net::URLRequestStatus(
255         net::URLRequestStatus::FAILED, net::ERR_FAILED));
256     return;
257   }
258   if (result < 0) {
259     ServiceWorkerMetrics::CountWriteResponseResult(
260         ServiceWorkerMetrics::WRITE_DATA_ERROR);
261     AsyncNotifyDoneHelper(net::URLRequestStatus(
262         net::URLRequestStatus::FAILED, result));
263     return;
264   }
265   ServiceWorkerMetrics::CountWriteResponseResult(
266       ServiceWorkerMetrics::WRITE_OK);
267   SetStatus(net::URLRequestStatus());  // Clear the IO_PENDING status
268   NotifyReadComplete(result);
269   TRACE_EVENT_ASYNC_END0("ServiceWorker",
270                          "ServiceWorkerWriteToCacheJob::ExecutingJob",
271                          this);
272 }
273
274 void ServiceWorkerWriteToCacheJob::OnReceivedRedirect(
275     net::URLRequest* request,
276     const net::RedirectInfo& redirect_info,
277     bool* defer_redirect) {
278   DCHECK_EQ(net_request_, request);
279   TRACE_EVENT0("ServiceWorker",
280                "ServiceWorkerWriteToCacheJob::OnReceivedRedirect");
281   // Script resources can't redirect.
282   AsyncNotifyDoneHelper(net::URLRequestStatus(
283       net::URLRequestStatus::FAILED, net::ERR_UNSAFE_REDIRECT));
284 }
285
286 void ServiceWorkerWriteToCacheJob::OnAuthRequired(
287     net::URLRequest* request,
288     net::AuthChallengeInfo* auth_info) {
289   DCHECK_EQ(net_request_, request);
290   TRACE_EVENT0("ServiceWorker",
291                "ServiceWorkerWriteToCacheJob::OnAuthRequired");
292   // TODO(michaeln): Pass this thru to our jobs client.
293   AsyncNotifyDoneHelper(net::URLRequestStatus(
294       net::URLRequestStatus::FAILED, net::ERR_FAILED));
295 }
296
297 void ServiceWorkerWriteToCacheJob::OnCertificateRequested(
298     net::URLRequest* request,
299     net::SSLCertRequestInfo* cert_request_info) {
300   DCHECK_EQ(net_request_, request);
301   TRACE_EVENT0("ServiceWorker",
302                "ServiceWorkerWriteToCacheJob::OnCertificateRequested");
303   // TODO(michaeln): Pass this thru to our jobs client.
304   // see NotifyCertificateRequested.
305   AsyncNotifyDoneHelper(net::URLRequestStatus(
306       net::URLRequestStatus::FAILED, net::ERR_FAILED));
307 }
308
309 void ServiceWorkerWriteToCacheJob:: OnSSLCertificateError(
310     net::URLRequest* request,
311     const net::SSLInfo& ssl_info,
312     bool fatal) {
313   DCHECK_EQ(net_request_, request);
314   TRACE_EVENT0("ServiceWorker",
315                "ServiceWorkerWriteToCacheJob::OnSSLCertificateError");
316   // TODO(michaeln): Pass this thru to our jobs client,
317   // see NotifySSLCertificateError.
318   AsyncNotifyDoneHelper(net::URLRequestStatus(
319       net::URLRequestStatus::FAILED, net::ERR_FAILED));
320 }
321
322 void ServiceWorkerWriteToCacheJob::OnBeforeNetworkStart(
323     net::URLRequest* request,
324     bool* defer) {
325   DCHECK_EQ(net_request_, request);
326   TRACE_EVENT0("ServiceWorker",
327                "ServiceWorkerWriteToCacheJob::OnBeforeNetworkStart");
328   NotifyBeforeNetworkStart(defer);
329 }
330
331 void ServiceWorkerWriteToCacheJob::OnResponseStarted(
332     net::URLRequest* request) {
333   DCHECK_EQ(net_request_, request);
334   if (!request->status().is_success()) {
335     AsyncNotifyDoneHelper(request->status());
336     return;
337   }
338   if (request->GetResponseCode() / 100 != 2) {
339     AsyncNotifyDoneHelper(net::URLRequestStatus(
340         net::URLRequestStatus::FAILED, net::ERR_FAILED));
341     // TODO(michaeln): Instead of error'ing immediately, send the net
342     // response to our consumer, just don't cache it?
343     return;
344   }
345   // To prevent most user-uploaded content from being used as a serviceworker.
346   if (version_->script_url() == url_) {
347     std::string mime_type;
348     request->GetMimeType(&mime_type);
349     if (mime_type != "application/x-javascript" &&
350         mime_type != "text/javascript" &&
351         mime_type != "application/javascript") {
352       AsyncNotifyDoneHelper(net::URLRequestStatus(
353           net::URLRequestStatus::FAILED, net::ERR_INSECURE_RESPONSE));
354       return;
355     }
356   }
357   WriteHeadersToCache();
358 }
359
360 void ServiceWorkerWriteToCacheJob::OnReadCompleted(
361     net::URLRequest* request,
362     int bytes_read) {
363   DCHECK_EQ(net_request_, request);
364   if (!request->status().is_success()) {
365     AsyncNotifyDoneHelper(request->status());
366     return;
367   }
368   if (bytes_read > 0) {
369     WriteDataToCache(bytes_read);
370     return;
371   }
372   TRACE_EVENT_ASYNC_STEP_INTO0("ServiceWorker",
373                                "ServiceWorkerWriteToCacheJob::ExecutingJob",
374                                this,
375                                "WriteHeadersToCache");
376   // We're done with all.
377   AsyncNotifyDoneHelper(request->status());
378   return;
379 }
380
381 void ServiceWorkerWriteToCacheJob::AsyncNotifyDoneHelper(
382     const net::URLRequestStatus& status) {
383   DCHECK(!status.is_io_pending());
384   version_->script_cache_map()->NotifyFinishedCaching(url_, status);
385   did_notify_finished_ = true;
386   SetStatus(status);
387   NotifyDone(status);
388 }
389
390 }  // namespace content