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/strings/stringprintf.h"
15 #include "base/time/time.h"
16 #include "content/browser/resource_context_impl.h"
17 #include "content/browser/service_worker/service_worker_fetch_dispatcher.h"
18 #include "content/browser/service_worker/service_worker_provider_host.h"
19 #include "content/browser/streams/stream.h"
20 #include "content/browser/streams/stream_context.h"
21 #include "content/browser/streams/stream_registry.h"
22 #include "content/common/resource_request_body.h"
23 #include "content/common/service_worker/service_worker_types.h"
24 #include "content/public/browser/blob_handle.h"
25 #include "content/public/browser/resource_request_info.h"
26 #include "content/public/browser/service_worker_context.h"
27 #include "content/public/common/referrer.h"
28 #include "net/base/net_errors.h"
29 #include "net/http/http_request_headers.h"
30 #include "net/http/http_response_headers.h"
31 #include "net/http/http_response_info.h"
32 #include "net/http/http_util.h"
33 #include "storage/browser/blob/blob_data_builder.h"
34 #include "storage/browser/blob/blob_data_handle.h"
35 #include "storage/browser/blob/blob_storage_context.h"
36 #include "storage/browser/blob/blob_url_request_job_factory.h"
37 #include "ui/base/page_transition_types.h"
41 ServiceWorkerURLRequestJob::ServiceWorkerURLRequestJob(
42 net::URLRequest
* request
,
43 net::NetworkDelegate
* network_delegate
,
44 base::WeakPtr
<ServiceWorkerProviderHost
> provider_host
,
45 base::WeakPtr
<storage::BlobStorageContext
> blob_storage_context
,
46 const ResourceContext
* resource_context
,
47 FetchRequestMode request_mode
,
48 FetchCredentialsMode credentials_mode
,
49 RequestContextType request_context_type
,
50 RequestContextFrameType frame_type
,
51 scoped_refptr
<ResourceRequestBody
> body
)
52 : net::URLRequestJob(request
, network_delegate
),
53 provider_host_(provider_host
),
54 response_type_(NOT_DETERMINED
),
56 service_worker_response_type_(blink::WebServiceWorkerResponseTypeDefault
),
57 blob_storage_context_(blob_storage_context
),
58 resource_context_(resource_context
),
59 stream_pending_buffer_size_(0),
60 request_mode_(request_mode
),
61 credentials_mode_(credentials_mode
),
62 request_context_type_(request_context_type
),
63 frame_type_(frame_type
),
64 fall_back_required_(false),
69 void ServiceWorkerURLRequestJob::FallbackToNetwork() {
70 DCHECK_EQ(NOT_DETERMINED
, response_type_
);
71 response_type_
= FALLBACK_TO_NETWORK
;
75 void ServiceWorkerURLRequestJob::ForwardToServiceWorker() {
76 DCHECK_EQ(NOT_DETERMINED
, response_type_
);
77 response_type_
= FORWARD_TO_SERVICE_WORKER
;
81 void ServiceWorkerURLRequestJob::Start() {
86 void ServiceWorkerURLRequestJob::Kill() {
87 net::URLRequestJob::Kill();
89 fetch_dispatcher_
.reset();
90 blob_request_
.reset();
91 weak_factory_
.InvalidateWeakPtrs();
94 net::LoadState
ServiceWorkerURLRequestJob::GetLoadState() const {
95 // TODO(kinuko): refine this for better debug.
96 return net::URLRequestJob::GetLoadState();
99 bool ServiceWorkerURLRequestJob::GetCharset(std::string
* charset
) {
102 return http_info()->headers
->GetCharset(charset
);
105 bool ServiceWorkerURLRequestJob::GetMimeType(std::string
* mime_type
) const {
108 return http_info()->headers
->GetMimeType(mime_type
);
111 void ServiceWorkerURLRequestJob::GetResponseInfo(net::HttpResponseInfo
* info
) {
114 const base::Time request_time
= info
->request_time
;
115 *info
= *http_info();
116 info
->request_time
= request_time
;
117 info
->response_time
= response_time_
;
120 void ServiceWorkerURLRequestJob::GetLoadTimingInfo(
121 net::LoadTimingInfo
* load_timing_info
) const {
122 *load_timing_info
= load_timing_info_
;
125 int ServiceWorkerURLRequestJob::GetResponseCode() const {
128 return http_info()->headers
->response_code();
131 void ServiceWorkerURLRequestJob::SetExtraRequestHeaders(
132 const net::HttpRequestHeaders
& headers
) {
133 std::string range_header
;
134 std::vector
<net::HttpByteRange
> ranges
;
135 if (!headers
.GetHeader(net::HttpRequestHeaders::kRange
, &range_header
) ||
136 !net::HttpUtil::ParseRangeHeader(range_header
, &ranges
)) {
140 // We don't support multiple range requests in one single URL request.
141 if (ranges
.size() == 1U)
142 byte_range_
= ranges
[0];
145 bool ServiceWorkerURLRequestJob::ReadRawData(
146 net::IOBuffer
* buf
, int buf_size
, int *bytes_read
) {
148 DCHECK_GE(buf_size
, 0);
150 DCHECK(waiting_stream_url_
.is_empty());
152 switch (stream_
->ReadRawData(buf
, buf_size
, bytes_read
)) {
153 case Stream::STREAM_HAS_DATA
:
154 DCHECK_GT(*bytes_read
, 0);
156 case Stream::STREAM_COMPLETE
:
157 DCHECK(!*bytes_read
);
159 case Stream::STREAM_EMPTY
:
160 stream_pending_buffer_
= buf
;
161 stream_pending_buffer_size_
= buf_size
;
162 SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING
, 0));
164 case Stream::STREAM_ABORTED
:
165 // Handle this as connection reset.
166 NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED
,
167 net::ERR_CONNECTION_RESET
));
174 if (!blob_request_
) {
178 blob_request_
->Read(buf
, buf_size
, bytes_read
);
179 net::URLRequestStatus status
= blob_request_
->status();
181 if (status
.is_io_pending())
183 return status
.is_success();
186 void ServiceWorkerURLRequestJob::OnReceivedRedirect(
187 net::URLRequest
* request
,
188 const net::RedirectInfo
& redirect_info
,
189 bool* defer_redirect
) {
193 void ServiceWorkerURLRequestJob::OnAuthRequired(
194 net::URLRequest
* request
,
195 net::AuthChallengeInfo
* auth_info
) {
199 void ServiceWorkerURLRequestJob::OnCertificateRequested(
200 net::URLRequest
* request
,
201 net::SSLCertRequestInfo
* cert_request_info
) {
205 void ServiceWorkerURLRequestJob::OnSSLCertificateError(
206 net::URLRequest
* request
,
207 const net::SSLInfo
& ssl_info
,
212 void ServiceWorkerURLRequestJob::OnBeforeNetworkStart(net::URLRequest
* request
,
217 void ServiceWorkerURLRequestJob::OnResponseStarted(net::URLRequest
* request
) {
218 // TODO(falken): Add Content-Length, Content-Type if they were not provided in
219 // the ServiceWorkerResponse.
220 response_time_
= base::Time::Now();
221 CommitResponseHeader();
224 void ServiceWorkerURLRequestJob::OnReadCompleted(net::URLRequest
* request
,
226 SetStatus(request
->status());
227 if (!request
->status().is_success()) {
228 NotifyDone(request
->status());
231 NotifyReadComplete(bytes_read
);
233 NotifyDone(request
->status());
236 void ServiceWorkerURLRequestJob::OnDataAvailable(Stream
* stream
) {
237 // Clear the IO_PENDING status.
238 SetStatus(net::URLRequestStatus());
239 // Do nothing if stream_pending_buffer_ is empty, i.e. there's no ReadRawData
240 // operation waiting for IO completion.
241 if (!stream_pending_buffer_
.get())
244 // stream_pending_buffer_ is set to the IOBuffer instance provided to
245 // ReadRawData() by URLRequestJob.
248 switch (stream_
->ReadRawData(
249 stream_pending_buffer_
.get(), stream_pending_buffer_size_
, &bytes_read
)) {
250 case Stream::STREAM_HAS_DATA
:
251 DCHECK_GT(bytes_read
, 0);
253 case Stream::STREAM_COMPLETE
:
254 // Calling NotifyReadComplete with 0 signals completion.
257 case Stream::STREAM_EMPTY
:
260 case Stream::STREAM_ABORTED
:
261 // Handle this as connection reset.
262 NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED
,
263 net::ERR_CONNECTION_RESET
));
267 // Clear the buffers before notifying the read is complete, so that it is
268 // safe for the observer to read.
269 stream_pending_buffer_
= nullptr;
270 stream_pending_buffer_size_
= 0;
271 NotifyReadComplete(bytes_read
);
274 void ServiceWorkerURLRequestJob::OnStreamRegistered(Stream
* stream
) {
275 StreamContext
* stream_context
=
276 GetStreamContextForResourceContext(resource_context_
);
277 stream_context
->registry()->RemoveRegisterObserver(waiting_stream_url_
);
278 waiting_stream_url_
= GURL();
280 stream_
->SetReadObserver(this);
281 CommitResponseHeader();
284 const net::HttpResponseInfo
* ServiceWorkerURLRequestJob::http_info() const {
285 if (!http_response_info_
)
287 if (range_response_info_
)
288 return range_response_info_
.get();
289 return http_response_info_
.get();
292 void ServiceWorkerURLRequestJob::GetExtraResponseInfo(
293 bool* was_fetched_via_service_worker
,
294 bool* was_fallback_required_by_service_worker
,
295 GURL
* original_url_via_service_worker
,
296 blink::WebServiceWorkerResponseType
* response_type_via_service_worker
,
297 base::TimeTicks
* fetch_start_time
,
298 base::TimeTicks
* fetch_ready_time
,
299 base::TimeTicks
* fetch_end_time
) const {
300 if (response_type_
!= FORWARD_TO_SERVICE_WORKER
) {
301 *was_fetched_via_service_worker
= false;
302 *was_fallback_required_by_service_worker
= false;
303 *original_url_via_service_worker
= GURL();
304 *response_type_via_service_worker
=
305 blink::WebServiceWorkerResponseTypeDefault
;
308 *was_fetched_via_service_worker
= true;
309 *was_fallback_required_by_service_worker
= fall_back_required_
;
310 *original_url_via_service_worker
= response_url_
;
311 *response_type_via_service_worker
= service_worker_response_type_
;
312 *fetch_start_time
= fetch_start_time_
;
313 *fetch_ready_time
= fetch_ready_time_
;
314 *fetch_end_time
= fetch_end_time_
;
318 ServiceWorkerURLRequestJob::~ServiceWorkerURLRequestJob() {
322 void ServiceWorkerURLRequestJob::MaybeStartRequest() {
323 if (is_started_
&& response_type_
!= NOT_DETERMINED
) {
324 // Start asynchronously.
325 base::MessageLoop::current()->PostTask(
327 base::Bind(&ServiceWorkerURLRequestJob::StartRequest
,
328 weak_factory_
.GetWeakPtr()));
332 void ServiceWorkerURLRequestJob::StartRequest() {
333 switch (response_type_
) {
338 case FALLBACK_TO_NETWORK
:
339 // Restart the request to create a new job. Our request handler will
340 // return nullptr, and the default job (which will hit network) should be
342 NotifyRestartRequired();
345 case FORWARD_TO_SERVICE_WORKER
:
346 DCHECK(provider_host_
&& provider_host_
->active_version());
347 DCHECK(!fetch_dispatcher_
);
348 // Send a fetch event to the ServiceWorker associated to the
350 fetch_dispatcher_
.reset(new ServiceWorkerFetchDispatcher(
351 CreateFetchRequest(),
352 provider_host_
->active_version(),
353 base::Bind(&ServiceWorkerURLRequestJob::DidPrepareFetchEvent
,
354 weak_factory_
.GetWeakPtr()),
355 base::Bind(&ServiceWorkerURLRequestJob::DidDispatchFetchEvent
,
356 weak_factory_
.GetWeakPtr())));
357 fetch_start_time_
= base::TimeTicks::Now();
358 load_timing_info_
.send_start
= fetch_start_time_
;
359 fetch_dispatcher_
->Run();
366 scoped_ptr
<ServiceWorkerFetchRequest
>
367 ServiceWorkerURLRequestJob::CreateFetchRequest() {
368 std::string blob_uuid
;
369 uint64 blob_size
= 0;
370 CreateRequestBodyBlob(&blob_uuid
, &blob_size
);
371 scoped_ptr
<ServiceWorkerFetchRequest
> request(
372 new ServiceWorkerFetchRequest());
373 request
->mode
= request_mode_
;
374 request
->request_context_type
= request_context_type_
;
375 request
->frame_type
= frame_type_
;
376 request
->url
= request_
->url();
377 request
->method
= request_
->method();
378 const net::HttpRequestHeaders
& headers
= request_
->extra_request_headers();
379 for (net::HttpRequestHeaders::Iterator
it(headers
); it
.GetNext();) {
380 if (ServiceWorkerContext::IsExcludedHeaderNameForFetchEvent(it
.name()))
382 request
->headers
[it
.name()] = it
.value();
384 request
->blob_uuid
= blob_uuid
;
385 request
->blob_size
= blob_size
;
386 request
->credentials_mode
= credentials_mode_
;
387 const ResourceRequestInfo
* info
= ResourceRequestInfo::ForRequest(request_
);
389 request
->is_reload
= ui::PageTransitionCoreTypeIs(
390 info
->GetPageTransition(), ui::PAGE_TRANSITION_RELOAD
);
392 Referrer(GURL(request_
->referrer()), info
->GetReferrerPolicy());
395 request_
->referrer_policy() ==
396 net::URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE
);
398 Referrer(GURL(request_
->referrer()), blink::WebReferrerPolicyDefault
);
400 return request
.Pass();
403 bool ServiceWorkerURLRequestJob::CreateRequestBodyBlob(std::string
* blob_uuid
,
405 if (!body_
.get() || !blob_storage_context_
)
408 // To ensure the blobs stick around until the end of the reading.
409 ScopedVector
<storage::BlobDataHandle
> handles
;
410 ScopedVector
<storage::BlobDataSnapshot
> snapshots
;
411 // TODO(dmurph): Allow blobs to be added below, so that the context can
412 // efficiently re-use blob items for the new blob.
413 std::vector
<const ResourceRequestBody::Element
*> resolved_elements
;
414 for (const ResourceRequestBody::Element
& element
: (*body_
->elements())) {
415 if (element
.type() != ResourceRequestBody::Element::TYPE_BLOB
) {
416 resolved_elements
.push_back(&element
);
419 scoped_ptr
<storage::BlobDataHandle
> handle
=
420 blob_storage_context_
->GetBlobDataFromUUID(element
.blob_uuid());
421 scoped_ptr
<storage::BlobDataSnapshot
> snapshot
= handle
->CreateSnapshot();
422 if (snapshot
->items().empty())
424 const auto& items
= snapshot
->items();
425 for (const auto& item
: items
) {
426 DCHECK_NE(storage::DataElement::TYPE_BLOB
, item
->type());
427 resolved_elements
.push_back(item
->data_element_ptr());
429 handles
.push_back(handle
.release());
430 snapshots
.push_back(snapshot
.release());
433 const std::string
uuid(base::GenerateGUID());
434 uint64 total_size
= 0;
436 storage::BlobDataBuilder
blob_builder(uuid
);
437 for (size_t i
= 0; i
< resolved_elements
.size(); ++i
) {
438 const ResourceRequestBody::Element
& element
= *resolved_elements
[i
];
439 if (total_size
!= kuint64max
&& element
.length() != kuint64max
)
440 total_size
+= element
.length();
442 total_size
= kuint64max
;
443 switch (element
.type()) {
444 case ResourceRequestBody::Element::TYPE_BYTES
:
445 blob_builder
.AppendData(element
.bytes(), element
.length());
447 case ResourceRequestBody::Element::TYPE_FILE
:
448 blob_builder
.AppendFile(element
.path(), element
.offset(),
450 element
.expected_modification_time());
452 case ResourceRequestBody::Element::TYPE_BLOB
:
453 // Blob elements should be resolved beforehand.
456 case ResourceRequestBody::Element::TYPE_FILE_FILESYSTEM
:
457 blob_builder
.AppendFileSystemFile(element
.filesystem_url(),
458 element
.offset(), element
.length(),
459 element
.expected_modification_time());
466 request_body_blob_data_handle_
=
467 blob_storage_context_
->AddFinishedBlob(&blob_builder
);
469 *blob_size
= total_size
;
473 void ServiceWorkerURLRequestJob::DidPrepareFetchEvent() {
474 fetch_ready_time_
= base::TimeTicks::Now();
477 void ServiceWorkerURLRequestJob::DidDispatchFetchEvent(
478 ServiceWorkerStatusCode status
,
479 ServiceWorkerFetchEventResult fetch_result
,
480 const ServiceWorkerResponse
& response
) {
481 fetch_dispatcher_
.reset();
483 // Check if we're not orphaned.
487 if (status
!= SERVICE_WORKER_OK
) {
488 // Dispatching event has been failed, returns an error response.
489 // TODO(kinuko): Would be nice to log the error case.
490 DeliverErrorResponse();
494 if (fetch_result
== SERVICE_WORKER_FETCH_EVENT_RESULT_FALLBACK
) {
495 // When the request_mode is |CORS| or |CORS-with-forced-preflight| we can't
496 // simply fallback to the network in the browser process. It is because the
497 // CORS preflight logic is implemented in the renderer. So we returns a
498 // fall_back_required response to the renderer.
499 if (request_mode_
== FETCH_REQUEST_MODE_CORS
||
500 request_mode_
== FETCH_REQUEST_MODE_CORS_WITH_FORCED_PREFLIGHT
) {
501 fall_back_required_
= true;
502 CreateResponseHeader(
503 400, "Service Worker Fallback Required", ServiceWorkerHeaderMap());
504 CommitResponseHeader();
507 // Change the response type and restart the request to fallback to
509 response_type_
= FALLBACK_TO_NETWORK
;
510 NotifyRestartRequired();
514 // We should have a response now.
515 DCHECK_EQ(SERVICE_WORKER_FETCH_EVENT_RESULT_RESPONSE
, fetch_result
);
517 // Treat a response whose status is 0 as a Network Error.
518 if (response
.status_code
== 0) {
520 net::URLRequestStatus(net::URLRequestStatus::FAILED
, net::ERR_FAILED
));
524 fetch_end_time_
= base::TimeTicks::Now();
525 load_timing_info_
.send_end
= fetch_end_time_
;
527 // Creates a new HttpResponseInfo using the the ServiceWorker script's
528 // HttpResponseInfo to show HTTPS padlock.
529 // TODO(horo): When we support mixed-content (HTTP) no-cors requests from a
530 // ServiceWorker, we have to check the security level of the responses.
531 DCHECK(!http_response_info_
);
532 const net::HttpResponseInfo
* main_script_http_info
=
533 provider_host_
->active_version()->GetMainScriptHttpResponseInfo();
534 DCHECK(main_script_http_info
);
535 http_response_info_
.reset(new net::HttpResponseInfo(*main_script_http_info
));
537 // Set up a request for reading the stream.
538 if (response
.stream_url
.is_valid()) {
539 DCHECK(response
.blob_uuid
.empty());
540 DCHECK(provider_host_
->active_version());
541 streaming_version_
= provider_host_
->active_version();
542 streaming_version_
->AddStreamingURLRequestJob(this);
543 response_url_
= response
.url
;
544 service_worker_response_type_
= response
.response_type
;
545 CreateResponseHeader(
546 response
.status_code
, response
.status_text
, response
.headers
);
547 load_timing_info_
.receive_headers_end
= base::TimeTicks::Now();
548 StreamContext
* stream_context
=
549 GetStreamContextForResourceContext(resource_context_
);
551 stream_context
->registry()->GetStream(response
.stream_url
);
552 if (!stream_
.get()) {
553 waiting_stream_url_
= response
.stream_url
;
554 // Wait for StreamHostMsg_StartBuilding message from the ServiceWorker.
555 stream_context
->registry()->SetRegisterObserver(waiting_stream_url_
,
559 stream_
->SetReadObserver(this);
560 CommitResponseHeader();
563 // Set up a request for reading the blob.
564 if (!response
.blob_uuid
.empty() && blob_storage_context_
) {
565 scoped_ptr
<storage::BlobDataHandle
> blob_data_handle
=
566 blob_storage_context_
->GetBlobDataFromUUID(response
.blob_uuid
);
567 if (!blob_data_handle
) {
568 // The renderer gave us a bad blob UUID.
569 DeliverErrorResponse();
572 blob_request_
= storage::BlobProtocolHandler::CreateBlobRequest(
573 blob_data_handle
.Pass(), request()->context(), this);
574 blob_request_
->Start();
577 response_url_
= response
.url
;
578 service_worker_response_type_
= response
.response_type
;
579 CreateResponseHeader(
580 response
.status_code
, response
.status_text
, response
.headers
);
581 load_timing_info_
.receive_headers_end
= base::TimeTicks::Now();
583 CommitResponseHeader();
586 void ServiceWorkerURLRequestJob::CreateResponseHeader(
588 const std::string
& status_text
,
589 const ServiceWorkerHeaderMap
& headers
) {
590 // TODO(kinuko): If the response has an identifier to on-disk cache entry,
591 // pull response header from the disk.
592 std::string
status_line(
593 base::StringPrintf("HTTP/1.1 %d %s", status_code
, status_text
.c_str()));
594 status_line
.push_back('\0');
595 http_response_headers_
= new net::HttpResponseHeaders(status_line
);
596 for (ServiceWorkerHeaderMap::const_iterator it
= headers
.begin();
600 header
.reserve(it
->first
.size() + 2 + it
->second
.size());
601 header
.append(it
->first
);
603 header
.append(it
->second
);
604 http_response_headers_
->AddHeader(header
);
608 void ServiceWorkerURLRequestJob::CommitResponseHeader() {
609 if (!http_response_info_
)
610 http_response_info_
.reset(new net::HttpResponseInfo());
611 http_response_info_
->headers
.swap(http_response_headers_
);
612 http_response_info_
->vary_data
= net::HttpVaryData();
613 http_response_info_
->metadata
= nullptr;
614 NotifyHeadersComplete();
617 void ServiceWorkerURLRequestJob::DeliverErrorResponse() {
618 // TODO(falken): Print an error to the console of the ServiceWorker and of
619 // the requesting page.
620 CreateResponseHeader(
621 500, "Service Worker Response Error", ServiceWorkerHeaderMap());
622 CommitResponseHeader();
625 void ServiceWorkerURLRequestJob::ClearStream() {
626 if (streaming_version_
) {
627 streaming_version_
->RemoveStreamingURLRequestJob(this);
628 streaming_version_
= nullptr;
631 stream_
->RemoveReadObserver(this);
635 if (!waiting_stream_url_
.is_empty()) {
636 StreamRegistry
* stream_registry
=
637 GetStreamContextForResourceContext(resource_context_
)->registry();
638 stream_registry
->RemoveRegisterObserver(waiting_stream_url_
);
639 stream_registry
->AbortPendingStream(waiting_stream_url_
);
643 } // namespace content