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/memory/scoped_vector.h"
14 #include "base/profiler/scoped_tracker.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/time/time.h"
17 #include "content/browser/resource_context_impl.h"
18 #include "content/browser/service_worker/service_worker_fetch_dispatcher.h"
19 #include "content/browser/service_worker/service_worker_provider_host.h"
20 #include "content/browser/streams/stream.h"
21 #include "content/browser/streams/stream_context.h"
22 #include "content/browser/streams/stream_registry.h"
23 #include "content/common/resource_request_body.h"
24 #include "content/common/service_worker/service_worker_types.h"
25 #include "content/public/browser/blob_handle.h"
26 #include "content/public/browser/resource_request_info.h"
27 #include "content/public/browser/service_worker_context.h"
28 #include "content/public/common/referrer.h"
29 #include "net/base/net_errors.h"
30 #include "net/http/http_request_headers.h"
31 #include "net/http/http_response_headers.h"
32 #include "net/http/http_response_info.h"
33 #include "net/http/http_util.h"
34 #include "storage/browser/blob/blob_data_builder.h"
35 #include "storage/browser/blob/blob_data_handle.h"
36 #include "storage/browser/blob/blob_storage_context.h"
37 #include "storage/browser/blob/blob_url_request_job_factory.h"
38 #include "ui/base/page_transition_types.h"
42 ServiceWorkerURLRequestJob::ServiceWorkerURLRequestJob(
43 net::URLRequest
* request
,
44 net::NetworkDelegate
* network_delegate
,
45 base::WeakPtr
<ServiceWorkerProviderHost
> provider_host
,
46 base::WeakPtr
<storage::BlobStorageContext
> blob_storage_context
,
47 const ResourceContext
* resource_context
,
48 FetchRequestMode request_mode
,
49 FetchCredentialsMode credentials_mode
,
50 RequestContextType request_context_type
,
51 RequestContextFrameType frame_type
,
52 scoped_refptr
<ResourceRequestBody
> body
)
53 : net::URLRequestJob(request
, network_delegate
),
54 provider_host_(provider_host
),
55 response_type_(NOT_DETERMINED
),
57 service_worker_response_type_(blink::WebServiceWorkerResponseTypeDefault
),
58 blob_storage_context_(blob_storage_context
),
59 resource_context_(resource_context
),
60 stream_pending_buffer_size_(0),
61 request_mode_(request_mode
),
62 credentials_mode_(credentials_mode
),
63 request_context_type_(request_context_type
),
64 frame_type_(frame_type
),
65 fall_back_required_(false),
70 void ServiceWorkerURLRequestJob::FallbackToNetwork() {
71 DCHECK_EQ(NOT_DETERMINED
, response_type_
);
72 response_type_
= FALLBACK_TO_NETWORK
;
76 void ServiceWorkerURLRequestJob::ForwardToServiceWorker() {
77 DCHECK_EQ(NOT_DETERMINED
, response_type_
);
78 response_type_
= FORWARD_TO_SERVICE_WORKER
;
82 void ServiceWorkerURLRequestJob::Start() {
87 void ServiceWorkerURLRequestJob::Kill() {
88 net::URLRequestJob::Kill();
90 fetch_dispatcher_
.reset();
91 blob_request_
.reset();
92 weak_factory_
.InvalidateWeakPtrs();
95 net::LoadState
ServiceWorkerURLRequestJob::GetLoadState() const {
96 // TODO(kinuko): refine this for better debug.
97 return net::URLRequestJob::GetLoadState();
100 bool ServiceWorkerURLRequestJob::GetCharset(std::string
* charset
) {
103 return http_info()->headers
->GetCharset(charset
);
106 bool ServiceWorkerURLRequestJob::GetMimeType(std::string
* mime_type
) const {
109 return http_info()->headers
->GetMimeType(mime_type
);
112 void ServiceWorkerURLRequestJob::GetResponseInfo(net::HttpResponseInfo
* info
) {
115 const base::Time request_time
= info
->request_time
;
116 *info
= *http_info();
117 info
->request_time
= request_time
;
118 info
->response_time
= response_time_
;
121 void ServiceWorkerURLRequestJob::GetLoadTimingInfo(
122 net::LoadTimingInfo
* load_timing_info
) const {
123 *load_timing_info
= load_timing_info_
;
126 int ServiceWorkerURLRequestJob::GetResponseCode() const {
129 return http_info()->headers
->response_code();
132 void ServiceWorkerURLRequestJob::SetExtraRequestHeaders(
133 const net::HttpRequestHeaders
& headers
) {
134 std::string range_header
;
135 std::vector
<net::HttpByteRange
> ranges
;
136 if (!headers
.GetHeader(net::HttpRequestHeaders::kRange
, &range_header
) ||
137 !net::HttpUtil::ParseRangeHeader(range_header
, &ranges
)) {
141 // We don't support multiple range requests in one single URL request.
142 if (ranges
.size() == 1U)
143 byte_range_
= ranges
[0];
146 bool ServiceWorkerURLRequestJob::ReadRawData(
147 net::IOBuffer
* buf
, int buf_size
, int *bytes_read
) {
148 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed.
149 tracked_objects::ScopedTracker
tracking_profile(
150 FROM_HERE_WITH_EXPLICIT_FUNCTION(
151 "423948 ServiceWorkerURLRequestJob::ReadRawData"));
154 DCHECK_GE(buf_size
, 0);
156 DCHECK(waiting_stream_url_
.is_empty());
158 switch (stream_
->ReadRawData(buf
, buf_size
, bytes_read
)) {
159 case Stream::STREAM_HAS_DATA
:
160 DCHECK_GT(*bytes_read
, 0);
162 case Stream::STREAM_COMPLETE
:
163 DCHECK(!*bytes_read
);
165 case Stream::STREAM_EMPTY
:
166 stream_pending_buffer_
= buf
;
167 stream_pending_buffer_size_
= buf_size
;
168 SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING
, 0));
170 case Stream::STREAM_ABORTED
:
171 // Handle this as connection reset.
172 NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED
,
173 net::ERR_CONNECTION_RESET
));
180 if (!blob_request_
) {
184 blob_request_
->Read(buf
, buf_size
, bytes_read
);
185 net::URLRequestStatus status
= blob_request_
->status();
187 if (status
.is_io_pending())
189 return status
.is_success();
192 void ServiceWorkerURLRequestJob::OnReceivedRedirect(
193 net::URLRequest
* request
,
194 const net::RedirectInfo
& redirect_info
,
195 bool* defer_redirect
) {
199 void ServiceWorkerURLRequestJob::OnAuthRequired(
200 net::URLRequest
* request
,
201 net::AuthChallengeInfo
* auth_info
) {
205 void ServiceWorkerURLRequestJob::OnCertificateRequested(
206 net::URLRequest
* request
,
207 net::SSLCertRequestInfo
* cert_request_info
) {
211 void ServiceWorkerURLRequestJob::OnSSLCertificateError(
212 net::URLRequest
* request
,
213 const net::SSLInfo
& ssl_info
,
218 void ServiceWorkerURLRequestJob::OnBeforeNetworkStart(net::URLRequest
* request
,
223 void ServiceWorkerURLRequestJob::OnResponseStarted(net::URLRequest
* request
) {
224 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed.
225 tracked_objects::ScopedTracker
tracking_profile(
226 FROM_HERE_WITH_EXPLICIT_FUNCTION(
227 "423948 ServiceWorkerURLRequestJob::OnResponseStarted"));
229 // TODO(falken): Add Content-Length, Content-Type if they were not provided in
230 // the ServiceWorkerResponse.
231 response_time_
= base::Time::Now();
232 CommitResponseHeader();
235 void ServiceWorkerURLRequestJob::OnReadCompleted(net::URLRequest
* request
,
237 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed.
238 tracked_objects::ScopedTracker
tracking_profile(
239 FROM_HERE_WITH_EXPLICIT_FUNCTION(
240 "423948 ServiceWorkerURLRequestJob::OnReadCompleted"));
242 SetStatus(request
->status());
243 if (!request
->status().is_success()) {
244 NotifyDone(request
->status());
247 NotifyReadComplete(bytes_read
);
249 NotifyDone(request
->status());
252 void ServiceWorkerURLRequestJob::OnDataAvailable(Stream
* stream
) {
253 // Clear the IO_PENDING status.
254 SetStatus(net::URLRequestStatus());
255 // Do nothing if stream_pending_buffer_ is empty, i.e. there's no ReadRawData
256 // operation waiting for IO completion.
257 if (!stream_pending_buffer_
.get())
260 // stream_pending_buffer_ is set to the IOBuffer instance provided to
261 // ReadRawData() by URLRequestJob.
264 switch (stream_
->ReadRawData(
265 stream_pending_buffer_
.get(), stream_pending_buffer_size_
, &bytes_read
)) {
266 case Stream::STREAM_HAS_DATA
:
267 DCHECK_GT(bytes_read
, 0);
269 case Stream::STREAM_COMPLETE
:
270 // Calling NotifyReadComplete with 0 signals completion.
273 case Stream::STREAM_EMPTY
:
276 case Stream::STREAM_ABORTED
:
277 // Handle this as connection reset.
278 NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED
,
279 net::ERR_CONNECTION_RESET
));
283 // Clear the buffers before notifying the read is complete, so that it is
284 // safe for the observer to read.
285 stream_pending_buffer_
= nullptr;
286 stream_pending_buffer_size_
= 0;
287 NotifyReadComplete(bytes_read
);
290 void ServiceWorkerURLRequestJob::OnStreamRegistered(Stream
* stream
) {
291 StreamContext
* stream_context
=
292 GetStreamContextForResourceContext(resource_context_
);
293 stream_context
->registry()->RemoveRegisterObserver(waiting_stream_url_
);
294 waiting_stream_url_
= GURL();
296 stream_
->SetReadObserver(this);
297 CommitResponseHeader();
300 const net::HttpResponseInfo
* ServiceWorkerURLRequestJob::http_info() const {
301 if (!http_response_info_
)
303 if (range_response_info_
)
304 return range_response_info_
.get();
305 return http_response_info_
.get();
308 void ServiceWorkerURLRequestJob::GetExtraResponseInfo(
309 bool* was_fetched_via_service_worker
,
310 bool* was_fallback_required_by_service_worker
,
311 GURL
* original_url_via_service_worker
,
312 blink::WebServiceWorkerResponseType
* response_type_via_service_worker
,
313 base::TimeTicks
* fetch_start_time
,
314 base::TimeTicks
* fetch_ready_time
,
315 base::TimeTicks
* fetch_end_time
) const {
316 if (response_type_
!= FORWARD_TO_SERVICE_WORKER
) {
317 *was_fetched_via_service_worker
= false;
318 *was_fallback_required_by_service_worker
= false;
319 *original_url_via_service_worker
= GURL();
320 *response_type_via_service_worker
=
321 blink::WebServiceWorkerResponseTypeDefault
;
324 *was_fetched_via_service_worker
= true;
325 *was_fallback_required_by_service_worker
= fall_back_required_
;
326 *original_url_via_service_worker
= response_url_
;
327 *response_type_via_service_worker
= service_worker_response_type_
;
328 *fetch_start_time
= fetch_start_time_
;
329 *fetch_ready_time
= fetch_ready_time_
;
330 *fetch_end_time
= fetch_end_time_
;
334 ServiceWorkerURLRequestJob::~ServiceWorkerURLRequestJob() {
338 void ServiceWorkerURLRequestJob::MaybeStartRequest() {
339 if (is_started_
&& response_type_
!= NOT_DETERMINED
) {
340 // Start asynchronously.
341 base::MessageLoop::current()->PostTask(
343 base::Bind(&ServiceWorkerURLRequestJob::StartRequest
,
344 weak_factory_
.GetWeakPtr()));
348 void ServiceWorkerURLRequestJob::StartRequest() {
349 switch (response_type_
) {
354 case FALLBACK_TO_NETWORK
:
355 // Restart the request to create a new job. Our request handler will
356 // return nullptr, and the default job (which will hit network) should be
358 NotifyRestartRequired();
361 case FORWARD_TO_SERVICE_WORKER
:
362 DCHECK(provider_host_
&& provider_host_
->active_version());
363 DCHECK(!fetch_dispatcher_
);
364 // Send a fetch event to the ServiceWorker associated to the
366 fetch_dispatcher_
.reset(new ServiceWorkerFetchDispatcher(
367 CreateFetchRequest(),
368 provider_host_
->active_version(),
369 base::Bind(&ServiceWorkerURLRequestJob::DidPrepareFetchEvent
,
370 weak_factory_
.GetWeakPtr()),
371 base::Bind(&ServiceWorkerURLRequestJob::DidDispatchFetchEvent
,
372 weak_factory_
.GetWeakPtr())));
373 fetch_start_time_
= base::TimeTicks::Now();
374 load_timing_info_
.send_start
= fetch_start_time_
;
375 fetch_dispatcher_
->Run();
382 scoped_ptr
<ServiceWorkerFetchRequest
>
383 ServiceWorkerURLRequestJob::CreateFetchRequest() {
384 std::string blob_uuid
;
385 uint64 blob_size
= 0;
386 CreateRequestBodyBlob(&blob_uuid
, &blob_size
);
387 scoped_ptr
<ServiceWorkerFetchRequest
> request(
388 new ServiceWorkerFetchRequest());
389 request
->mode
= request_mode_
;
390 request
->request_context_type
= request_context_type_
;
391 request
->frame_type
= frame_type_
;
392 request
->url
= request_
->url();
393 request
->method
= request_
->method();
394 const net::HttpRequestHeaders
& headers
= request_
->extra_request_headers();
395 for (net::HttpRequestHeaders::Iterator
it(headers
); it
.GetNext();) {
396 if (ServiceWorkerContext::IsExcludedHeaderNameForFetchEvent(it
.name()))
398 request
->headers
[it
.name()] = it
.value();
400 request
->blob_uuid
= blob_uuid
;
401 request
->blob_size
= blob_size
;
402 request
->credentials_mode
= credentials_mode_
;
403 const ResourceRequestInfo
* info
= ResourceRequestInfo::ForRequest(request_
);
405 request
->is_reload
= ui::PageTransitionCoreTypeIs(
406 info
->GetPageTransition(), ui::PAGE_TRANSITION_RELOAD
);
408 Referrer(GURL(request_
->referrer()), info
->GetReferrerPolicy());
411 request_
->referrer_policy() ==
412 net::URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE
);
414 Referrer(GURL(request_
->referrer()), blink::WebReferrerPolicyDefault
);
416 return request
.Pass();
419 bool ServiceWorkerURLRequestJob::CreateRequestBodyBlob(std::string
* blob_uuid
,
421 if (!body_
.get() || !blob_storage_context_
)
424 // To ensure the blobs stick around until the end of the reading.
425 ScopedVector
<storage::BlobDataHandle
> handles
;
426 ScopedVector
<storage::BlobDataSnapshot
> snapshots
;
427 // TODO(dmurph): Allow blobs to be added below, so that the context can
428 // efficiently re-use blob items for the new blob.
429 std::vector
<const ResourceRequestBody::Element
*> resolved_elements
;
430 for (const ResourceRequestBody::Element
& element
: (*body_
->elements())) {
431 if (element
.type() != ResourceRequestBody::Element::TYPE_BLOB
) {
432 resolved_elements
.push_back(&element
);
435 scoped_ptr
<storage::BlobDataHandle
> handle
=
436 blob_storage_context_
->GetBlobDataFromUUID(element
.blob_uuid());
437 scoped_ptr
<storage::BlobDataSnapshot
> snapshot
= handle
->CreateSnapshot();
438 if (snapshot
->items().empty())
440 const auto& items
= snapshot
->items();
441 for (const auto& item
: items
) {
442 DCHECK_NE(storage::DataElement::TYPE_BLOB
, item
->type());
443 resolved_elements
.push_back(item
->data_element_ptr());
445 handles
.push_back(handle
.release());
446 snapshots
.push_back(snapshot
.release());
449 const std::string
uuid(base::GenerateGUID());
450 uint64 total_size
= 0;
452 storage::BlobDataBuilder
blob_builder(uuid
);
453 for (size_t i
= 0; i
< resolved_elements
.size(); ++i
) {
454 const ResourceRequestBody::Element
& element
= *resolved_elements
[i
];
455 if (total_size
!= kuint64max
&& element
.length() != kuint64max
)
456 total_size
+= element
.length();
458 total_size
= kuint64max
;
459 switch (element
.type()) {
460 case ResourceRequestBody::Element::TYPE_BYTES
:
461 blob_builder
.AppendData(element
.bytes(), element
.length());
463 case ResourceRequestBody::Element::TYPE_FILE
:
464 blob_builder
.AppendFile(element
.path(), element
.offset(),
466 element
.expected_modification_time());
468 case ResourceRequestBody::Element::TYPE_BLOB
:
469 // Blob elements should be resolved beforehand.
472 case ResourceRequestBody::Element::TYPE_FILE_FILESYSTEM
:
473 blob_builder
.AppendFileSystemFile(element
.filesystem_url(),
474 element
.offset(), element
.length(),
475 element
.expected_modification_time());
482 request_body_blob_data_handle_
=
483 blob_storage_context_
->AddFinishedBlob(blob_builder
);
485 *blob_size
= total_size
;
489 void ServiceWorkerURLRequestJob::DidPrepareFetchEvent() {
490 fetch_ready_time_
= base::TimeTicks::Now();
493 void ServiceWorkerURLRequestJob::DidDispatchFetchEvent(
494 ServiceWorkerStatusCode status
,
495 ServiceWorkerFetchEventResult fetch_result
,
496 const ServiceWorkerResponse
& response
) {
497 fetch_dispatcher_
.reset();
499 // Check if we're not orphaned.
503 if (status
!= SERVICE_WORKER_OK
) {
504 // Dispatching event has been failed, returns an error response.
505 // TODO(kinuko): Would be nice to log the error case.
506 DeliverErrorResponse();
510 if (fetch_result
== SERVICE_WORKER_FETCH_EVENT_RESULT_FALLBACK
) {
511 // When the request_mode is |CORS| or |CORS-with-forced-preflight| we can't
512 // simply fallback to the network in the browser process. It is because the
513 // CORS preflight logic is implemented in the renderer. So we returns a
514 // fall_back_required response to the renderer.
515 if (request_mode_
== FETCH_REQUEST_MODE_CORS
||
516 request_mode_
== FETCH_REQUEST_MODE_CORS_WITH_FORCED_PREFLIGHT
) {
517 fall_back_required_
= true;
518 CreateResponseHeader(
519 400, "Service Worker Fallback Required", ServiceWorkerHeaderMap());
520 CommitResponseHeader();
523 // Change the response type and restart the request to fallback to
525 response_type_
= FALLBACK_TO_NETWORK
;
526 NotifyRestartRequired();
530 // We should have a response now.
531 DCHECK_EQ(SERVICE_WORKER_FETCH_EVENT_RESULT_RESPONSE
, fetch_result
);
533 // Treat a response whose status is 0 as a Network Error.
534 if (response
.status_code
== 0) {
536 net::URLRequestStatus(net::URLRequestStatus::FAILED
, net::ERR_FAILED
));
540 fetch_end_time_
= base::TimeTicks::Now();
541 load_timing_info_
.send_end
= fetch_end_time_
;
543 // Creates a new HttpResponseInfo using the the ServiceWorker script's
544 // HttpResponseInfo to show HTTPS padlock.
545 // TODO(horo): When we support mixed-content (HTTP) no-cors requests from a
546 // ServiceWorker, we have to check the security level of the responses.
547 DCHECK(!http_response_info_
);
548 const net::HttpResponseInfo
* main_script_http_info
=
549 provider_host_
->active_version()->GetMainScriptHttpResponseInfo();
550 DCHECK(main_script_http_info
);
551 http_response_info_
.reset(new net::HttpResponseInfo(*main_script_http_info
));
553 // Set up a request for reading the stream.
554 if (response
.stream_url
.is_valid()) {
555 DCHECK(response
.blob_uuid
.empty());
556 DCHECK(provider_host_
->active_version());
557 streaming_version_
= provider_host_
->active_version();
558 streaming_version_
->AddStreamingURLRequestJob(this);
559 response_url_
= response
.url
;
560 service_worker_response_type_
= response
.response_type
;
561 CreateResponseHeader(
562 response
.status_code
, response
.status_text
, response
.headers
);
563 load_timing_info_
.receive_headers_end
= base::TimeTicks::Now();
564 StreamContext
* stream_context
=
565 GetStreamContextForResourceContext(resource_context_
);
567 stream_context
->registry()->GetStream(response
.stream_url
);
568 if (!stream_
.get()) {
569 waiting_stream_url_
= response
.stream_url
;
570 // Wait for StreamHostMsg_StartBuilding message from the ServiceWorker.
571 stream_context
->registry()->SetRegisterObserver(waiting_stream_url_
,
575 stream_
->SetReadObserver(this);
576 CommitResponseHeader();
579 // Set up a request for reading the blob.
580 if (!response
.blob_uuid
.empty() && blob_storage_context_
) {
581 scoped_ptr
<storage::BlobDataHandle
> blob_data_handle
=
582 blob_storage_context_
->GetBlobDataFromUUID(response
.blob_uuid
);
583 if (!blob_data_handle
) {
584 // The renderer gave us a bad blob UUID.
585 DeliverErrorResponse();
588 blob_request_
= storage::BlobProtocolHandler::CreateBlobRequest(
589 blob_data_handle
.Pass(), request()->context(), this);
590 blob_request_
->Start();
593 response_url_
= response
.url
;
594 service_worker_response_type_
= response
.response_type
;
595 CreateResponseHeader(
596 response
.status_code
, response
.status_text
, response
.headers
);
597 load_timing_info_
.receive_headers_end
= base::TimeTicks::Now();
599 CommitResponseHeader();
602 void ServiceWorkerURLRequestJob::CreateResponseHeader(
604 const std::string
& status_text
,
605 const ServiceWorkerHeaderMap
& headers
) {
606 // TODO(kinuko): If the response has an identifier to on-disk cache entry,
607 // pull response header from the disk.
608 std::string
status_line(
609 base::StringPrintf("HTTP/1.1 %d %s", status_code
, status_text
.c_str()));
610 status_line
.push_back('\0');
611 http_response_headers_
= new net::HttpResponseHeaders(status_line
);
612 for (ServiceWorkerHeaderMap::const_iterator it
= headers
.begin();
616 header
.reserve(it
->first
.size() + 2 + it
->second
.size());
617 header
.append(it
->first
);
619 header
.append(it
->second
);
620 http_response_headers_
->AddHeader(header
);
624 void ServiceWorkerURLRequestJob::CommitResponseHeader() {
625 if (!http_response_info_
)
626 http_response_info_
.reset(new net::HttpResponseInfo());
627 http_response_info_
->headers
.swap(http_response_headers_
);
628 http_response_info_
->vary_data
= net::HttpVaryData();
629 http_response_info_
->metadata
= nullptr;
630 NotifyHeadersComplete();
633 void ServiceWorkerURLRequestJob::DeliverErrorResponse() {
634 // TODO(falken): Print an error to the console of the ServiceWorker and of
635 // the requesting page.
636 CreateResponseHeader(
637 500, "Service Worker Response Error", ServiceWorkerHeaderMap());
638 CommitResponseHeader();
641 void ServiceWorkerURLRequestJob::ClearStream() {
642 if (streaming_version_
) {
643 streaming_version_
->RemoveStreamingURLRequestJob(this);
644 streaming_version_
= nullptr;
647 stream_
->RemoveReadObserver(this);
651 if (!waiting_stream_url_
.is_empty()) {
652 StreamRegistry
* stream_registry
=
653 GetStreamContextForResourceContext(resource_context_
)->registry();
654 stream_registry
->RemoveRegisterObserver(waiting_stream_url_
);
655 stream_registry
->AbortPendingStream(waiting_stream_url_
);
659 } // namespace content