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/child/web_url_loader_impl.h"
10 #include "base/bind.h"
11 #include "base/command_line.h"
12 #include "base/files/file_path.h"
13 #include "base/logging.h"
14 #include "base/memory/scoped_ptr.h"
15 #include "base/single_thread_task_runner.h"
16 #include "base/strings/string_util.h"
17 #include "base/time/time.h"
18 #include "components/mime_util/mime_util.h"
19 #include "content/child/child_thread_impl.h"
20 #include "content/child/ftp_directory_listing_response_delegate.h"
21 #include "content/child/multipart_response_delegate.h"
22 #include "content/child/request_extra_data.h"
23 #include "content/child/request_info.h"
24 #include "content/child/resource_dispatcher.h"
25 #include "content/child/shared_memory_data_consumer_handle.h"
26 #include "content/child/sync_load_response.h"
27 #include "content/child/web_url_request_util.h"
28 #include "content/child/weburlresponse_extradata_impl.h"
29 #include "content/common/resource_messages.h"
30 #include "content/common/resource_request_body.h"
31 #include "content/common/service_worker/service_worker_types.h"
32 #include "content/common/ssl_status_serialization.h"
33 #include "content/public/child/fixed_received_data.h"
34 #include "content/public/child/request_peer.h"
35 #include "content/public/common/content_switches.h"
36 #include "net/base/data_url.h"
37 #include "net/base/filename_util.h"
38 #include "net/base/net_errors.h"
39 #include "net/http/http_response_headers.h"
40 #include "net/http/http_util.h"
41 #include "net/ssl/ssl_cipher_suite_names.h"
42 #include "net/ssl/ssl_connection_status_flags.h"
43 #include "net/url_request/url_request_data_job.h"
44 #include "third_party/WebKit/public/platform/WebHTTPLoadInfo.h"
45 #include "third_party/WebKit/public/platform/WebURL.h"
46 #include "third_party/WebKit/public/platform/WebURLError.h"
47 #include "third_party/WebKit/public/platform/WebURLLoadTiming.h"
48 #include "third_party/WebKit/public/platform/WebURLLoaderClient.h"
49 #include "third_party/WebKit/public/platform/WebURLRequest.h"
50 #include "third_party/WebKit/public/platform/WebURLResponse.h"
51 #include "third_party/WebKit/public/web/WebSecurityPolicy.h"
54 using base::TimeTicks
;
56 using blink::WebHTTPBody
;
57 using blink::WebHTTPHeaderVisitor
;
58 using blink::WebHTTPLoadInfo
;
59 using blink::WebReferrerPolicy
;
60 using blink::WebSecurityPolicy
;
61 using blink::WebString
;
63 using blink::WebURLError
;
64 using blink::WebURLLoadTiming
;
65 using blink::WebURLLoader
;
66 using blink::WebURLLoaderClient
;
67 using blink::WebURLRequest
;
68 using blink::WebURLResponse
;
72 // Utilities ------------------------------------------------------------------
76 using HeadersVector
= ResourceDevToolsInfo::HeadersVector
;
78 // Converts timing data from |load_timing| to the format used by WebKit.
79 void PopulateURLLoadTiming(const net::LoadTimingInfo
& load_timing
,
80 WebURLLoadTiming
* url_timing
) {
81 DCHECK(!load_timing
.request_start
.is_null());
83 const TimeTicks kNullTicks
;
84 url_timing
->initialize();
85 url_timing
->setRequestTime(
86 (load_timing
.request_start
- kNullTicks
).InSecondsF());
87 url_timing
->setProxyStart(
88 (load_timing
.proxy_resolve_start
- kNullTicks
).InSecondsF());
89 url_timing
->setProxyEnd(
90 (load_timing
.proxy_resolve_end
- kNullTicks
).InSecondsF());
91 url_timing
->setDNSStart(
92 (load_timing
.connect_timing
.dns_start
- kNullTicks
).InSecondsF());
93 url_timing
->setDNSEnd(
94 (load_timing
.connect_timing
.dns_end
- kNullTicks
).InSecondsF());
95 url_timing
->setConnectStart(
96 (load_timing
.connect_timing
.connect_start
- kNullTicks
).InSecondsF());
97 url_timing
->setConnectEnd(
98 (load_timing
.connect_timing
.connect_end
- kNullTicks
).InSecondsF());
99 url_timing
->setSSLStart(
100 (load_timing
.connect_timing
.ssl_start
- kNullTicks
).InSecondsF());
101 url_timing
->setSSLEnd(
102 (load_timing
.connect_timing
.ssl_end
- kNullTicks
).InSecondsF());
103 url_timing
->setSendStart(
104 (load_timing
.send_start
- kNullTicks
).InSecondsF());
105 url_timing
->setSendEnd(
106 (load_timing
.send_end
- kNullTicks
).InSecondsF());
107 url_timing
->setReceiveHeadersEnd(
108 (load_timing
.receive_headers_end
- kNullTicks
).InSecondsF());
111 net::RequestPriority
ConvertWebKitPriorityToNetPriority(
112 const WebURLRequest::Priority
& priority
) {
114 case WebURLRequest::PriorityVeryHigh
:
117 case WebURLRequest::PriorityHigh
:
120 case WebURLRequest::PriorityMedium
:
123 case WebURLRequest::PriorityLow
:
126 case WebURLRequest::PriorityVeryLow
:
129 case WebURLRequest::PriorityUnresolved
:
136 // Extracts info from a data scheme URL |url| into |info| and |data|. Returns
137 // net::OK if successful. Returns a net error code otherwise.
138 int GetInfoFromDataURL(const GURL
& url
,
139 ResourceResponseInfo
* info
,
141 // Assure same time for all time fields of data: URLs.
142 Time now
= Time::Now();
143 info
->load_timing
.request_start
= TimeTicks::Now();
144 info
->load_timing
.request_start_time
= now
;
145 info
->request_time
= now
;
146 info
->response_time
= now
;
148 std::string mime_type
;
150 scoped_refptr
<net::HttpResponseHeaders
> headers(
151 new net::HttpResponseHeaders(std::string()));
152 int result
= net::URLRequestDataJob::BuildResponse(
153 url
, &mime_type
, &charset
, data
, headers
.get());
154 if (result
!= net::OK
)
157 info
->headers
= headers
;
158 info
->mime_type
.swap(mime_type
);
159 info
->charset
.swap(charset
);
160 info
->security_info
.clear();
161 info
->content_length
= data
->length();
162 info
->encoded_data_length
= 0;
167 void SetSecurityStyleAndDetails(const GURL
& url
,
168 const std::string
& security_info
,
169 WebURLResponse
* response
,
170 bool report_security_info
) {
171 if (!report_security_info
) {
172 response
->setSecurityStyle(WebURLResponse::SecurityStyleUnknown
);
175 if (!url
.SchemeIsCryptographic()) {
176 response
->setSecurityStyle(WebURLResponse::SecurityStyleUnauthenticated
);
180 // There are cases where an HTTPS request can come in without security
181 // info attached (such as a redirect response).
182 if (security_info
.empty()) {
183 response
->setSecurityStyle(WebURLResponse::SecurityStyleUnknown
);
187 SSLStatus ssl_status
;
188 if (!DeserializeSecurityInfo(security_info
, &ssl_status
)) {
189 response
->setSecurityStyle(WebURLResponse::SecurityStyleUnknown
);
191 << "DeserializeSecurityInfo() failed for an authenticated request.";
196 net::SSLConnectionStatusToVersion(ssl_status
.connection_status
);
197 const char* protocol
;
198 net::SSLVersionToString(&protocol
, ssl_version
);
200 const char* key_exchange
;
204 uint16_t cipher_suite
=
205 net::SSLConnectionStatusToCipherSuite(ssl_status
.connection_status
);
206 net::SSLCipherSuiteToStrings(&key_exchange
, &cipher
, &mac
, &is_aead
,
213 blink::WebURLResponse::SecurityStyle securityStyle
=
214 WebURLResponse::SecurityStyleUnknown
;
215 switch (ssl_status
.security_style
) {
216 case SECURITY_STYLE_UNKNOWN
:
217 securityStyle
= WebURLResponse::SecurityStyleUnknown
;
219 case SECURITY_STYLE_UNAUTHENTICATED
:
220 securityStyle
= WebURLResponse::SecurityStyleUnauthenticated
;
222 case SECURITY_STYLE_AUTHENTICATION_BROKEN
:
223 securityStyle
= WebURLResponse::SecurityStyleAuthenticationBroken
;
225 case SECURITY_STYLE_WARNING
:
226 securityStyle
= WebURLResponse::SecurityStyleWarning
;
228 case SECURITY_STYLE_AUTHENTICATED
:
229 securityStyle
= WebURLResponse::SecurityStyleAuthenticated
;
233 response
->setSecurityStyle(securityStyle
);
235 blink::WebString protocol_string
= blink::WebString::fromUTF8(protocol
);
236 blink::WebString cipher_string
= blink::WebString::fromUTF8(cipher
);
237 blink::WebString key_exchange_string
=
238 blink::WebString::fromUTF8(key_exchange
);
239 blink::WebString mac_string
= blink::WebString::fromUTF8(mac
);
240 response
->setSecurityDetails(protocol_string
, key_exchange_string
,
241 cipher_string
, mac_string
,
247 // WebURLLoaderImpl::Context --------------------------------------------------
249 // This inner class exists since the WebURLLoader may be deleted while inside a
250 // call to WebURLLoaderClient. Refcounting is to keep the context from being
251 // deleted if it may have work to do after calling into the client.
252 class WebURLLoaderImpl::Context
: public base::RefCounted
<Context
>,
255 Context(WebURLLoaderImpl
* loader
,
256 ResourceDispatcher
* resource_dispatcher
,
257 scoped_refptr
<base::SingleThreadTaskRunner
> task_runner
);
259 WebURLLoaderClient
* client() const { return client_
; }
260 void set_client(WebURLLoaderClient
* client
) { client_
= client
; }
263 void SetDefersLoading(bool value
);
264 void DidChangePriority(WebURLRequest::Priority new_priority
,
265 int intra_priority_value
);
266 bool AttachThreadedDataReceiver(
267 blink::WebThreadedDataReceiver
* threaded_data_receiver
);
268 void Start(const WebURLRequest
& request
,
269 SyncLoadResponse
* sync_load_response
);
271 // RequestPeer methods:
272 void OnUploadProgress(uint64 position
, uint64 size
) override
;
273 bool OnReceivedRedirect(const net::RedirectInfo
& redirect_info
,
274 const ResourceResponseInfo
& info
) override
;
275 void OnReceivedResponse(const ResourceResponseInfo
& info
) override
;
276 void OnDownloadedData(int len
, int encoded_data_length
) override
;
277 void OnReceivedData(scoped_ptr
<ReceivedData
> data
) override
;
278 void OnReceivedCachedMetadata(const char* data
, int len
) override
;
279 void OnCompletedRequest(int error_code
,
280 bool was_ignored_by_handler
,
281 bool stale_copy_in_cache
,
282 const std::string
& security_info
,
283 const base::TimeTicks
& completion_time
,
284 int64 total_transfer_size
) override
;
285 void OnReceivedCompletedResponse(const ResourceResponseInfo
& info
,
286 scoped_ptr
<ReceivedData
> data
,
288 bool was_ignored_by_handler
,
289 bool stale_copy_in_cache
,
290 const std::string
& security_info
,
291 const base::TimeTicks
& completion_time
,
292 int64 total_transfer_size
) override
;
295 friend class base::RefCounted
<Context
>;
298 // Called when the body data stream is detached from the reader side.
299 void CancelBodyStreaming();
300 // We can optimize the handling of data URLs in most cases.
301 bool CanHandleDataURLRequestLocally() const;
302 void HandleDataURL();
304 WebURLLoaderImpl
* loader_
;
305 WebURLRequest request_
;
306 WebURLLoaderClient
* client_
;
307 ResourceDispatcher
* resource_dispatcher_
;
308 scoped_refptr
<base::SingleThreadTaskRunner
> task_runner_
;
309 WebReferrerPolicy referrer_policy_
;
310 scoped_ptr
<FtpDirectoryListingResponseDelegate
> ftp_listing_delegate_
;
311 scoped_ptr
<MultipartResponseDelegate
> multipart_delegate_
;
312 scoped_ptr
<StreamOverrideParameters
> stream_override_
;
313 scoped_ptr
<SharedMemoryDataConsumerHandle::Writer
> body_stream_writer_
;
314 enum DeferState
{NOT_DEFERRING
, SHOULD_DEFER
, DEFERRED_DATA
};
315 DeferState defers_loading_
;
319 WebURLLoaderImpl::Context::Context(
320 WebURLLoaderImpl
* loader
,
321 ResourceDispatcher
* resource_dispatcher
,
322 scoped_refptr
<base::SingleThreadTaskRunner
> task_runner
)
325 resource_dispatcher_(resource_dispatcher
),
326 task_runner_(task_runner
),
327 referrer_policy_(blink::WebReferrerPolicyDefault
),
328 defers_loading_(NOT_DEFERRING
),
332 void WebURLLoaderImpl::Context::Cancel() {
333 if (resource_dispatcher_
&& // NULL in unittest.
335 resource_dispatcher_
->Cancel(request_id_
);
339 if (body_stream_writer_
)
340 body_stream_writer_
->Fail();
342 // Ensure that we do not notify the multipart delegate anymore as it has
343 // its own pointer to the client.
344 if (multipart_delegate_
)
345 multipart_delegate_
->Cancel();
346 // Ditto for the ftp delegate.
347 if (ftp_listing_delegate_
)
348 ftp_listing_delegate_
->Cancel();
350 // Do not make any further calls to the client.
355 void WebURLLoaderImpl::Context::SetDefersLoading(bool value
) {
356 if (request_id_
!= -1)
357 resource_dispatcher_
->SetDefersLoading(request_id_
, value
);
358 if (value
&& defers_loading_
== NOT_DEFERRING
) {
359 defers_loading_
= SHOULD_DEFER
;
360 } else if (!value
&& defers_loading_
!= NOT_DEFERRING
) {
361 if (defers_loading_
== DEFERRED_DATA
) {
362 task_runner_
->PostTask(FROM_HERE
,
363 base::Bind(&Context::HandleDataURL
, this));
365 defers_loading_
= NOT_DEFERRING
;
369 void WebURLLoaderImpl::Context::DidChangePriority(
370 WebURLRequest::Priority new_priority
, int intra_priority_value
) {
371 if (request_id_
!= -1) {
372 resource_dispatcher_
->DidChangePriority(
374 ConvertWebKitPriorityToNetPriority(new_priority
),
375 intra_priority_value
);
379 bool WebURLLoaderImpl::Context::AttachThreadedDataReceiver(
380 blink::WebThreadedDataReceiver
* threaded_data_receiver
) {
381 if (request_id_
!= -1) {
382 return resource_dispatcher_
->AttachThreadedDataReceiver(
383 request_id_
, threaded_data_receiver
);
389 void WebURLLoaderImpl::Context::Start(const WebURLRequest
& request
,
390 SyncLoadResponse
* sync_load_response
) {
391 DCHECK(request_id_
== -1);
392 request_
= request
; // Save the request.
393 if (request
.extraData()) {
394 RequestExtraData
* extra_data
=
395 static_cast<RequestExtraData
*>(request
.extraData());
396 stream_override_
= extra_data
->TakeStreamOverrideOwnership();
399 GURL url
= request
.url();
401 // PlzNavigate: during navigation, the renderer should request a stream which
402 // contains the body of the response. The request has already been made by the
404 if (stream_override_
.get()) {
405 CHECK(base::CommandLine::ForCurrentProcess()->HasSwitch(
406 switches::kEnableBrowserSideNavigation
));
407 DCHECK(!sync_load_response
);
408 DCHECK_NE(WebURLRequest::FrameTypeNone
, request
.frameType());
409 DCHECK_EQ("GET", request
.httpMethod().latin1());
410 url
= stream_override_
->stream_url
;
413 if (CanHandleDataURLRequestLocally()) {
414 if (sync_load_response
) {
415 // This is a sync load. Do the work now.
416 sync_load_response
->url
= url
;
417 sync_load_response
->error_code
=
418 GetInfoFromDataURL(sync_load_response
->url
, sync_load_response
,
419 &sync_load_response
->data
);
421 task_runner_
->PostTask(FROM_HERE
,
422 base::Bind(&Context::HandleDataURL
, this));
427 // PlzNavigate: outside of tests, the only navigation requests going through
428 // the WebURLLoader are the ones created by CommitNavigation. Several browser
429 // tests load HTML directly through a data url which will be handled by the
431 DCHECK_IMPLIES(base::CommandLine::ForCurrentProcess()->HasSwitch(
432 switches::kEnableBrowserSideNavigation
),
433 stream_override_
.get() ||
434 request
.frameType() == WebURLRequest::FrameTypeNone
);
437 request
.httpHeaderField(WebString::fromUTF8("Referer")).latin1());
438 const std::string
& method
= request
.httpMethod().latin1();
440 // TODO(brettw) this should take parameter encoding into account when
441 // creating the GURLs.
443 // TODO(horo): Check credentials flag is unset when credentials mode is omit.
444 // Check credentials flag is set when credentials mode is include.
446 RequestInfo request_info
;
447 request_info
.method
= method
;
448 request_info
.url
= url
;
449 request_info
.first_party_for_cookies
= request
.firstPartyForCookies();
450 referrer_policy_
= request
.referrerPolicy();
451 request_info
.referrer
= Referrer(referrer_url
, referrer_policy_
);
452 request_info
.headers
= GetWebURLRequestHeaders(request
);
453 request_info
.load_flags
= GetLoadFlagsForWebURLRequest(request
);
454 request_info
.enable_load_timing
= true;
455 request_info
.enable_upload_progress
= request
.reportUploadProgress();
456 if (request
.requestContext() == WebURLRequest::RequestContextXMLHttpRequest
&&
457 (url
.has_username() || url
.has_password())) {
458 request_info
.do_not_prompt_for_login
= true;
460 // requestor_pid only needs to be non-zero if the request originates outside
461 // the render process, so we can use requestorProcessID even for requests
462 // from in-process plugins.
463 request_info
.requestor_pid
= request
.requestorProcessID();
464 request_info
.request_type
= WebURLRequestToResourceType(request
);
465 request_info
.priority
=
466 ConvertWebKitPriorityToNetPriority(request
.priority());
467 request_info
.appcache_host_id
= request
.appCacheHostID();
468 request_info
.routing_id
= request
.requestorID();
469 request_info
.download_to_file
= request
.downloadToFile();
470 request_info
.has_user_gesture
= request
.hasUserGesture();
471 request_info
.skip_service_worker
= request
.skipServiceWorker();
472 request_info
.should_reset_appcache
= request
.shouldResetAppCache();
473 request_info
.fetch_request_mode
=
474 GetFetchRequestModeForWebURLRequest(request
);
475 request_info
.fetch_credentials_mode
=
476 GetFetchCredentialsModeForWebURLRequest(request
);
477 request_info
.fetch_redirect_mode
=
478 GetFetchRedirectModeForWebURLRequest(request
);
479 request_info
.fetch_request_context_type
=
480 GetRequestContextTypeForWebURLRequest(request
);
481 request_info
.fetch_frame_type
=
482 GetRequestContextFrameTypeForWebURLRequest(request
);
483 request_info
.extra_data
= request
.extraData();
484 request_info
.report_raw_headers
= request
.reportRawHeaders();
486 scoped_refptr
<ResourceRequestBody
> request_body
=
487 GetRequestBodyForWebURLRequest(request
).get();
489 if (sync_load_response
) {
490 resource_dispatcher_
->StartSync(
491 request_info
, request_body
.get(), sync_load_response
);
495 request_id_
= resource_dispatcher_
->StartAsync(
496 request_info
, request_body
.get(), this);
499 void WebURLLoaderImpl::Context::OnUploadProgress(uint64 position
, uint64 size
) {
501 client_
->didSendData(loader_
, position
, size
);
504 bool WebURLLoaderImpl::Context::OnReceivedRedirect(
505 const net::RedirectInfo
& redirect_info
,
506 const ResourceResponseInfo
& info
) {
510 WebURLResponse response
;
511 response
.initialize();
512 PopulateURLResponse(request_
.url(), info
, &response
,
513 request_
.reportRawHeaders());
515 WebURLRequest new_request
;
516 new_request
.initialize();
517 PopulateURLRequestForRedirect(request_
, redirect_info
, referrer_policy_
,
518 !info
.was_fetched_via_service_worker
,
521 // Protect from deletion during call to willSendRequest.
522 scoped_refptr
<Context
> protect(this);
524 client_
->willSendRequest(loader_
, new_request
, response
);
525 request_
= new_request
;
527 // Only follow the redirect if WebKit left the URL unmodified.
528 if (redirect_info
.new_url
== GURL(new_request
.url())) {
529 // First-party cookie logic moved from DocumentLoader in Blink to
530 // net::URLRequest in the browser. Assert that Blink didn't try to change it
531 // to something else.
532 DCHECK_EQ(redirect_info
.new_first_party_for_cookies
.spec(),
533 request_
.firstPartyForCookies().string().utf8());
537 // We assume that WebKit only changes the URL to suppress a redirect, and we
538 // assume that it does so by setting it to be invalid.
539 DCHECK(!new_request
.url().isValid());
543 void WebURLLoaderImpl::Context::OnReceivedResponse(
544 const ResourceResponseInfo
& initial_info
) {
548 ResourceResponseInfo info
= initial_info
;
550 // PlzNavigate: during navigations, the ResourceResponse has already been
551 // received on the browser side, and has been passed down to the renderer.
552 if (stream_override_
.get()) {
553 CHECK(base::CommandLine::ForCurrentProcess()->HasSwitch(
554 switches::kEnableBrowserSideNavigation
));
555 info
= stream_override_
->response
;
558 WebURLResponse response
;
559 response
.initialize();
560 PopulateURLResponse(request_
.url(), info
, &response
,
561 request_
.reportRawHeaders());
563 bool show_raw_listing
= (GURL(request_
.url()).query() == "raw");
565 if (info
.mime_type
== "text/vnd.chromium.ftp-dir") {
566 if (show_raw_listing
) {
567 // Set the MIME type to plain text to prevent any active content.
568 response
.setMIMEType("text/plain");
570 // We're going to produce a parsed listing in HTML.
571 response
.setMIMEType("text/html");
575 // Prevent |this| from being destroyed if the client destroys the loader,
576 // ether in didReceiveResponse, or when the multipart/ftp delegate calls into
578 scoped_refptr
<Context
> protect(this);
580 if (request_
.useStreamOnResponse()) {
581 SharedMemoryDataConsumerHandle::BackpressureMode mode
=
582 SharedMemoryDataConsumerHandle::kDoNotApplyBackpressure
;
584 info
.headers
->HasHeaderValue("Cache-Control", "no-store")) {
585 mode
= SharedMemoryDataConsumerHandle::kApplyBackpressure
;
588 auto read_handle
= make_scoped_ptr(new SharedMemoryDataConsumerHandle(
589 mode
, base::Bind(&Context::CancelBodyStreaming
, this),
590 &body_stream_writer_
));
592 // Here |body_stream_writer_| has an indirect reference to |this| and that
593 // creates a reference cycle, but it is not a problem because the cycle
594 // will break if one of the following happens:
595 // 1) The body data transfer is done (with or without an error).
596 // 2) |read_handle| (and its reader) is detached.
598 // The client takes |read_handle|'s ownership.
599 client_
->didReceiveResponse(loader_
, response
, read_handle
.release());
600 // TODO(yhirano): Support ftp listening and multipart
603 client_
->didReceiveResponse(loader_
, response
);
606 // We may have been cancelled after didReceiveResponse, which would leave us
607 // without a client and therefore without much need to do further handling.
611 DCHECK(!ftp_listing_delegate_
.get());
612 DCHECK(!multipart_delegate_
.get());
613 if (info
.headers
.get() && info
.mime_type
== "multipart/x-mixed-replace") {
614 std::string content_type
;
615 info
.headers
->EnumerateHeader(NULL
, "content-type", &content_type
);
617 std::string mime_type
;
619 bool had_charset
= false;
620 std::string boundary
;
621 net::HttpUtil::ParseContentType(content_type
, &mime_type
, &charset
,
622 &had_charset
, &boundary
);
623 base::TrimString(boundary
, " \"", &boundary
);
625 // If there's no boundary, just handle the request normally. In the gecko
626 // code, nsMultiMixedConv::OnStartRequest throws an exception.
627 if (!boundary
.empty()) {
628 multipart_delegate_
.reset(
629 new MultipartResponseDelegate(client_
, loader_
, response
, boundary
));
631 } else if (info
.mime_type
== "text/vnd.chromium.ftp-dir" &&
633 ftp_listing_delegate_
.reset(
634 new FtpDirectoryListingResponseDelegate(client_
, loader_
, response
));
638 void WebURLLoaderImpl::Context::OnDownloadedData(int len
,
639 int encoded_data_length
) {
641 client_
->didDownloadData(loader_
, len
, encoded_data_length
);
644 void WebURLLoaderImpl::Context::OnReceivedData(scoped_ptr
<ReceivedData
> data
) {
645 const char* payload
= data
->payload();
646 int data_length
= data
->length();
647 int encoded_data_length
= data
->encoded_length();
651 if (ftp_listing_delegate_
) {
652 // The FTP listing delegate will make the appropriate calls to
653 // client_->didReceiveData and client_->didReceiveResponse. Since the
654 // delegate may want to do work after sending data to the delegate, keep
655 // |this| and the delegate alive until it's finished handling the data.
656 scoped_refptr
<Context
> protect(this);
657 ftp_listing_delegate_
->OnReceivedData(payload
, data_length
);
658 } else if (multipart_delegate_
) {
659 // The multipart delegate will make the appropriate calls to
660 // client_->didReceiveData and client_->didReceiveResponse. Since the
661 // delegate may want to do work after sending data to the delegate, keep
662 // |this| and the delegate alive until it's finished handling the data.
663 scoped_refptr
<Context
> protect(this);
664 multipart_delegate_
->OnReceivedData(payload
, data_length
,
665 encoded_data_length
);
667 scoped_refptr
<Context
> protect(this);
668 // We dispatch the data even when |useStreamOnResponse()| is set, in order
669 // to make Devtools work.
670 client_
->didReceiveData(loader_
, payload
, data_length
, encoded_data_length
);
672 if (request_
.useStreamOnResponse()) {
673 // We don't support ftp_listening_delegate_ and multipart_delegate_ for
675 // TODO(yhirano): Support ftp listening and multipart.
676 body_stream_writer_
->AddData(data
.Pass());
681 void WebURLLoaderImpl::Context::OnReceivedCachedMetadata(
682 const char* data
, int len
) {
684 client_
->didReceiveCachedMetadata(loader_
, data
, len
);
687 void WebURLLoaderImpl::Context::OnCompletedRequest(
689 bool was_ignored_by_handler
,
690 bool stale_copy_in_cache
,
691 const std::string
& security_info
,
692 const base::TimeTicks
& completion_time
,
693 int64 total_transfer_size
) {
694 // The WebURLLoaderImpl may be deleted in any of the calls to the client or
695 // the delegates below (As they also may call in to the client). Keep |this|
696 // alive in that case, to avoid a crash. If that happens, the request will be
697 // cancelled and |client_| will be set to NULL.
698 scoped_refptr
<Context
> protect(this);
700 if (ftp_listing_delegate_
) {
701 ftp_listing_delegate_
->OnCompletedRequest();
702 ftp_listing_delegate_
.reset(NULL
);
703 } else if (multipart_delegate_
) {
704 multipart_delegate_
->OnCompletedRequest();
705 multipart_delegate_
.reset(NULL
);
708 if (body_stream_writer_
&& error_code
!= net::OK
)
709 body_stream_writer_
->Fail();
710 body_stream_writer_
.reset();
713 if (error_code
!= net::OK
) {
716 CreateWebURLError(request_
.url(), stale_copy_in_cache
, error_code
,
717 was_ignored_by_handler
));
719 client_
->didFinishLoading(loader_
,
720 (completion_time
- TimeTicks()).InSecondsF(),
721 total_transfer_size
);
726 void WebURLLoaderImpl::Context::OnReceivedCompletedResponse(
727 const ResourceResponseInfo
& info
,
728 scoped_ptr
<ReceivedData
> data
,
730 bool was_ignored_by_handler
,
731 bool stale_copy_in_cache
,
732 const std::string
& security_info
,
733 const base::TimeTicks
& completion_time
,
734 int64 total_transfer_size
) {
735 scoped_refptr
<Context
> protect(this);
737 OnReceivedResponse(info
);
739 OnReceivedData(data
.Pass());
740 OnCompletedRequest(error_code
, was_ignored_by_handler
, stale_copy_in_cache
,
741 security_info
, completion_time
, total_transfer_size
);
744 WebURLLoaderImpl::Context::~Context() {
745 if (request_id_
>= 0) {
746 resource_dispatcher_
->RemovePendingRequest(request_id_
);
750 void WebURLLoaderImpl::Context::CancelBodyStreaming() {
751 scoped_refptr
<Context
> protect(this);
753 // Notify renderer clients that the request is canceled.
754 if (ftp_listing_delegate_
) {
755 ftp_listing_delegate_
->OnCompletedRequest();
756 ftp_listing_delegate_
.reset(NULL
);
757 } else if (multipart_delegate_
) {
758 multipart_delegate_
->OnCompletedRequest();
759 multipart_delegate_
.reset(NULL
);
762 if (body_stream_writer_
) {
763 body_stream_writer_
->Fail();
764 body_stream_writer_
.reset();
767 // TODO(yhirano): Set |stale_copy_in_cache| appropriately if possible.
769 loader_
, CreateWebURLError(request_
.url(), false, net::ERR_ABORTED
));
772 // Notify the browser process that the request is canceled.
776 bool WebURLLoaderImpl::Context::CanHandleDataURLRequestLocally() const {
777 GURL url
= request_
.url();
778 if (!url
.SchemeIs(url::kDataScheme
))
781 // The fast paths for data URL, Start() and HandleDataURL(), don't support
782 // the downloadToFile option.
783 if (request_
.downloadToFile())
786 // Data url requests from object tags may need to be intercepted as streams
787 // and so need to be sent to the browser.
788 if (request_
.requestContext() == WebURLRequest::RequestContextObject
)
791 // Optimize for the case where we can handle a data URL locally. We must
792 // skip this for data URLs targetted at frames since those could trigger a
795 // NOTE: We special case MIME types we can render both for performance
796 // reasons as well as to support unit tests.
798 #if defined(OS_ANDROID)
799 // For compatibility reasons on Android we need to expose top-level data://
801 if (request_
.frameType() == WebURLRequest::FrameTypeTopLevel
)
805 if (request_
.frameType() != WebURLRequest::FrameTypeTopLevel
&&
806 request_
.frameType() != WebURLRequest::FrameTypeNested
)
809 std::string mime_type
, unused_charset
;
810 if (net::DataURL::Parse(request_
.url(), &mime_type
, &unused_charset
, NULL
) &&
811 mime_util::IsSupportedMimeType(mime_type
))
817 void WebURLLoaderImpl::Context::HandleDataURL() {
818 DCHECK_NE(defers_loading_
, DEFERRED_DATA
);
819 if (defers_loading_
== SHOULD_DEFER
) {
820 defers_loading_
= DEFERRED_DATA
;
824 ResourceResponseInfo info
;
827 int error_code
= GetInfoFromDataURL(request_
.url(), &info
, &data
);
829 if (error_code
== net::OK
) {
830 OnReceivedResponse(info
);
833 make_scoped_ptr(new FixedReceivedData(data
.data(), data
.size(), 0)));
836 OnCompletedRequest(error_code
, false, false, info
.security_info
,
837 base::TimeTicks::Now(), 0);
840 // WebURLLoaderImpl -----------------------------------------------------------
842 WebURLLoaderImpl::WebURLLoaderImpl(
843 ResourceDispatcher
* resource_dispatcher
,
844 scoped_refptr
<base::SingleThreadTaskRunner
> task_runner
)
845 : context_(new Context(this, resource_dispatcher
, task_runner
)) {
848 WebURLLoaderImpl::~WebURLLoaderImpl() {
852 void WebURLLoaderImpl::PopulateURLResponse(const GURL
& url
,
853 const ResourceResponseInfo
& info
,
854 WebURLResponse
* response
,
855 bool report_security_info
) {
856 response
->setURL(url
);
857 response
->setResponseTime(info
.response_time
.ToInternalValue());
858 response
->setMIMEType(WebString::fromUTF8(info
.mime_type
));
859 response
->setTextEncodingName(WebString::fromUTF8(info
.charset
));
860 response
->setExpectedContentLength(info
.content_length
);
861 response
->setSecurityInfo(info
.security_info
);
862 response
->setAppCacheID(info
.appcache_id
);
863 response
->setAppCacheManifestURL(info
.appcache_manifest_url
);
864 response
->setWasCached(!info
.load_timing
.request_start_time
.is_null() &&
865 info
.response_time
< info
.load_timing
.request_start_time
);
866 response
->setRemoteIPAddress(
867 WebString::fromUTF8(info
.socket_address
.host()));
868 response
->setRemotePort(info
.socket_address
.port());
869 response
->setConnectionID(info
.load_timing
.socket_log_id
);
870 response
->setConnectionReused(info
.load_timing
.socket_reused
);
871 response
->setDownloadFilePath(info
.download_file_path
.AsUTF16Unsafe());
872 response
->setWasFetchedViaSPDY(info
.was_fetched_via_spdy
);
873 response
->setWasFetchedViaServiceWorker(info
.was_fetched_via_service_worker
);
874 response
->setWasFallbackRequiredByServiceWorker(
875 info
.was_fallback_required_by_service_worker
);
876 response
->setServiceWorkerResponseType(info
.response_type_via_service_worker
);
877 response
->setOriginalURLViaServiceWorker(
878 info
.original_url_via_service_worker
);
880 SetSecurityStyleAndDetails(url
, info
.security_info
, response
,
881 report_security_info
);
883 WebURLResponseExtraDataImpl
* extra_data
=
884 new WebURLResponseExtraDataImpl(info
.npn_negotiated_protocol
);
885 response
->setExtraData(extra_data
);
886 extra_data
->set_was_fetched_via_spdy(info
.was_fetched_via_spdy
);
887 extra_data
->set_was_npn_negotiated(info
.was_npn_negotiated
);
888 extra_data
->set_was_alternate_protocol_available(
889 info
.was_alternate_protocol_available
);
890 extra_data
->set_connection_info(info
.connection_info
);
891 extra_data
->set_was_fetched_via_proxy(info
.was_fetched_via_proxy
);
892 extra_data
->set_proxy_server(info
.proxy_server
);
894 // If there's no received headers end time, don't set load timing. This is
895 // the case for non-HTTP requests, requests that don't go over the wire, and
896 // certain error cases.
897 if (!info
.load_timing
.receive_headers_end
.is_null()) {
898 WebURLLoadTiming timing
;
899 PopulateURLLoadTiming(info
.load_timing
, &timing
);
900 const TimeTicks kNullTicks
;
901 timing
.setWorkerStart(
902 (info
.service_worker_start_time
- kNullTicks
).InSecondsF());
903 timing
.setWorkerReady(
904 (info
.service_worker_ready_time
- kNullTicks
).InSecondsF());
905 response
->setLoadTiming(timing
);
908 if (info
.devtools_info
.get()) {
909 WebHTTPLoadInfo load_info
;
911 load_info
.setHTTPStatusCode(info
.devtools_info
->http_status_code
);
912 load_info
.setHTTPStatusText(WebString::fromLatin1(
913 info
.devtools_info
->http_status_text
));
914 load_info
.setEncodedDataLength(info
.encoded_data_length
);
916 load_info
.setRequestHeadersText(WebString::fromLatin1(
917 info
.devtools_info
->request_headers_text
));
918 load_info
.setResponseHeadersText(WebString::fromLatin1(
919 info
.devtools_info
->response_headers_text
));
920 const HeadersVector
& request_headers
= info
.devtools_info
->request_headers
;
921 for (HeadersVector::const_iterator it
= request_headers
.begin();
922 it
!= request_headers
.end(); ++it
) {
923 load_info
.addRequestHeader(WebString::fromLatin1(it
->first
),
924 WebString::fromLatin1(it
->second
));
926 const HeadersVector
& response_headers
=
927 info
.devtools_info
->response_headers
;
928 for (HeadersVector::const_iterator it
= response_headers
.begin();
929 it
!= response_headers
.end(); ++it
) {
930 load_info
.addResponseHeader(WebString::fromLatin1(it
->first
),
931 WebString::fromLatin1(it
->second
));
933 load_info
.setNPNNegotiatedProtocol(WebString::fromLatin1(
934 info
.npn_negotiated_protocol
));
935 response
->setHTTPLoadInfo(load_info
);
938 const net::HttpResponseHeaders
* headers
= info
.headers
.get();
942 WebURLResponse::HTTPVersion version
= WebURLResponse::Unknown
;
943 if (headers
->GetHttpVersion() == net::HttpVersion(0, 9))
944 version
= WebURLResponse::HTTP_0_9
;
945 else if (headers
->GetHttpVersion() == net::HttpVersion(1, 0))
946 version
= WebURLResponse::HTTP_1_0
;
947 else if (headers
->GetHttpVersion() == net::HttpVersion(1, 1))
948 version
= WebURLResponse::HTTP_1_1
;
949 response
->setHTTPVersion(version
);
950 response
->setHTTPStatusCode(headers
->response_code());
951 response
->setHTTPStatusText(WebString::fromLatin1(headers
->GetStatusText()));
953 // TODO(darin): We should leverage HttpResponseHeaders for this, and this
954 // should be using the same code as ResourceDispatcherHost.
955 // TODO(jungshik): Figure out the actual value of the referrer charset and
956 // pass it to GetSuggestedFilename.
958 headers
->EnumerateHeader(NULL
, "content-disposition", &value
);
959 response
->setSuggestedFileName(
960 net::GetSuggestedFilename(url
,
962 std::string(), // referrer_charset
963 std::string(), // suggested_name
964 std::string(), // mime_type
965 std::string())); // default_name
968 if (headers
->GetLastModifiedValue(&time_val
))
969 response
->setLastModifiedDate(time_val
.ToDoubleT());
971 // Build up the header map.
974 while (headers
->EnumerateHeaderLines(&iter
, &name
, &value
)) {
975 response
->addHTTPHeaderField(WebString::fromLatin1(name
),
976 WebString::fromLatin1(value
));
980 void WebURLLoaderImpl::PopulateURLRequestForRedirect(
981 const blink::WebURLRequest
& request
,
982 const net::RedirectInfo
& redirect_info
,
983 blink::WebReferrerPolicy referrer_policy
,
984 bool skip_service_worker
,
985 blink::WebURLRequest
* new_request
) {
986 // TODO(darin): We lack sufficient information to construct the actual
987 // request that resulted from the redirect.
988 new_request
->setURL(redirect_info
.new_url
);
989 new_request
->setFirstPartyForCookies(
990 redirect_info
.new_first_party_for_cookies
);
991 new_request
->setDownloadToFile(request
.downloadToFile());
992 new_request
->setUseStreamOnResponse(request
.useStreamOnResponse());
993 new_request
->setRequestContext(request
.requestContext());
994 new_request
->setFrameType(request
.frameType());
995 new_request
->setSkipServiceWorker(skip_service_worker
);
996 new_request
->setShouldResetAppCache(request
.shouldResetAppCache());
997 new_request
->setFetchRequestMode(request
.fetchRequestMode());
998 new_request
->setFetchCredentialsMode(request
.fetchCredentialsMode());
1000 new_request
->setHTTPReferrer(WebString::fromUTF8(redirect_info
.new_referrer
),
1003 std::string old_method
= request
.httpMethod().utf8();
1004 new_request
->setHTTPMethod(WebString::fromUTF8(redirect_info
.new_method
));
1005 if (redirect_info
.new_method
== old_method
)
1006 new_request
->setHTTPBody(request
.httpBody());
1009 void WebURLLoaderImpl::loadSynchronously(const WebURLRequest
& request
,
1010 WebURLResponse
& response
,
1013 SyncLoadResponse sync_load_response
;
1014 context_
->Start(request
, &sync_load_response
);
1016 const GURL
& final_url
= sync_load_response
.url
;
1018 // TODO(tc): For file loads, we may want to include a more descriptive
1019 // status code or status text.
1020 int error_code
= sync_load_response
.error_code
;
1021 if (error_code
!= net::OK
) {
1022 response
.setURL(final_url
);
1023 error
.domain
= WebString::fromUTF8(net::kErrorDomain
);
1024 error
.reason
= error_code
;
1025 error
.unreachableURL
= final_url
;
1029 PopulateURLResponse(final_url
, sync_load_response
, &response
,
1030 request
.reportRawHeaders());
1032 data
.assign(sync_load_response
.data
.data(),
1033 sync_load_response
.data
.size());
1036 void WebURLLoaderImpl::loadAsynchronously(const WebURLRequest
& request
,
1037 WebURLLoaderClient
* client
) {
1038 DCHECK(!context_
->client());
1040 context_
->set_client(client
);
1041 context_
->Start(request
, NULL
);
1044 void WebURLLoaderImpl::cancel() {
1048 void WebURLLoaderImpl::setDefersLoading(bool value
) {
1049 context_
->SetDefersLoading(value
);
1052 void WebURLLoaderImpl::didChangePriority(WebURLRequest::Priority new_priority
,
1053 int intra_priority_value
) {
1054 context_
->DidChangePriority(new_priority
, intra_priority_value
);
1057 bool WebURLLoaderImpl::attachThreadedDataReceiver(
1058 blink::WebThreadedDataReceiver
* threaded_data_receiver
) {
1059 return context_
->AttachThreadedDataReceiver(threaded_data_receiver
);
1062 } // namespace content