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/resource_scheduler.h"
7 #include "base/memory/scoped_vector.h"
8 #include "base/message_loop/message_loop.h"
9 #include "base/strings/string_number_conversions.h"
10 #include "content/browser/browser_thread_impl.h"
11 #include "content/browser/loader/resource_dispatcher_host_impl.h"
12 #include "content/browser/loader/resource_message_filter.h"
13 #include "content/browser/loader/resource_request_info_impl.h"
14 #include "content/common/resource_messages.h"
15 #include "content/public/browser/resource_context.h"
16 #include "content/public/browser/resource_controller.h"
17 #include "content/public/browser/resource_throttle.h"
18 #include "content/public/common/process_type.h"
19 #include "net/base/host_port_pair.h"
20 #include "net/base/request_priority.h"
21 #include "net/http/http_server_properties_impl.h"
22 #include "net/url_request/url_request.h"
23 #include "net/url_request/url_request_test_util.h"
24 #include "testing/gtest/include/gtest/gtest.h"
25 #include "webkit/common/resource_type.h"
31 class TestRequestFactory
;
33 const int kChildId
= 30;
34 const int kRouteId
= 75;
36 class TestRequest
: public ResourceController
{
38 TestRequest(scoped_ptr
<ResourceThrottle
> throttle
,
39 scoped_ptr
<net::URLRequest
> url_request
)
41 throttle_(throttle
.Pass()),
42 url_request_(url_request
.Pass()) {
43 throttle_
->set_controller_for_testing(this);
46 bool started() const { return started_
; }
49 bool deferred
= false;
50 throttle_
->WillStartRequest(&deferred
);
54 const net::URLRequest
* url_request() const { return url_request_
.get(); }
57 // ResourceController interface:
58 virtual void Cancel() OVERRIDE
{}
59 virtual void CancelAndIgnore() OVERRIDE
{}
60 virtual void CancelWithError(int error_code
) OVERRIDE
{}
61 virtual void Resume() OVERRIDE
{ started_
= true; }
65 scoped_ptr
<ResourceThrottle
> throttle_
;
66 scoped_ptr
<net::URLRequest
> url_request_
;
69 class CancelingTestRequest
: public TestRequest
{
71 CancelingTestRequest(scoped_ptr
<ResourceThrottle
> throttle
,
72 scoped_ptr
<net::URLRequest
> url_request
)
73 : TestRequest(throttle
.Pass(), url_request
.Pass()) {
76 void set_request_to_cancel(scoped_ptr
<TestRequest
> request_to_cancel
) {
77 request_to_cancel_
= request_to_cancel
.Pass();
81 virtual void Resume() OVERRIDE
{
82 TestRequest::Resume();
83 request_to_cancel_
.reset();
86 scoped_ptr
<TestRequest
> request_to_cancel_
;
89 class FakeResourceContext
: public ResourceContext
{
91 virtual net::HostResolver
* GetHostResolver() OVERRIDE
{ return NULL
; }
92 virtual net::URLRequestContext
* GetRequestContext() OVERRIDE
{ return NULL
; }
93 virtual bool AllowMicAccess(const GURL
& origin
) OVERRIDE
{ return false; }
94 virtual bool AllowCameraAccess(const GURL
& origin
) OVERRIDE
{ return false; }
95 virtual std::string
GetMediaDeviceIDSalt() OVERRIDE
{ return std::string(); }
98 class FakeResourceMessageFilter
: public ResourceMessageFilter
{
100 FakeResourceMessageFilter(int child_id
)
101 : ResourceMessageFilter(
103 PROCESS_TYPE_RENDERER
,
104 NULL
/* appcache_service */,
105 NULL
/* blob_storage_context */,
106 NULL
/* file_system_context */,
107 base::Bind(&FakeResourceMessageFilter::GetContexts
,
108 base::Unretained(this))) {
112 virtual ~FakeResourceMessageFilter() {}
114 void GetContexts(const ResourceHostMsg_Request
& request
,
115 ResourceContext
** resource_context
,
116 net::URLRequestContext
** request_context
) {
117 *resource_context
= &context_
;
118 *request_context
= NULL
;
121 FakeResourceContext context_
;
124 class ResourceSchedulerTest
: public testing::Test
{
126 ResourceSchedulerTest()
127 : next_request_id_(0),
128 message_loop_(base::MessageLoop::TYPE_IO
),
129 ui_thread_(BrowserThread::UI
, &message_loop_
),
130 io_thread_(BrowserThread::IO
, &message_loop_
) {
131 scheduler_
.OnClientCreated(kChildId
, kRouteId
);
132 context_
.set_http_server_properties(http_server_properties_
.GetWeakPtr());
135 virtual ~ResourceSchedulerTest() {
136 scheduler_
.OnClientDeleted(kChildId
, kRouteId
);
139 scoped_ptr
<net::URLRequest
> NewURLRequestWithRoute(
141 net::RequestPriority priority
,
143 scoped_ptr
<net::URLRequest
> url_request(
144 context_
.CreateRequest(GURL(url
), priority
, NULL
));
145 ResourceRequestInfoImpl
* info
= new ResourceRequestInfoImpl(
146 PROCESS_TYPE_RENDERER
, // process_type
147 kChildId
, // child_id
148 route_id
, // route_id
150 ++next_request_id_
, // request_id
151 MSG_ROUTING_NONE
, // render_frame_id
152 false, // is_main_frame
154 false, // parent_is_main_frame
155 0, // parent_frame_id
156 ResourceType::SUB_RESOURCE
, // resource_type
157 PAGE_TRANSITION_LINK
, // transition_type
158 false, // should_replace_current_entry
159 false, // is_download
161 true, // allow_download
162 false, // has_user_gesture
163 blink::WebReferrerPolicyDefault
, // referrer_policy
165 base::WeakPtr
<ResourceMessageFilter
>(), // filter
167 info
->AssociateWithRequest(url_request
.get());
168 return url_request
.Pass();
171 scoped_ptr
<net::URLRequest
> NewURLRequest(const char* url
,
172 net::RequestPriority priority
) {
173 return NewURLRequestWithRoute(url
, priority
, kRouteId
);
176 TestRequest
* NewRequestWithRoute(const char* url
,
177 net::RequestPriority priority
,
179 scoped_ptr
<net::URLRequest
> url_request(
180 NewURLRequestWithRoute(url
, priority
, route_id
));
181 scoped_ptr
<ResourceThrottle
> throttle(scheduler_
.ScheduleRequest(
182 kChildId
, route_id
, url_request
.get()));
183 TestRequest
* request
= new TestRequest(throttle
.Pass(), url_request
.Pass());
188 TestRequest
* NewRequest(const char* url
, net::RequestPriority priority
) {
189 return NewRequestWithRoute(url
, priority
, kRouteId
);
192 void ChangeRequestPriority(TestRequest
* request
,
193 net::RequestPriority new_priority
) {
194 scoped_refptr
<FakeResourceMessageFilter
> filter(
195 new FakeResourceMessageFilter(kChildId
));
196 const ResourceRequestInfoImpl
* info
= ResourceRequestInfoImpl::ForRequest(
197 request
->url_request());
198 const GlobalRequestID
& id
= info
->GetGlobalRequestID();
199 ResourceHostMsg_DidChangePriority
msg(id
.request_id
, new_priority
);
201 rdh_
.OnMessageReceived(msg
, filter
.get(), &ok
);
205 int next_request_id_
;
206 base::MessageLoop message_loop_
;
207 BrowserThreadImpl ui_thread_
;
208 BrowserThreadImpl io_thread_
;
209 ResourceDispatcherHostImpl rdh_
;
210 ResourceScheduler scheduler_
;
211 net::HttpServerPropertiesImpl http_server_properties_
;
212 net::TestURLRequestContext context_
;
215 TEST_F(ResourceSchedulerTest
, OneIsolatedLowRequest
) {
216 scoped_ptr
<TestRequest
> request(NewRequest("http://host/1", net::LOWEST
));
217 EXPECT_TRUE(request
->started());
220 TEST_F(ResourceSchedulerTest
, OneLowLoadsUntilIdle
) {
221 scoped_ptr
<TestRequest
> high(NewRequest("http://host/high", net::HIGHEST
));
222 scoped_ptr
<TestRequest
> low(NewRequest("http://host/low", net::LOWEST
));
223 scoped_ptr
<TestRequest
> low2(NewRequest("http://host/low", net::LOWEST
));
224 EXPECT_TRUE(high
->started());
225 EXPECT_TRUE(low
->started());
226 EXPECT_FALSE(low2
->started());
228 EXPECT_TRUE(low2
->started());
231 TEST_F(ResourceSchedulerTest
, OneLowLoadsUntilBodyInserted
) {
232 scoped_ptr
<TestRequest
> high(NewRequest("http://host/high", net::HIGHEST
));
233 scoped_ptr
<TestRequest
> low(NewRequest("http://host/low", net::LOWEST
));
234 scoped_ptr
<TestRequest
> low2(NewRequest("http://host/low", net::LOWEST
));
235 EXPECT_TRUE(high
->started());
236 EXPECT_TRUE(low
->started());
237 EXPECT_FALSE(low2
->started());
238 scheduler_
.OnWillInsertBody(kChildId
, kRouteId
);
239 EXPECT_TRUE(low2
->started());
242 TEST_F(ResourceSchedulerTest
, OneLowLoadsUntilBodyInsertedExceptSpdy
) {
243 http_server_properties_
.SetSupportsSpdy(
244 net::HostPortPair("spdyhost", 443), true);
245 scoped_ptr
<TestRequest
> high(NewRequest("http://host/high", net::HIGHEST
));
246 scoped_ptr
<TestRequest
> low_spdy(
247 NewRequest("https://spdyhost/high", net::LOWEST
));
248 scoped_ptr
<TestRequest
> low(NewRequest("http://host/low", net::LOWEST
));
249 scoped_ptr
<TestRequest
> low2(NewRequest("http://host/low", net::LOWEST
));
250 EXPECT_TRUE(high
->started());
251 EXPECT_TRUE(low_spdy
->started());
252 EXPECT_TRUE(low
->started());
253 EXPECT_FALSE(low2
->started());
254 scheduler_
.OnWillInsertBody(kChildId
, kRouteId
);
255 EXPECT_TRUE(low2
->started());
258 TEST_F(ResourceSchedulerTest
, NavigationResetsState
) {
259 scheduler_
.OnWillInsertBody(kChildId
, kRouteId
);
260 scheduler_
.OnNavigate(kChildId
, kRouteId
);
261 scoped_ptr
<TestRequest
> high(NewRequest("http://host/high", net::HIGHEST
));
262 scoped_ptr
<TestRequest
> low(NewRequest("http://host/low", net::LOWEST
));
263 scoped_ptr
<TestRequest
> low2(NewRequest("http://host/low", net::LOWEST
));
264 EXPECT_TRUE(high
->started());
265 EXPECT_TRUE(low
->started());
266 EXPECT_FALSE(low2
->started());
269 TEST_F(ResourceSchedulerTest
, BackgroundRequestStartsImmediately
) {
270 const int route_id
= 0; // Indicates a background request.
271 scoped_ptr
<TestRequest
> request(NewRequestWithRoute("http://host/1",
272 net::LOWEST
, route_id
));
273 EXPECT_TRUE(request
->started());
276 TEST_F(ResourceSchedulerTest
, StartMultipleLowRequestsWhenIdle
) {
277 scoped_ptr
<TestRequest
> high1(NewRequest("http://host/high1", net::HIGHEST
));
278 scoped_ptr
<TestRequest
> high2(NewRequest("http://host/high2", net::HIGHEST
));
279 scoped_ptr
<TestRequest
> low(NewRequest("http://host/low", net::LOWEST
));
280 scoped_ptr
<TestRequest
> low2(NewRequest("http://host/low", net::LOWEST
));
281 EXPECT_TRUE(high1
->started());
282 EXPECT_TRUE(high2
->started());
283 EXPECT_TRUE(low
->started());
284 EXPECT_FALSE(low2
->started());
286 EXPECT_FALSE(low2
->started());
288 EXPECT_TRUE(low2
->started());
291 TEST_F(ResourceSchedulerTest
, CancelOtherRequestsWhileResuming
) {
292 scoped_ptr
<TestRequest
> high(NewRequest("http://host/high", net::HIGHEST
));
293 scoped_ptr
<TestRequest
> low1(NewRequest("http://host/low1", net::LOWEST
));
295 scoped_ptr
<net::URLRequest
> url_request(
296 NewURLRequest("http://host/low2", net::LOWEST
));
297 scoped_ptr
<ResourceThrottle
> throttle(scheduler_
.ScheduleRequest(
298 kChildId
, kRouteId
, url_request
.get()));
299 scoped_ptr
<CancelingTestRequest
> low2(new CancelingTestRequest(
300 throttle
.Pass(), url_request
.Pass()));
303 scoped_ptr
<TestRequest
> low3(NewRequest("http://host/low3", net::LOWEST
));
304 low2
->set_request_to_cancel(low3
.Pass());
305 scoped_ptr
<TestRequest
> low4(NewRequest("http://host/low4", net::LOWEST
));
307 EXPECT_TRUE(high
->started());
308 EXPECT_FALSE(low2
->started());
310 EXPECT_TRUE(low1
->started());
311 EXPECT_TRUE(low2
->started());
312 EXPECT_TRUE(low4
->started());
315 TEST_F(ResourceSchedulerTest
, LimitedNumberOfDelayableRequestsInFlight
) {
316 // We only load low priority resources if there's a body.
317 scheduler_
.OnWillInsertBody(kChildId
, kRouteId
);
319 // Throw in one high priority request to make sure that's not a factor.
320 scoped_ptr
<TestRequest
> high(NewRequest("http://host/high", net::HIGHEST
));
321 EXPECT_TRUE(high
->started());
323 const int kMaxNumDelayableRequestsPerClient
= 10; // Should match the .cc.
324 const int kMaxNumDelayableRequestsPerHost
= 6;
325 ScopedVector
<TestRequest
> lows_singlehost
;
326 // Queue up to the per-host limit (we subtract the current high-pri request).
327 for (int i
= 0; i
< kMaxNumDelayableRequestsPerHost
- 1; ++i
) {
328 string url
= "http://host/low" + base::IntToString(i
);
329 lows_singlehost
.push_back(NewRequest(url
.c_str(), net::LOWEST
));
330 EXPECT_TRUE(lows_singlehost
[i
]->started());
333 scoped_ptr
<TestRequest
> second_last_singlehost(NewRequest("http://host/last",
335 scoped_ptr
<TestRequest
> last_singlehost(NewRequest("http://host/s_last",
338 EXPECT_FALSE(second_last_singlehost
->started());
340 EXPECT_TRUE(second_last_singlehost
->started());
341 EXPECT_FALSE(last_singlehost
->started());
342 lows_singlehost
.erase(lows_singlehost
.begin());
343 EXPECT_TRUE(last_singlehost
->started());
345 // Queue more requests from different hosts until we reach the total limit.
346 int expected_slots_left
=
347 kMaxNumDelayableRequestsPerClient
- kMaxNumDelayableRequestsPerHost
;
348 EXPECT_GT(expected_slots_left
, 0);
349 ScopedVector
<TestRequest
> lows_differenthosts
;
350 for (int i
= 0; i
< expected_slots_left
; ++i
) {
351 string url
= "http://host" + base::IntToString(i
) + "/low";
352 lows_differenthosts
.push_back(NewRequest(url
.c_str(), net::LOWEST
));
353 EXPECT_TRUE(lows_differenthosts
[i
]->started());
356 scoped_ptr
<TestRequest
> last_differenthost(NewRequest("http://host_new/last",
358 EXPECT_FALSE(last_differenthost
->started());
361 TEST_F(ResourceSchedulerTest
, RaisePriorityAndStart
) {
362 // Dummies to enforce scheduling.
363 scoped_ptr
<TestRequest
> high(NewRequest("http://host/high", net::HIGHEST
));
364 scoped_ptr
<TestRequest
> low(NewRequest("http://host/req", net::LOWEST
));
366 scoped_ptr
<TestRequest
> request(NewRequest("http://host/req", net::LOWEST
));
367 EXPECT_FALSE(request
->started());
369 ChangeRequestPriority(request
.get(), net::HIGHEST
);
370 EXPECT_TRUE(request
->started());
373 TEST_F(ResourceSchedulerTest
, RaisePriorityInQueue
) {
374 // Dummies to enforce scheduling.
375 scoped_ptr
<TestRequest
> high(NewRequest("http://host/high", net::HIGHEST
));
376 scoped_ptr
<TestRequest
> low(NewRequest("http://host/low", net::LOWEST
));
378 scoped_ptr
<TestRequest
> request(NewRequest("http://host/req", net::IDLE
));
379 scoped_ptr
<TestRequest
> idle(NewRequest("http://host/idle", net::IDLE
));
380 EXPECT_FALSE(request
->started());
381 EXPECT_FALSE(idle
->started());
383 ChangeRequestPriority(request
.get(), net::LOWEST
);
384 EXPECT_FALSE(request
->started());
385 EXPECT_FALSE(idle
->started());
387 const int kMaxNumDelayableRequestsPerClient
= 10; // Should match the .cc.
388 ScopedVector
<TestRequest
> lows
;
389 for (int i
= 0; i
< kMaxNumDelayableRequestsPerClient
- 1; ++i
) {
390 string url
= "http://host/low" + base::IntToString(i
);
391 lows
.push_back(NewRequest(url
.c_str(), net::LOWEST
));
394 scheduler_
.OnWillInsertBody(kChildId
, kRouteId
);
395 EXPECT_TRUE(request
->started());
396 EXPECT_FALSE(idle
->started());
399 TEST_F(ResourceSchedulerTest
, LowerPriority
) {
400 // Dummies to enforce scheduling.
401 scoped_ptr
<TestRequest
> high(NewRequest("http://host/high", net::HIGHEST
));
402 scoped_ptr
<TestRequest
> low(NewRequest("http://host/low", net::LOWEST
));
404 scoped_ptr
<TestRequest
> request(NewRequest("http://host/req", net::LOWEST
));
405 scoped_ptr
<TestRequest
> idle(NewRequest("http://host/idle", net::IDLE
));
406 EXPECT_FALSE(request
->started());
407 EXPECT_FALSE(idle
->started());
409 ChangeRequestPriority(request
.get(), net::IDLE
);
410 EXPECT_FALSE(request
->started());
411 EXPECT_FALSE(idle
->started());
413 const int kMaxNumDelayableRequestsPerClient
= 10; // Should match the .cc.
414 // 2 fewer filler requests: 1 for the "low" dummy at the start, and 1 for the
415 // one at the end, which will be tested.
416 const int kNumFillerRequests
= kMaxNumDelayableRequestsPerClient
- 2;
417 ScopedVector
<TestRequest
> lows
;
418 for (int i
= 0; i
< kNumFillerRequests
; ++i
) {
419 string url
= "http://host" + base::IntToString(i
) + "/low";
420 lows
.push_back(NewRequest(url
.c_str(), net::LOWEST
));
423 scheduler_
.OnWillInsertBody(kChildId
, kRouteId
);
424 EXPECT_FALSE(request
->started());
425 EXPECT_TRUE(idle
->started());
428 TEST_F(ResourceSchedulerTest
, ReprioritizedRequestGoesToBackOfQueue
) {
429 // Dummies to enforce scheduling.
430 scoped_ptr
<TestRequest
> high(NewRequest("http://host/high", net::HIGHEST
));
431 scoped_ptr
<TestRequest
> low(NewRequest("http://host/high", net::LOWEST
));
433 scoped_ptr
<TestRequest
> request(NewRequest("http://host/req", net::LOWEST
));
434 scoped_ptr
<TestRequest
> idle(NewRequest("http://host/idle", net::IDLE
));
435 EXPECT_FALSE(request
->started());
436 EXPECT_FALSE(idle
->started());
438 const int kMaxNumDelayableRequestsPerClient
= 10; // Should match the .cc.
439 ScopedVector
<TestRequest
> lows
;
440 for (int i
= 0; i
< kMaxNumDelayableRequestsPerClient
; ++i
) {
441 string url
= "http://host/low" + base::IntToString(i
);
442 lows
.push_back(NewRequest(url
.c_str(), net::LOWEST
));
445 ChangeRequestPriority(request
.get(), net::IDLE
);
446 EXPECT_FALSE(request
->started());
447 EXPECT_FALSE(idle
->started());
449 ChangeRequestPriority(request
.get(), net::LOWEST
);
450 EXPECT_FALSE(request
->started());
451 EXPECT_FALSE(idle
->started());
453 scheduler_
.OnWillInsertBody(kChildId
, kRouteId
);
454 EXPECT_FALSE(request
->started());
455 EXPECT_FALSE(idle
->started());
458 TEST_F(ResourceSchedulerTest
, NonHTTPSchedulesImmediately
) {
459 // Dummies to enforce scheduling.
460 scoped_ptr
<TestRequest
> high(NewRequest("http://host/high", net::HIGHEST
));
461 scoped_ptr
<TestRequest
> low(NewRequest("http://host/high", net::LOWEST
));
463 scoped_ptr
<TestRequest
> request(
464 NewRequest("chrome-extension://req", net::LOWEST
));
465 EXPECT_TRUE(request
->started());
468 TEST_F(ResourceSchedulerTest
, SpdyProxySchedulesImmediately
) {
469 scoped_ptr
<TestRequest
> high(NewRequest("http://host/high", net::HIGHEST
));
470 scoped_ptr
<TestRequest
> low(NewRequest("http://host/low", net::LOWEST
));
472 scoped_ptr
<TestRequest
> request(NewRequest("http://host/req", net::IDLE
));
473 EXPECT_FALSE(request
->started());
475 scheduler_
.OnReceivedSpdyProxiedHttpResponse(kChildId
, kRouteId
);
476 EXPECT_TRUE(request
->started());
478 scoped_ptr
<TestRequest
> after(NewRequest("http://host/after", net::IDLE
));
479 EXPECT_TRUE(after
->started());
482 } // unnamed namespace
484 } // namespace content