Battery Status API: add UMA logging for Linux.
[chromium-blink-merge.git] / content / browser / service_worker / service_worker_url_request_job.cc
blobaa1cf1ffff75baa73649d11c78f81ef36dfad55e
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.
5 #include "content/browser/service_worker/service_worker_url_request_job.h"
7 #include <map>
8 #include <string>
9 #include <vector>
11 #include "base/bind.h"
12 #include "base/guid.h"
13 #include "base/strings/stringprintf.h"
14 #include "content/browser/service_worker/service_worker_fetch_dispatcher.h"
15 #include "content/browser/service_worker/service_worker_provider_host.h"
16 #include "content/common/resource_request_body.h"
17 #include "content/common/service_worker/service_worker_types.h"
18 #include "content/public/browser/blob_handle.h"
19 #include "content/public/browser/resource_request_info.h"
20 #include "content/public/common/page_transition_types.h"
21 #include "net/http/http_request_headers.h"
22 #include "net/http/http_response_headers.h"
23 #include "net/http/http_response_info.h"
24 #include "net/http/http_util.h"
25 #include "webkit/browser/blob/blob_data_handle.h"
26 #include "webkit/browser/blob/blob_storage_context.h"
27 #include "webkit/browser/blob/blob_url_request_job_factory.h"
29 namespace content {
31 ServiceWorkerURLRequestJob::ServiceWorkerURLRequestJob(
32 net::URLRequest* request,
33 net::NetworkDelegate* network_delegate,
34 base::WeakPtr<ServiceWorkerProviderHost> provider_host,
35 base::WeakPtr<storage::BlobStorageContext> blob_storage_context,
36 scoped_refptr<ResourceRequestBody> body)
37 : net::URLRequestJob(request, network_delegate),
38 provider_host_(provider_host),
39 response_type_(NOT_DETERMINED),
40 is_started_(false),
41 blob_storage_context_(blob_storage_context),
42 body_(body),
43 weak_factory_(this) {
46 void ServiceWorkerURLRequestJob::FallbackToNetwork() {
47 DCHECK_EQ(NOT_DETERMINED, response_type_);
48 response_type_ = FALLBACK_TO_NETWORK;
49 MaybeStartRequest();
52 void ServiceWorkerURLRequestJob::ForwardToServiceWorker() {
53 DCHECK_EQ(NOT_DETERMINED, response_type_);
54 response_type_ = FORWARD_TO_SERVICE_WORKER;
55 MaybeStartRequest();
58 void ServiceWorkerURLRequestJob::Start() {
59 is_started_ = true;
60 MaybeStartRequest();
63 void ServiceWorkerURLRequestJob::Kill() {
64 net::URLRequestJob::Kill();
65 fetch_dispatcher_.reset();
66 blob_request_.reset();
67 weak_factory_.InvalidateWeakPtrs();
70 net::LoadState ServiceWorkerURLRequestJob::GetLoadState() const {
71 // TODO(kinuko): refine this for better debug.
72 return net::URLRequestJob::GetLoadState();
75 bool ServiceWorkerURLRequestJob::GetCharset(std::string* charset) {
76 if (!http_info())
77 return false;
78 return http_info()->headers->GetCharset(charset);
81 bool ServiceWorkerURLRequestJob::GetMimeType(std::string* mime_type) const {
82 if (!http_info())
83 return false;
84 return http_info()->headers->GetMimeType(mime_type);
87 void ServiceWorkerURLRequestJob::GetResponseInfo(net::HttpResponseInfo* info) {
88 if (!http_info())
89 return;
90 *info = *http_info();
93 int ServiceWorkerURLRequestJob::GetResponseCode() const {
94 if (!http_info())
95 return -1;
96 return http_info()->headers->response_code();
99 void ServiceWorkerURLRequestJob::SetExtraRequestHeaders(
100 const net::HttpRequestHeaders& headers) {
101 std::string range_header;
102 std::vector<net::HttpByteRange> ranges;
103 if (!headers.GetHeader(net::HttpRequestHeaders::kRange, &range_header) ||
104 !net::HttpUtil::ParseRangeHeader(range_header, &ranges)) {
105 return;
108 // We don't support multiple range requests in one single URL request.
109 if (ranges.size() == 1U)
110 byte_range_ = ranges[0];
113 bool ServiceWorkerURLRequestJob::ReadRawData(
114 net::IOBuffer* buf, int buf_size, int *bytes_read) {
115 if (!blob_request_) {
116 *bytes_read = 0;
117 return true;
120 blob_request_->Read(buf, buf_size, bytes_read);
121 net::URLRequestStatus status = blob_request_->status();
122 SetStatus(status);
123 if (status.is_io_pending())
124 return false;
125 return status.is_success();
128 void ServiceWorkerURLRequestJob::OnReceivedRedirect(
129 net::URLRequest* request,
130 const net::RedirectInfo& redirect_info,
131 bool* defer_redirect) {
132 NOTREACHED();
135 void ServiceWorkerURLRequestJob::OnAuthRequired(
136 net::URLRequest* request,
137 net::AuthChallengeInfo* auth_info) {
138 NOTREACHED();
141 void ServiceWorkerURLRequestJob::OnCertificateRequested(
142 net::URLRequest* request,
143 net::SSLCertRequestInfo* cert_request_info) {
144 NOTREACHED();
147 void ServiceWorkerURLRequestJob::OnSSLCertificateError(
148 net::URLRequest* request,
149 const net::SSLInfo& ssl_info,
150 bool fatal) {
151 NOTREACHED();
154 void ServiceWorkerURLRequestJob::OnBeforeNetworkStart(net::URLRequest* request,
155 bool* defer) {
156 NOTREACHED();
159 void ServiceWorkerURLRequestJob::OnResponseStarted(net::URLRequest* request) {
160 // TODO(falken): Add Content-Length, Content-Type if they were not provided in
161 // the ServiceWorkerResponse.
162 CommitResponseHeader();
165 void ServiceWorkerURLRequestJob::OnReadCompleted(net::URLRequest* request,
166 int bytes_read) {
167 SetStatus(request->status());
168 if (!request->status().is_success()) {
169 NotifyDone(request->status());
170 return;
172 NotifyReadComplete(bytes_read);
173 if (bytes_read == 0)
174 NotifyDone(request->status());
177 const net::HttpResponseInfo* ServiceWorkerURLRequestJob::http_info() const {
178 if (!http_response_info_)
179 return NULL;
180 if (range_response_info_)
181 return range_response_info_.get();
182 return http_response_info_.get();
185 void ServiceWorkerURLRequestJob::GetExtraResponseInfo(
186 bool* was_fetched_via_service_worker,
187 GURL* original_url_via_service_worker) const {
188 if (response_type_ != FORWARD_TO_SERVICE_WORKER) {
189 *was_fetched_via_service_worker = false;
190 *original_url_via_service_worker = GURL();
191 return;
193 *was_fetched_via_service_worker = true;
194 *original_url_via_service_worker = response_url_;
198 ServiceWorkerURLRequestJob::~ServiceWorkerURLRequestJob() {
201 void ServiceWorkerURLRequestJob::MaybeStartRequest() {
202 if (is_started_ && response_type_ != NOT_DETERMINED) {
203 // Start asynchronously.
204 base::MessageLoop::current()->PostTask(
205 FROM_HERE,
206 base::Bind(&ServiceWorkerURLRequestJob::StartRequest,
207 weak_factory_.GetWeakPtr()));
211 void ServiceWorkerURLRequestJob::StartRequest() {
212 switch (response_type_) {
213 case NOT_DETERMINED:
214 NOTREACHED();
215 return;
217 case FALLBACK_TO_NETWORK:
218 // Restart the request to create a new job. Our request handler will
219 // return NULL, and the default job (which will hit network) should be
220 // created.
221 NotifyRestartRequired();
222 return;
224 case FORWARD_TO_SERVICE_WORKER:
225 DCHECK(provider_host_ && provider_host_->active_version());
226 DCHECK(!fetch_dispatcher_);
227 // Send a fetch event to the ServiceWorker associated to the
228 // provider_host.
229 fetch_dispatcher_.reset(new ServiceWorkerFetchDispatcher(
230 CreateFetchRequest(),
231 provider_host_->active_version(),
232 base::Bind(&ServiceWorkerURLRequestJob::DidPrepareFetchEvent,
233 weak_factory_.GetWeakPtr()),
234 base::Bind(&ServiceWorkerURLRequestJob::DidDispatchFetchEvent,
235 weak_factory_.GetWeakPtr())));
236 fetch_dispatcher_->Run();
237 return;
240 NOTREACHED();
243 scoped_ptr<ServiceWorkerFetchRequest>
244 ServiceWorkerURLRequestJob::CreateFetchRequest() {
245 std::string blob_uuid;
246 uint64 blob_size = 0;
247 CreateRequestBodyBlob(&blob_uuid, &blob_size);
248 scoped_ptr<ServiceWorkerFetchRequest> request(
249 new ServiceWorkerFetchRequest());
251 request->url = request_->url();
252 request->method = request_->method();
253 const net::HttpRequestHeaders& headers = request_->extra_request_headers();
254 for (net::HttpRequestHeaders::Iterator it(headers); it.GetNext();)
255 request->headers[it.name()] = it.value();
256 request->blob_uuid = blob_uuid;
257 request->blob_size = blob_size;
258 request->referrer = GURL(request_->referrer());
259 const ResourceRequestInfo* info = ResourceRequestInfo::ForRequest(request_);
260 if (info) {
261 request->is_reload = PageTransitionCoreTypeIs(info->GetPageTransition(),
262 PAGE_TRANSITION_RELOAD);
264 return request.Pass();
267 bool ServiceWorkerURLRequestJob::CreateRequestBodyBlob(std::string* blob_uuid,
268 uint64* blob_size) {
269 if (!body_.get() || !blob_storage_context_)
270 return false;
271 const std::string uuid(base::GenerateGUID());
272 uint64 size = 0;
273 std::vector<const ResourceRequestBody::Element*> resolved_elements;
274 for (size_t i = 0; i < body_->elements()->size(); ++i) {
275 const ResourceRequestBody::Element& element = (*body_->elements())[i];
276 if (element.type() != ResourceRequestBody::Element::TYPE_BLOB) {
277 resolved_elements.push_back(&element);
278 continue;
280 scoped_ptr<storage::BlobDataHandle> handle =
281 blob_storage_context_->GetBlobDataFromUUID(element.blob_uuid());
282 if (handle->data()->items().empty())
283 continue;
284 for (size_t i = 0; i < handle->data()->items().size(); ++i) {
285 const storage::BlobData::Item& item = handle->data()->items().at(i);
286 DCHECK_NE(storage::BlobData::Item::TYPE_BLOB, item.type());
287 resolved_elements.push_back(&item);
290 scoped_refptr<storage::BlobData> blob_data = new storage::BlobData(uuid);
291 for (size_t i = 0; i < resolved_elements.size(); ++i) {
292 const ResourceRequestBody::Element& element = *resolved_elements[i];
293 size += element.length();
294 switch (element.type()) {
295 case ResourceRequestBody::Element::TYPE_BYTES:
296 blob_data->AppendData(element.bytes(), element.length());
297 break;
298 case ResourceRequestBody::Element::TYPE_FILE:
299 blob_data->AppendFile(element.path(),
300 element.offset(),
301 element.length(),
302 element.expected_modification_time());
303 break;
304 case ResourceRequestBody::Element::TYPE_BLOB:
305 // Blob elements should be resolved beforehand.
306 NOTREACHED();
307 break;
308 case ResourceRequestBody::Element::TYPE_FILE_FILESYSTEM:
309 blob_data->AppendFileSystemFile(element.filesystem_url(),
310 element.offset(),
311 element.length(),
312 element.expected_modification_time());
313 break;
314 default:
315 NOTIMPLEMENTED();
319 request_body_blob_data_handle_ =
320 blob_storage_context_->AddFinishedBlob(blob_data.get());
321 *blob_uuid = uuid;
322 *blob_size = size;
323 return true;
326 void ServiceWorkerURLRequestJob::DidPrepareFetchEvent() {
327 // TODO(shimazu): Set the timestamp to measure the time to launch SW
328 // This is related to this (http://crbug.com/401389)
331 void ServiceWorkerURLRequestJob::DidDispatchFetchEvent(
332 ServiceWorkerStatusCode status,
333 ServiceWorkerFetchEventResult fetch_result,
334 const ServiceWorkerResponse& response) {
335 fetch_dispatcher_.reset();
337 // Check if we're not orphaned.
338 if (!request())
339 return;
341 if (status != SERVICE_WORKER_OK) {
342 // Dispatching event has been failed, falling back to the network.
343 // (Tentative behavior described on github)
344 // TODO(kinuko): consider returning error if we've come here because
345 // unexpected worker termination etc (so that we could fix bugs).
346 // TODO(kinuko): Would be nice to log the error case.
347 response_type_ = FALLBACK_TO_NETWORK;
348 NotifyRestartRequired();
349 return;
352 if (fetch_result == SERVICE_WORKER_FETCH_EVENT_RESULT_FALLBACK) {
353 // Change the response type and restart the request to fallback to
354 // the network.
355 response_type_ = FALLBACK_TO_NETWORK;
356 NotifyRestartRequired();
357 return;
360 // We should have a response now.
361 DCHECK_EQ(SERVICE_WORKER_FETCH_EVENT_RESULT_RESPONSE, fetch_result);
363 // Set up a request for reading the blob.
364 if (!response.blob_uuid.empty() && blob_storage_context_) {
365 scoped_ptr<storage::BlobDataHandle> blob_data_handle =
366 blob_storage_context_->GetBlobDataFromUUID(response.blob_uuid);
367 if (!blob_data_handle) {
368 // The renderer gave us a bad blob UUID.
369 DeliverErrorResponse();
370 return;
372 blob_request_ = storage::BlobProtocolHandler::CreateBlobRequest(
373 blob_data_handle.Pass(), request()->context(), this);
374 blob_request_->Start();
377 response_url_ = response.url;
378 CreateResponseHeader(
379 response.status_code, response.status_text, response.headers);
380 if (!blob_request_)
381 CommitResponseHeader();
384 void ServiceWorkerURLRequestJob::CreateResponseHeader(
385 int status_code,
386 const std::string& status_text,
387 const std::map<std::string, std::string>& headers) {
388 // TODO(kinuko): If the response has an identifier to on-disk cache entry,
389 // pull response header from the disk.
390 std::string status_line(
391 base::StringPrintf("HTTP/1.1 %d %s", status_code, status_text.c_str()));
392 status_line.push_back('\0');
393 http_response_headers_ = new net::HttpResponseHeaders(status_line);
394 for (std::map<std::string, std::string>::const_iterator it = headers.begin();
395 it != headers.end();
396 ++it) {
397 std::string header;
398 header.reserve(it->first.size() + 2 + it->second.size());
399 header.append(it->first);
400 header.append(": ");
401 header.append(it->second);
402 http_response_headers_->AddHeader(header);
406 void ServiceWorkerURLRequestJob::CommitResponseHeader() {
407 http_response_info_.reset(new net::HttpResponseInfo());
408 http_response_info_->headers.swap(http_response_headers_);
409 NotifyHeadersComplete();
412 void ServiceWorkerURLRequestJob::DeliverErrorResponse() {
413 // TODO(falken): Print an error to the console of the ServiceWorker and of
414 // the requesting page.
415 CreateResponseHeader(500,
416 "Service Worker Response Error",
417 std::map<std::string, std::string>());
418 CommitResponseHeader();
421 } // namespace content