Upstream version 9.38.198.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 "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"
18
19 namespace content {
20
21 ServiceWorkerWriteToCacheJob::ServiceWorkerWriteToCacheJob(
22     net::URLRequest* request,
23     net::NetworkDelegate* network_delegate,
24     ResourceType resource_type,
25     base::WeakPtr<ServiceWorkerContextCore> context,
26     ServiceWorkerVersion* version,
27     int extra_load_flags,
28     int64 response_id)
29     : net::URLRequestJob(request, network_delegate),
30       resource_type_(resource_type),
31       context_(context),
32       url_(request->url()),
33       response_id_(response_id),
34       version_(version),
35       has_been_killed_(false),
36       did_notify_started_(false),
37       did_notify_finished_(false),
38       weak_factory_(this) {
39   InitNetRequest(extra_load_flags);
40 }
41
42 ServiceWorkerWriteToCacheJob::~ServiceWorkerWriteToCacheJob() {
43   DCHECK_EQ(did_notify_started_, did_notify_finished_);
44 }
45
46 void ServiceWorkerWriteToCacheJob::Start() {
47   if (!context_) {
48     NotifyStartError(net::URLRequestStatus(
49         net::URLRequestStatus::FAILED, net::ERR_FAILED));
50     return;
51   }
52   version_->script_cache_map()->NotifyStartedCaching(
53       url_, response_id_);
54   did_notify_started_ = true;
55   StartNetRequest();
56 }
57
58 void ServiceWorkerWriteToCacheJob::Kill() {
59   if (has_been_killed_)
60     return;
61   weak_factory_.InvalidateWeakPtrs();
62   has_been_killed_ = true;
63   net_request_.reset();
64   if (did_notify_started_ && !did_notify_finished_) {
65     version_->script_cache_map()->NotifyFinishedCaching(
66         url_, false);
67     did_notify_finished_ = true;
68   }
69   writer_.reset();
70   context_.reset();
71   net::URLRequestJob::Kill();
72 }
73
74 net::LoadState ServiceWorkerWriteToCacheJob::GetLoadState() const {
75   if (writer_ && writer_->IsWritePending())
76     return net::LOAD_STATE_WAITING_FOR_APPCACHE;
77   if (net_request_)
78     return net_request_->GetLoadState().state;
79   return net::LOAD_STATE_IDLE;
80 }
81
82 bool ServiceWorkerWriteToCacheJob::GetCharset(std::string* charset) {
83   if (!http_info())
84     return false;
85   return http_info()->headers->GetCharset(charset);
86 }
87
88 bool ServiceWorkerWriteToCacheJob::GetMimeType(std::string* mime_type) const {
89   if (!http_info())
90     return false;
91   return http_info()->headers->GetMimeType(mime_type);
92 }
93
94 void ServiceWorkerWriteToCacheJob::GetResponseInfo(
95     net::HttpResponseInfo* info) {
96   if (!http_info())
97     return;
98   *info = *http_info();
99 }
100
101 int ServiceWorkerWriteToCacheJob::GetResponseCode() const {
102   if (!http_info())
103     return -1;
104   return http_info()->headers->response_code();
105 }
106
107 void ServiceWorkerWriteToCacheJob::SetExtraRequestHeaders(
108       const net::HttpRequestHeaders& headers) {
109   std::string value;
110   DCHECK(!headers.GetHeader(net::HttpRequestHeaders::kRange, &value));
111   net_request_->SetExtraRequestHeaders(headers);
112 }
113
114 bool ServiceWorkerWriteToCacheJob::ReadRawData(
115     net::IOBuffer* buf,
116     int buf_size,
117     int *bytes_read) {
118   net::URLRequestStatus status = ReadNetData(buf, buf_size, bytes_read);
119   SetStatus(status);
120   if (status.is_io_pending())
121     return false;
122
123   // No more data to process, the job is complete.
124   io_buffer_ = NULL;
125   version_->script_cache_map()->NotifyFinishedCaching(
126       url_, status.is_success());
127   did_notify_finished_ = true;
128   return status.is_success();
129 }
130
131 const net::HttpResponseInfo* ServiceWorkerWriteToCacheJob::http_info() const {
132   return http_info_.get();
133 }
134
135 void ServiceWorkerWriteToCacheJob::InitNetRequest(
136     int extra_load_flags) {
137   DCHECK(request());
138   net_request_ = request()->context()->CreateRequest(
139       request()->url(),
140       request()->priority(),
141       this,
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);
148
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);
153   }
154 }
155
156 void ServiceWorkerWriteToCacheJob::StartNetRequest() {
157   net_request_->Start();  // We'll continue in OnResponseStarted.
158 }
159
160 net::URLRequestStatus ServiceWorkerWriteToCacheJob::ReadNetData(
161     net::IOBuffer* buf,
162     int buf_size,
163     int *bytes_read) {
164   DCHECK_GT(buf_size, 0);
165   DCHECK(bytes_read);
166
167   *bytes_read = 0;
168   io_buffer_ = buf;
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();
175   }
176
177   if (net_bytes_read != 0) {
178     WriteDataToCache(net_bytes_read);
179     DCHECK(GetStatus().is_io_pending());
180     return GetStatus();
181   }
182
183   DCHECK(net_request_->status().is_success());
184   return net_request_->status();
185 }
186
187 void ServiceWorkerWriteToCacheJob::WriteHeadersToCache() {
188   if (!context_) {
189     AsyncNotifyDoneHelper(net::URLRequestStatus(
190         net::URLRequestStatus::FAILED, net::ERR_FAILED));
191     return;
192   }
193   writer_ = context_->storage()->CreateResponseWriter(response_id_);
194   info_buffer_ = new HttpResponseInfoIOBuffer(
195       new net::HttpResponseInfo(net_request_->response_info()));
196   writer_->WriteInfo(
197       info_buffer_,
198       base::Bind(&ServiceWorkerWriteToCacheJob::OnWriteHeadersComplete,
199                  weak_factory_.GetWeakPtr()));
200   SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0));
201 }
202
203 void ServiceWorkerWriteToCacheJob::OnWriteHeadersComplete(int result) {
204   SetStatus(net::URLRequestStatus());  // Clear the IO_PENDING status
205   if (result < 0) {
206     ServiceWorkerMetrics::CountWriteResponseResult(
207         ServiceWorkerMetrics::WRITE_HEADERS_ERROR);
208     AsyncNotifyDoneHelper(net::URLRequestStatus(
209         net::URLRequestStatus::FAILED, result));
210     return;
211   }
212   http_info_.reset(info_buffer_->http_info.release());
213   info_buffer_ = NULL;
214   NotifyHeadersComplete();
215 }
216
217 void ServiceWorkerWriteToCacheJob::WriteDataToCache(int amount_to_write) {
218   DCHECK_NE(0, amount_to_write);
219   SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0));
220   writer_->WriteData(
221       io_buffer_, amount_to_write,
222       base::Bind(&ServiceWorkerWriteToCacheJob::OnWriteDataComplete,
223                  weak_factory_.GetWeakPtr()));
224 }
225
226 void ServiceWorkerWriteToCacheJob::OnWriteDataComplete(int result) {
227   DCHECK_NE(0, result);
228   io_buffer_ = NULL;
229   if (!context_) {
230     AsyncNotifyDoneHelper(net::URLRequestStatus(
231         net::URLRequestStatus::FAILED, net::ERR_FAILED));
232     return;
233   }
234   if (result < 0) {
235     ServiceWorkerMetrics::CountWriteResponseResult(
236         ServiceWorkerMetrics::WRITE_DATA_ERROR);
237     AsyncNotifyDoneHelper(net::URLRequestStatus(
238         net::URLRequestStatus::FAILED, result));
239     return;
240   }
241   ServiceWorkerMetrics::CountWriteResponseResult(
242       ServiceWorkerMetrics::WRITE_OK);
243   SetStatus(net::URLRequestStatus());  // Clear the IO_PENDING status
244   NotifyReadComplete(result);
245 }
246
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));
255 }
256
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));
264 }
265
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));
274 }
275
276 void ServiceWorkerWriteToCacheJob:: OnSSLCertificateError(
277     net::URLRequest* request,
278     const net::SSLInfo& ssl_info,
279     bool fatal) {
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));
285 }
286
287 void ServiceWorkerWriteToCacheJob::OnBeforeNetworkStart(
288     net::URLRequest* request,
289     bool* defer) {
290   DCHECK_EQ(net_request_, request);
291   NotifyBeforeNetworkStart(defer);
292 }
293
294 void ServiceWorkerWriteToCacheJob::OnResponseStarted(
295     net::URLRequest* request) {
296   DCHECK_EQ(net_request_, request);
297   if (!request->status().is_success()) {
298     AsyncNotifyDoneHelper(request->status());
299     return;
300   }
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?
306     return;
307   }
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));
317       return;
318     }
319   }
320   WriteHeadersToCache();
321 }
322
323 void ServiceWorkerWriteToCacheJob::OnReadCompleted(
324     net::URLRequest* request,
325     int bytes_read) {
326   DCHECK_EQ(net_request_, request);
327   if (!request->status().is_success()) {
328     AsyncNotifyDoneHelper(request->status());
329     return;
330   }
331   if (bytes_read > 0) {
332     WriteDataToCache(bytes_read);
333     return;
334   }
335   // We're done with all.
336   AsyncNotifyDoneHelper(request->status());
337   return;
338 }
339
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;
346   SetStatus(status);
347   NotifyDone(status);
348 }
349
350 }  // namespace content