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