Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / content / browser / service_worker / service_worker_url_request_job.cc
blob2572c93a59b5ad3b7190b1cfa47dd3e383cf762f
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/location.h"
14 #include "base/memory/scoped_vector.h"
15 #include "base/single_thread_task_runner.h"
16 #include "base/strings/stringprintf.h"
17 #include "base/thread_task_runner_handle.h"
18 #include "base/time/time.h"
19 #include "content/browser/resource_context_impl.h"
20 #include "content/browser/service_worker/service_worker_fetch_dispatcher.h"
21 #include "content/browser/service_worker/service_worker_provider_host.h"
22 #include "content/browser/streams/stream.h"
23 #include "content/browser/streams/stream_context.h"
24 #include "content/browser/streams/stream_registry.h"
25 #include "content/common/resource_request_body.h"
26 #include "content/common/service_worker/service_worker_types.h"
27 #include "content/public/browser/blob_handle.h"
28 #include "content/public/browser/resource_request_info.h"
29 #include "content/public/browser/service_worker_context.h"
30 #include "content/public/common/referrer.h"
31 #include "content/public/common/resource_response_info.h"
32 #include "net/base/net_errors.h"
33 #include "net/http/http_request_headers.h"
34 #include "net/http/http_response_headers.h"
35 #include "net/http/http_response_info.h"
36 #include "net/http/http_util.h"
37 #include "net/log/net_log.h"
38 #include "storage/browser/blob/blob_data_builder.h"
39 #include "storage/browser/blob/blob_data_handle.h"
40 #include "storage/browser/blob/blob_storage_context.h"
41 #include "storage/browser/blob/blob_url_request_job_factory.h"
42 #include "ui/base/page_transition_types.h"
44 namespace content {
46 namespace {
48 net::NetLog::EventType RequestJobResultToNetEventType(
49 ServiceWorkerMetrics::URLRequestJobResult result) {
50 using n = net::NetLog;
51 using m = ServiceWorkerMetrics;
52 switch (result) {
53 case m::REQUEST_JOB_FALLBACK_RESPONSE:
54 return n::TYPE_SERVICE_WORKER_FALLBACK_RESPONSE;
55 case m::REQUEST_JOB_FALLBACK_FOR_CORS:
56 return n::TYPE_SERVICE_WORKER_FALLBACK_FOR_CORS;
57 case m::REQUEST_JOB_HEADERS_ONLY_RESPONSE:
58 return n::TYPE_SERVICE_WORKER_HEADERS_ONLY_RESPONSE;
59 case m::REQUEST_JOB_STREAM_RESPONSE:
60 return n::TYPE_SERVICE_WORKER_STREAM_RESPONSE;
61 case m::REQUEST_JOB_BLOB_RESPONSE:
62 return n::TYPE_SERVICE_WORKER_BLOB_RESPONSE;
63 case m::REQUEST_JOB_ERROR_RESPONSE_STATUS_ZERO:
64 return n::TYPE_SERVICE_WORKER_ERROR_RESPONSE_STATUS_ZERO;
65 case m::REQUEST_JOB_ERROR_BAD_BLOB:
66 return n::TYPE_SERVICE_WORKER_ERROR_BAD_BLOB;
67 case m::REQUEST_JOB_ERROR_NO_PROVIDER_HOST:
68 return n::TYPE_SERVICE_WORKER_ERROR_NO_PROVIDER_HOST;
69 case m::REQUEST_JOB_ERROR_NO_ACTIVE_VERSION:
70 return n::TYPE_SERVICE_WORKER_ERROR_NO_ACTIVE_VERSION;
71 case m::REQUEST_JOB_ERROR_FETCH_EVENT_DISPATCH:
72 return n::TYPE_SERVICE_WORKER_ERROR_FETCH_EVENT_DISPATCH;
73 case m::REQUEST_JOB_ERROR_BLOB_READ:
74 return n::TYPE_SERVICE_WORKER_ERROR_BLOB_READ;
75 case m::REQUEST_JOB_ERROR_STREAM_ABORTED:
76 return n::TYPE_SERVICE_WORKER_ERROR_STREAM_ABORTED;
77 case m::REQUEST_JOB_ERROR_KILLED:
78 return n::TYPE_SERVICE_WORKER_ERROR_KILLED;
79 case m::REQUEST_JOB_ERROR_KILLED_WITH_BLOB:
80 return n::TYPE_SERVICE_WORKER_ERROR_KILLED_WITH_BLOB;
81 case m::REQUEST_JOB_ERROR_KILLED_WITH_STREAM:
82 return n::TYPE_SERVICE_WORKER_ERROR_KILLED_WITH_STREAM;
83 // We can't log if there's no request; fallthrough.
84 case m::REQUEST_JOB_ERROR_NO_REQUEST:
85 // Obsolete types; fallthrough.
86 case m::REQUEST_JOB_ERROR_DESTROYED:
87 case m::REQUEST_JOB_ERROR_DESTROYED_WITH_BLOB:
88 case m::REQUEST_JOB_ERROR_DESTROYED_WITH_STREAM:
89 // Invalid type.
90 case m::NUM_REQUEST_JOB_RESULT_TYPES:
91 NOTREACHED() << result;
93 NOTREACHED() << result;
94 return n::TYPE_FAILED;
97 } // namespace
99 ServiceWorkerURLRequestJob::ServiceWorkerURLRequestJob(
100 net::URLRequest* request,
101 net::NetworkDelegate* network_delegate,
102 base::WeakPtr<ServiceWorkerProviderHost> provider_host,
103 base::WeakPtr<storage::BlobStorageContext> blob_storage_context,
104 const ResourceContext* resource_context,
105 FetchRequestMode request_mode,
106 FetchCredentialsMode credentials_mode,
107 FetchRedirectMode redirect_mode,
108 bool is_main_resource_load,
109 RequestContextType request_context_type,
110 RequestContextFrameType frame_type,
111 scoped_refptr<ResourceRequestBody> body)
112 : net::URLRequestJob(request, network_delegate),
113 provider_host_(provider_host),
114 response_type_(NOT_DETERMINED),
115 is_started_(false),
116 service_worker_response_type_(blink::WebServiceWorkerResponseTypeDefault),
117 blob_storage_context_(blob_storage_context),
118 resource_context_(resource_context),
119 stream_pending_buffer_size_(0),
120 request_mode_(request_mode),
121 credentials_mode_(credentials_mode),
122 redirect_mode_(redirect_mode),
123 is_main_resource_load_(is_main_resource_load),
124 request_context_type_(request_context_type),
125 frame_type_(frame_type),
126 fall_back_required_(false),
127 body_(body),
128 weak_factory_(this) {}
130 void ServiceWorkerURLRequestJob::FallbackToNetwork() {
131 DCHECK_EQ(NOT_DETERMINED, response_type_);
132 response_type_ = FALLBACK_TO_NETWORK;
133 MaybeStartRequest();
136 void ServiceWorkerURLRequestJob::ForwardToServiceWorker() {
137 DCHECK_EQ(NOT_DETERMINED, response_type_);
138 response_type_ = FORWARD_TO_SERVICE_WORKER;
139 MaybeStartRequest();
142 void ServiceWorkerURLRequestJob::Start() {
143 is_started_ = true;
144 MaybeStartRequest();
147 void ServiceWorkerURLRequestJob::Kill() {
148 net::URLRequestJob::Kill();
149 ClearStream();
150 fetch_dispatcher_.reset();
151 blob_request_.reset();
152 weak_factory_.InvalidateWeakPtrs();
155 net::LoadState ServiceWorkerURLRequestJob::GetLoadState() const {
156 // TODO(kinuko): refine this for better debug.
157 return net::URLRequestJob::GetLoadState();
160 bool ServiceWorkerURLRequestJob::GetCharset(std::string* charset) {
161 if (!http_info())
162 return false;
163 return http_info()->headers->GetCharset(charset);
166 bool ServiceWorkerURLRequestJob::GetMimeType(std::string* mime_type) const {
167 if (!http_info())
168 return false;
169 return http_info()->headers->GetMimeType(mime_type);
172 void ServiceWorkerURLRequestJob::GetResponseInfo(net::HttpResponseInfo* info) {
173 if (!http_info())
174 return;
175 const base::Time request_time = info->request_time;
176 *info = *http_info();
177 info->request_time = request_time;
178 info->response_time = response_time_;
181 void ServiceWorkerURLRequestJob::GetLoadTimingInfo(
182 net::LoadTimingInfo* load_timing_info) const {
183 *load_timing_info = load_timing_info_;
186 int ServiceWorkerURLRequestJob::GetResponseCode() const {
187 if (!http_info())
188 return -1;
189 return http_info()->headers->response_code();
192 void ServiceWorkerURLRequestJob::SetExtraRequestHeaders(
193 const net::HttpRequestHeaders& headers) {
194 std::string range_header;
195 std::vector<net::HttpByteRange> ranges;
196 if (!headers.GetHeader(net::HttpRequestHeaders::kRange, &range_header) ||
197 !net::HttpUtil::ParseRangeHeader(range_header, &ranges)) {
198 return;
201 // We don't support multiple range requests in one single URL request.
202 if (ranges.size() == 1U)
203 byte_range_ = ranges[0];
206 bool ServiceWorkerURLRequestJob::ReadRawData(
207 net::IOBuffer* buf, int buf_size, int *bytes_read) {
208 DCHECK(buf);
209 DCHECK_GE(buf_size, 0);
210 DCHECK(bytes_read);
211 DCHECK(waiting_stream_url_.is_empty());
212 if (stream_.get()) {
213 switch (stream_->ReadRawData(buf, buf_size, bytes_read)) {
214 case Stream::STREAM_HAS_DATA:
215 DCHECK_GT(*bytes_read, 0);
216 return true;
217 case Stream::STREAM_COMPLETE:
218 DCHECK(!*bytes_read);
219 RecordResult(ServiceWorkerMetrics::REQUEST_JOB_STREAM_RESPONSE);
220 return true;
221 case Stream::STREAM_EMPTY:
222 stream_pending_buffer_ = buf;
223 stream_pending_buffer_size_ = buf_size;
224 SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0));
225 return false;
226 case Stream::STREAM_ABORTED:
227 // Handle this as connection reset.
228 RecordResult(ServiceWorkerMetrics::REQUEST_JOB_ERROR_STREAM_ABORTED);
229 NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED,
230 net::ERR_CONNECTION_RESET));
231 return false;
233 NOTREACHED();
234 return false;
237 if (!blob_request_) {
238 *bytes_read = 0;
239 return true;
241 blob_request_->Read(buf, buf_size, bytes_read);
242 net::URLRequestStatus status = blob_request_->status();
243 SetStatus(status);
244 if (status.is_io_pending())
245 return false;
246 if (status.is_success() && *bytes_read == 0)
247 RecordResult(ServiceWorkerMetrics::REQUEST_JOB_BLOB_RESPONSE);
248 return status.is_success();
251 // TODO(falken): Refactor Blob and Stream specific handling to separate classes.
252 // Overrides for Blob reading -------------------------------------------------
254 void ServiceWorkerURLRequestJob::OnReceivedRedirect(
255 net::URLRequest* request,
256 const net::RedirectInfo& redirect_info,
257 bool* defer_redirect) {
258 NOTREACHED();
261 void ServiceWorkerURLRequestJob::OnAuthRequired(
262 net::URLRequest* request,
263 net::AuthChallengeInfo* auth_info) {
264 NOTREACHED();
267 void ServiceWorkerURLRequestJob::OnCertificateRequested(
268 net::URLRequest* request,
269 net::SSLCertRequestInfo* cert_request_info) {
270 NOTREACHED();
273 void ServiceWorkerURLRequestJob::OnSSLCertificateError(
274 net::URLRequest* request,
275 const net::SSLInfo& ssl_info,
276 bool fatal) {
277 NOTREACHED();
280 void ServiceWorkerURLRequestJob::OnBeforeNetworkStart(net::URLRequest* request,
281 bool* defer) {
282 NOTREACHED();
285 void ServiceWorkerURLRequestJob::OnResponseStarted(net::URLRequest* request) {
286 // TODO(falken): Add Content-Length, Content-Type if they were not provided in
287 // the ServiceWorkerResponse.
288 response_time_ = base::Time::Now();
289 CommitResponseHeader();
292 void ServiceWorkerURLRequestJob::OnReadCompleted(net::URLRequest* request,
293 int bytes_read) {
294 SetStatus(request->status());
295 if (!request->status().is_success()) {
296 RecordResult(ServiceWorkerMetrics::REQUEST_JOB_ERROR_BLOB_READ);
297 NotifyDone(request->status());
298 return;
301 if (bytes_read == 0) {
302 // Protect because NotifyReadComplete() can destroy |this|.
303 scoped_refptr<ServiceWorkerURLRequestJob> protect(this);
304 RecordResult(ServiceWorkerMetrics::REQUEST_JOB_BLOB_RESPONSE);
305 NotifyReadComplete(bytes_read);
306 NotifyDone(request->status());
307 return;
309 NotifyReadComplete(bytes_read);
312 // Overrides for Stream reading -----------------------------------------------
314 void ServiceWorkerURLRequestJob::OnDataAvailable(Stream* stream) {
315 // Clear the IO_PENDING status.
316 SetStatus(net::URLRequestStatus());
317 // Do nothing if stream_pending_buffer_ is empty, i.e. there's no ReadRawData
318 // operation waiting for IO completion.
319 if (!stream_pending_buffer_.get())
320 return;
322 // stream_pending_buffer_ is set to the IOBuffer instance provided to
323 // ReadRawData() by URLRequestJob.
325 int bytes_read = 0;
326 switch (stream_->ReadRawData(
327 stream_pending_buffer_.get(), stream_pending_buffer_size_, &bytes_read)) {
328 case Stream::STREAM_HAS_DATA:
329 DCHECK_GT(bytes_read, 0);
330 break;
331 case Stream::STREAM_COMPLETE:
332 // Calling NotifyReadComplete with 0 signals completion.
333 DCHECK(!bytes_read);
334 RecordResult(ServiceWorkerMetrics::REQUEST_JOB_STREAM_RESPONSE);
335 break;
336 case Stream::STREAM_EMPTY:
337 NOTREACHED();
338 break;
339 case Stream::STREAM_ABORTED:
340 // Handle this as connection reset.
341 RecordResult(ServiceWorkerMetrics::REQUEST_JOB_ERROR_STREAM_ABORTED);
342 NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED,
343 net::ERR_CONNECTION_RESET));
344 break;
347 // Clear the buffers before notifying the read is complete, so that it is
348 // safe for the observer to read.
349 stream_pending_buffer_ = nullptr;
350 stream_pending_buffer_size_ = 0;
351 NotifyReadComplete(bytes_read);
354 void ServiceWorkerURLRequestJob::OnStreamRegistered(Stream* stream) {
355 StreamContext* stream_context =
356 GetStreamContextForResourceContext(resource_context_);
357 stream_context->registry()->RemoveRegisterObserver(waiting_stream_url_);
358 waiting_stream_url_ = GURL();
359 stream_ = stream;
360 stream_->SetReadObserver(this);
361 CommitResponseHeader();
364 // Misc -----------------------------------------------------------------------
366 const net::HttpResponseInfo* ServiceWorkerURLRequestJob::http_info() const {
367 if (!http_response_info_)
368 return nullptr;
369 if (range_response_info_)
370 return range_response_info_.get();
371 return http_response_info_.get();
374 void ServiceWorkerURLRequestJob::GetExtraResponseInfo(
375 ResourceResponseInfo* response_info) const {
376 if (response_type_ != FORWARD_TO_SERVICE_WORKER) {
377 response_info->was_fetched_via_service_worker = false;
378 response_info->was_fallback_required_by_service_worker = false;
379 response_info->original_url_via_service_worker = GURL();
380 response_info->response_type_via_service_worker =
381 blink::WebServiceWorkerResponseTypeDefault;
382 return;
384 response_info->was_fetched_via_service_worker = true;
385 response_info->was_fallback_required_by_service_worker = fall_back_required_;
386 response_info->original_url_via_service_worker = response_url_;
387 response_info->response_type_via_service_worker =
388 service_worker_response_type_;
389 response_info->service_worker_start_time = worker_start_time_;
390 response_info->service_worker_ready_time = worker_ready_time_;
394 ServiceWorkerURLRequestJob::~ServiceWorkerURLRequestJob() {
395 ClearStream();
397 if (!ShouldRecordResult())
398 return;
399 ServiceWorkerMetrics::URLRequestJobResult result =
400 ServiceWorkerMetrics::REQUEST_JOB_ERROR_KILLED;
401 if (response_body_type_ == STREAM)
402 result = ServiceWorkerMetrics::REQUEST_JOB_ERROR_KILLED_WITH_STREAM;
403 else if (response_body_type_ == BLOB)
404 result = ServiceWorkerMetrics::REQUEST_JOB_ERROR_KILLED_WITH_BLOB;
405 RecordResult(result);
408 void ServiceWorkerURLRequestJob::MaybeStartRequest() {
409 if (is_started_ && response_type_ != NOT_DETERMINED) {
410 // Start asynchronously.
411 base::ThreadTaskRunnerHandle::Get()->PostTask(
412 FROM_HERE, base::Bind(&ServiceWorkerURLRequestJob::StartRequest,
413 weak_factory_.GetWeakPtr()));
417 void ServiceWorkerURLRequestJob::StartRequest() {
418 if (request()) {
419 request()->net_log().AddEvent(
420 net::NetLog::TYPE_SERVICE_WORKER_START_REQUEST);
423 switch (response_type_) {
424 case NOT_DETERMINED:
425 NOTREACHED();
426 return;
428 case FALLBACK_TO_NETWORK:
429 // Restart the request to create a new job. Our request handler will
430 // return nullptr, and the default job (which will hit network) should be
431 // created.
432 NotifyRestartRequired();
433 return;
435 case FORWARD_TO_SERVICE_WORKER:
436 if (!provider_host_) {
437 RecordResult(ServiceWorkerMetrics::REQUEST_JOB_ERROR_NO_PROVIDER_HOST);
438 DeliverErrorResponse();
439 return;
441 if (!provider_host_->active_version()) {
442 RecordResult(ServiceWorkerMetrics::REQUEST_JOB_ERROR_NO_ACTIVE_VERSION);
443 DeliverErrorResponse();
444 return;
447 DCHECK(!fetch_dispatcher_);
448 // Send a fetch event to the ServiceWorker associated to the
449 // provider_host.
450 fetch_dispatcher_.reset(new ServiceWorkerFetchDispatcher(
451 CreateFetchRequest(),
452 provider_host_->active_version(),
453 base::Bind(&ServiceWorkerURLRequestJob::DidPrepareFetchEvent,
454 weak_factory_.GetWeakPtr()),
455 base::Bind(&ServiceWorkerURLRequestJob::DidDispatchFetchEvent,
456 weak_factory_.GetWeakPtr())));
457 worker_start_time_ = base::TimeTicks::Now();
458 fetch_dispatcher_->Run();
459 return;
462 NOTREACHED();
465 scoped_ptr<ServiceWorkerFetchRequest>
466 ServiceWorkerURLRequestJob::CreateFetchRequest() {
467 std::string blob_uuid;
468 uint64 blob_size = 0;
469 CreateRequestBodyBlob(&blob_uuid, &blob_size);
470 scoped_ptr<ServiceWorkerFetchRequest> request(
471 new ServiceWorkerFetchRequest());
472 request->mode = request_mode_;
473 request->request_context_type = request_context_type_;
474 request->frame_type = frame_type_;
475 request->url = request_->url();
476 request->method = request_->method();
477 const net::HttpRequestHeaders& headers = request_->extra_request_headers();
478 for (net::HttpRequestHeaders::Iterator it(headers); it.GetNext();) {
479 if (ServiceWorkerContext::IsExcludedHeaderNameForFetchEvent(it.name()))
480 continue;
481 request->headers[it.name()] = it.value();
483 request->blob_uuid = blob_uuid;
484 request->blob_size = blob_size;
485 request->credentials_mode = credentials_mode_;
486 request->redirect_mode = redirect_mode_;
487 const ResourceRequestInfo* info = ResourceRequestInfo::ForRequest(request_);
488 if (info) {
489 request->is_reload = ui::PageTransitionCoreTypeIs(
490 info->GetPageTransition(), ui::PAGE_TRANSITION_RELOAD);
491 request->referrer =
492 Referrer(GURL(request_->referrer()), info->GetReferrerPolicy());
493 } else {
494 CHECK(
495 request_->referrer_policy() ==
496 net::URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE);
497 request->referrer =
498 Referrer(GURL(request_->referrer()), blink::WebReferrerPolicyDefault);
500 return request.Pass();
503 bool ServiceWorkerURLRequestJob::CreateRequestBodyBlob(std::string* blob_uuid,
504 uint64* blob_size) {
505 if (!body_.get() || !blob_storage_context_)
506 return false;
508 // To ensure the blobs stick around until the end of the reading.
509 ScopedVector<storage::BlobDataHandle> handles;
510 ScopedVector<storage::BlobDataSnapshot> snapshots;
511 // TODO(dmurph): Allow blobs to be added below, so that the context can
512 // efficiently re-use blob items for the new blob.
513 std::vector<const ResourceRequestBody::Element*> resolved_elements;
514 for (const ResourceRequestBody::Element& element : (*body_->elements())) {
515 if (element.type() != ResourceRequestBody::Element::TYPE_BLOB) {
516 resolved_elements.push_back(&element);
517 continue;
519 scoped_ptr<storage::BlobDataHandle> handle =
520 blob_storage_context_->GetBlobDataFromUUID(element.blob_uuid());
521 scoped_ptr<storage::BlobDataSnapshot> snapshot = handle->CreateSnapshot();
522 if (snapshot->items().empty())
523 continue;
524 const auto& items = snapshot->items();
525 for (const auto& item : items) {
526 DCHECK_NE(storage::DataElement::TYPE_BLOB, item->type());
527 resolved_elements.push_back(item->data_element_ptr());
529 handles.push_back(handle.release());
530 snapshots.push_back(snapshot.release());
533 const std::string uuid(base::GenerateGUID());
534 uint64 total_size = 0;
536 storage::BlobDataBuilder blob_builder(uuid);
537 for (size_t i = 0; i < resolved_elements.size(); ++i) {
538 const ResourceRequestBody::Element& element = *resolved_elements[i];
539 if (total_size != kuint64max && element.length() != kuint64max)
540 total_size += element.length();
541 else
542 total_size = kuint64max;
543 switch (element.type()) {
544 case ResourceRequestBody::Element::TYPE_BYTES:
545 blob_builder.AppendData(element.bytes(), element.length());
546 break;
547 case ResourceRequestBody::Element::TYPE_FILE:
548 blob_builder.AppendFile(element.path(), element.offset(),
549 element.length(),
550 element.expected_modification_time());
551 break;
552 case ResourceRequestBody::Element::TYPE_BLOB:
553 // Blob elements should be resolved beforehand.
554 NOTREACHED();
555 break;
556 case ResourceRequestBody::Element::TYPE_FILE_FILESYSTEM:
557 blob_builder.AppendFileSystemFile(element.filesystem_url(),
558 element.offset(), element.length(),
559 element.expected_modification_time());
560 break;
561 default:
562 NOTIMPLEMENTED();
566 request_body_blob_data_handle_ =
567 blob_storage_context_->AddFinishedBlob(&blob_builder);
568 *blob_uuid = uuid;
569 *blob_size = total_size;
570 return true;
573 void ServiceWorkerURLRequestJob::DidPrepareFetchEvent() {
574 worker_ready_time_ = base::TimeTicks::Now();
575 load_timing_info_.send_start = worker_ready_time_;
578 void ServiceWorkerURLRequestJob::DidDispatchFetchEvent(
579 ServiceWorkerStatusCode status,
580 ServiceWorkerFetchEventResult fetch_result,
581 const ServiceWorkerResponse& response,
582 scoped_refptr<ServiceWorkerVersion> version) {
583 fetch_dispatcher_.reset();
584 ServiceWorkerMetrics::RecordFetchEventStatus(is_main_resource_load_, status);
586 // Check if we're not orphaned.
587 if (!request()) {
588 RecordResult(ServiceWorkerMetrics::REQUEST_JOB_ERROR_NO_REQUEST);
589 return;
592 // A null |provider_host_| probably means the tab was closed. The null value
593 // would cause problems down the line, so bail out.
594 if (!provider_host_) {
595 RecordResult(ServiceWorkerMetrics::REQUEST_JOB_ERROR_NO_PROVIDER_HOST);
596 DeliverErrorResponse();
597 return;
600 if (status != SERVICE_WORKER_OK) {
601 RecordResult(ServiceWorkerMetrics::REQUEST_JOB_ERROR_FETCH_EVENT_DISPATCH);
602 if (is_main_resource_load_) {
603 // Using the service worker failed, so fallback to network. Detach the
604 // controller so subresource requests also skip the worker.
605 provider_host_->NotifyControllerLost();
606 response_type_ = FALLBACK_TO_NETWORK;
607 NotifyRestartRequired();
608 } else {
609 DeliverErrorResponse();
611 return;
614 if (fetch_result == SERVICE_WORKER_FETCH_EVENT_RESULT_FALLBACK) {
615 ServiceWorkerMetrics::RecordFallbackedRequestMode(request_mode_);
616 // When the request_mode is |CORS| or |CORS-with-forced-preflight| and the
617 // origin of the request URL is different from the security origin of the
618 // document, we can't simply fallback to the network in the browser process.
619 // It is because the CORS preflight logic is implemented in the renderer. So
620 // we returns a fall_back_required response to the renderer.
621 if ((request_mode_ == FETCH_REQUEST_MODE_CORS ||
622 request_mode_ == FETCH_REQUEST_MODE_CORS_WITH_FORCED_PREFLIGHT) &&
623 provider_host_->document_url().GetOrigin() !=
624 request()->url().GetOrigin()) {
625 fall_back_required_ = true;
626 RecordResult(ServiceWorkerMetrics::REQUEST_JOB_FALLBACK_FOR_CORS);
627 CreateResponseHeader(
628 400, "Service Worker Fallback Required", ServiceWorkerHeaderMap());
629 CommitResponseHeader();
630 return;
632 // Change the response type and restart the request to fallback to
633 // the network.
634 RecordResult(ServiceWorkerMetrics::REQUEST_JOB_FALLBACK_RESPONSE);
635 response_type_ = FALLBACK_TO_NETWORK;
636 NotifyRestartRequired();
637 return;
640 // We should have a response now.
641 DCHECK_EQ(SERVICE_WORKER_FETCH_EVENT_RESULT_RESPONSE, fetch_result);
643 // A response with status code 0 is Blink telling us to respond with network
644 // error.
645 if (response.status_code == 0) {
646 RecordStatusZeroResponseError(response.error);
647 NotifyDone(
648 net::URLRequestStatus(net::URLRequestStatus::FAILED, net::ERR_FAILED));
649 return;
652 load_timing_info_.send_end = base::TimeTicks::Now();
654 // Creates a new HttpResponseInfo using the the ServiceWorker script's
655 // HttpResponseInfo to show HTTPS padlock.
656 // TODO(horo): When we support mixed-content (HTTP) no-cors requests from a
657 // ServiceWorker, we have to check the security level of the responses.
658 DCHECK(!http_response_info_);
659 DCHECK(version);
660 const net::HttpResponseInfo* main_script_http_info =
661 version->GetMainScriptHttpResponseInfo();
662 if (main_script_http_info) {
663 // In normal case |main_script_http_info| must be set while starting the
664 // ServiceWorker. But when the ServiceWorker registration database was not
665 // written correctly, it may be null.
666 // TODO(horo): Change this line to DCHECK when crbug.com/485900 is fixed.
667 http_response_info_.reset(
668 new net::HttpResponseInfo(*main_script_http_info));
671 // Set up a request for reading the stream.
672 if (response.stream_url.is_valid()) {
673 DCHECK(response.blob_uuid.empty());
674 SetResponseBodyType(STREAM);
675 streaming_version_ = version;
676 streaming_version_->AddStreamingURLRequestJob(this);
677 response_url_ = response.url;
678 service_worker_response_type_ = response.response_type;
679 CreateResponseHeader(
680 response.status_code, response.status_text, response.headers);
681 load_timing_info_.receive_headers_end = base::TimeTicks::Now();
682 StreamContext* stream_context =
683 GetStreamContextForResourceContext(resource_context_);
684 stream_ =
685 stream_context->registry()->GetStream(response.stream_url);
686 if (!stream_.get()) {
687 waiting_stream_url_ = response.stream_url;
688 // Wait for StreamHostMsg_StartBuilding message from the ServiceWorker.
689 stream_context->registry()->SetRegisterObserver(waiting_stream_url_,
690 this);
691 return;
693 stream_->SetReadObserver(this);
694 CommitResponseHeader();
695 return;
698 // Set up a request for reading the blob.
699 if (!response.blob_uuid.empty() && blob_storage_context_) {
700 SetResponseBodyType(BLOB);
701 scoped_ptr<storage::BlobDataHandle> blob_data_handle =
702 blob_storage_context_->GetBlobDataFromUUID(response.blob_uuid);
703 if (!blob_data_handle) {
704 // The renderer gave us a bad blob UUID.
705 RecordResult(ServiceWorkerMetrics::REQUEST_JOB_ERROR_BAD_BLOB);
706 DeliverErrorResponse();
707 return;
709 blob_request_ = storage::BlobProtocolHandler::CreateBlobRequest(
710 blob_data_handle.Pass(), request()->context(), this);
711 blob_request_->Start();
714 response_url_ = response.url;
715 service_worker_response_type_ = response.response_type;
716 CreateResponseHeader(
717 response.status_code, response.status_text, response.headers);
718 load_timing_info_.receive_headers_end = base::TimeTicks::Now();
719 if (!blob_request_) {
720 RecordResult(ServiceWorkerMetrics::REQUEST_JOB_HEADERS_ONLY_RESPONSE);
721 CommitResponseHeader();
725 void ServiceWorkerURLRequestJob::CreateResponseHeader(
726 int status_code,
727 const std::string& status_text,
728 const ServiceWorkerHeaderMap& headers) {
729 // TODO(kinuko): If the response has an identifier to on-disk cache entry,
730 // pull response header from the disk.
731 std::string status_line(
732 base::StringPrintf("HTTP/1.1 %d %s", status_code, status_text.c_str()));
733 status_line.push_back('\0');
734 http_response_headers_ = new net::HttpResponseHeaders(status_line);
735 for (ServiceWorkerHeaderMap::const_iterator it = headers.begin();
736 it != headers.end();
737 ++it) {
738 std::string header;
739 header.reserve(it->first.size() + 2 + it->second.size());
740 header.append(it->first);
741 header.append(": ");
742 header.append(it->second);
743 http_response_headers_->AddHeader(header);
747 void ServiceWorkerURLRequestJob::CommitResponseHeader() {
748 if (!http_response_info_)
749 http_response_info_.reset(new net::HttpResponseInfo());
750 http_response_info_->headers.swap(http_response_headers_);
751 http_response_info_->vary_data = net::HttpVaryData();
752 http_response_info_->metadata = nullptr;
753 NotifyHeadersComplete();
756 void ServiceWorkerURLRequestJob::DeliverErrorResponse() {
757 // TODO(falken): Print an error to the console of the ServiceWorker and of
758 // the requesting page.
759 CreateResponseHeader(
760 500, "Service Worker Response Error", ServiceWorkerHeaderMap());
761 CommitResponseHeader();
764 void ServiceWorkerURLRequestJob::SetResponseBodyType(ResponseBodyType type) {
765 DCHECK_EQ(response_body_type_, UNKNOWN);
766 DCHECK_NE(type, UNKNOWN);
767 response_body_type_ = type;
770 bool ServiceWorkerURLRequestJob::ShouldRecordResult() {
771 return !did_record_result_ && is_started_ &&
772 response_type_ == FORWARD_TO_SERVICE_WORKER;
775 void ServiceWorkerURLRequestJob::RecordResult(
776 ServiceWorkerMetrics::URLRequestJobResult result) {
777 // It violates style guidelines to handle a NOTREACHED() failure but if there
778 // is a bug don't let it corrupt UMA results by double-counting.
779 if (!ShouldRecordResult()) {
780 NOTREACHED();
781 return;
783 did_record_result_ = true;
784 ServiceWorkerMetrics::RecordURLRequestJobResult(is_main_resource_load_,
785 result);
786 if (request())
787 request()->net_log().AddEvent(RequestJobResultToNetEventType(result));
790 void ServiceWorkerURLRequestJob::RecordStatusZeroResponseError(
791 blink::WebServiceWorkerResponseError error) {
792 // It violates style guidelines to handle a NOTREACHED() failure but if there
793 // is a bug don't let it corrupt UMA results by double-counting.
794 if (!ShouldRecordResult()) {
795 NOTREACHED();
796 return;
798 RecordResult(ServiceWorkerMetrics::REQUEST_JOB_ERROR_RESPONSE_STATUS_ZERO);
799 ServiceWorkerMetrics::RecordStatusZeroResponseError(is_main_resource_load_,
800 error);
803 void ServiceWorkerURLRequestJob::ClearStream() {
804 if (streaming_version_) {
805 streaming_version_->RemoveStreamingURLRequestJob(this);
806 streaming_version_ = nullptr;
808 if (stream_) {
809 stream_->RemoveReadObserver(this);
810 stream_->Abort();
811 stream_ = nullptr;
813 if (!waiting_stream_url_.is_empty()) {
814 StreamRegistry* stream_registry =
815 GetStreamContextForResourceContext(resource_context_)->registry();
816 stream_registry->RemoveRegisterObserver(waiting_stream_url_);
817 stream_registry->AbortPendingStream(waiting_stream_url_);
821 } // namespace content