Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2015 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 #include "content/browser/download/url_downloader.h" | |
| 6 | |
| 7 #include "base/location.h" | |
| 8 #include "base/thread_task_runner_handle.h" | |
| 9 #include "content/browser/appcache/appcache_interceptor.h" | |
| 10 #include "content/browser/download/download_resource_handler.h" | |
| 11 #include "content/browser/loader/resource_request_info_impl.h" | |
| 12 #include "content/browser/service_worker/service_worker_request_handler.h" | |
| 13 #include "content/browser/ssl/ssl_policy.h" | |
| 14 #include "content/common/ssl_status_serialization.h" | |
| 15 #include "content/public/browser/cert_store.h" | |
| 16 #include "content/public/browser/download_save_info.h" | |
| 17 #include "content/public/browser/resource_context.h" | |
| 18 #include "content/public/browser/signed_certificate_timestamp_store.h" | |
| 19 #include "content/public/common/process_type.h" | |
| 20 #include "content/public/common/resource_response.h" | |
| 21 #include "content/public/common/security_style.h" | |
| 22 #include "net/base/io_buffer.h" | |
| 23 #include "net/base/load_flags.h" | |
| 24 #include "net/http/http_response_headers.h" | |
| 25 #include "net/http/http_status_code.h" | |
| 26 #include "ui/base/page_transition_types.h" | |
| 27 | |
| 28 namespace content { | |
| 29 // The average private bytes increase of the browser for each new pending | |
| 30 // request. Experimentally obtained. | |
| 31 static const int kAvgBytesPerOutstandingRequest = 4400; | |
| 32 | |
| 33 int CalculateApproximateMemoryCost(net::URLRequest* request) { | |
| 34 // The following fields should be a minor size contribution (experimentally | |
| 35 // on the order of 100). However since they are variable length, it could | |
| 36 // in theory be a sizeable contribution. | |
| 37 int strings_cost = request->extra_request_headers().ToString().size() + | |
| 38 request->original_url().spec().size() + | |
| 39 request->referrer().size() + request->method().size(); | |
| 40 | |
| 41 // Note that this expression will typically be dominated by: | |
| 42 // |kAvgBytesPerOutstandingRequest|. | |
| 43 return kAvgBytesPerOutstandingRequest + strings_cost; | |
| 44 } | |
| 45 | |
| 46 void StoreSignedCertificateTimestamps( | |
| 47 const net::SignedCertificateTimestampAndStatusList& sct_list, | |
| 48 int process_id, | |
| 49 SignedCertificateTimestampIDStatusList* sct_ids) { | |
| 50 SignedCertificateTimestampStore* sct_store( | |
| 51 SignedCertificateTimestampStore::GetInstance()); | |
| 52 | |
| 53 for (auto iter = sct_list.begin(); iter != sct_list.end(); ++iter) { | |
| 54 const int sct_id(sct_store->Store(iter->sct.get(), process_id)); | |
| 55 sct_ids->push_back( | |
| 56 SignedCertificateTimestampIDAndStatus(sct_id, iter->status)); | |
| 57 } | |
| 58 } | |
| 59 | |
| 60 void GetSSLStatusForRequest(const GURL& url, | |
| 61 const net::SSLInfo& ssl_info, | |
| 62 int child_id, | |
| 63 SSLStatus* ssl_status) { | |
| 64 DCHECK(ssl_info.cert); | |
| 65 | |
| 66 int cert_id = | |
| 67 CertStore::GetInstance()->StoreCert(ssl_info.cert.get(), child_id); | |
| 68 | |
| 69 SignedCertificateTimestampIDStatusList signed_certificate_timestamp_ids; | |
| 70 StoreSignedCertificateTimestamps(ssl_info.signed_certificate_timestamps, | |
| 71 child_id, &signed_certificate_timestamp_ids); | |
| 72 | |
| 73 *ssl_status = SSLStatus(SSLPolicy::GetSecurityStyleForResource( | |
| 74 url, cert_id, ssl_info.cert_status), | |
| 75 cert_id, signed_certificate_timestamp_ids, ssl_info); | |
| 76 } | |
| 77 | |
| 78 void PopulateResourceResponse(ResourceRequestInfoImpl* info, | |
| 79 net::URLRequest* request, | |
| 80 ResourceResponse* response) { | |
| 81 response->head.request_time = request->request_time(); | |
| 82 response->head.response_time = request->response_time(); | |
| 83 response->head.headers = request->response_headers(); | |
| 84 request->GetCharset(&response->head.charset); | |
| 85 response->head.content_length = request->GetExpectedContentSize(); | |
| 86 request->GetMimeType(&response->head.mime_type); | |
| 87 net::HttpResponseInfo response_info = request->response_info(); | |
| 88 response->head.was_fetched_via_spdy = response_info.was_fetched_via_spdy; | |
| 89 response->head.was_npn_negotiated = response_info.was_npn_negotiated; | |
| 90 response->head.npn_negotiated_protocol = | |
| 91 response_info.npn_negotiated_protocol; | |
| 92 response->head.connection_info = response_info.connection_info; | |
| 93 response->head.was_fetched_via_proxy = request->was_fetched_via_proxy(); | |
| 94 response->head.proxy_server = response_info.proxy_server; | |
| 95 response->head.socket_address = request->GetSocketAddress(); | |
| 96 const content::ResourceRequestInfo* request_info = | |
| 97 content::ResourceRequestInfo::ForRequest(request); | |
| 98 if (request_info) | |
| 99 response->head.is_using_lofi = request_info->IsUsingLoFi(); | |
| 100 if (ServiceWorkerRequestHandler* handler = | |
| 101 ServiceWorkerRequestHandler::GetHandler(request)) { | |
| 102 handler->GetExtraResponseInfo(&response->head); | |
| 103 } | |
| 104 AppCacheInterceptor::GetExtraResponseInfo( | |
| 105 request, &response->head.appcache_id, | |
| 106 &response->head.appcache_manifest_url); | |
| 107 if (info->is_load_timing_enabled()) | |
| 108 request->GetLoadTimingInfo(&response->head.load_timing); | |
| 109 | |
| 110 if (request->ssl_info().cert.get()) { | |
| 111 SSLStatus ssl_status; | |
| 112 GetSSLStatusForRequest(request->url(), request->ssl_info(), | |
| 113 info->GetChildID(), &ssl_status); | |
| 114 response->head.security_info = SerializeSecurityInfo(ssl_status); | |
| 115 } else { | |
| 116 // We should not have any SSL state. | |
| 117 DCHECK(!request->ssl_info().cert_status); | |
| 118 DCHECK_EQ(request->ssl_info().security_bits, -1); | |
| 119 DCHECK_EQ(request->ssl_info().key_exchange_info, 0); | |
| 120 DCHECK(!request->ssl_info().connection_status); | |
| 121 } | |
| 122 } | |
| 123 | |
| 124 DownloadInterruptReason UrlDownloader::BeginDownload( | |
| 125 base::WeakPtr<DownloadManagerImpl> download_manager, | |
| 126 scoped_ptr<net::URLRequest> request, | |
| 127 const Referrer& referrer, | |
| 128 bool is_content_initiated, | |
| 129 ResourceContext* context, | |
| 130 bool prefer_cache, | |
| 131 bool do_not_prompt_for_login, | |
| 132 scoped_ptr<DownloadSaveInfo> save_info, | |
| 133 uint32 download_id, | |
| 134 const DownloadUrlParameters::OnStartedCallback& started_callback) { | |
| 135 if (!referrer.url.is_valid()) | |
| 136 request->SetReferrer(std::string()); | |
| 137 else | |
| 138 request->SetReferrer(referrer.url.spec()); | |
| 139 | |
| 140 int extra_load_flags = net::LOAD_NORMAL; | |
| 141 if (prefer_cache) { | |
| 142 // If there is upload data attached, only retrieve from cache because there | |
| 143 // is no current mechanism to prompt the user for their consent for a | |
| 144 // re-post. For GETs, try to retrieve data from the cache and skip | |
| 145 // validating the entry if present. | |
| 146 if (request->get_upload() != NULL) | |
| 147 extra_load_flags |= net::LOAD_ONLY_FROM_CACHE; | |
| 148 else | |
| 149 extra_load_flags |= net::LOAD_PREFERRING_CACHE; | |
| 150 } else { | |
| 151 extra_load_flags |= net::LOAD_DISABLE_CACHE; | |
| 152 } | |
| 153 request->SetLoadFlags(request->load_flags() | extra_load_flags); | |
| 154 | |
| 155 // We treat a download as a main frame load, and thus update the policy URL on | |
| 156 // redirects. | |
| 157 // | |
| 158 // TODO(davidben): Is this correct? If this came from a | |
| 159 // ViewHostMsg_DownloadUrl in a frame, should it have first-party URL set | |
| 160 // appropriately? | |
| 161 request->set_first_party_url_policy( | |
| 162 net::URLRequest::UPDATE_FIRST_PARTY_URL_ON_REDIRECT); | |
| 163 | |
| 164 ResourceRequestInfoImpl* extra_info = new ResourceRequestInfoImpl( | |
|
asanka
2015/11/25 15:53:57
I'm still not too happy about creating this Resour
svaldez
2015/11/25 17:53:28
Wouldn't we still need to create the RRII here any
asanka
2015/11/25 19:43:11
If we are using a captive DRH, we'd need an RRII.
| |
| 165 PROCESS_TYPE_RENDERER, -1, -1, | |
| 166 -1, // frame_tree_node_id | |
| 167 0, -1, -1, | |
| 168 false, // is_main_frame | |
| 169 false, // parent_is_main_frame | |
| 170 -1, // parent_render_frame_id | |
| 171 RESOURCE_TYPE_SUB_RESOURCE, ui::PAGE_TRANSITION_LINK, | |
| 172 false, // should_replace_current_entry | |
| 173 true, // is_download | |
| 174 false, // is_stream | |
| 175 true, // allow_download | |
| 176 false, // has_user_gesture | |
| 177 false, // enable_load_timing | |
| 178 false, // enable_upload_progress | |
| 179 false, // do_not_prompt_for_login | |
| 180 blink::WebReferrerPolicyDefault, blink::WebPageVisibilityStateVisible, | |
| 181 context, | |
| 182 base::WeakPtr<ResourceMessageFilter>(), // filter | |
| 183 false, // report_raw_headers | |
| 184 true, // is_async | |
| 185 false); // is_using_lofi | |
| 186 | |
| 187 extra_info->set_do_not_prompt_for_login(do_not_prompt_for_login); | |
| 188 extra_info->AssociateWithRequest(request.get()); // Request takes ownership. | |
| 189 | |
| 190 if (request->url().SchemeIs(url::kBlobScheme)) | |
| 191 return DOWNLOAD_INTERRUPT_REASON_FILE_FAILED; | |
| 192 | |
| 193 extra_info->set_memory_cost(CalculateApproximateMemoryCost(request.get())); | |
| 194 | |
| 195 DownloadResourceHandler* handler = new DownloadResourceHandler( | |
| 196 download_id, request.get(), started_callback, save_info.Pass()); | |
| 197 handler->set_download_manager(download_manager); | |
| 198 | |
| 199 // From this point forward, the |UrlDownloader| is responsible for | |
| 200 // |started_callback|. | |
| 201 linked_ptr<UrlDownloader> downloader( | |
| 202 new UrlDownloader(request.Pass(), scoped_ptr<ResourceHandler>(handler))); | |
| 203 | |
| 204 downloader->Start(); | |
| 205 | |
| 206 g_active_downloaders.push_back(downloader); | |
| 207 | |
| 208 return DOWNLOAD_INTERRUPT_REASON_NONE; | |
| 209 } | |
| 210 | |
| 211 UrlDownloader::UrlDownloader(scoped_ptr<net::URLRequest> request, | |
| 212 scoped_ptr<ResourceHandler> handler) | |
| 213 : request_(request.Pass()), | |
| 214 handler_(handler.Pass()), | |
| 215 weak_ptr_factory_(this) {} | |
| 216 | |
| 217 UrlDownloader::~UrlDownloader() { | |
| 218 handler_.reset(); | |
| 219 } | |
| 220 | |
| 221 void UrlDownloader::Start() { | |
| 222 // Give the handler a chance to delay the URLRequest from being started. | |
| 223 bool defer_start = false; | |
| 224 if (!handler_->OnWillStart(request_->url(), &defer_start)) { | |
| 225 request_->CancelWithError(net::ERR_ABORTED); | |
| 226 return; | |
| 227 } | |
| 228 | |
| 229 DCHECK(!defer_start); | |
| 230 DCHECK(!request_->is_pending()); | |
| 231 | |
| 232 if (!request_->status().is_success()) { | |
| 233 return; | |
| 234 } | |
| 235 | |
| 236 request_->set_delegate(this); | |
| 237 request_->Start(); | |
| 238 } | |
| 239 | |
| 240 void UrlDownloader::OnReceivedRedirect(net::URLRequest* request, | |
| 241 const net::RedirectInfo& redirect_info, | |
| 242 bool* defer_redirect) { | |
| 243 DVLOG(1) << "OnReceivedRedirect: " << request_->url().spec(); | |
| 244 request_->CancelWithError(net::ERR_ABORTED); | |
| 245 } | |
| 246 | |
| 247 void UrlDownloader::OnResponseStarted(net::URLRequest* request) { | |
| 248 DVLOG(1) << "OnResponseStarted: " << request_->url().spec(); | |
| 249 | |
| 250 if (!request_->status().is_success()) { | |
| 251 ResponseCompleted(); | |
| 252 return; | |
| 253 } | |
| 254 | |
| 255 ResourceRequestInfoImpl* info = | |
| 256 ResourceRequestInfoImpl::ForRequest(request_.get()); | |
| 257 scoped_refptr<ResourceResponse> response(new ResourceResponse()); | |
| 258 PopulateResourceResponse(info, request_.get(), response.get()); | |
| 259 | |
| 260 bool defer = false; | |
| 261 if (!handler_->OnResponseStarted(response.get(), &defer)) { | |
| 262 request_->CancelWithError(net::ERR_ABORTED); | |
| 263 return; | |
| 264 } | |
| 265 DCHECK(!defer); | |
| 266 | |
| 267 if (request_->status().is_success()) | |
| 268 StartReading(false); // Read the first chunk. | |
| 269 else | |
| 270 ResponseCompleted(); | |
| 271 } | |
| 272 | |
| 273 void UrlDownloader::OnReadCompleted(net::URLRequest* request, int bytes_read) { | |
| 274 DVLOG(1) << "OnReadCompleted: \"" << request_->url().spec() << "\"" | |
| 275 << " bytes_read = " << bytes_read; | |
| 276 | |
| 277 // bytes_read == -1 always implies an error. | |
| 278 if (bytes_read == -1 || !request_->status().is_success()) { | |
| 279 ResponseCompleted(); | |
| 280 return; | |
| 281 } | |
| 282 | |
| 283 DCHECK(bytes_read >= 0); | |
| 284 DCHECK(request_->status().is_success()); | |
| 285 | |
| 286 bool defer = false; | |
| 287 if (!handler_->OnReadCompleted(bytes_read, &defer)) { | |
| 288 request_->CancelWithError(net::ERR_ABORTED); | |
| 289 return; | |
| 290 } | |
| 291 DCHECK(!defer); | |
| 292 | |
| 293 if (!request_->status().is_success()) | |
| 294 return; | |
| 295 | |
| 296 if (bytes_read > 0) { | |
| 297 StartReading(true); // Read the next chunk. | |
| 298 } else { | |
| 299 // URLRequest reported an EOF. Call ResponseCompleted. | |
| 300 DCHECK_EQ(0, bytes_read); | |
| 301 ResponseCompleted(); | |
| 302 } | |
| 303 } | |
| 304 | |
| 305 void UrlDownloader::StartReading(bool is_continuation) { | |
| 306 int bytes_read; | |
| 307 | |
| 308 // Make sure we track the buffer in at least one place. This ensures it gets | |
| 309 // deleted even in the case the request has already finished its job and | |
| 310 // doesn't use the buffer. | |
| 311 scoped_refptr<net::IOBuffer> buf; | |
| 312 int buf_size; | |
| 313 if (!handler_->OnWillRead(&buf, &buf_size, -1)) { | |
| 314 request_->CancelWithError(net::ERR_ABORTED); | |
| 315 return; | |
| 316 } | |
| 317 | |
| 318 DCHECK(buf.get()); | |
| 319 DCHECK(buf_size > 0); | |
| 320 | |
| 321 request_->Read(buf.get(), buf_size, &bytes_read); | |
| 322 | |
| 323 // If IO is pending, wait for the URLRequest to call OnReadCompleted. | |
| 324 if (request_->status().is_io_pending()) | |
| 325 return; | |
| 326 | |
| 327 if (!is_continuation || bytes_read <= 0) { | |
| 328 OnReadCompleted(request_.get(), bytes_read); | |
| 329 } else { | |
| 330 // Else, trigger OnReadCompleted asynchronously to avoid starving the IO | |
| 331 // thread in case the URLRequest can provide data synchronously. | |
| 332 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 333 FROM_HERE, | |
| 334 base::Bind(&UrlDownloader::OnReadCompleted, | |
| 335 weak_ptr_factory_.GetWeakPtr(), request_.get(), bytes_read)); | |
| 336 } | |
| 337 } | |
| 338 | |
| 339 void UrlDownloader::ResponseCompleted() { | |
| 340 DVLOG(1) << "ResponseCompleted: " << request_->url().spec(); | |
| 341 | |
| 342 ResourceRequestInfoImpl* info = | |
| 343 ResourceRequestInfoImpl::ForRequest(request_.get()); | |
| 344 | |
| 345 std::string security_info; | |
| 346 const net::SSLInfo& ssl_info = request_->ssl_info(); | |
| 347 if (ssl_info.cert.get() != NULL) { | |
| 348 SSLStatus ssl_status; | |
| 349 GetSSLStatusForRequest(request_->url(), ssl_info, info->GetChildID(), | |
| 350 &ssl_status); | |
| 351 | |
| 352 security_info = SerializeSecurityInfo(ssl_status); | |
| 353 } | |
| 354 | |
| 355 bool defer = false; | |
| 356 handler_->OnResponseCompleted(request_->status(), security_info, &defer); | |
| 357 DCHECK(!defer); | |
| 358 } | |
| 359 | |
| 360 } // namespace content | |
| OLD | NEW |