Home | History | Annotate | Download | only in loader
      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/resource_scheduler.h"
      6 
      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"
     26 
     27 namespace content {
     28 
     29 namespace {
     30 
     31 class TestRequestFactory;
     32 
     33 const int kChildId = 30;
     34 const int kRouteId = 75;
     35 
     36 class TestRequest : public ResourceController {
     37  public:
     38   TestRequest(scoped_ptr<ResourceThrottle> throttle,
     39               scoped_ptr<net::URLRequest> url_request)
     40       : started_(false),
     41         throttle_(throttle.Pass()),
     42         url_request_(url_request.Pass()) {
     43     throttle_->set_controller_for_testing(this);
     44   }
     45 
     46   bool started() const { return started_; }
     47 
     48   void Start() {
     49     bool deferred = false;
     50     throttle_->WillStartRequest(&deferred);
     51     started_ = !deferred;
     52   }
     53 
     54   const net::URLRequest* url_request() const { return url_request_.get(); }
     55 
     56  protected:
     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; }
     62 
     63  private:
     64   bool started_;
     65   scoped_ptr<ResourceThrottle> throttle_;
     66   scoped_ptr<net::URLRequest> url_request_;
     67 };
     68 
     69 class CancelingTestRequest : public TestRequest {
     70  public:
     71   CancelingTestRequest(scoped_ptr<ResourceThrottle> throttle,
     72                        scoped_ptr<net::URLRequest> url_request)
     73       : TestRequest(throttle.Pass(), url_request.Pass()) {
     74   }
     75 
     76   void set_request_to_cancel(scoped_ptr<TestRequest> request_to_cancel) {
     77     request_to_cancel_ = request_to_cancel.Pass();
     78   }
     79 
     80  private:
     81   virtual void Resume() OVERRIDE {
     82     TestRequest::Resume();
     83     request_to_cancel_.reset();
     84   }
     85 
     86   scoped_ptr<TestRequest> request_to_cancel_;
     87 };
     88 
     89 class FakeResourceContext : public ResourceContext {
     90  private:
     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 };
     96 
     97 class FakeResourceMessageFilter : public ResourceMessageFilter {
     98  public:
     99   FakeResourceMessageFilter(int child_id)
    100       : ResourceMessageFilter(
    101           child_id,
    102           PROCESS_TYPE_RENDERER,
    103           NULL  /* appcache_service */,
    104           NULL  /* blob_storage_context */,
    105           NULL  /* file_system_context */,
    106           NULL  /* service_worker_context */,
    107           base::Bind(&FakeResourceMessageFilter::GetContexts,
    108                      base::Unretained(this))) {
    109   }
    110 
    111  private:
    112   virtual ~FakeResourceMessageFilter() {}
    113 
    114   void GetContexts(const ResourceHostMsg_Request& request,
    115                    ResourceContext** resource_context,
    116                    net::URLRequestContext** request_context) {
    117     *resource_context = &context_;
    118     *request_context = NULL;
    119   }
    120 
    121   FakeResourceContext context_;
    122 };
    123 
    124 class ResourceSchedulerTest : public testing::Test {
    125  protected:
    126   ResourceSchedulerTest()
    127       : next_request_id_(0),
    128         ui_thread_(BrowserThread::UI, &message_loop_),
    129         io_thread_(BrowserThread::IO, &message_loop_) {
    130     scheduler_.OnClientCreated(kChildId, kRouteId);
    131     context_.set_http_server_properties(http_server_properties_.GetWeakPtr());
    132   }
    133 
    134   virtual ~ResourceSchedulerTest() {
    135     scheduler_.OnClientDeleted(kChildId, kRouteId);
    136   }
    137 
    138   scoped_ptr<net::URLRequest> NewURLRequestWithRoute(
    139       const char* url,
    140       net::RequestPriority priority,
    141       int route_id) {
    142     scoped_ptr<net::URLRequest> url_request(
    143         context_.CreateRequest(GURL(url), priority, NULL, NULL));
    144     ResourceRequestInfoImpl* info = new ResourceRequestInfoImpl(
    145         PROCESS_TYPE_RENDERER,             // process_type
    146         kChildId,                          // child_id
    147         route_id,                          // route_id
    148         0,                                 // origin_pid
    149         ++next_request_id_,                // request_id
    150         MSG_ROUTING_NONE,                  // render_frame_id
    151         false,                             // is_main_frame
    152         false,                             // parent_is_main_frame
    153         0,                                 // parent_render_frame_id
    154         ResourceType::SUB_RESOURCE,        // resource_type
    155         PAGE_TRANSITION_LINK,              // transition_type
    156         false,                             // should_replace_current_entry
    157         false,                             // is_download
    158         false,                             // is_stream
    159         true,                              // allow_download
    160         false,                             // has_user_gesture
    161         blink::WebReferrerPolicyDefault,   // referrer_policy
    162         blink::WebPageVisibilityStateVisible,  // visibility_state
    163         NULL,                              // context
    164         base::WeakPtr<ResourceMessageFilter>(),  // filter
    165         true);                             // is_async
    166     info->AssociateWithRequest(url_request.get());
    167     return url_request.Pass();
    168   }
    169 
    170   scoped_ptr<net::URLRequest> NewURLRequest(const char* url,
    171                                             net::RequestPriority priority) {
    172     return NewURLRequestWithRoute(url, priority, kRouteId);
    173   }
    174 
    175   TestRequest* NewRequestWithRoute(const char* url,
    176                                    net::RequestPriority priority,
    177                                    int route_id) {
    178     scoped_ptr<net::URLRequest> url_request(
    179         NewURLRequestWithRoute(url, priority, route_id));
    180     scoped_ptr<ResourceThrottle> throttle(scheduler_.ScheduleRequest(
    181         kChildId, route_id, url_request.get()));
    182     TestRequest* request = new TestRequest(throttle.Pass(), url_request.Pass());
    183     request->Start();
    184     return request;
    185   }
    186 
    187   TestRequest* NewRequest(const char* url, net::RequestPriority priority) {
    188     return NewRequestWithRoute(url, priority, kRouteId);
    189   }
    190 
    191   void ChangeRequestPriority(TestRequest* request,
    192                              net::RequestPriority new_priority,
    193                              int intra_priority = 0) {
    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,
    200                                           intra_priority);
    201     rdh_.OnMessageReceived(msg, filter.get());
    202   }
    203 
    204   int next_request_id_;
    205   base::MessageLoopForIO message_loop_;
    206   BrowserThreadImpl ui_thread_;
    207   BrowserThreadImpl io_thread_;
    208   ResourceDispatcherHostImpl rdh_;
    209   ResourceScheduler scheduler_;
    210   net::HttpServerPropertiesImpl http_server_properties_;
    211   net::TestURLRequestContext context_;
    212 };
    213 
    214 TEST_F(ResourceSchedulerTest, OneIsolatedLowRequest) {
    215   scoped_ptr<TestRequest> request(NewRequest("http://host/1", net::LOWEST));
    216   EXPECT_TRUE(request->started());
    217 }
    218 
    219 TEST_F(ResourceSchedulerTest, OneLowLoadsUntilIdle) {
    220   scoped_ptr<TestRequest> high(NewRequest("http://host/high", net::HIGHEST));
    221   scoped_ptr<TestRequest> low(NewRequest("http://host/low", net::LOWEST));
    222   scoped_ptr<TestRequest> low2(NewRequest("http://host/low", net::LOWEST));
    223   EXPECT_TRUE(high->started());
    224   EXPECT_TRUE(low->started());
    225   EXPECT_FALSE(low2->started());
    226   high.reset();
    227   EXPECT_TRUE(low2->started());
    228 }
    229 
    230 TEST_F(ResourceSchedulerTest, OneLowLoadsUntilBodyInserted) {
    231   scoped_ptr<TestRequest> high(NewRequest("http://host/high", net::HIGHEST));
    232   scoped_ptr<TestRequest> low(NewRequest("http://host/low", net::LOWEST));
    233   scoped_ptr<TestRequest> low2(NewRequest("http://host/low", net::LOWEST));
    234   EXPECT_TRUE(high->started());
    235   EXPECT_TRUE(low->started());
    236   EXPECT_FALSE(low2->started());
    237   scheduler_.OnWillInsertBody(kChildId, kRouteId);
    238   EXPECT_TRUE(low2->started());
    239 }
    240 
    241 TEST_F(ResourceSchedulerTest, OneLowLoadsUntilBodyInsertedExceptSpdy) {
    242   http_server_properties_.SetSupportsSpdy(
    243       net::HostPortPair("spdyhost", 443), true);
    244   scoped_ptr<TestRequest> high(NewRequest("http://host/high", net::HIGHEST));
    245   scoped_ptr<TestRequest> low_spdy(
    246       NewRequest("https://spdyhost/high", net::LOWEST));
    247   scoped_ptr<TestRequest> low(NewRequest("http://host/low", net::LOWEST));
    248   scoped_ptr<TestRequest> low2(NewRequest("http://host/low", net::LOWEST));
    249   EXPECT_TRUE(high->started());
    250   EXPECT_TRUE(low_spdy->started());
    251   EXPECT_TRUE(low->started());
    252   EXPECT_FALSE(low2->started());
    253   scheduler_.OnWillInsertBody(kChildId, kRouteId);
    254   EXPECT_TRUE(low2->started());
    255 }
    256 
    257 TEST_F(ResourceSchedulerTest, NavigationResetsState) {
    258   scheduler_.OnWillInsertBody(kChildId, kRouteId);
    259   scheduler_.OnNavigate(kChildId, kRouteId);
    260   scoped_ptr<TestRequest> high(NewRequest("http://host/high", net::HIGHEST));
    261   scoped_ptr<TestRequest> low(NewRequest("http://host/low", net::LOWEST));
    262   scoped_ptr<TestRequest> low2(NewRequest("http://host/low", net::LOWEST));
    263   EXPECT_TRUE(high->started());
    264   EXPECT_TRUE(low->started());
    265   EXPECT_FALSE(low2->started());
    266 }
    267 
    268 TEST_F(ResourceSchedulerTest, BackgroundRequestStartsImmediately) {
    269   const int route_id = 0;  // Indicates a background request.
    270   scoped_ptr<TestRequest> request(NewRequestWithRoute("http://host/1",
    271                                                       net::LOWEST, route_id));
    272   EXPECT_TRUE(request->started());
    273 }
    274 
    275 TEST_F(ResourceSchedulerTest, StartMultipleLowRequestsWhenIdle) {
    276   scoped_ptr<TestRequest> high1(NewRequest("http://host/high1", net::HIGHEST));
    277   scoped_ptr<TestRequest> high2(NewRequest("http://host/high2", net::HIGHEST));
    278   scoped_ptr<TestRequest> low(NewRequest("http://host/low", net::LOWEST));
    279   scoped_ptr<TestRequest> low2(NewRequest("http://host/low", net::LOWEST));
    280   EXPECT_TRUE(high1->started());
    281   EXPECT_TRUE(high2->started());
    282   EXPECT_TRUE(low->started());
    283   EXPECT_FALSE(low2->started());
    284   high1.reset();
    285   EXPECT_FALSE(low2->started());
    286   high2.reset();
    287   EXPECT_TRUE(low2->started());
    288 }
    289 
    290 TEST_F(ResourceSchedulerTest, CancelOtherRequestsWhileResuming) {
    291   scoped_ptr<TestRequest> high(NewRequest("http://host/high", net::HIGHEST));
    292   scoped_ptr<TestRequest> low1(NewRequest("http://host/low1", net::LOWEST));
    293 
    294   scoped_ptr<net::URLRequest> url_request(
    295       NewURLRequest("http://host/low2", net::LOWEST));
    296   scoped_ptr<ResourceThrottle> throttle(scheduler_.ScheduleRequest(
    297       kChildId, kRouteId, url_request.get()));
    298   scoped_ptr<CancelingTestRequest> low2(new CancelingTestRequest(
    299       throttle.Pass(), url_request.Pass()));
    300   low2->Start();
    301 
    302   scoped_ptr<TestRequest> low3(NewRequest("http://host/low3", net::LOWEST));
    303   low2->set_request_to_cancel(low3.Pass());
    304   scoped_ptr<TestRequest> low4(NewRequest("http://host/low4", net::LOWEST));
    305 
    306   EXPECT_TRUE(high->started());
    307   EXPECT_FALSE(low2->started());
    308   high.reset();
    309   EXPECT_TRUE(low1->started());
    310   EXPECT_TRUE(low2->started());
    311   EXPECT_TRUE(low4->started());
    312 }
    313 
    314 TEST_F(ResourceSchedulerTest, LimitedNumberOfDelayableRequestsInFlight) {
    315   // We only load low priority resources if there's a body.
    316   scheduler_.OnWillInsertBody(kChildId, kRouteId);
    317 
    318   // Throw in one high priority request to make sure that's not a factor.
    319   scoped_ptr<TestRequest> high(NewRequest("http://host/high", net::HIGHEST));
    320   EXPECT_TRUE(high->started());
    321 
    322   const int kMaxNumDelayableRequestsPerClient = 10;  // Should match the .cc.
    323   const int kMaxNumDelayableRequestsPerHost = 6;
    324   ScopedVector<TestRequest> lows_singlehost;
    325   // Queue up to the per-host limit (we subtract the current high-pri request).
    326   for (int i = 0; i < kMaxNumDelayableRequestsPerHost - 1; ++i) {
    327     string url = "http://host/low" + base::IntToString(i);
    328     lows_singlehost.push_back(NewRequest(url.c_str(), net::LOWEST));
    329     EXPECT_TRUE(lows_singlehost[i]->started());
    330   }
    331 
    332   scoped_ptr<TestRequest> second_last_singlehost(NewRequest("http://host/last",
    333                                                             net::LOWEST));
    334   scoped_ptr<TestRequest> last_singlehost(NewRequest("http://host/s_last",
    335                                                      net::LOWEST));
    336 
    337   EXPECT_FALSE(second_last_singlehost->started());
    338   high.reset();
    339   EXPECT_TRUE(second_last_singlehost->started());
    340   EXPECT_FALSE(last_singlehost->started());
    341   lows_singlehost.erase(lows_singlehost.begin());
    342   EXPECT_TRUE(last_singlehost->started());
    343 
    344   // Queue more requests from different hosts until we reach the total limit.
    345   int expected_slots_left =
    346       kMaxNumDelayableRequestsPerClient - kMaxNumDelayableRequestsPerHost;
    347   EXPECT_GT(expected_slots_left, 0);
    348   ScopedVector<TestRequest> lows_differenthosts;
    349   for (int i = 0; i < expected_slots_left; ++i) {
    350     string url = "http://host" + base::IntToString(i) + "/low";
    351     lows_differenthosts.push_back(NewRequest(url.c_str(), net::LOWEST));
    352     EXPECT_TRUE(lows_differenthosts[i]->started());
    353   }
    354 
    355   scoped_ptr<TestRequest> last_differenthost(NewRequest("http://host_new/last",
    356                                                         net::LOWEST));
    357   EXPECT_FALSE(last_differenthost->started());
    358 }
    359 
    360 TEST_F(ResourceSchedulerTest, RaisePriorityAndStart) {
    361   // Dummies to enforce scheduling.
    362   scoped_ptr<TestRequest> high(NewRequest("http://host/high", net::HIGHEST));
    363   scoped_ptr<TestRequest> low(NewRequest("http://host/req", net::LOWEST));
    364 
    365   scoped_ptr<TestRequest> request(NewRequest("http://host/req", net::LOWEST));
    366   EXPECT_FALSE(request->started());
    367 
    368   ChangeRequestPriority(request.get(), net::HIGHEST);
    369   EXPECT_TRUE(request->started());
    370 }
    371 
    372 TEST_F(ResourceSchedulerTest, RaisePriorityInQueue) {
    373   // Dummies to enforce scheduling.
    374   scoped_ptr<TestRequest> high(NewRequest("http://host/high", net::HIGHEST));
    375   scoped_ptr<TestRequest> low(NewRequest("http://host/low", net::LOWEST));
    376 
    377   scoped_ptr<TestRequest> request(NewRequest("http://host/req", net::IDLE));
    378   scoped_ptr<TestRequest> idle(NewRequest("http://host/idle", net::IDLE));
    379   EXPECT_FALSE(request->started());
    380   EXPECT_FALSE(idle->started());
    381 
    382   ChangeRequestPriority(request.get(), net::LOWEST);
    383   EXPECT_FALSE(request->started());
    384   EXPECT_FALSE(idle->started());
    385 
    386   const int kMaxNumDelayableRequestsPerClient = 10;  // Should match the .cc.
    387   ScopedVector<TestRequest> lows;
    388   for (int i = 0; i < kMaxNumDelayableRequestsPerClient - 1; ++i) {
    389     string url = "http://host/low" + base::IntToString(i);
    390     lows.push_back(NewRequest(url.c_str(), net::LOWEST));
    391   }
    392 
    393   scheduler_.OnWillInsertBody(kChildId, kRouteId);
    394   EXPECT_TRUE(request->started());
    395   EXPECT_FALSE(idle->started());
    396 }
    397 
    398 TEST_F(ResourceSchedulerTest, LowerPriority) {
    399   // Dummies to enforce scheduling.
    400   scoped_ptr<TestRequest> high(NewRequest("http://host/high", net::HIGHEST));
    401   scoped_ptr<TestRequest> low(NewRequest("http://host/low", net::LOWEST));
    402 
    403   scoped_ptr<TestRequest> request(NewRequest("http://host/req", net::LOWEST));
    404   scoped_ptr<TestRequest> idle(NewRequest("http://host/idle", net::IDLE));
    405   EXPECT_FALSE(request->started());
    406   EXPECT_FALSE(idle->started());
    407 
    408   ChangeRequestPriority(request.get(), net::IDLE);
    409   EXPECT_FALSE(request->started());
    410   EXPECT_FALSE(idle->started());
    411 
    412   const int kMaxNumDelayableRequestsPerClient = 10;  // Should match the .cc.
    413   // 2 fewer filler requests: 1 for the "low" dummy at the start, and 1 for the
    414   // one at the end, which will be tested.
    415   const int kNumFillerRequests = kMaxNumDelayableRequestsPerClient - 2;
    416   ScopedVector<TestRequest> lows;
    417   for (int i = 0; i < kNumFillerRequests; ++i) {
    418     string url = "http://host" + base::IntToString(i) + "/low";
    419     lows.push_back(NewRequest(url.c_str(), net::LOWEST));
    420   }
    421 
    422   scheduler_.OnWillInsertBody(kChildId, kRouteId);
    423   EXPECT_FALSE(request->started());
    424   EXPECT_TRUE(idle->started());
    425 }
    426 
    427 TEST_F(ResourceSchedulerTest, ReprioritizedRequestGoesToBackOfQueue) {
    428   // Dummies to enforce scheduling.
    429   scoped_ptr<TestRequest> high(NewRequest("http://host/high", net::HIGHEST));
    430   scoped_ptr<TestRequest> low(NewRequest("http://host/high", net::LOWEST));
    431 
    432   scoped_ptr<TestRequest> request(NewRequest("http://host/req", net::LOWEST));
    433   scoped_ptr<TestRequest> idle(NewRequest("http://host/idle", net::IDLE));
    434   EXPECT_FALSE(request->started());
    435   EXPECT_FALSE(idle->started());
    436 
    437   const int kMaxNumDelayableRequestsPerClient = 10;  // Should match the .cc.
    438   ScopedVector<TestRequest> lows;
    439   for (int i = 0; i < kMaxNumDelayableRequestsPerClient; ++i) {
    440     string url = "http://host/low" + base::IntToString(i);
    441     lows.push_back(NewRequest(url.c_str(), net::LOWEST));
    442   }
    443 
    444   ChangeRequestPriority(request.get(), net::IDLE);
    445   EXPECT_FALSE(request->started());
    446   EXPECT_FALSE(idle->started());
    447 
    448   ChangeRequestPriority(request.get(), net::LOWEST);
    449   EXPECT_FALSE(request->started());
    450   EXPECT_FALSE(idle->started());
    451 
    452   scheduler_.OnWillInsertBody(kChildId, kRouteId);
    453   EXPECT_FALSE(request->started());
    454   EXPECT_FALSE(idle->started());
    455 }
    456 
    457 TEST_F(ResourceSchedulerTest, HigherIntraPriorityGoesToFrontOfQueue) {
    458   // Dummies to enforce scheduling.
    459   scoped_ptr<TestRequest> high(NewRequest("http://host/high", net::HIGHEST));
    460   scoped_ptr<TestRequest> low(NewRequest("http://host/high", net::LOWEST));
    461 
    462   const int kMaxNumDelayableRequestsPerClient = 10;  // Should match the .cc.
    463   ScopedVector<TestRequest> lows;
    464   for (int i = 0; i < kMaxNumDelayableRequestsPerClient; ++i) {
    465     string url = "http://host/low" + base::IntToString(i);
    466     lows.push_back(NewRequest(url.c_str(), net::IDLE));
    467   }
    468 
    469   scoped_ptr<TestRequest> request(NewRequest("http://host/req", net::IDLE));
    470   EXPECT_FALSE(request->started());
    471 
    472   ChangeRequestPriority(request.get(), net::IDLE, 1);
    473   EXPECT_FALSE(request->started());
    474 
    475   scheduler_.OnWillInsertBody(kChildId, kRouteId);
    476   EXPECT_TRUE(request->started());
    477 }
    478 
    479 TEST_F(ResourceSchedulerTest, NonHTTPSchedulesImmediately) {
    480   // Dummies to enforce scheduling.
    481   scoped_ptr<TestRequest> high(NewRequest("http://host/high", net::HIGHEST));
    482   scoped_ptr<TestRequest> low(NewRequest("http://host/high", net::LOWEST));
    483 
    484   scoped_ptr<TestRequest> request(
    485       NewRequest("chrome-extension://req", net::LOWEST));
    486   EXPECT_TRUE(request->started());
    487 }
    488 
    489 TEST_F(ResourceSchedulerTest, SpdyProxySchedulesImmediately) {
    490   scoped_ptr<TestRequest> high(NewRequest("http://host/high", net::HIGHEST));
    491   scoped_ptr<TestRequest> low(NewRequest("http://host/low", net::LOWEST));
    492 
    493   scoped_ptr<TestRequest> request(NewRequest("http://host/req", net::IDLE));
    494   EXPECT_FALSE(request->started());
    495 
    496   scheduler_.OnReceivedSpdyProxiedHttpResponse(kChildId, kRouteId);
    497   EXPECT_TRUE(request->started());
    498 
    499   scoped_ptr<TestRequest> after(NewRequest("http://host/after", net::IDLE));
    500   EXPECT_TRUE(after->started());
    501 }
    502 
    503 TEST_F(ResourceSchedulerTest, NewSpdyHostInDelayableRequests) {
    504   scheduler_.OnWillInsertBody(kChildId, kRouteId);
    505   const int kMaxNumDelayableRequestsPerClient = 10;  // Should match the .cc.
    506 
    507   scoped_ptr<TestRequest> low1_spdy(
    508       NewRequest("http://spdyhost1:8080/low", net::LOWEST));
    509   // Cancel a request after we learn the server supports SPDY.
    510   ScopedVector<TestRequest> lows;
    511   for (int i = 0; i < kMaxNumDelayableRequestsPerClient - 1; ++i) {
    512     string url = "http://host" + base::IntToString(i) + "/low";
    513     lows.push_back(NewRequest(url.c_str(), net::LOWEST));
    514   }
    515   scoped_ptr<TestRequest> low1(NewRequest("http://host/low", net::LOWEST));
    516   EXPECT_FALSE(low1->started());
    517   http_server_properties_.SetSupportsSpdy(
    518       net::HostPortPair("spdyhost1", 8080), true);
    519   low1_spdy.reset();
    520   EXPECT_TRUE(low1->started());
    521 
    522   low1.reset();
    523   scoped_ptr<TestRequest> low2_spdy(
    524       NewRequest("http://spdyhost2:8080/low", net::IDLE));
    525   // Reprioritize a request after we learn the server supports SPDY.
    526   EXPECT_TRUE(low2_spdy->started());
    527   http_server_properties_.SetSupportsSpdy(
    528       net::HostPortPair("spdyhost2", 8080), true);
    529   ChangeRequestPriority(low2_spdy.get(), net::LOWEST);
    530   scoped_ptr<TestRequest> low2(NewRequest("http://host/low", net::LOWEST));
    531   EXPECT_TRUE(low2->started());
    532 }
    533 
    534 }  // unnamed namespace
    535 
    536 }  // namespace content
    537