Upstream version 8.37.180.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_histograms.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     base::WeakPtr<ServiceWorkerContextCore> context,
25     ServiceWorkerVersion* version,
26     int64 response_id)
27     : net::URLRequestJob(request, network_delegate),
28       context_(context),
29       url_(request->url()),
30       response_id_(response_id),
31       version_(version),
32       has_been_killed_(false),
33       did_notify_started_(false),
34       did_notify_finished_(false),
35       weak_factory_(this) {
36   InitNetRequest();
37 }
38
39 ServiceWorkerWriteToCacheJob::~ServiceWorkerWriteToCacheJob() {
40   DCHECK_EQ(did_notify_started_, did_notify_finished_);
41 }
42
43 void ServiceWorkerWriteToCacheJob::Start() {
44   if (!context_) {
45     NotifyStartError(net::URLRequestStatus(
46         net::URLRequestStatus::FAILED, net::ERR_FAILED));
47     return;
48   }
49   version_->script_cache_map()->NotifyStartedCaching(
50       url_, response_id_);
51   did_notify_started_ = true;
52   StartNetRequest();
53 }
54
55 void ServiceWorkerWriteToCacheJob::Kill() {
56   if (has_been_killed_)
57     return;
58   weak_factory_.InvalidateWeakPtrs();
59   has_been_killed_ = true;
60   net_request_.reset();
61   if (did_notify_started_ && !did_notify_finished_) {
62     version_->script_cache_map()->NotifyFinishedCaching(
63         url_, false);
64     did_notify_finished_ = true;
65   }
66   writer_.reset();
67   context_.reset();
68   net::URLRequestJob::Kill();
69 }
70
71 net::LoadState ServiceWorkerWriteToCacheJob::GetLoadState() const {
72   if (writer_ && writer_->IsWritePending())
73     return net::LOAD_STATE_WAITING_FOR_APPCACHE;
74   if (net_request_)
75     return net_request_->GetLoadState().state;
76   return net::LOAD_STATE_IDLE;
77 }
78
79 bool ServiceWorkerWriteToCacheJob::GetCharset(std::string* charset) {
80   if (!http_info())
81     return false;
82   return http_info()->headers->GetCharset(charset);
83 }
84
85 bool ServiceWorkerWriteToCacheJob::GetMimeType(std::string* mime_type) const {
86   if (!http_info())
87     return false;
88   return http_info()->headers->GetMimeType(mime_type);
89 }
90
91 void ServiceWorkerWriteToCacheJob::GetResponseInfo(
92     net::HttpResponseInfo* info) {
93   if (!http_info())
94     return;
95   *info = *http_info();
96 }
97
98 int ServiceWorkerWriteToCacheJob::GetResponseCode() const {
99   if (!http_info())
100     return -1;
101   return http_info()->headers->response_code();
102 }
103
104 void ServiceWorkerWriteToCacheJob::SetExtraRequestHeaders(
105       const net::HttpRequestHeaders& headers) {
106   std::string value;
107   DCHECK(!headers.GetHeader(net::HttpRequestHeaders::kRange, &value));
108   net_request_->SetExtraRequestHeaders(headers);
109 }
110
111 bool ServiceWorkerWriteToCacheJob::ReadRawData(
112     net::IOBuffer* buf,
113     int buf_size,
114     int *bytes_read) {
115   net::URLRequestStatus status = ReadNetData(buf, buf_size, bytes_read);
116   SetStatus(status);
117   if (status.is_io_pending())
118     return false;
119
120   // No more data to process, the job is complete.
121   io_buffer_ = NULL;
122   version_->script_cache_map()->NotifyFinishedCaching(
123       url_, status.is_success());
124   did_notify_finished_ = true;
125   return status.is_success();
126 }
127
128 const net::HttpResponseInfo* ServiceWorkerWriteToCacheJob::http_info() const {
129   return http_info_.get();
130 }
131
132 void ServiceWorkerWriteToCacheJob::InitNetRequest() {
133   DCHECK(request());
134   net_request_ = request()->context()->CreateRequest(
135       request()->url(),
136       request()->priority(),
137       this,
138       this->GetCookieStore());
139   net_request_->set_first_party_for_cookies(
140       request()->first_party_for_cookies());
141   net_request_->SetReferrer(request()->referrer());
142   net_request_->SetExtraRequestHeaders(request()->extra_request_headers());
143 }
144
145 void ServiceWorkerWriteToCacheJob::StartNetRequest() {
146   net_request_->Start();  // We'll continue in OnResponseStarted.
147 }
148
149 net::URLRequestStatus ServiceWorkerWriteToCacheJob::ReadNetData(
150     net::IOBuffer* buf,
151     int buf_size,
152     int *bytes_read) {
153   DCHECK_GT(buf_size, 0);
154   DCHECK(bytes_read);
155
156   *bytes_read = 0;
157   io_buffer_ = buf;
158   int net_bytes_read = 0;
159   if (!net_request_->Read(buf, buf_size, &net_bytes_read)) {
160     if (net_request_->status().is_io_pending())
161       return net_request_->status();
162     DCHECK(!net_request_->status().is_success());
163     return net_request_->status();
164   }
165
166   if (net_bytes_read != 0) {
167     WriteDataToCache(net_bytes_read);
168     DCHECK(GetStatus().is_io_pending());
169     return GetStatus();
170   }
171
172   DCHECK(net_request_->status().is_success());
173   return net_request_->status();
174 }
175
176 void ServiceWorkerWriteToCacheJob::WriteHeadersToCache() {
177   if (!context_) {
178     AsyncNotifyDoneHelper(net::URLRequestStatus(
179         net::URLRequestStatus::FAILED, net::ERR_FAILED));
180     return;
181   }
182   writer_ = context_->storage()->CreateResponseWriter(response_id_);
183   info_buffer_ = new HttpResponseInfoIOBuffer(
184       new net::HttpResponseInfo(net_request_->response_info()));
185   writer_->WriteInfo(
186       info_buffer_,
187       base::Bind(&ServiceWorkerWriteToCacheJob::OnWriteHeadersComplete,
188                  weak_factory_.GetWeakPtr()));
189   SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0));
190 }
191
192 void ServiceWorkerWriteToCacheJob::OnWriteHeadersComplete(int result) {
193   SetStatus(net::URLRequestStatus());  // Clear the IO_PENDING status
194   if (result < 0) {
195     ServiceWorkerHistograms::CountWriteResponseResult(
196         ServiceWorkerHistograms::WRITE_HEADERS_ERROR);
197     AsyncNotifyDoneHelper(net::URLRequestStatus(
198         net::URLRequestStatus::FAILED, result));
199     return;
200   }
201   http_info_.reset(info_buffer_->http_info.release());
202   info_buffer_ = NULL;
203   NotifyHeadersComplete();
204 }
205
206 void ServiceWorkerWriteToCacheJob::WriteDataToCache(int amount_to_write) {
207   DCHECK_NE(0, amount_to_write);
208   SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0));
209   writer_->WriteData(
210       io_buffer_, amount_to_write,
211       base::Bind(&ServiceWorkerWriteToCacheJob::OnWriteDataComplete,
212                  weak_factory_.GetWeakPtr()));
213 }
214
215 void ServiceWorkerWriteToCacheJob::OnWriteDataComplete(int result) {
216   DCHECK_NE(0, result);
217   io_buffer_ = NULL;
218   if (!context_) {
219     AsyncNotifyDoneHelper(net::URLRequestStatus(
220         net::URLRequestStatus::FAILED, net::ERR_FAILED));
221     return;
222   }
223   if (result < 0) {
224     ServiceWorkerHistograms::CountWriteResponseResult(
225         ServiceWorkerHistograms::WRITE_DATA_ERROR);
226     AsyncNotifyDoneHelper(net::URLRequestStatus(
227         net::URLRequestStatus::FAILED, result));
228     return;
229   }
230   ServiceWorkerHistograms::CountWriteResponseResult(
231       ServiceWorkerHistograms::WRITE_OK);
232   SetStatus(net::URLRequestStatus());  // Clear the IO_PENDING status
233   NotifyReadComplete(result);
234 }
235
236 void ServiceWorkerWriteToCacheJob::OnReceivedRedirect(
237     net::URLRequest* request,
238     const GURL& new_url,
239     bool* defer_redirect) {
240   DCHECK_EQ(net_request_, request);
241   // Script resources can't redirect.
242   AsyncNotifyDoneHelper(net::URLRequestStatus(
243       net::URLRequestStatus::FAILED, net::ERR_FAILED));
244 }
245
246 void ServiceWorkerWriteToCacheJob::OnAuthRequired(
247     net::URLRequest* request,
248     net::AuthChallengeInfo* auth_info) {
249   DCHECK_EQ(net_request_, request);
250   // TODO(michaeln): Pass this thru to our jobs client.
251   AsyncNotifyDoneHelper(net::URLRequestStatus(
252       net::URLRequestStatus::FAILED, net::ERR_FAILED));
253 }
254
255 void ServiceWorkerWriteToCacheJob::OnCertificateRequested(
256     net::URLRequest* request,
257     net::SSLCertRequestInfo* cert_request_info) {
258   DCHECK_EQ(net_request_, request);
259   // TODO(michaeln): Pass this thru to our jobs client.
260   // see NotifyCertificateRequested.
261   AsyncNotifyDoneHelper(net::URLRequestStatus(
262       net::URLRequestStatus::FAILED, net::ERR_FAILED));
263 }
264
265 void ServiceWorkerWriteToCacheJob:: OnSSLCertificateError(
266     net::URLRequest* request,
267     const net::SSLInfo& ssl_info,
268     bool fatal) {
269   DCHECK_EQ(net_request_, request);
270   // TODO(michaeln): Pass this thru to our jobs client,
271   // see NotifySSLCertificateError.
272   AsyncNotifyDoneHelper(net::URLRequestStatus(
273       net::URLRequestStatus::FAILED, net::ERR_FAILED));
274 }
275
276 void ServiceWorkerWriteToCacheJob::OnBeforeNetworkStart(
277     net::URLRequest* request,
278     bool* defer) {
279   DCHECK_EQ(net_request_, request);
280   NotifyBeforeNetworkStart(defer);
281 }
282
283 void ServiceWorkerWriteToCacheJob::OnResponseStarted(
284     net::URLRequest* request) {
285   DCHECK_EQ(net_request_, request);
286   if (!request->status().is_success()) {
287     AsyncNotifyDoneHelper(request->status());
288     return;
289   }
290   if (request->GetResponseCode() / 100 != 2) {
291     AsyncNotifyDoneHelper(net::URLRequestStatus(
292         net::URLRequestStatus::FAILED, net::ERR_FAILED));
293     // TODO(michaeln): Instead of error'ing immediately, send the net
294     // response to our consumer, just don't cache it?
295     return;
296   }
297   WriteHeadersToCache();
298 }
299
300 void ServiceWorkerWriteToCacheJob::OnReadCompleted(
301     net::URLRequest* request,
302     int bytes_read) {
303   DCHECK_EQ(net_request_, request);
304   if (!request->status().is_success()) {
305     AsyncNotifyDoneHelper(request->status());
306     return;
307   }
308   if (bytes_read > 0) {
309     WriteDataToCache(bytes_read);
310     return;
311   }
312   // We're done with all.
313   AsyncNotifyDoneHelper(request->status());
314   return;
315 }
316
317 void ServiceWorkerWriteToCacheJob::AsyncNotifyDoneHelper(
318     const net::URLRequestStatus& status) {
319   DCHECK(!status.is_io_pending());
320   version_->script_cache_map()->NotifyFinishedCaching(
321       url_, status.is_success());
322   did_notify_finished_ = true;
323   SetStatus(status);
324   NotifyDone(status);
325 }
326
327 }  // namespace content