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"
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"
48 net::NetLog::EventType
RequestJobResultToNetEventType(
49 ServiceWorkerMetrics::URLRequestJobResult result
) {
50 using n
= net::NetLog
;
51 using m
= ServiceWorkerMetrics
;
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
:
90 case m::NUM_REQUEST_JOB_RESULT_TYPES
:
91 NOTREACHED() << result
;
93 NOTREACHED() << result
;
94 return n::TYPE_FAILED
;
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
),
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),
128 weak_factory_(this) {}
130 void ServiceWorkerURLRequestJob::FallbackToNetwork() {
131 DCHECK_EQ(NOT_DETERMINED
, response_type_
);
132 response_type_
= FALLBACK_TO_NETWORK
;
136 void ServiceWorkerURLRequestJob::ForwardToServiceWorker() {
137 DCHECK_EQ(NOT_DETERMINED
, response_type_
);
138 response_type_
= FORWARD_TO_SERVICE_WORKER
;
142 void ServiceWorkerURLRequestJob::Start() {
147 void ServiceWorkerURLRequestJob::Kill() {
148 net::URLRequestJob::Kill();
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
) {
163 return http_info()->headers
->GetCharset(charset
);
166 bool ServiceWorkerURLRequestJob::GetMimeType(std::string
* mime_type
) const {
169 return http_info()->headers
->GetMimeType(mime_type
);
172 void ServiceWorkerURLRequestJob::GetResponseInfo(net::HttpResponseInfo
* info
) {
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 {
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
)) {
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
) {
209 DCHECK_GE(buf_size
, 0);
211 DCHECK(waiting_stream_url_
.is_empty());
213 switch (stream_
->ReadRawData(buf
, buf_size
, bytes_read
)) {
214 case Stream::STREAM_HAS_DATA
:
215 DCHECK_GT(*bytes_read
, 0);
217 case Stream::STREAM_COMPLETE
:
218 DCHECK(!*bytes_read
);
219 RecordResult(ServiceWorkerMetrics::REQUEST_JOB_STREAM_RESPONSE
);
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));
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
));
237 if (!blob_request_
) {
241 blob_request_
->Read(buf
, buf_size
, bytes_read
);
242 net::URLRequestStatus status
= blob_request_
->status();
244 if (status
.is_io_pending())
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
) {
261 void ServiceWorkerURLRequestJob::OnAuthRequired(
262 net::URLRequest
* request
,
263 net::AuthChallengeInfo
* auth_info
) {
267 void ServiceWorkerURLRequestJob::OnCertificateRequested(
268 net::URLRequest
* request
,
269 net::SSLCertRequestInfo
* cert_request_info
) {
273 void ServiceWorkerURLRequestJob::OnSSLCertificateError(
274 net::URLRequest
* request
,
275 const net::SSLInfo
& ssl_info
,
280 void ServiceWorkerURLRequestJob::OnBeforeNetworkStart(net::URLRequest
* request
,
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
,
294 SetStatus(request
->status());
295 if (!request
->status().is_success()) {
296 RecordResult(ServiceWorkerMetrics::REQUEST_JOB_ERROR_BLOB_READ
);
297 NotifyDone(request
->status());
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());
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())
322 // stream_pending_buffer_ is set to the IOBuffer instance provided to
323 // ReadRawData() by URLRequestJob.
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);
331 case Stream::STREAM_COMPLETE
:
332 // Calling NotifyReadComplete with 0 signals completion.
334 RecordResult(ServiceWorkerMetrics::REQUEST_JOB_STREAM_RESPONSE
);
336 case Stream::STREAM_EMPTY
:
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
));
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();
360 stream_
->SetReadObserver(this);
361 CommitResponseHeader();
364 // Misc -----------------------------------------------------------------------
366 const net::HttpResponseInfo
* ServiceWorkerURLRequestJob::http_info() const {
367 if (!http_response_info_
)
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
;
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() {
397 if (!ShouldRecordResult())
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() {
419 request()->net_log().AddEvent(
420 net::NetLog::TYPE_SERVICE_WORKER_START_REQUEST
);
423 switch (response_type_
) {
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
432 NotifyRestartRequired();
435 case FORWARD_TO_SERVICE_WORKER
:
436 if (!provider_host_
) {
437 RecordResult(ServiceWorkerMetrics::REQUEST_JOB_ERROR_NO_PROVIDER_HOST
);
438 DeliverErrorResponse();
441 if (!provider_host_
->active_version()) {
442 RecordResult(ServiceWorkerMetrics::REQUEST_JOB_ERROR_NO_ACTIVE_VERSION
);
443 DeliverErrorResponse();
447 DCHECK(!fetch_dispatcher_
);
448 // Send a fetch event to the ServiceWorker associated to the
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();
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()))
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_
);
489 request
->is_reload
= ui::PageTransitionCoreTypeIs(
490 info
->GetPageTransition(), ui::PAGE_TRANSITION_RELOAD
);
492 Referrer(GURL(request_
->referrer()), info
->GetReferrerPolicy());
495 request_
->referrer_policy() ==
496 net::URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE
);
498 Referrer(GURL(request_
->referrer()), blink::WebReferrerPolicyDefault
);
500 return request
.Pass();
503 bool ServiceWorkerURLRequestJob::CreateRequestBodyBlob(std::string
* blob_uuid
,
505 if (!body_
.get() || !blob_storage_context_
)
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
);
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())
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();
542 total_size
= kuint64max
;
543 switch (element
.type()) {
544 case ResourceRequestBody::Element::TYPE_BYTES
:
545 blob_builder
.AppendData(element
.bytes(), element
.length());
547 case ResourceRequestBody::Element::TYPE_FILE
:
548 blob_builder
.AppendFile(element
.path(), element
.offset(),
550 element
.expected_modification_time());
552 case ResourceRequestBody::Element::TYPE_BLOB
:
553 // Blob elements should be resolved beforehand.
556 case ResourceRequestBody::Element::TYPE_FILE_FILESYSTEM
:
557 blob_builder
.AppendFileSystemFile(element
.filesystem_url(),
558 element
.offset(), element
.length(),
559 element
.expected_modification_time());
566 request_body_blob_data_handle_
=
567 blob_storage_context_
->AddFinishedBlob(&blob_builder
);
569 *blob_size
= total_size
;
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.
588 RecordResult(ServiceWorkerMetrics::REQUEST_JOB_ERROR_NO_REQUEST
);
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();
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();
609 DeliverErrorResponse();
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();
632 // Change the response type and restart the request to fallback to
634 RecordResult(ServiceWorkerMetrics::REQUEST_JOB_FALLBACK_RESPONSE
);
635 response_type_
= FALLBACK_TO_NETWORK
;
636 NotifyRestartRequired();
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
645 if (response
.status_code
== 0) {
646 RecordStatusZeroResponseError(response
.error
);
648 net::URLRequestStatus(net::URLRequestStatus::FAILED
, net::ERR_FAILED
));
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_
);
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_
);
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_
,
693 stream_
->SetReadObserver(this);
694 CommitResponseHeader();
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();
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(
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();
739 header
.reserve(it
->first
.size() + 2 + it
->second
.size());
740 header
.append(it
->first
);
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()) {
783 did_record_result_
= true;
784 ServiceWorkerMetrics::RecordURLRequestJobResult(is_main_resource_load_
,
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()) {
798 RecordResult(ServiceWorkerMetrics::REQUEST_JOB_ERROR_RESPONSE_STATUS_ZERO
);
799 ServiceWorkerMetrics::RecordStatusZeroResponseError(is_main_resource_load_
,
803 void ServiceWorkerURLRequestJob::ClearStream() {
804 if (streaming_version_
) {
805 streaming_version_
->RemoveStreamingURLRequestJob(this);
806 streaming_version_
= nullptr;
809 stream_
->RemoveReadObserver(this);
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