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