1// Copyright (c) 2012 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/loader/async_resource_handler.h" 6 7#include <algorithm> 8#include <vector> 9 10#include "base/command_line.h" 11#include "base/containers/hash_tables.h" 12#include "base/debug/alias.h" 13#include "base/logging.h" 14#include "base/memory/shared_memory.h" 15#include "base/metrics/histogram.h" 16#include "base/strings/string_number_conversions.h" 17#include "content/browser/devtools/devtools_netlog_observer.h" 18#include "content/browser/host_zoom_map_impl.h" 19#include "content/browser/loader/resource_buffer.h" 20#include "content/browser/loader/resource_dispatcher_host_impl.h" 21#include "content/browser/loader/resource_message_filter.h" 22#include "content/browser/loader/resource_request_info_impl.h" 23#include "content/browser/resource_context_impl.h" 24#include "content/common/resource_messages.h" 25#include "content/common/view_messages.h" 26#include "content/public/browser/resource_dispatcher_host_delegate.h" 27#include "content/public/common/resource_response.h" 28#include "net/base/io_buffer.h" 29#include "net/base/load_flags.h" 30#include "net/base/net_log.h" 31#include "net/base/net_util.h" 32 33using base::TimeTicks; 34 35namespace content { 36namespace { 37 38static int kBufferSize = 1024 * 512; 39static int kMinAllocationSize = 1024 * 4; 40static int kMaxAllocationSize = 1024 * 32; 41 42void GetNumericArg(const std::string& name, int* result) { 43 const std::string& value = 44 CommandLine::ForCurrentProcess()->GetSwitchValueASCII(name); 45 if (!value.empty()) 46 base::StringToInt(value, result); 47} 48 49void InitializeResourceBufferConstants() { 50 static bool did_init = false; 51 if (did_init) 52 return; 53 did_init = true; 54 55 GetNumericArg("resource-buffer-size", &kBufferSize); 56 GetNumericArg("resource-buffer-min-allocation-size", &kMinAllocationSize); 57 GetNumericArg("resource-buffer-max-allocation-size", &kMaxAllocationSize); 58} 59 60int CalcUsedPercentage(int bytes_read, int buffer_size) { 61 double ratio = static_cast<double>(bytes_read) / buffer_size; 62 return static_cast<int>(ratio * 100.0 + 0.5); // Round to nearest integer. 63} 64 65} // namespace 66 67class DependentIOBuffer : public net::WrappedIOBuffer { 68 public: 69 DependentIOBuffer(ResourceBuffer* backing, char* memory) 70 : net::WrappedIOBuffer(memory), 71 backing_(backing) { 72 } 73 private: 74 virtual ~DependentIOBuffer() {} 75 scoped_refptr<ResourceBuffer> backing_; 76}; 77 78AsyncResourceHandler::AsyncResourceHandler( 79 net::URLRequest* request, 80 ResourceDispatcherHostImpl* rdh) 81 : ResourceHandler(request), 82 ResourceMessageDelegate(request), 83 rdh_(rdh), 84 pending_data_count_(0), 85 allocation_size_(0), 86 did_defer_(false), 87 has_checked_for_sufficient_resources_(false), 88 sent_received_response_msg_(false), 89 sent_first_data_msg_(false), 90 reported_transfer_size_(0) { 91 InitializeResourceBufferConstants(); 92} 93 94AsyncResourceHandler::~AsyncResourceHandler() { 95 if (has_checked_for_sufficient_resources_) 96 rdh_->FinishedWithResourcesForRequest(request()); 97} 98 99bool AsyncResourceHandler::OnMessageReceived(const IPC::Message& message) { 100 bool handled = true; 101 IPC_BEGIN_MESSAGE_MAP(AsyncResourceHandler, message) 102 IPC_MESSAGE_HANDLER(ResourceHostMsg_FollowRedirect, OnFollowRedirect) 103 IPC_MESSAGE_HANDLER(ResourceHostMsg_DataReceived_ACK, OnDataReceivedACK) 104 IPC_MESSAGE_UNHANDLED(handled = false) 105 IPC_END_MESSAGE_MAP() 106 return handled; 107} 108 109void AsyncResourceHandler::OnFollowRedirect(int request_id) { 110 if (!request()->status().is_success()) { 111 DVLOG(1) << "OnFollowRedirect for invalid request"; 112 return; 113 } 114 115 ResumeIfDeferred(); 116} 117 118void AsyncResourceHandler::OnDataReceivedACK(int request_id) { 119 if (pending_data_count_) { 120 --pending_data_count_; 121 122 buffer_->RecycleLeastRecentlyAllocated(); 123 if (buffer_->CanAllocate()) 124 ResumeIfDeferred(); 125 } 126} 127 128bool AsyncResourceHandler::OnUploadProgress(uint64 position, 129 uint64 size) { 130 ResourceMessageFilter* filter = GetFilter(); 131 if (!filter) 132 return false; 133 return filter->Send( 134 new ResourceMsg_UploadProgress(GetRequestID(), position, size)); 135} 136 137bool AsyncResourceHandler::OnRequestRedirected(const GURL& new_url, 138 ResourceResponse* response, 139 bool* defer) { 140 const ResourceRequestInfoImpl* info = GetRequestInfo(); 141 if (!info->filter()) 142 return false; 143 144 *defer = did_defer_ = true; 145 OnDefer(); 146 147 if (rdh_->delegate()) { 148 rdh_->delegate()->OnRequestRedirected( 149 new_url, request(), info->GetContext(), response); 150 } 151 152 DevToolsNetLogObserver::PopulateResponseInfo(request(), response); 153 response->head.encoded_data_length = request()->GetTotalReceivedBytes(); 154 reported_transfer_size_ = 0; 155 response->head.request_start = request()->creation_time(); 156 response->head.response_start = TimeTicks::Now(); 157 // TODO(davidben): Is it necessary to pass the new first party URL for 158 // cookies? The only case where it can change is top-level navigation requests 159 // and hopefully those will eventually all be owned by the browser. It's 160 // possible this is still needed while renderer-owned ones exist. 161 return info->filter()->Send(new ResourceMsg_ReceivedRedirect( 162 GetRequestID(), new_url, request()->first_party_for_cookies(), 163 response->head)); 164} 165 166bool AsyncResourceHandler::OnResponseStarted(ResourceResponse* response, 167 bool* defer) { 168 // For changes to the main frame, inform the renderer of the new URL's 169 // per-host settings before the request actually commits. This way the 170 // renderer will be able to set these precisely at the time the 171 // request commits, avoiding the possibility of e.g. zooming the old content 172 // or of having to layout the new content twice. 173 174 const ResourceRequestInfoImpl* info = GetRequestInfo(); 175 if (!info->filter()) 176 return false; 177 178 if (rdh_->delegate()) { 179 rdh_->delegate()->OnResponseStarted( 180 request(), info->GetContext(), response, info->filter()); 181 } 182 183 DevToolsNetLogObserver::PopulateResponseInfo(request(), response); 184 185 HostZoomMap* host_zoom_map = 186 GetHostZoomMapForResourceContext(info->GetContext()); 187 188 if (info->GetResourceType() == ResourceType::MAIN_FRAME && host_zoom_map) { 189 const GURL& request_url = request()->url(); 190 info->filter()->Send(new ViewMsg_SetZoomLevelForLoadingURL( 191 info->GetRouteID(), 192 request_url, host_zoom_map->GetZoomLevelForHostAndScheme( 193 request_url.scheme(), 194 net::GetHostOrSpecFromURL(request_url)))); 195 } 196 197 // If the parent handler downloaded the resource to a file, grant the child 198 // read permissions on it. 199 if (!response->head.download_file_path.empty()) { 200 rdh_->RegisterDownloadedTempFile( 201 info->GetChildID(), info->GetRequestID(), 202 response->head.download_file_path); 203 } 204 205 response->head.request_start = request()->creation_time(); 206 response->head.response_start = TimeTicks::Now(); 207 info->filter()->Send(new ResourceMsg_ReceivedResponse(GetRequestID(), 208 response->head)); 209 sent_received_response_msg_ = true; 210 211 if (request()->response_info().metadata.get()) { 212 std::vector<char> copy(request()->response_info().metadata->data(), 213 request()->response_info().metadata->data() + 214 request()->response_info().metadata->size()); 215 info->filter()->Send(new ResourceMsg_ReceivedCachedMetadata(GetRequestID(), 216 copy)); 217 } 218 219 return true; 220} 221 222bool AsyncResourceHandler::OnWillStart(const GURL& url, bool* defer) { 223 return true; 224} 225 226bool AsyncResourceHandler::OnBeforeNetworkStart(const GURL& url, bool* defer) { 227 return true; 228} 229 230bool AsyncResourceHandler::OnWillRead(scoped_refptr<net::IOBuffer>* buf, 231 int* buf_size, 232 int min_size) { 233 DCHECK_EQ(-1, min_size); 234 235 if (!EnsureResourceBufferIsInitialized()) 236 return false; 237 238 DCHECK(buffer_->CanAllocate()); 239 char* memory = buffer_->Allocate(&allocation_size_); 240 CHECK(memory); 241 242 *buf = new DependentIOBuffer(buffer_.get(), memory); 243 *buf_size = allocation_size_; 244 245 UMA_HISTOGRAM_CUSTOM_COUNTS( 246 "Net.AsyncResourceHandler_SharedIOBuffer_Alloc", 247 *buf_size, 0, kMaxAllocationSize, 100); 248 return true; 249} 250 251bool AsyncResourceHandler::OnReadCompleted(int bytes_read, bool* defer) { 252 DCHECK_GE(bytes_read, 0); 253 254 if (!bytes_read) 255 return true; 256 257 ResourceMessageFilter* filter = GetFilter(); 258 if (!filter) 259 return false; 260 261 buffer_->ShrinkLastAllocation(bytes_read); 262 263 UMA_HISTOGRAM_CUSTOM_COUNTS( 264 "Net.AsyncResourceHandler_SharedIOBuffer_Used", 265 bytes_read, 0, kMaxAllocationSize, 100); 266 UMA_HISTOGRAM_PERCENTAGE( 267 "Net.AsyncResourceHandler_SharedIOBuffer_UsedPercentage", 268 CalcUsedPercentage(bytes_read, allocation_size_)); 269 270 if (!sent_first_data_msg_) { 271 base::SharedMemoryHandle handle; 272 int size; 273 if (!buffer_->ShareToProcess(filter->PeerHandle(), &handle, &size)) 274 return false; 275 filter->Send(new ResourceMsg_SetDataBuffer( 276 GetRequestID(), handle, size, filter->peer_pid())); 277 sent_first_data_msg_ = true; 278 } 279 280 int data_offset = buffer_->GetLastAllocationOffset(); 281 282 int64_t current_transfer_size = request()->GetTotalReceivedBytes(); 283 int encoded_data_length = current_transfer_size - reported_transfer_size_; 284 reported_transfer_size_ = current_transfer_size; 285 286 filter->Send(new ResourceMsg_DataReceived( 287 GetRequestID(), data_offset, bytes_read, encoded_data_length)); 288 ++pending_data_count_; 289 UMA_HISTOGRAM_CUSTOM_COUNTS( 290 "Net.AsyncResourceHandler_PendingDataCount", 291 pending_data_count_, 0, 100, 100); 292 293 if (!buffer_->CanAllocate()) { 294 UMA_HISTOGRAM_CUSTOM_COUNTS( 295 "Net.AsyncResourceHandler_PendingDataCount_WhenFull", 296 pending_data_count_, 0, 100, 100); 297 *defer = did_defer_ = true; 298 OnDefer(); 299 } 300 301 return true; 302} 303 304void AsyncResourceHandler::OnDataDownloaded(int bytes_downloaded) { 305 int64_t current_transfer_size = request()->GetTotalReceivedBytes(); 306 int encoded_data_length = current_transfer_size - reported_transfer_size_; 307 reported_transfer_size_ = current_transfer_size; 308 309 ResourceMessageFilter* filter = GetFilter(); 310 if (filter) { 311 filter->Send(new ResourceMsg_DataDownloaded( 312 GetRequestID(), bytes_downloaded, encoded_data_length)); 313 } 314} 315 316void AsyncResourceHandler::OnResponseCompleted( 317 const net::URLRequestStatus& status, 318 const std::string& security_info, 319 bool* defer) { 320 const ResourceRequestInfoImpl* info = GetRequestInfo(); 321 if (!info->filter()) 322 return; 323 324 // If we crash here, figure out what URL the renderer was requesting. 325 // http://crbug.com/107692 326 char url_buf[128]; 327 base::strlcpy(url_buf, request()->url().spec().c_str(), arraysize(url_buf)); 328 base::debug::Alias(url_buf); 329 330 // TODO(gavinp): Remove this CHECK when we figure out the cause of 331 // http://crbug.com/124680 . This check mirrors closely check in 332 // WebURLLoaderImpl::OnCompletedRequest that routes this message to a WebCore 333 // ResourceHandleInternal which asserts on its state and crashes. By crashing 334 // when the message is sent, we should get better crash reports. 335 CHECK(status.status() != net::URLRequestStatus::SUCCESS || 336 sent_received_response_msg_); 337 338 int error_code = status.error(); 339 bool was_ignored_by_handler = info->WasIgnoredByHandler(); 340 341 DCHECK(status.status() != net::URLRequestStatus::IO_PENDING); 342 // If this check fails, then we're in an inconsistent state because all 343 // requests ignored by the handler should be canceled (which should result in 344 // the ERR_ABORTED error code). 345 DCHECK(!was_ignored_by_handler || error_code == net::ERR_ABORTED); 346 347 // TODO(mkosiba): Fix up cases where we create a URLRequestStatus 348 // with a status() != SUCCESS and an error_code() == net::OK. 349 if (status.status() == net::URLRequestStatus::CANCELED && 350 error_code == net::OK) { 351 error_code = net::ERR_ABORTED; 352 } else if (status.status() == net::URLRequestStatus::FAILED && 353 error_code == net::OK) { 354 error_code = net::ERR_FAILED; 355 } 356 357 ResourceMsg_RequestCompleteData request_complete_data; 358 request_complete_data.error_code = error_code; 359 request_complete_data.was_ignored_by_handler = was_ignored_by_handler; 360 request_complete_data.exists_in_cache = request()->response_info().was_cached; 361 request_complete_data.security_info = security_info; 362 request_complete_data.completion_time = TimeTicks::Now(); 363 request_complete_data.encoded_data_length = 364 request()->GetTotalReceivedBytes(); 365 info->filter()->Send( 366 new ResourceMsg_RequestComplete(GetRequestID(), request_complete_data)); 367} 368 369bool AsyncResourceHandler::EnsureResourceBufferIsInitialized() { 370 if (buffer_.get() && buffer_->IsInitialized()) 371 return true; 372 373 if (!has_checked_for_sufficient_resources_) { 374 has_checked_for_sufficient_resources_ = true; 375 if (!rdh_->HasSufficientResourcesForRequest(request())) { 376 controller()->CancelWithError(net::ERR_INSUFFICIENT_RESOURCES); 377 return false; 378 } 379 } 380 381 buffer_ = new ResourceBuffer(); 382 return buffer_->Initialize(kBufferSize, 383 kMinAllocationSize, 384 kMaxAllocationSize); 385} 386 387void AsyncResourceHandler::ResumeIfDeferred() { 388 if (did_defer_) { 389 did_defer_ = false; 390 request()->LogUnblocked(); 391 controller()->Resume(); 392 } 393} 394 395void AsyncResourceHandler::OnDefer() { 396 request()->LogBlockedBy("AsyncResourceHandler"); 397} 398 399} // namespace content 400