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.
5 #include "content/browser/loader/async_resource_handler.h"
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"
34 using base::TimeTicks;
39 static int kBufferSize = 1024 * 512;
40 static int kMinAllocationSize = 1024 * 4;
41 static int kMaxAllocationSize = 1024 * 32;
43 void GetNumericArg(const std::string& name, int* result) {
44 const std::string& value =
45 CommandLine::ForCurrentProcess()->GetSwitchValueASCII(name);
47 base::StringToInt(value, result);
50 void InitializeResourceBufferConstants() {
51 static bool did_init = false;
56 GetNumericArg("resource-buffer-size", &kBufferSize);
57 GetNumericArg("resource-buffer-min-allocation-size", &kMinAllocationSize);
58 GetNumericArg("resource-buffer-max-allocation-size", &kMaxAllocationSize);
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.
68 class DependentIOBuffer : public net::WrappedIOBuffer {
70 DependentIOBuffer(ResourceBuffer* backing, char* memory)
71 : net::WrappedIOBuffer(memory),
75 virtual ~DependentIOBuffer() {}
76 scoped_refptr<ResourceBuffer> backing_;
79 AsyncResourceHandler::AsyncResourceHandler(
80 net::URLRequest* request,
81 ResourceDispatcherHostImpl* rdh)
82 : ResourceHandler(request),
83 ResourceMessageDelegate(request),
85 pending_data_count_(0),
88 has_checked_for_sufficient_resources_(false),
89 sent_received_response_msg_(false),
90 sent_first_data_msg_(false) {
91 InitializeResourceBufferConstants();
94 AsyncResourceHandler::~AsyncResourceHandler() {
95 if (has_checked_for_sufficient_resources_)
96 rdh_->FinishedWithResourcesForRequest(request());
99 bool AsyncResourceHandler::OnMessageReceived(const IPC::Message& message,
100 bool* message_was_ok) {
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()
110 void AsyncResourceHandler::OnFollowRedirect(
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";
119 if (has_new_first_party_for_cookies)
120 request()->set_first_party_for_cookies(new_first_party_for_cookies);
125 void AsyncResourceHandler::OnDataReceivedACK(int request_id) {
126 if (pending_data_count_) {
127 --pending_data_count_;
129 buffer_->RecycleLeastRecentlyAllocated();
130 if (buffer_->CanAllocate())
135 bool AsyncResourceHandler::OnUploadProgress(int request_id,
138 ResourceMessageFilter* filter = GetFilter();
142 new ResourceMsg_UploadProgress(request_id, position, size));
145 bool AsyncResourceHandler::OnRequestRedirected(int request_id,
147 ResourceResponse* response,
149 const ResourceRequestInfoImpl* info = GetRequestInfo();
153 *defer = did_defer_ = true;
155 if (rdh_->delegate()) {
156 rdh_->delegate()->OnRequestRedirected(
157 new_url, request(), info->GetContext(), response);
160 DevToolsNetLogObserver::PopulateResponseInfo(request(), response);
161 response->head.request_start = request()->creation_time();
162 response->head.response_start = TimeTicks::Now();
163 return info->filter()->Send(new ResourceMsg_ReceivedRedirect(
164 request_id, new_url, response->head));
167 bool AsyncResourceHandler::OnResponseStarted(int request_id,
168 ResourceResponse* response,
170 // For changes to the main frame, inform the renderer of the new URL's
171 // per-host settings before the request actually commits. This way the
172 // renderer will be able to set these precisely at the time the
173 // request commits, avoiding the possibility of e.g. zooming the old content
174 // or of having to layout the new content twice.
176 const ResourceRequestInfoImpl* info = GetRequestInfo();
180 if (rdh_->delegate()) {
181 rdh_->delegate()->OnResponseStarted(
182 request(), info->GetContext(), response, info->filter());
185 DevToolsNetLogObserver::PopulateResponseInfo(request(), response);
187 HostZoomMap* host_zoom_map =
188 GetHostZoomMapForResourceContext(info->GetContext());
190 if (info->GetResourceType() == ResourceType::MAIN_FRAME && host_zoom_map) {
191 const GURL& request_url = request()->url();
192 info->filter()->Send(new ViewMsg_SetZoomLevelForLoadingURL(
194 request_url, host_zoom_map->GetZoomLevelForHostAndScheme(
195 request_url.scheme(),
196 net::GetHostOrSpecFromURL(request_url))));
199 response->head.request_start = request()->creation_time();
200 response->head.response_start = TimeTicks::Now();
201 info->filter()->Send(new ResourceMsg_ReceivedResponse(request_id,
203 sent_received_response_msg_ = true;
205 if (request()->response_info().metadata.get()) {
206 std::vector<char> copy(request()->response_info().metadata->data(),
207 request()->response_info().metadata->data() +
208 request()->response_info().metadata->size());
209 info->filter()->Send(new ResourceMsg_ReceivedCachedMetadata(request_id,
216 bool AsyncResourceHandler::OnWillStart(int request_id,
222 bool AsyncResourceHandler::OnWillRead(int request_id,
223 scoped_refptr<net::IOBuffer>* buf,
226 DCHECK_EQ(-1, min_size);
228 if (!EnsureResourceBufferIsInitialized())
231 DCHECK(buffer_->CanAllocate());
232 char* memory = buffer_->Allocate(&allocation_size_);
235 *buf = new DependentIOBuffer(buffer_.get(), memory);
236 *buf_size = allocation_size_;
238 UMA_HISTOGRAM_CUSTOM_COUNTS(
239 "Net.AsyncResourceHandler_SharedIOBuffer_Alloc",
240 *buf_size, 0, kMaxAllocationSize, 100);
244 bool AsyncResourceHandler::OnReadCompleted(int request_id, int bytes_read,
246 DCHECK_GE(bytes_read, 0);
251 ResourceMessageFilter* filter = GetFilter();
255 buffer_->ShrinkLastAllocation(bytes_read);
257 UMA_HISTOGRAM_CUSTOM_COUNTS(
258 "Net.AsyncResourceHandler_SharedIOBuffer_Used",
259 bytes_read, 0, kMaxAllocationSize, 100);
260 UMA_HISTOGRAM_PERCENTAGE(
261 "Net.AsyncResourceHandler_SharedIOBuffer_UsedPercentage",
262 CalcUsedPercentage(bytes_read, allocation_size_));
264 if (!sent_first_data_msg_) {
265 base::SharedMemoryHandle handle;
267 if (!buffer_->ShareToProcess(filter->PeerHandle(), &handle, &size))
269 filter->Send(new ResourceMsg_SetDataBuffer(
270 request_id, handle, size, filter->peer_pid()));
271 sent_first_data_msg_ = true;
274 int data_offset = buffer_->GetLastAllocationOffset();
275 int encoded_data_length =
276 DevToolsNetLogObserver::GetAndResetEncodedDataLength(request());
278 filter->Send(new ResourceMsg_DataReceived(
279 request_id, data_offset, bytes_read, encoded_data_length));
280 ++pending_data_count_;
281 UMA_HISTOGRAM_CUSTOM_COUNTS(
282 "Net.AsyncResourceHandler_PendingDataCount",
283 pending_data_count_, 0, 100, 100);
285 if (!buffer_->CanAllocate()) {
286 UMA_HISTOGRAM_CUSTOM_COUNTS(
287 "Net.AsyncResourceHandler_PendingDataCount_WhenFull",
288 pending_data_count_, 0, 100, 100);
289 *defer = did_defer_ = true;
295 void AsyncResourceHandler::OnDataDownloaded(
296 int request_id, int bytes_downloaded) {
297 int encoded_data_length =
298 DevToolsNetLogObserver::GetAndResetEncodedDataLength(request());
300 ResourceMessageFilter* filter = GetFilter();
302 filter->Send(new ResourceMsg_DataDownloaded(
303 request_id, bytes_downloaded, encoded_data_length));
307 bool AsyncResourceHandler::OnResponseCompleted(
309 const net::URLRequestStatus& status,
310 const std::string& security_info) {
311 const ResourceRequestInfoImpl* info = GetRequestInfo();
315 // If we crash here, figure out what URL the renderer was requesting.
316 // http://crbug.com/107692
318 base::strlcpy(url_buf, request()->url().spec().c_str(), arraysize(url_buf));
319 base::debug::Alias(url_buf);
321 // TODO(gavinp): Remove this CHECK when we figure out the cause of
322 // http://crbug.com/124680 . This check mirrors closely check in
323 // WebURLLoaderImpl::OnCompletedRequest that routes this message to a WebCore
324 // ResourceHandleInternal which asserts on its state and crashes. By crashing
325 // when the message is sent, we should get better crash reports.
326 CHECK(status.status() != net::URLRequestStatus::SUCCESS ||
327 sent_received_response_msg_);
329 TimeTicks completion_time = TimeTicks::Now();
331 int error_code = status.error();
332 bool was_ignored_by_handler = info->WasIgnoredByHandler();
334 DCHECK(status.status() != net::URLRequestStatus::IO_PENDING);
335 // If this check fails, then we're in an inconsistent state because all
336 // requests ignored by the handler should be canceled (which should result in
337 // the ERR_ABORTED error code).
338 DCHECK(!was_ignored_by_handler || error_code == net::ERR_ABORTED);
340 // TODO(mkosiba): Fix up cases where we create a URLRequestStatus
341 // with a status() != SUCCESS and an error_code() == net::OK.
342 if (status.status() == net::URLRequestStatus::CANCELED &&
343 error_code == net::OK) {
344 error_code = net::ERR_ABORTED;
345 } else if (status.status() == net::URLRequestStatus::FAILED &&
346 error_code == net::OK) {
347 error_code = net::ERR_FAILED;
350 info->filter()->Send(
351 new ResourceMsg_RequestComplete(request_id,
353 was_ignored_by_handler,
359 bool AsyncResourceHandler::EnsureResourceBufferIsInitialized() {
360 if (buffer_.get() && buffer_->IsInitialized())
363 if (!has_checked_for_sufficient_resources_) {
364 has_checked_for_sufficient_resources_ = true;
365 if (!rdh_->HasSufficientResourcesForRequest(request())) {
366 controller()->CancelWithError(net::ERR_INSUFFICIENT_RESOURCES);
371 buffer_ = new ResourceBuffer();
372 return buffer_->Initialize(kBufferSize,
377 void AsyncResourceHandler::ResumeIfDeferred() {
380 controller()->Resume();
384 } // namespace content