Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / content / child / web_url_loader_impl.cc
blob83c9ad5b64db596b8d5e7941f2c8abb3ea6ea493
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"
7 #include <algorithm>
8 #include <string>
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"
53 using base::Time;
54 using base::TimeTicks;
55 using blink::WebData;
56 using blink::WebHTTPBody;
57 using blink::WebHTTPHeaderVisitor;
58 using blink::WebHTTPLoadInfo;
59 using blink::WebReferrerPolicy;
60 using blink::WebSecurityPolicy;
61 using blink::WebString;
62 using blink::WebURL;
63 using blink::WebURLError;
64 using blink::WebURLLoadTiming;
65 using blink::WebURLLoader;
66 using blink::WebURLLoaderClient;
67 using blink::WebURLRequest;
68 using blink::WebURLResponse;
70 namespace content {
72 // Utilities ------------------------------------------------------------------
74 namespace {
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) {
113 switch (priority) {
114 case WebURLRequest::PriorityVeryHigh:
115 return net::HIGHEST;
117 case WebURLRequest::PriorityHigh:
118 return net::MEDIUM;
120 case WebURLRequest::PriorityMedium:
121 return net::LOW;
123 case WebURLRequest::PriorityLow:
124 return net::LOWEST;
126 case WebURLRequest::PriorityVeryLow:
127 return net::IDLE;
129 case WebURLRequest::PriorityUnresolved:
130 default:
131 NOTREACHED();
132 return net::LOW;
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,
140 std::string* data) {
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;
149 std::string charset;
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)
155 return result;
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;
164 return net::OK;
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);
173 return;
175 if (!url.SchemeIsCryptographic()) {
176 response->setSecurityStyle(WebURLResponse::SecurityStyleUnauthenticated);
177 return;
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);
184 return;
187 SSLStatus ssl_status;
188 if (!DeserializeSecurityInfo(security_info, &ssl_status)) {
189 response->setSecurityStyle(WebURLResponse::SecurityStyleUnknown);
190 DLOG(ERROR)
191 << "DeserializeSecurityInfo() failed for an authenticated request.";
192 return;
195 int ssl_version =
196 net::SSLConnectionStatusToVersion(ssl_status.connection_status);
197 const char* protocol;
198 net::SSLVersionToString(&protocol, ssl_version);
200 const char* key_exchange;
201 const char* cipher;
202 const char* mac;
203 bool is_aead;
204 uint16_t cipher_suite =
205 net::SSLConnectionStatusToCipherSuite(ssl_status.connection_status);
206 net::SSLCipherSuiteToStrings(&key_exchange, &cipher, &mac, &is_aead,
207 cipher_suite);
208 if (mac == NULL) {
209 DCHECK(is_aead);
210 mac = "";
213 blink::WebURLResponse::SecurityStyle securityStyle =
214 WebURLResponse::SecurityStyleUnknown;
215 switch (ssl_status.security_style) {
216 case SECURITY_STYLE_UNKNOWN:
217 securityStyle = WebURLResponse::SecurityStyleUnknown;
218 break;
219 case SECURITY_STYLE_UNAUTHENTICATED:
220 securityStyle = WebURLResponse::SecurityStyleUnauthenticated;
221 break;
222 case SECURITY_STYLE_AUTHENTICATION_BROKEN:
223 securityStyle = WebURLResponse::SecurityStyleAuthenticationBroken;
224 break;
225 case SECURITY_STYLE_WARNING:
226 securityStyle = WebURLResponse::SecurityStyleWarning;
227 break;
228 case SECURITY_STYLE_AUTHENTICATED:
229 securityStyle = WebURLResponse::SecurityStyleAuthenticated;
230 break;
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,
242 ssl_status.cert_id);
245 } // namespace
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>,
253 public RequestPeer {
254 public:
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; }
262 void Cancel();
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,
287 int error_code,
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;
294 private:
295 friend class base::RefCounted<Context>;
296 ~Context() override;
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_;
316 int request_id_;
319 WebURLLoaderImpl::Context::Context(
320 WebURLLoaderImpl* loader,
321 ResourceDispatcher* resource_dispatcher,
322 scoped_refptr<base::SingleThreadTaskRunner> task_runner)
323 : loader_(loader),
324 client_(NULL),
325 resource_dispatcher_(resource_dispatcher),
326 task_runner_(task_runner),
327 referrer_policy_(blink::WebReferrerPolicyDefault),
328 defers_loading_(NOT_DEFERRING),
329 request_id_(-1) {
332 void WebURLLoaderImpl::Context::Cancel() {
333 if (resource_dispatcher_ && // NULL in unittest.
334 request_id_ != -1) {
335 resource_dispatcher_->Cancel(request_id_);
336 request_id_ = -1;
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.
351 client_ = NULL;
352 loader_ = NULL;
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(
373 request_id_,
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);
386 return false;
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
403 // browser.
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);
420 } else {
421 task_runner_->PostTask(FROM_HERE,
422 base::Bind(&Context::HandleDataURL, this));
424 return;
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
430 // block above.
431 DCHECK_IMPLIES(base::CommandLine::ForCurrentProcess()->HasSwitch(
432 switches::kEnableBrowserSideNavigation),
433 stream_override_.get() ||
434 request.frameType() == WebURLRequest::FrameTypeNone);
436 GURL referrer_url(
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);
492 return;
495 request_id_ = resource_dispatcher_->StartAsync(
496 request_info, request_body.get(), this);
499 void WebURLLoaderImpl::Context::OnUploadProgress(uint64 position, uint64 size) {
500 if (client_)
501 client_->didSendData(loader_, position, size);
504 bool WebURLLoaderImpl::Context::OnReceivedRedirect(
505 const net::RedirectInfo& redirect_info,
506 const ResourceResponseInfo& info) {
507 if (!client_)
508 return false;
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,
519 &new_request);
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());
534 return true;
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());
540 return false;
543 void WebURLLoaderImpl::Context::OnReceivedResponse(
544 const ResourceResponseInfo& initial_info) {
545 if (!client_)
546 return;
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");
569 } else {
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
577 // it.
578 scoped_refptr<Context> protect(this);
580 if (request_.useStreamOnResponse()) {
581 SharedMemoryDataConsumerHandle::BackpressureMode mode =
582 SharedMemoryDataConsumerHandle::kDoNotApplyBackpressure;
583 if (info.headers &&
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
601 return;
602 } else {
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.
608 if (!client_)
609 return;
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;
618 std::string charset;
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" &&
632 !show_raw_listing) {
633 ftp_listing_delegate_.reset(
634 new FtpDirectoryListingResponseDelegate(client_, loader_, response));
638 void WebURLLoaderImpl::Context::OnDownloadedData(int len,
639 int encoded_data_length) {
640 if (client_)
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();
648 if (!client_)
649 return;
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);
666 } else {
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
674 // now.
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) {
683 if (client_)
684 client_->didReceiveCachedMetadata(loader_, data, len);
687 void WebURLLoaderImpl::Context::OnCompletedRequest(
688 int error_code,
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();
712 if (client_) {
713 if (error_code != net::OK) {
714 client_->didFail(
715 loader_,
716 CreateWebURLError(request_.url(), stale_copy_in_cache, error_code,
717 was_ignored_by_handler));
718 } else {
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,
729 int error_code,
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);
738 if (data)
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();
766 if (client_) {
767 // TODO(yhirano): Set |stale_copy_in_cache| appropriately if possible.
768 client_->didFail(
769 loader_, CreateWebURLError(request_.url(), false, net::ERR_ABORTED));
772 // Notify the browser process that the request is canceled.
773 Cancel();
776 bool WebURLLoaderImpl::Context::CanHandleDataURLRequestLocally() const {
777 GURL url = request_.url();
778 if (!url.SchemeIs(url::kDataScheme))
779 return false;
781 // The fast paths for data URL, Start() and HandleDataURL(), don't support
782 // the downloadToFile option.
783 if (request_.downloadToFile())
784 return false;
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)
789 return false;
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
793 // download.
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://
800 // to the browser.
801 if (request_.frameType() == WebURLRequest::FrameTypeTopLevel)
802 return false;
803 #endif
805 if (request_.frameType() != WebURLRequest::FrameTypeTopLevel &&
806 request_.frameType() != WebURLRequest::FrameTypeNested)
807 return true;
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))
812 return true;
814 return false;
817 void WebURLLoaderImpl::Context::HandleDataURL() {
818 DCHECK_NE(defers_loading_, DEFERRED_DATA);
819 if (defers_loading_ == SHOULD_DEFER) {
820 defers_loading_ = DEFERRED_DATA;
821 return;
824 ResourceResponseInfo info;
825 std::string data;
827 int error_code = GetInfoFromDataURL(request_.url(), &info, &data);
829 if (error_code == net::OK) {
830 OnReceivedResponse(info);
831 if (!data.empty())
832 OnReceivedData(
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() {
849 cancel();
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();
939 if (!headers)
940 return;
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.
957 std::string value;
958 headers->EnumerateHeader(NULL, "content-disposition", &value);
959 response->setSuggestedFileName(
960 net::GetSuggestedFilename(url,
961 value,
962 std::string(), // referrer_charset
963 std::string(), // suggested_name
964 std::string(), // mime_type
965 std::string())); // default_name
967 Time time_val;
968 if (headers->GetLastModifiedValue(&time_val))
969 response->setLastModifiedDate(time_val.ToDoubleT());
971 // Build up the header map.
972 void* iter = NULL;
973 std::string name;
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),
1001 referrer_policy);
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,
1011 WebURLError& error,
1012 WebData& data) {
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;
1026 return;
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() {
1045 context_->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