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;
156 if (rdh_
->delegate()) {
157 rdh_
->delegate()->OnRequestRedirected(
158 new_url
, request(), info
->GetContext(), response
);
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
));
168 bool AsyncResourceHandler::OnResponseStarted(int request_id
,
169 ResourceResponse
* response
,
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.
177 const ResourceRequestInfoImpl
* info
= GetRequestInfo();
181 if (rdh_
->delegate()) {
182 rdh_
->delegate()->OnResponseStarted(
183 request(), info
->GetContext(), response
, info
->filter());
186 DevToolsNetLogObserver::PopulateResponseInfo(request(), response
);
188 HostZoomMap
* host_zoom_map
=
189 GetHostZoomMapForResourceContext(info
->GetContext());
191 if (info
->GetResourceType() == ResourceType::MAIN_FRAME
&& host_zoom_map
) {
192 const GURL
& request_url
= request()->url();
193 info
->filter()->Send(new ViewMsg_SetZoomLevelForLoadingURL(
195 request_url
, host_zoom_map
->GetZoomLevelForHostAndScheme(
196 request_url
.scheme(),
197 net::GetHostOrSpecFromURL(request_url
))));
200 response
->head
.request_start
= request()->creation_time();
201 response
->head
.response_start
= TimeTicks::Now();
202 info
->filter()->Send(new ResourceMsg_ReceivedResponse(request_id
,
204 sent_received_response_msg_
= true;
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
,
217 bool AsyncResourceHandler::OnWillStart(int request_id
,
223 bool AsyncResourceHandler::OnBeforeNetworkStart(int request_id
,
229 bool AsyncResourceHandler::OnWillRead(int request_id
,
230 scoped_refptr
<net::IOBuffer
>* buf
,
233 DCHECK_EQ(-1, min_size
);
235 if (!EnsureResourceBufferIsInitialized())
238 DCHECK(buffer_
->CanAllocate());
239 char* memory
= buffer_
->Allocate(&allocation_size_
);
242 *buf
= new DependentIOBuffer(buffer_
.get(), memory
);
243 *buf_size
= allocation_size_
;
245 UMA_HISTOGRAM_CUSTOM_COUNTS(
246 "Net.AsyncResourceHandler_SharedIOBuffer_Alloc",
247 *buf_size
, 0, kMaxAllocationSize
, 100);
251 bool AsyncResourceHandler::OnReadCompleted(int request_id
, int bytes_read
,
253 DCHECK_GE(bytes_read
, 0);
258 ResourceMessageFilter
* filter
= GetFilter();
262 buffer_
->ShrinkLastAllocation(bytes_read
);
264 UMA_HISTOGRAM_CUSTOM_COUNTS(
265 "Net.AsyncResourceHandler_SharedIOBuffer_Used",
266 bytes_read
, 0, kMaxAllocationSize
, 100);
267 UMA_HISTOGRAM_PERCENTAGE(
268 "Net.AsyncResourceHandler_SharedIOBuffer_UsedPercentage",
269 CalcUsedPercentage(bytes_read
, allocation_size_
));
271 if (!sent_first_data_msg_
) {
272 base::SharedMemoryHandle handle
;
274 if (!buffer_
->ShareToProcess(filter
->PeerHandle(), &handle
, &size
))
276 filter
->Send(new ResourceMsg_SetDataBuffer(
277 request_id
, handle
, size
, filter
->peer_pid()));
278 sent_first_data_msg_
= true;
281 int data_offset
= buffer_
->GetLastAllocationOffset();
282 int encoded_data_length
=
283 DevToolsNetLogObserver::GetAndResetEncodedDataLength(request());
285 filter
->Send(new ResourceMsg_DataReceived(
286 request_id
, data_offset
, bytes_read
, encoded_data_length
));
287 ++pending_data_count_
;
288 UMA_HISTOGRAM_CUSTOM_COUNTS(
289 "Net.AsyncResourceHandler_PendingDataCount",
290 pending_data_count_
, 0, 100, 100);
292 if (!buffer_
->CanAllocate()) {
293 UMA_HISTOGRAM_CUSTOM_COUNTS(
294 "Net.AsyncResourceHandler_PendingDataCount_WhenFull",
295 pending_data_count_
, 0, 100, 100);
296 *defer
= did_defer_
= true;
303 void AsyncResourceHandler::OnDataDownloaded(
304 int request_id
, int bytes_downloaded
) {
305 int encoded_data_length
=
306 DevToolsNetLogObserver::GetAndResetEncodedDataLength(request());
308 ResourceMessageFilter
* filter
= GetFilter();
310 filter
->Send(new ResourceMsg_DataDownloaded(
311 request_id
, bytes_downloaded
, encoded_data_length
));
315 void AsyncResourceHandler::OnResponseCompleted(
317 const net::URLRequestStatus
& status
,
318 const std::string
& security_info
,
320 const ResourceRequestInfoImpl
* info
= GetRequestInfo();
324 // If we crash here, figure out what URL the renderer was requesting.
325 // http://crbug.com/107692
327 base::strlcpy(url_buf
, request()->url().spec().c_str(), arraysize(url_buf
));
328 base::debug::Alias(url_buf
);
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_
);
338 TimeTicks completion_time
= TimeTicks::Now();
340 int error_code
= status
.error();
341 bool was_ignored_by_handler
= info
->WasIgnoredByHandler();
343 DCHECK(status
.status() != net::URLRequestStatus::IO_PENDING
);
344 // If this check fails, then we're in an inconsistent state because all
345 // requests ignored by the handler should be canceled (which should result in
346 // the ERR_ABORTED error code).
347 DCHECK(!was_ignored_by_handler
|| error_code
== net::ERR_ABORTED
);
349 // TODO(mkosiba): Fix up cases where we create a URLRequestStatus
350 // with a status() != SUCCESS and an error_code() == net::OK.
351 if (status
.status() == net::URLRequestStatus::CANCELED
&&
352 error_code
== net::OK
) {
353 error_code
= net::ERR_ABORTED
;
354 } else if (status
.status() == net::URLRequestStatus::FAILED
&&
355 error_code
== net::OK
) {
356 error_code
= net::ERR_FAILED
;
359 info
->filter()->Send(
360 new ResourceMsg_RequestComplete(request_id
,
362 was_ignored_by_handler
,
367 bool AsyncResourceHandler::EnsureResourceBufferIsInitialized() {
368 if (buffer_
.get() && buffer_
->IsInitialized())
371 if (!has_checked_for_sufficient_resources_
) {
372 has_checked_for_sufficient_resources_
= true;
373 if (!rdh_
->HasSufficientResourcesForRequest(request())) {
374 controller()->CancelWithError(net::ERR_INSUFFICIENT_RESOURCES
);
379 buffer_
= new ResourceBuffer();
380 return buffer_
->Initialize(kBufferSize
,
385 void AsyncResourceHandler::ResumeIfDeferred() {
388 request()->LogUnblocked();
389 controller()->Resume();
393 void AsyncResourceHandler::OnDefer() {
394 request()->LogBlockedBy("AsyncResourceHandler");
397 } // namespace content