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. 4 5 // An implementation of WebURLLoader in terms of ResourceLoaderBridge. 6 7 #include "content/child/web_url_loader_impl.h" 8 9 #include "base/bind.h" 10 #include "base/files/file_path.h" 11 #include "base/memory/scoped_ptr.h" 12 #include "base/message_loop/message_loop.h" 13 #include "base/strings/string_util.h" 14 #include "base/time/time.h" 15 #include "content/child/ftp_directory_listing_response_delegate.h" 16 #include "content/child/multipart_response_delegate.h" 17 #include "content/child/request_extra_data.h" 18 #include "content/child/request_info.h" 19 #include "content/child/resource_dispatcher.h" 20 #include "content/child/resource_loader_bridge.h" 21 #include "content/child/sync_load_response.h" 22 #include "content/child/web_url_request_util.h" 23 #include "content/child/weburlresponse_extradata_impl.h" 24 #include "content/common/resource_request_body.h" 25 #include "content/public/child/request_peer.h" 26 #include "net/base/data_url.h" 27 #include "net/base/filename_util.h" 28 #include "net/base/load_flags.h" 29 #include "net/base/mime_util.h" 30 #include "net/base/net_errors.h" 31 #include "net/http/http_response_headers.h" 32 #include "net/http/http_util.h" 33 #include "net/url_request/redirect_info.h" 34 #include "net/url_request/url_request_data_job.h" 35 #include "third_party/WebKit/public/platform/WebHTTPHeaderVisitor.h" 36 #include "third_party/WebKit/public/platform/WebHTTPLoadInfo.h" 37 #include "third_party/WebKit/public/platform/WebURL.h" 38 #include "third_party/WebKit/public/platform/WebURLError.h" 39 #include "third_party/WebKit/public/platform/WebURLLoadTiming.h" 40 #include "third_party/WebKit/public/platform/WebURLLoaderClient.h" 41 #include "third_party/WebKit/public/platform/WebURLRequest.h" 42 #include "third_party/WebKit/public/platform/WebURLResponse.h" 43 #include "third_party/WebKit/public/web/WebSecurityPolicy.h" 44 45 using base::Time; 46 using base::TimeTicks; 47 using blink::WebData; 48 using blink::WebHTTPBody; 49 using blink::WebHTTPHeaderVisitor; 50 using blink::WebHTTPLoadInfo; 51 using blink::WebReferrerPolicy; 52 using blink::WebSecurityPolicy; 53 using blink::WebString; 54 using blink::WebURL; 55 using blink::WebURLError; 56 using blink::WebURLLoadTiming; 57 using blink::WebURLLoader; 58 using blink::WebURLLoaderClient; 59 using blink::WebURLRequest; 60 using blink::WebURLResponse; 61 62 namespace content { 63 64 // Utilities ------------------------------------------------------------------ 65 66 namespace { 67 68 const char kThrottledErrorDescription[] = 69 "Request throttled. Visit http://dev.chromium.org/throttling for more " 70 "information."; 71 72 class HeaderFlattener : public WebHTTPHeaderVisitor { 73 public: 74 HeaderFlattener() : has_accept_header_(false) {} 75 76 virtual void visitHeader(const WebString& name, const WebString& value) { 77 // Headers are latin1. 78 const std::string& name_latin1 = name.latin1(); 79 const std::string& value_latin1 = value.latin1(); 80 81 // Skip over referrer headers found in the header map because we already 82 // pulled it out as a separate parameter. 83 if (LowerCaseEqualsASCII(name_latin1, "referer")) 84 return; 85 86 if (LowerCaseEqualsASCII(name_latin1, "accept")) 87 has_accept_header_ = true; 88 89 if (!buffer_.empty()) 90 buffer_.append("\r\n"); 91 buffer_.append(name_latin1 + ": " + value_latin1); 92 } 93 94 const std::string& GetBuffer() { 95 // In some cases, WebKit doesn't add an Accept header, but not having the 96 // header confuses some web servers. See bug 808613. 97 if (!has_accept_header_) { 98 if (!buffer_.empty()) 99 buffer_.append("\r\n"); 100 buffer_.append("Accept: */*"); 101 has_accept_header_ = true; 102 } 103 return buffer_; 104 } 105 106 private: 107 std::string buffer_; 108 bool has_accept_header_; 109 }; 110 111 typedef ResourceDevToolsInfo::HeadersVector HeadersVector; 112 113 // Converts timing data from |load_timing| to the format used by WebKit. 114 void PopulateURLLoadTiming(const net::LoadTimingInfo& load_timing, 115 WebURLLoadTiming* url_timing) { 116 DCHECK(!load_timing.request_start.is_null()); 117 118 const TimeTicks kNullTicks; 119 url_timing->initialize(); 120 url_timing->setRequestTime( 121 (load_timing.request_start - kNullTicks).InSecondsF()); 122 url_timing->setProxyStart( 123 (load_timing.proxy_resolve_start - kNullTicks).InSecondsF()); 124 url_timing->setProxyEnd( 125 (load_timing.proxy_resolve_end - kNullTicks).InSecondsF()); 126 url_timing->setDNSStart( 127 (load_timing.connect_timing.dns_start - kNullTicks).InSecondsF()); 128 url_timing->setDNSEnd( 129 (load_timing.connect_timing.dns_end - kNullTicks).InSecondsF()); 130 url_timing->setConnectStart( 131 (load_timing.connect_timing.connect_start - kNullTicks).InSecondsF()); 132 url_timing->setConnectEnd( 133 (load_timing.connect_timing.connect_end - kNullTicks).InSecondsF()); 134 url_timing->setSSLStart( 135 (load_timing.connect_timing.ssl_start - kNullTicks).InSecondsF()); 136 url_timing->setSSLEnd( 137 (load_timing.connect_timing.ssl_end - kNullTicks).InSecondsF()); 138 url_timing->setSendStart( 139 (load_timing.send_start - kNullTicks).InSecondsF()); 140 url_timing->setSendEnd( 141 (load_timing.send_end - kNullTicks).InSecondsF()); 142 url_timing->setReceiveHeadersEnd( 143 (load_timing.receive_headers_end - kNullTicks).InSecondsF()); 144 } 145 146 net::RequestPriority ConvertWebKitPriorityToNetPriority( 147 const WebURLRequest::Priority& priority) { 148 switch (priority) { 149 case WebURLRequest::PriorityVeryHigh: 150 return net::HIGHEST; 151 152 case WebURLRequest::PriorityHigh: 153 return net::MEDIUM; 154 155 case WebURLRequest::PriorityMedium: 156 return net::LOW; 157 158 case WebURLRequest::PriorityLow: 159 return net::LOWEST; 160 161 case WebURLRequest::PriorityVeryLow: 162 return net::IDLE; 163 164 case WebURLRequest::PriorityUnresolved: 165 default: 166 NOTREACHED(); 167 return net::LOW; 168 } 169 } 170 171 // Extracts info from a data scheme URL into |info| and |data|. Returns net::OK 172 // if successful. Returns a net error code otherwise. Exported only for testing. 173 int GetInfoFromDataURL(const GURL& url, 174 ResourceResponseInfo* info, 175 std::string* data) { 176 // Assure same time for all time fields of data: URLs. 177 Time now = Time::Now(); 178 info->load_timing.request_start = TimeTicks::Now(); 179 info->load_timing.request_start_time = now; 180 info->request_time = now; 181 info->response_time = now; 182 183 std::string mime_type; 184 std::string charset; 185 scoped_refptr<net::HttpResponseHeaders> headers( 186 new net::HttpResponseHeaders(std::string())); 187 int result = net::URLRequestDataJob::BuildResponse( 188 url, &mime_type, &charset, data, headers.get()); 189 if (result != net::OK) 190 return result; 191 192 info->headers = headers; 193 info->mime_type.swap(mime_type); 194 info->charset.swap(charset); 195 info->security_info.clear(); 196 info->content_length = data->length(); 197 info->encoded_data_length = 0; 198 199 return net::OK; 200 } 201 202 } // namespace 203 204 // WebURLLoaderImpl::Context -------------------------------------------------- 205 206 // This inner class exists since the WebURLLoader may be deleted while inside a 207 // call to WebURLLoaderClient. Refcounting is to keep the context from being 208 // deleted if it may have work to do after calling into the client. 209 class WebURLLoaderImpl::Context : public base::RefCounted<Context>, 210 public RequestPeer { 211 public: 212 Context(WebURLLoaderImpl* loader, ResourceDispatcher* resource_dispatcher); 213 214 WebURLLoaderClient* client() const { return client_; } 215 void set_client(WebURLLoaderClient* client) { client_ = client; } 216 217 void Cancel(); 218 void SetDefersLoading(bool value); 219 void DidChangePriority(WebURLRequest::Priority new_priority, 220 int intra_priority_value); 221 bool AttachThreadedDataReceiver( 222 blink::WebThreadedDataReceiver* threaded_data_receiver); 223 void Start(const WebURLRequest& request, 224 SyncLoadResponse* sync_load_response); 225 226 // RequestPeer methods: 227 virtual void OnUploadProgress(uint64 position, uint64 size) OVERRIDE; 228 virtual bool OnReceivedRedirect(const net::RedirectInfo& redirect_info, 229 const ResourceResponseInfo& info) OVERRIDE; 230 virtual void OnReceivedResponse(const ResourceResponseInfo& info) OVERRIDE; 231 virtual void OnDownloadedData(int len, int encoded_data_length) OVERRIDE; 232 virtual void OnReceivedData(const char* data, 233 int data_length, 234 int encoded_data_length) OVERRIDE; 235 virtual void OnReceivedCachedMetadata(const char* data, int len) OVERRIDE; 236 virtual void OnCompletedRequest( 237 int error_code, 238 bool was_ignored_by_handler, 239 bool stale_copy_in_cache, 240 const std::string& security_info, 241 const base::TimeTicks& completion_time, 242 int64 total_transfer_size) OVERRIDE; 243 244 private: 245 friend class base::RefCounted<Context>; 246 virtual ~Context() {} 247 248 // We can optimize the handling of data URLs in most cases. 249 bool CanHandleDataURLRequestLocally() const; 250 void HandleDataURL(); 251 252 WebURLLoaderImpl* loader_; 253 WebURLRequest request_; 254 WebURLLoaderClient* client_; 255 ResourceDispatcher* resource_dispatcher_; 256 WebReferrerPolicy referrer_policy_; 257 scoped_ptr<ResourceLoaderBridge> bridge_; 258 scoped_ptr<FtpDirectoryListingResponseDelegate> ftp_listing_delegate_; 259 scoped_ptr<MultipartResponseDelegate> multipart_delegate_; 260 scoped_ptr<ResourceLoaderBridge> completed_bridge_; 261 }; 262 263 WebURLLoaderImpl::Context::Context(WebURLLoaderImpl* loader, 264 ResourceDispatcher* resource_dispatcher) 265 : loader_(loader), 266 client_(NULL), 267 resource_dispatcher_(resource_dispatcher), 268 referrer_policy_(blink::WebReferrerPolicyDefault) { 269 } 270 271 void WebURLLoaderImpl::Context::Cancel() { 272 if (bridge_) { 273 bridge_->Cancel(); 274 bridge_.reset(); 275 } 276 277 // Ensure that we do not notify the multipart delegate anymore as it has 278 // its own pointer to the client. 279 if (multipart_delegate_) 280 multipart_delegate_->Cancel(); 281 // Ditto for the ftp delegate. 282 if (ftp_listing_delegate_) 283 ftp_listing_delegate_->Cancel(); 284 285 // Do not make any further calls to the client. 286 client_ = NULL; 287 loader_ = NULL; 288 } 289 290 void WebURLLoaderImpl::Context::SetDefersLoading(bool value) { 291 if (bridge_) 292 bridge_->SetDefersLoading(value); 293 } 294 295 void WebURLLoaderImpl::Context::DidChangePriority( 296 WebURLRequest::Priority new_priority, int intra_priority_value) { 297 if (bridge_) 298 bridge_->DidChangePriority( 299 ConvertWebKitPriorityToNetPriority(new_priority), intra_priority_value); 300 } 301 302 bool WebURLLoaderImpl::Context::AttachThreadedDataReceiver( 303 blink::WebThreadedDataReceiver* threaded_data_receiver) { 304 if (bridge_) 305 return bridge_->AttachThreadedDataReceiver(threaded_data_receiver); 306 307 return false; 308 } 309 310 void WebURLLoaderImpl::Context::Start(const WebURLRequest& request, 311 SyncLoadResponse* sync_load_response) { 312 DCHECK(!bridge_.get()); 313 314 request_ = request; // Save the request. 315 316 GURL url = request.url(); 317 if (CanHandleDataURLRequestLocally()) { 318 if (sync_load_response) { 319 // This is a sync load. Do the work now. 320 sync_load_response->url = url; 321 sync_load_response->error_code = 322 GetInfoFromDataURL(sync_load_response->url, sync_load_response, 323 &sync_load_response->data); 324 } else { 325 base::MessageLoop::current()->PostTask( 326 FROM_HERE, base::Bind(&Context::HandleDataURL, this)); 327 } 328 return; 329 } 330 331 GURL referrer_url( 332 request.httpHeaderField(WebString::fromUTF8("Referer")).latin1()); 333 const std::string& method = request.httpMethod().latin1(); 334 335 int load_flags = net::LOAD_NORMAL; 336 switch (request.cachePolicy()) { 337 case WebURLRequest::ReloadIgnoringCacheData: 338 // Required by LayoutTests/http/tests/misc/refresh-headers.php 339 load_flags |= net::LOAD_VALIDATE_CACHE; 340 break; 341 case WebURLRequest::ReloadBypassingCache: 342 load_flags |= net::LOAD_BYPASS_CACHE; 343 break; 344 case WebURLRequest::ReturnCacheDataElseLoad: 345 load_flags |= net::LOAD_PREFERRING_CACHE; 346 break; 347 case WebURLRequest::ReturnCacheDataDontLoad: 348 load_flags |= net::LOAD_ONLY_FROM_CACHE; 349 break; 350 case WebURLRequest::UseProtocolCachePolicy: 351 break; 352 default: 353 NOTREACHED(); 354 } 355 356 if (request.reportUploadProgress()) 357 load_flags |= net::LOAD_ENABLE_UPLOAD_PROGRESS; 358 if (request.reportRawHeaders()) 359 load_flags |= net::LOAD_REPORT_RAW_HEADERS; 360 361 if (!request.allowStoredCredentials()) { 362 load_flags |= net::LOAD_DO_NOT_SAVE_COOKIES; 363 load_flags |= net::LOAD_DO_NOT_SEND_COOKIES; 364 } 365 366 if (!request.allowStoredCredentials()) 367 load_flags |= net::LOAD_DO_NOT_SEND_AUTH_DATA; 368 369 if (request.requestContext() == WebURLRequest::RequestContextXMLHttpRequest && 370 (url.has_username() || url.has_password())) { 371 load_flags |= net::LOAD_DO_NOT_PROMPT_FOR_LOGIN; 372 } 373 374 HeaderFlattener flattener; 375 request.visitHTTPHeaderFields(&flattener); 376 377 // TODO(brettw) this should take parameter encoding into account when 378 // creating the GURLs. 379 380 RequestInfo request_info; 381 request_info.method = method; 382 request_info.url = url; 383 request_info.first_party_for_cookies = request.firstPartyForCookies(); 384 request_info.referrer = referrer_url; 385 request_info.headers = flattener.GetBuffer(); 386 request_info.load_flags = load_flags; 387 request_info.enable_load_timing = true; 388 // requestor_pid only needs to be non-zero if the request originates outside 389 // the render process, so we can use requestorProcessID even for requests 390 // from in-process plugins. 391 request_info.requestor_pid = request.requestorProcessID(); 392 request_info.request_type = WebURLRequestToResourceType(request); 393 request_info.priority = 394 ConvertWebKitPriorityToNetPriority(request.priority()); 395 request_info.appcache_host_id = request.appCacheHostID(); 396 request_info.routing_id = request.requestorID(); 397 request_info.download_to_file = request.downloadToFile(); 398 request_info.has_user_gesture = request.hasUserGesture(); 399 request_info.skip_service_worker = request.skipServiceWorker(); 400 request_info.extra_data = request.extraData(); 401 referrer_policy_ = request.referrerPolicy(); 402 request_info.referrer_policy = request.referrerPolicy(); 403 bridge_.reset(resource_dispatcher_->CreateBridge(request_info)); 404 405 if (!request.httpBody().isNull()) { 406 // GET and HEAD requests shouldn't have http bodies. 407 DCHECK(method != "GET" && method != "HEAD"); 408 const WebHTTPBody& httpBody = request.httpBody(); 409 size_t i = 0; 410 WebHTTPBody::Element element; 411 scoped_refptr<ResourceRequestBody> request_body = new ResourceRequestBody; 412 while (httpBody.elementAt(i++, element)) { 413 switch (element.type) { 414 case WebHTTPBody::Element::TypeData: 415 if (!element.data.isEmpty()) { 416 // WebKit sometimes gives up empty data to append. These aren't 417 // necessary so we just optimize those out here. 418 request_body->AppendBytes( 419 element.data.data(), static_cast<int>(element.data.size())); 420 } 421 break; 422 case WebHTTPBody::Element::TypeFile: 423 if (element.fileLength == -1) { 424 request_body->AppendFileRange( 425 base::FilePath::FromUTF16Unsafe(element.filePath), 426 0, kuint64max, base::Time()); 427 } else { 428 request_body->AppendFileRange( 429 base::FilePath::FromUTF16Unsafe(element.filePath), 430 static_cast<uint64>(element.fileStart), 431 static_cast<uint64>(element.fileLength), 432 base::Time::FromDoubleT(element.modificationTime)); 433 } 434 break; 435 case WebHTTPBody::Element::TypeFileSystemURL: { 436 GURL file_system_url = element.fileSystemURL; 437 DCHECK(file_system_url.SchemeIsFileSystem()); 438 request_body->AppendFileSystemFileRange( 439 file_system_url, 440 static_cast<uint64>(element.fileStart), 441 static_cast<uint64>(element.fileLength), 442 base::Time::FromDoubleT(element.modificationTime)); 443 break; 444 } 445 case WebHTTPBody::Element::TypeBlob: 446 request_body->AppendBlob(element.blobUUID.utf8()); 447 break; 448 default: 449 NOTREACHED(); 450 } 451 } 452 request_body->set_identifier(request.httpBody().identifier()); 453 bridge_->SetRequestBody(request_body.get()); 454 } 455 456 if (sync_load_response) { 457 bridge_->SyncLoad(sync_load_response); 458 return; 459 } 460 461 // TODO(mmenke): This case probably never happens, anyways. Probably should 462 // not handle this case at all. If it's worth handling, this code currently 463 // results in the request just hanging, which should be fixed. 464 if (!bridge_->Start(this)) 465 bridge_.reset(); 466 } 467 468 void WebURLLoaderImpl::Context::OnUploadProgress(uint64 position, uint64 size) { 469 if (client_) 470 client_->didSendData(loader_, position, size); 471 } 472 473 bool WebURLLoaderImpl::Context::OnReceivedRedirect( 474 const net::RedirectInfo& redirect_info, 475 const ResourceResponseInfo& info) { 476 if (!client_) 477 return false; 478 479 WebURLResponse response; 480 response.initialize(); 481 PopulateURLResponse(request_.url(), info, &response); 482 483 // TODO(darin): We lack sufficient information to construct the actual 484 // request that resulted from the redirect. 485 WebURLRequest new_request(redirect_info.new_url); 486 new_request.setFirstPartyForCookies( 487 redirect_info.new_first_party_for_cookies); 488 new_request.setDownloadToFile(request_.downloadToFile()); 489 new_request.setRequestContext(request_.requestContext()); 490 new_request.setFrameType(request_.frameType()); 491 492 new_request.setHTTPReferrer(WebString::fromUTF8(redirect_info.new_referrer), 493 referrer_policy_); 494 495 std::string old_method = request_.httpMethod().utf8(); 496 new_request.setHTTPMethod(WebString::fromUTF8(redirect_info.new_method)); 497 if (redirect_info.new_method == old_method) 498 new_request.setHTTPBody(request_.httpBody()); 499 500 // Protect from deletion during call to willSendRequest. 501 scoped_refptr<Context> protect(this); 502 503 client_->willSendRequest(loader_, new_request, response); 504 request_ = new_request; 505 506 // Only follow the redirect if WebKit left the URL unmodified. 507 if (redirect_info.new_url == GURL(new_request.url())) { 508 // First-party cookie logic moved from DocumentLoader in Blink to 509 // net::URLRequest in the browser. Assert that Blink didn't try to change it 510 // to something else. 511 DCHECK_EQ(redirect_info.new_first_party_for_cookies.spec(), 512 request_.firstPartyForCookies().string().utf8()); 513 return true; 514 } 515 516 // We assume that WebKit only changes the URL to suppress a redirect, and we 517 // assume that it does so by setting it to be invalid. 518 DCHECK(!new_request.url().isValid()); 519 return false; 520 } 521 522 void WebURLLoaderImpl::Context::OnReceivedResponse( 523 const ResourceResponseInfo& info) { 524 if (!client_) 525 return; 526 527 WebURLResponse response; 528 response.initialize(); 529 // Updates the request url if the response was fetched by a ServiceWorker, 530 // and it was not generated inside the ServiceWorker. 531 if (info.was_fetched_via_service_worker && 532 !info.original_url_via_service_worker.is_empty()) { 533 request_.setURL(info.original_url_via_service_worker); 534 } 535 PopulateURLResponse(request_.url(), info, &response); 536 537 bool show_raw_listing = (GURL(request_.url()).query() == "raw"); 538 539 if (info.mime_type == "text/vnd.chromium.ftp-dir") { 540 if (show_raw_listing) { 541 // Set the MIME type to plain text to prevent any active content. 542 response.setMIMEType("text/plain"); 543 } else { 544 // We're going to produce a parsed listing in HTML. 545 response.setMIMEType("text/html"); 546 } 547 } 548 549 // Prevent |this| from being destroyed if the client destroys the loader, 550 // ether in didReceiveResponse, or when the multipart/ftp delegate calls into 551 // it. 552 scoped_refptr<Context> protect(this); 553 client_->didReceiveResponse(loader_, response); 554 555 // We may have been cancelled after didReceiveResponse, which would leave us 556 // without a client and therefore without much need to do further handling. 557 if (!client_) 558 return; 559 560 DCHECK(!ftp_listing_delegate_.get()); 561 DCHECK(!multipart_delegate_.get()); 562 if (info.headers.get() && info.mime_type == "multipart/x-mixed-replace") { 563 std::string content_type; 564 info.headers->EnumerateHeader(NULL, "content-type", &content_type); 565 566 std::string mime_type; 567 std::string charset; 568 bool had_charset = false; 569 std::string boundary; 570 net::HttpUtil::ParseContentType(content_type, &mime_type, &charset, 571 &had_charset, &boundary); 572 base::TrimString(boundary, " \"", &boundary); 573 574 // If there's no boundary, just handle the request normally. In the gecko 575 // code, nsMultiMixedConv::OnStartRequest throws an exception. 576 if (!boundary.empty()) { 577 multipart_delegate_.reset( 578 new MultipartResponseDelegate(client_, loader_, response, boundary)); 579 } 580 } else if (info.mime_type == "text/vnd.chromium.ftp-dir" && 581 !show_raw_listing) { 582 ftp_listing_delegate_.reset( 583 new FtpDirectoryListingResponseDelegate(client_, loader_, response)); 584 } 585 } 586 587 void WebURLLoaderImpl::Context::OnDownloadedData(int len, 588 int encoded_data_length) { 589 if (client_) 590 client_->didDownloadData(loader_, len, encoded_data_length); 591 } 592 593 void WebURLLoaderImpl::Context::OnReceivedData(const char* data, 594 int data_length, 595 int encoded_data_length) { 596 if (!client_) 597 return; 598 599 if (ftp_listing_delegate_) { 600 // The FTP listing delegate will make the appropriate calls to 601 // client_->didReceiveData and client_->didReceiveResponse. Since the 602 // delegate may want to do work after sending data to the delegate, keep 603 // |this| and the delegate alive until it's finished handling the data. 604 scoped_refptr<Context> protect(this); 605 ftp_listing_delegate_->OnReceivedData(data, data_length); 606 } else if (multipart_delegate_) { 607 // The multipart delegate will make the appropriate calls to 608 // client_->didReceiveData and client_->didReceiveResponse. Since the 609 // delegate may want to do work after sending data to the delegate, keep 610 // |this| and the delegate alive until it's finished handling the data. 611 scoped_refptr<Context> protect(this); 612 multipart_delegate_->OnReceivedData(data, data_length, encoded_data_length); 613 } else { 614 client_->didReceiveData(loader_, data, data_length, encoded_data_length); 615 } 616 } 617 618 void WebURLLoaderImpl::Context::OnReceivedCachedMetadata( 619 const char* data, int len) { 620 if (client_) 621 client_->didReceiveCachedMetadata(loader_, data, len); 622 } 623 624 void WebURLLoaderImpl::Context::OnCompletedRequest( 625 int error_code, 626 bool was_ignored_by_handler, 627 bool stale_copy_in_cache, 628 const std::string& security_info, 629 const base::TimeTicks& completion_time, 630 int64 total_transfer_size) { 631 // The WebURLLoaderImpl may be deleted in any of the calls to the client or 632 // the delegates below (As they also may call in to the client). Keep |this| 633 // alive in that case, to avoid a crash. If that happens, the request will be 634 // cancelled and |client_| will be set to NULL. 635 scoped_refptr<Context> protect(this); 636 637 if (ftp_listing_delegate_) { 638 ftp_listing_delegate_->OnCompletedRequest(); 639 ftp_listing_delegate_.reset(NULL); 640 } else if (multipart_delegate_) { 641 multipart_delegate_->OnCompletedRequest(); 642 multipart_delegate_.reset(NULL); 643 } 644 645 // Prevent any further IPC to the browser now that we're complete, but 646 // don't delete it to keep any downloaded temp files alive. 647 DCHECK(!completed_bridge_.get()); 648 completed_bridge_.swap(bridge_); 649 650 if (client_) { 651 if (error_code != net::OK) { 652 client_->didFail(loader_, CreateError(request_.url(), 653 stale_copy_in_cache, 654 error_code)); 655 } else { 656 client_->didFinishLoading( 657 loader_, (completion_time - TimeTicks()).InSecondsF(), 658 total_transfer_size); 659 } 660 } 661 } 662 663 bool WebURLLoaderImpl::Context::CanHandleDataURLRequestLocally() const { 664 GURL url = request_.url(); 665 if (!url.SchemeIs(url::kDataScheme)) 666 return false; 667 668 // The fast paths for data URL, Start() and HandleDataURL(), don't support 669 // the downloadToFile option. 670 if (request_.downloadToFile()) 671 return false; 672 673 // Optimize for the case where we can handle a data URL locally. We must 674 // skip this for data URLs targetted at frames since those could trigger a 675 // download. 676 // 677 // NOTE: We special case MIME types we can render both for performance 678 // reasons as well as to support unit tests, which do not have an underlying 679 // ResourceLoaderBridge implementation. 680 681 #if defined(OS_ANDROID) 682 // For compatibility reasons on Android we need to expose top-level data:// 683 // to the browser. 684 if (request_.frameType() == WebURLRequest::FrameTypeTopLevel) 685 return false; 686 #endif 687 688 if (request_.frameType() != WebURLRequest::FrameTypeTopLevel && 689 request_.frameType() != WebURLRequest::FrameTypeNested) 690 return true; 691 692 std::string mime_type, unused_charset; 693 if (net::DataURL::Parse(request_.url(), &mime_type, &unused_charset, NULL) && 694 net::IsSupportedMimeType(mime_type)) 695 return true; 696 697 return false; 698 } 699 700 void WebURLLoaderImpl::Context::HandleDataURL() { 701 ResourceResponseInfo info; 702 std::string data; 703 704 int error_code = GetInfoFromDataURL(request_.url(), &info, &data); 705 706 if (error_code == net::OK) { 707 OnReceivedResponse(info); 708 if (!data.empty()) 709 OnReceivedData(data.data(), data.size(), 0); 710 } 711 712 OnCompletedRequest(error_code, false, false, info.security_info, 713 base::TimeTicks::Now(), 0); 714 } 715 716 // WebURLLoaderImpl ----------------------------------------------------------- 717 718 WebURLLoaderImpl::WebURLLoaderImpl(ResourceDispatcher* resource_dispatcher) 719 : context_(new Context(this, resource_dispatcher)) { 720 } 721 722 WebURLLoaderImpl::~WebURLLoaderImpl() { 723 cancel(); 724 } 725 726 WebURLError WebURLLoaderImpl::CreateError(const WebURL& unreachable_url, 727 bool stale_copy_in_cache, 728 int reason) { 729 WebURLError error; 730 error.domain = WebString::fromUTF8(net::kErrorDomain); 731 error.reason = reason; 732 error.unreachableURL = unreachable_url; 733 error.staleCopyInCache = stale_copy_in_cache; 734 if (reason == net::ERR_ABORTED) { 735 error.isCancellation = true; 736 } else if (reason == net::ERR_TEMPORARILY_THROTTLED) { 737 error.localizedDescription = WebString::fromUTF8( 738 kThrottledErrorDescription); 739 } else { 740 error.localizedDescription = WebString::fromUTF8( 741 net::ErrorToString(reason)); 742 } 743 return error; 744 } 745 746 void WebURLLoaderImpl::PopulateURLResponse(const GURL& url, 747 const ResourceResponseInfo& info, 748 WebURLResponse* response) { 749 response->setURL(url); 750 response->setResponseTime(info.response_time.ToDoubleT()); 751 response->setMIMEType(WebString::fromUTF8(info.mime_type)); 752 response->setTextEncodingName(WebString::fromUTF8(info.charset)); 753 response->setExpectedContentLength(info.content_length); 754 response->setSecurityInfo(info.security_info); 755 response->setAppCacheID(info.appcache_id); 756 response->setAppCacheManifestURL(info.appcache_manifest_url); 757 response->setWasCached(!info.load_timing.request_start_time.is_null() && 758 info.response_time < info.load_timing.request_start_time); 759 response->setRemoteIPAddress( 760 WebString::fromUTF8(info.socket_address.host())); 761 response->setRemotePort(info.socket_address.port()); 762 response->setConnectionID(info.load_timing.socket_log_id); 763 response->setConnectionReused(info.load_timing.socket_reused); 764 response->setDownloadFilePath(info.download_file_path.AsUTF16Unsafe()); 765 response->setWasFetchedViaServiceWorker(info.was_fetched_via_service_worker); 766 WebURLResponseExtraDataImpl* extra_data = 767 new WebURLResponseExtraDataImpl(info.npn_negotiated_protocol); 768 response->setExtraData(extra_data); 769 extra_data->set_was_fetched_via_spdy(info.was_fetched_via_spdy); 770 extra_data->set_was_npn_negotiated(info.was_npn_negotiated); 771 extra_data->set_was_alternate_protocol_available( 772 info.was_alternate_protocol_available); 773 extra_data->set_connection_info(info.connection_info); 774 extra_data->set_was_fetched_via_proxy(info.was_fetched_via_proxy); 775 776 // If there's no received headers end time, don't set load timing. This is 777 // the case for non-HTTP requests, requests that don't go over the wire, and 778 // certain error cases. 779 if (!info.load_timing.receive_headers_end.is_null()) { 780 WebURLLoadTiming timing; 781 PopulateURLLoadTiming(info.load_timing, &timing); 782 const TimeTicks kNullTicks; 783 timing.setServiceWorkerFetchStart( 784 (info.service_worker_fetch_start - kNullTicks).InSecondsF()); 785 timing.setServiceWorkerFetchReady( 786 (info.service_worker_fetch_ready - kNullTicks).InSecondsF()); 787 timing.setServiceWorkerFetchEnd( 788 (info.service_worker_fetch_end - kNullTicks).InSecondsF()); 789 response->setLoadTiming(timing); 790 } 791 792 if (info.devtools_info.get()) { 793 WebHTTPLoadInfo load_info; 794 795 load_info.setHTTPStatusCode(info.devtools_info->http_status_code); 796 load_info.setHTTPStatusText(WebString::fromLatin1( 797 info.devtools_info->http_status_text)); 798 load_info.setEncodedDataLength(info.encoded_data_length); 799 800 load_info.setRequestHeadersText(WebString::fromLatin1( 801 info.devtools_info->request_headers_text)); 802 load_info.setResponseHeadersText(WebString::fromLatin1( 803 info.devtools_info->response_headers_text)); 804 const HeadersVector& request_headers = info.devtools_info->request_headers; 805 for (HeadersVector::const_iterator it = request_headers.begin(); 806 it != request_headers.end(); ++it) { 807 load_info.addRequestHeader(WebString::fromLatin1(it->first), 808 WebString::fromLatin1(it->second)); 809 } 810 const HeadersVector& response_headers = 811 info.devtools_info->response_headers; 812 for (HeadersVector::const_iterator it = response_headers.begin(); 813 it != response_headers.end(); ++it) { 814 load_info.addResponseHeader(WebString::fromLatin1(it->first), 815 WebString::fromLatin1(it->second)); 816 } 817 response->setHTTPLoadInfo(load_info); 818 } 819 820 const net::HttpResponseHeaders* headers = info.headers.get(); 821 if (!headers) 822 return; 823 824 WebURLResponse::HTTPVersion version = WebURLResponse::Unknown; 825 if (headers->GetHttpVersion() == net::HttpVersion(0, 9)) 826 version = WebURLResponse::HTTP_0_9; 827 else if (headers->GetHttpVersion() == net::HttpVersion(1, 0)) 828 version = WebURLResponse::HTTP_1_0; 829 else if (headers->GetHttpVersion() == net::HttpVersion(1, 1)) 830 version = WebURLResponse::HTTP_1_1; 831 response->setHTTPVersion(version); 832 response->setHTTPStatusCode(headers->response_code()); 833 response->setHTTPStatusText(WebString::fromLatin1(headers->GetStatusText())); 834 835 // TODO(darin): We should leverage HttpResponseHeaders for this, and this 836 // should be using the same code as ResourceDispatcherHost. 837 // TODO(jungshik): Figure out the actual value of the referrer charset and 838 // pass it to GetSuggestedFilename. 839 std::string value; 840 headers->EnumerateHeader(NULL, "content-disposition", &value); 841 response->setSuggestedFileName( 842 net::GetSuggestedFilename(url, 843 value, 844 std::string(), // referrer_charset 845 std::string(), // suggested_name 846 std::string(), // mime_type 847 std::string())); // default_name 848 849 Time time_val; 850 if (headers->GetLastModifiedValue(&time_val)) 851 response->setLastModifiedDate(time_val.ToDoubleT()); 852 853 // Build up the header map. 854 void* iter = NULL; 855 std::string name; 856 while (headers->EnumerateHeaderLines(&iter, &name, &value)) { 857 response->addHTTPHeaderField(WebString::fromLatin1(name), 858 WebString::fromLatin1(value)); 859 } 860 } 861 862 void WebURLLoaderImpl::loadSynchronously(const WebURLRequest& request, 863 WebURLResponse& response, 864 WebURLError& error, 865 WebData& data) { 866 SyncLoadResponse sync_load_response; 867 context_->Start(request, &sync_load_response); 868 869 const GURL& final_url = sync_load_response.url; 870 871 // TODO(tc): For file loads, we may want to include a more descriptive 872 // status code or status text. 873 int error_code = sync_load_response.error_code; 874 if (error_code != net::OK) { 875 response.setURL(final_url); 876 error.domain = WebString::fromUTF8(net::kErrorDomain); 877 error.reason = error_code; 878 error.unreachableURL = final_url; 879 return; 880 } 881 882 PopulateURLResponse(final_url, sync_load_response, &response); 883 884 data.assign(sync_load_response.data.data(), 885 sync_load_response.data.size()); 886 } 887 888 void WebURLLoaderImpl::loadAsynchronously(const WebURLRequest& request, 889 WebURLLoaderClient* client) { 890 DCHECK(!context_->client()); 891 892 context_->set_client(client); 893 context_->Start(request, NULL); 894 } 895 896 void WebURLLoaderImpl::cancel() { 897 context_->Cancel(); 898 } 899 900 void WebURLLoaderImpl::setDefersLoading(bool value) { 901 context_->SetDefersLoading(value); 902 } 903 904 void WebURLLoaderImpl::didChangePriority(WebURLRequest::Priority new_priority, 905 int intra_priority_value) { 906 context_->DidChangePriority(new_priority, intra_priority_value); 907 } 908 909 bool WebURLLoaderImpl::attachThreadedDataReceiver( 910 blink::WebThreadedDataReceiver* threaded_data_receiver) { 911 return context_->AttachThreadedDataReceiver(threaded_data_receiver); 912 } 913 914 } // namespace content 915