Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / content / browser / service_worker / service_worker_url_request_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_url_request_job.h"
6
7 #include <map>
8 #include <string>
9 #include <vector>
10
11 #include "base/bind.h"
12 #include "base/strings/stringprintf.h"
13 #include "content/browser/service_worker/service_worker_fetch_dispatcher.h"
14 #include "content/browser/service_worker/service_worker_provider_host.h"
15 #include "net/http/http_request_headers.h"
16 #include "net/http/http_response_headers.h"
17 #include "net/http/http_response_info.h"
18 #include "net/http/http_util.h"
19 #include "webkit/browser/blob/blob_data_handle.h"
20 #include "webkit/browser/blob/blob_storage_context.h"
21 #include "webkit/browser/blob/blob_url_request_job_factory.h"
22
23 namespace content {
24
25 ServiceWorkerURLRequestJob::ServiceWorkerURLRequestJob(
26     net::URLRequest* request,
27     net::NetworkDelegate* network_delegate,
28     base::WeakPtr<ServiceWorkerProviderHost> provider_host,
29     base::WeakPtr<webkit_blob::BlobStorageContext> blob_storage_context)
30     : net::URLRequestJob(request, network_delegate),
31       provider_host_(provider_host),
32       response_type_(NOT_DETERMINED),
33       is_started_(false),
34       blob_storage_context_(blob_storage_context),
35       weak_factory_(this) {
36 }
37
38 void ServiceWorkerURLRequestJob::FallbackToNetwork() {
39   DCHECK_EQ(NOT_DETERMINED, response_type_);
40   response_type_ = FALLBACK_TO_NETWORK;
41   MaybeStartRequest();
42 }
43
44 void ServiceWorkerURLRequestJob::ForwardToServiceWorker() {
45   DCHECK_EQ(NOT_DETERMINED, response_type_);
46   response_type_ = FORWARD_TO_SERVICE_WORKER;
47   MaybeStartRequest();
48 }
49
50 void ServiceWorkerURLRequestJob::Start() {
51   is_started_ = true;
52   MaybeStartRequest();
53 }
54
55 void ServiceWorkerURLRequestJob::Kill() {
56   net::URLRequestJob::Kill();
57   fetch_dispatcher_.reset();
58   blob_request_.reset();
59   weak_factory_.InvalidateWeakPtrs();
60 }
61
62 net::LoadState ServiceWorkerURLRequestJob::GetLoadState() const {
63   // TODO(kinuko): refine this for better debug.
64   return net::URLRequestJob::GetLoadState();
65 }
66
67 bool ServiceWorkerURLRequestJob::GetCharset(std::string* charset) {
68   if (!http_info())
69     return false;
70   return http_info()->headers->GetCharset(charset);
71 }
72
73 bool ServiceWorkerURLRequestJob::GetMimeType(std::string* mime_type) const {
74   if (!http_info())
75     return false;
76   return http_info()->headers->GetMimeType(mime_type);
77 }
78
79 void ServiceWorkerURLRequestJob::GetResponseInfo(net::HttpResponseInfo* info) {
80   if (!http_info())
81     return;
82   *info = *http_info();
83 }
84
85 int ServiceWorkerURLRequestJob::GetResponseCode() const {
86   if (!http_info())
87     return -1;
88   return http_info()->headers->response_code();
89 }
90
91 void ServiceWorkerURLRequestJob::SetExtraRequestHeaders(
92     const net::HttpRequestHeaders& headers) {
93   std::string range_header;
94   std::vector<net::HttpByteRange> ranges;
95   if (!headers.GetHeader(net::HttpRequestHeaders::kRange, &range_header) ||
96       !net::HttpUtil::ParseRangeHeader(range_header, &ranges)) {
97     return;
98   }
99
100   // We don't support multiple range requests in one single URL request.
101   if (ranges.size() == 1U)
102     byte_range_ = ranges[0];
103 }
104
105 bool ServiceWorkerURLRequestJob::ReadRawData(
106     net::IOBuffer* buf, int buf_size, int *bytes_read) {
107   if (!blob_request_) {
108     *bytes_read = 0;
109     return true;
110   }
111
112   blob_request_->Read(buf, buf_size, bytes_read);
113   net::URLRequestStatus status = blob_request_->status();
114   SetStatus(status);
115   if (status.is_io_pending())
116     return false;
117   return status.is_success();
118 }
119
120 void ServiceWorkerURLRequestJob::OnReceivedRedirect(
121     net::URLRequest* request,
122     const net::RedirectInfo& redirect_info,
123     bool* defer_redirect) {
124   NOTREACHED();
125 }
126
127 void ServiceWorkerURLRequestJob::OnAuthRequired(
128     net::URLRequest* request,
129     net::AuthChallengeInfo* auth_info) {
130   NOTREACHED();
131 }
132
133 void ServiceWorkerURLRequestJob::OnCertificateRequested(
134     net::URLRequest* request,
135     net::SSLCertRequestInfo* cert_request_info) {
136   NOTREACHED();
137 }
138
139 void ServiceWorkerURLRequestJob::OnSSLCertificateError(
140     net::URLRequest* request,
141     const net::SSLInfo& ssl_info,
142     bool fatal) {
143   NOTREACHED();
144 }
145
146 void ServiceWorkerURLRequestJob::OnBeforeNetworkStart(net::URLRequest* request,
147                                                       bool* defer) {
148   NOTREACHED();
149 }
150
151 void ServiceWorkerURLRequestJob::OnResponseStarted(net::URLRequest* request) {
152   // TODO(falken): Add Content-Length, Content-Type if they were not provided in
153   // the ServiceWorkerResponse.
154   CommitResponseHeader();
155 }
156
157 void ServiceWorkerURLRequestJob::OnReadCompleted(net::URLRequest* request,
158                                                  int bytes_read) {
159   SetStatus(request->status());
160   if (!request->status().is_success()) {
161     NotifyDone(request->status());
162     return;
163   }
164   NotifyReadComplete(bytes_read);
165   if (bytes_read == 0)
166     NotifyDone(request->status());
167 }
168
169 const net::HttpResponseInfo* ServiceWorkerURLRequestJob::http_info() const {
170   if (!http_response_info_)
171     return NULL;
172   if (range_response_info_)
173     return range_response_info_.get();
174   return http_response_info_.get();
175 }
176
177 void ServiceWorkerURLRequestJob::GetExtraResponseInfo(
178     bool* was_fetched_via_service_worker,
179     GURL* original_url_via_service_worker) const {
180   if (response_type_ != FORWARD_TO_SERVICE_WORKER) {
181     *was_fetched_via_service_worker = false;
182     *original_url_via_service_worker = GURL();
183     return;
184   }
185   *was_fetched_via_service_worker = true;
186   *original_url_via_service_worker = response_url_;
187 }
188
189
190 ServiceWorkerURLRequestJob::~ServiceWorkerURLRequestJob() {
191 }
192
193 void ServiceWorkerURLRequestJob::MaybeStartRequest() {
194   if (is_started_ && response_type_ != NOT_DETERMINED) {
195     // Start asynchronously.
196     base::MessageLoop::current()->PostTask(
197         FROM_HERE,
198         base::Bind(&ServiceWorkerURLRequestJob::StartRequest,
199                    weak_factory_.GetWeakPtr()));
200   }
201 }
202
203 void ServiceWorkerURLRequestJob::StartRequest() {
204   switch (response_type_) {
205     case NOT_DETERMINED:
206       NOTREACHED();
207       return;
208
209     case FALLBACK_TO_NETWORK:
210       // Restart the request to create a new job. Our request handler will
211       // return NULL, and the default job (which will hit network) should be
212       // created.
213       NotifyRestartRequired();
214       return;
215
216     case FORWARD_TO_SERVICE_WORKER:
217       DCHECK(provider_host_ && provider_host_->active_version());
218       DCHECK(!fetch_dispatcher_);
219
220       // Send a fetch event to the ServiceWorker associated to the
221       // provider_host.
222       fetch_dispatcher_.reset(new ServiceWorkerFetchDispatcher(
223           request(), provider_host_->active_version(),
224           base::Bind(&ServiceWorkerURLRequestJob::DidDispatchFetchEvent,
225                      weak_factory_.GetWeakPtr())));
226       fetch_dispatcher_->Run();
227       return;
228   }
229
230   NOTREACHED();
231 }
232
233 void ServiceWorkerURLRequestJob::DidDispatchFetchEvent(
234     ServiceWorkerStatusCode status,
235     ServiceWorkerFetchEventResult fetch_result,
236     const ServiceWorkerResponse& response) {
237   fetch_dispatcher_.reset();
238
239   // Check if we're not orphaned.
240   if (!request())
241     return;
242
243   if (status != SERVICE_WORKER_OK) {
244     // Dispatching event has been failed, falling back to the network.
245     // (Tentative behavior described on github)
246     // TODO(kinuko): consider returning error if we've come here because
247     // unexpected worker termination etc (so that we could fix bugs).
248     // TODO(kinuko): Would be nice to log the error case.
249     response_type_ = FALLBACK_TO_NETWORK;
250     NotifyRestartRequired();
251     return;
252   }
253
254   if (fetch_result == SERVICE_WORKER_FETCH_EVENT_RESULT_FALLBACK) {
255     // Change the response type and restart the request to fallback to
256     // the network.
257     response_type_ = FALLBACK_TO_NETWORK;
258     NotifyRestartRequired();
259     return;
260   }
261
262   // We should have a response now.
263   DCHECK_EQ(SERVICE_WORKER_FETCH_EVENT_RESULT_RESPONSE, fetch_result);
264
265   // Set up a request for reading the blob.
266   if (!response.blob_uuid.empty() && blob_storage_context_) {
267     scoped_ptr<webkit_blob::BlobDataHandle> blob_data_handle =
268         blob_storage_context_->GetBlobDataFromUUID(response.blob_uuid);
269     if (!blob_data_handle) {
270       // The renderer gave us a bad blob UUID.
271       DeliverErrorResponse();
272       return;
273     }
274     blob_request_ = webkit_blob::BlobProtocolHandler::CreateBlobRequest(
275         blob_data_handle.Pass(), request()->context(), this);
276     blob_request_->Start();
277   }
278
279   response_url_ = response.url;
280   CreateResponseHeader(
281       response.status_code, response.status_text, response.headers);
282   if (!blob_request_)
283     CommitResponseHeader();
284 }
285
286 void ServiceWorkerURLRequestJob::CreateResponseHeader(
287     int status_code,
288     const std::string& status_text,
289     const std::map<std::string, std::string>& headers) {
290   // TODO(kinuko): If the response has an identifier to on-disk cache entry,
291   // pull response header from the disk.
292   std::string status_line(
293       base::StringPrintf("HTTP/1.1 %d %s", status_code, status_text.c_str()));
294   status_line.push_back('\0');
295   http_response_headers_ = new net::HttpResponseHeaders(status_line);
296   for (std::map<std::string, std::string>::const_iterator it = headers.begin();
297        it != headers.end();
298        ++it) {
299     std::string header;
300     header.reserve(it->first.size() + 2 + it->second.size());
301     header.append(it->first);
302     header.append(": ");
303     header.append(it->second);
304     http_response_headers_->AddHeader(header);
305   }
306 }
307
308 void ServiceWorkerURLRequestJob::CommitResponseHeader() {
309   http_response_info_.reset(new net::HttpResponseInfo());
310   http_response_info_->headers.swap(http_response_headers_);
311   NotifyHeadersComplete();
312 }
313
314 void ServiceWorkerURLRequestJob::DeliverErrorResponse() {
315   // TODO(falken): Print an error to the console of the ServiceWorker and of
316   // the requesting page.
317   CreateResponseHeader(500,
318                        "Service Worker Response Error",
319                        std::map<std::string, std::string>());
320   CommitResponseHeader();
321 }
322
323 }  // namespace content