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 "base/command_line.h" 6 #include "base/memory/ref_counted.h" 7 #include "base/message_loop/message_loop_proxy.h" 8 #include "base/synchronization/waitable_event.h" 9 #include "base/threading/thread.h" 10 #include "base/values.h" 11 #include "chrome/service/cloud_print/cloud_print_url_fetcher.h" 12 #include "chrome/service/service_process.h" 13 #include "net/test/spawned_test_server/spawned_test_server.h" 14 #include "net/url_request/url_request_context_getter.h" 15 #include "net/url_request/url_request_status.h" 16 #include "net/url_request/url_request_test_util.h" 17 #include "net/url_request/url_request_throttler_manager.h" 18 #include "testing/gtest/include/gtest/gtest.h" 19 #include "url/gurl.h" 20 21 using base::Time; 22 using base::TimeDelta; 23 24 namespace cloud_print { 25 26 const base::FilePath::CharType kDocRoot[] = 27 FILE_PATH_LITERAL("chrome/test/data"); 28 29 int g_request_context_getter_instances = 0; 30 class TrackingTestURLRequestContextGetter 31 : public net::TestURLRequestContextGetter { 32 public: 33 explicit TrackingTestURLRequestContextGetter( 34 base::MessageLoopProxy* io_message_loop_proxy, 35 net::URLRequestThrottlerManager* throttler_manager) 36 : TestURLRequestContextGetter(io_message_loop_proxy), 37 throttler_manager_(throttler_manager) { 38 g_request_context_getter_instances++; 39 } 40 41 virtual net::TestURLRequestContext* GetURLRequestContext() OVERRIDE { 42 if (!context_.get()) { 43 context_.reset(new net::TestURLRequestContext(true)); 44 context_->set_throttler_manager(throttler_manager_); 45 context_->Init(); 46 } 47 return context_.get(); 48 } 49 50 protected: 51 virtual ~TrackingTestURLRequestContextGetter() { 52 g_request_context_getter_instances--; 53 } 54 55 private: 56 // Not owned here. 57 net::URLRequestThrottlerManager* throttler_manager_; 58 scoped_ptr<net::TestURLRequestContext> context_; 59 }; 60 61 class TestCloudPrintURLFetcher : public CloudPrintURLFetcher { 62 public: 63 explicit TestCloudPrintURLFetcher( 64 base::MessageLoopProxy* io_message_loop_proxy) 65 : io_message_loop_proxy_(io_message_loop_proxy) { 66 } 67 68 virtual net::URLRequestContextGetter* GetRequestContextGetter() OVERRIDE { 69 return new TrackingTestURLRequestContextGetter( 70 io_message_loop_proxy_.get(), throttler_manager()); 71 } 72 73 net::URLRequestThrottlerManager* throttler_manager() { 74 return &throttler_manager_; 75 } 76 77 private: 78 virtual ~TestCloudPrintURLFetcher() {} 79 80 scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy_; 81 82 // We set this as the throttler manager for the 83 // TestURLRequestContext we create. 84 net::URLRequestThrottlerManager throttler_manager_; 85 }; 86 87 class CloudPrintURLFetcherTest : public testing::Test, 88 public CloudPrintURLFetcherDelegate { 89 public: 90 CloudPrintURLFetcherTest() : max_retries_(0), fetcher_(NULL) { } 91 92 // Creates a URLFetcher, using the program's main thread to do IO. 93 virtual void CreateFetcher(const GURL& url, int max_retries); 94 95 // CloudPrintURLFetcher::Delegate 96 virtual CloudPrintURLFetcher::ResponseAction HandleRawResponse( 97 const net::URLFetcher* source, 98 const GURL& url, 99 const net::URLRequestStatus& status, 100 int response_code, 101 const net::ResponseCookies& cookies, 102 const std::string& data) OVERRIDE; 103 104 virtual CloudPrintURLFetcher::ResponseAction OnRequestAuthError() OVERRIDE { 105 ADD_FAILURE(); 106 return CloudPrintURLFetcher::STOP_PROCESSING; 107 } 108 109 virtual std::string GetAuthHeader() OVERRIDE { 110 return std::string(); 111 } 112 113 scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy() { 114 return io_message_loop_proxy_; 115 } 116 117 protected: 118 virtual void SetUp() { 119 testing::Test::SetUp(); 120 121 io_message_loop_proxy_ = base::MessageLoopProxy::current(); 122 } 123 124 virtual void TearDown() { 125 fetcher_ = NULL; 126 // Deleting the fetcher causes a task to be posted to the IO thread to 127 // release references to the URLRequestContextGetter. We need to run all 128 // pending tasks to execute that (this is the IO thread). 129 base::MessageLoop::current()->RunUntilIdle(); 130 EXPECT_EQ(0, g_request_context_getter_instances); 131 } 132 133 // URLFetcher is designed to run on the main UI thread, but in our tests 134 // we assume that the current thread is the IO thread where the URLFetcher 135 // dispatches its requests to. When we wish to simulate being used from 136 // a UI thread, we dispatch a worker thread to do so. 137 base::MessageLoopForIO io_loop_; 138 scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy_; 139 int max_retries_; 140 Time start_time_; 141 scoped_refptr<TestCloudPrintURLFetcher> fetcher_; 142 }; 143 144 class CloudPrintURLFetcherBasicTest : public CloudPrintURLFetcherTest { 145 public: 146 CloudPrintURLFetcherBasicTest() 147 : handle_raw_response_(false), handle_raw_data_(false) { } 148 // CloudPrintURLFetcher::Delegate 149 virtual CloudPrintURLFetcher::ResponseAction HandleRawResponse( 150 const net::URLFetcher* source, 151 const GURL& url, 152 const net::URLRequestStatus& status, 153 int response_code, 154 const net::ResponseCookies& cookies, 155 const std::string& data) OVERRIDE; 156 157 virtual CloudPrintURLFetcher::ResponseAction HandleRawData( 158 const net::URLFetcher* source, 159 const GURL& url, 160 const std::string& data) OVERRIDE; 161 162 virtual CloudPrintURLFetcher::ResponseAction HandleJSONData( 163 const net::URLFetcher* source, 164 const GURL& url, 165 base::DictionaryValue* json_data, 166 bool succeeded) OVERRIDE; 167 168 void SetHandleRawResponse(bool handle_raw_response) { 169 handle_raw_response_ = handle_raw_response; 170 } 171 void SetHandleRawData(bool handle_raw_data) { 172 handle_raw_data_ = handle_raw_data; 173 } 174 private: 175 bool handle_raw_response_; 176 bool handle_raw_data_; 177 }; 178 179 // Version of CloudPrintURLFetcherTest that tests overload protection. 180 class CloudPrintURLFetcherOverloadTest : public CloudPrintURLFetcherTest { 181 public: 182 CloudPrintURLFetcherOverloadTest() : response_count_(0) { 183 } 184 185 // CloudPrintURLFetcher::Delegate 186 virtual CloudPrintURLFetcher::ResponseAction HandleRawData( 187 const net::URLFetcher* source, 188 const GURL& url, 189 const std::string& data) OVERRIDE; 190 191 private: 192 int response_count_; 193 }; 194 195 // Version of CloudPrintURLFetcherTest that tests backoff protection. 196 class CloudPrintURLFetcherRetryBackoffTest : public CloudPrintURLFetcherTest { 197 public: 198 CloudPrintURLFetcherRetryBackoffTest() : response_count_(0) { 199 } 200 201 // CloudPrintURLFetcher::Delegate 202 virtual CloudPrintURLFetcher::ResponseAction HandleRawData( 203 const net::URLFetcher* source, 204 const GURL& url, 205 const std::string& data) OVERRIDE; 206 207 virtual void OnRequestGiveUp() OVERRIDE; 208 209 private: 210 int response_count_; 211 }; 212 213 214 void CloudPrintURLFetcherTest::CreateFetcher(const GURL& url, int max_retries) { 215 fetcher_ = new TestCloudPrintURLFetcher(io_message_loop_proxy().get()); 216 217 // Registers an entry for test url. It only allows 3 requests to be sent 218 // in 200 milliseconds. 219 scoped_refptr<net::URLRequestThrottlerEntry> 220 entry(new net::URLRequestThrottlerEntry( 221 fetcher_->throttler_manager(), std::string(), 200, 3, 1, 2.0, 0.0, 256)); 222 fetcher_->throttler_manager()->OverrideEntryForTests(url, entry.get()); 223 224 max_retries_ = max_retries; 225 start_time_ = Time::Now(); 226 fetcher_->StartGetRequest(CloudPrintURLFetcher::REQUEST_MAX, url, this, 227 max_retries_, std::string()); 228 } 229 230 CloudPrintURLFetcher::ResponseAction 231 CloudPrintURLFetcherTest::HandleRawResponse( 232 const net::URLFetcher* source, 233 const GURL& url, 234 const net::URLRequestStatus& status, 235 int response_code, 236 const net::ResponseCookies& cookies, 237 const std::string& data) { 238 EXPECT_TRUE(status.is_success()); 239 EXPECT_EQ(200, response_code); // HTTP OK 240 EXPECT_FALSE(data.empty()); 241 return CloudPrintURLFetcher::CONTINUE_PROCESSING; 242 } 243 244 CloudPrintURLFetcher::ResponseAction 245 CloudPrintURLFetcherBasicTest::HandleRawResponse( 246 const net::URLFetcher* source, 247 const GURL& url, 248 const net::URLRequestStatus& status, 249 int response_code, 250 const net::ResponseCookies& cookies, 251 const std::string& data) { 252 EXPECT_TRUE(status.is_success()); 253 EXPECT_EQ(200, response_code); // HTTP OK 254 EXPECT_FALSE(data.empty()); 255 256 if (handle_raw_response_) { 257 // If the current message loop is not the IO loop, it will be shut down when 258 // the main loop returns and this thread subsequently goes out of scope. 259 io_message_loop_proxy()->PostTask(FROM_HERE, 260 base::MessageLoop::QuitClosure()); 261 return CloudPrintURLFetcher::STOP_PROCESSING; 262 } 263 return CloudPrintURLFetcher::CONTINUE_PROCESSING; 264 } 265 266 CloudPrintURLFetcher::ResponseAction 267 CloudPrintURLFetcherBasicTest::HandleRawData( 268 const net::URLFetcher* source, 269 const GURL& url, 270 const std::string& data) { 271 // We should never get here if we returned true in HandleRawResponse 272 EXPECT_FALSE(handle_raw_response_); 273 if (handle_raw_data_) { 274 io_message_loop_proxy()->PostTask(FROM_HERE, 275 base::MessageLoop::QuitClosure()); 276 return CloudPrintURLFetcher::STOP_PROCESSING; 277 } 278 return CloudPrintURLFetcher::CONTINUE_PROCESSING; 279 } 280 281 CloudPrintURLFetcher::ResponseAction 282 CloudPrintURLFetcherBasicTest::HandleJSONData( 283 const net::URLFetcher* source, 284 const GURL& url, 285 base::DictionaryValue* json_data, 286 bool succeeded) { 287 // We should never get here if we returned true in one of the above methods. 288 EXPECT_FALSE(handle_raw_response_); 289 EXPECT_FALSE(handle_raw_data_); 290 io_message_loop_proxy()->PostTask(FROM_HERE, 291 base::MessageLoop::QuitClosure()); 292 return CloudPrintURLFetcher::STOP_PROCESSING; 293 } 294 295 CloudPrintURLFetcher::ResponseAction 296 CloudPrintURLFetcherOverloadTest::HandleRawData( 297 const net::URLFetcher* source, 298 const GURL& url, 299 const std::string& data) { 300 const TimeDelta one_second = TimeDelta::FromMilliseconds(1000); 301 response_count_++; 302 if (response_count_ < 20) { 303 fetcher_->StartGetRequest(CloudPrintURLFetcher::REQUEST_MAX, url, this, 304 max_retries_, std::string()); 305 } else { 306 // We have already sent 20 requests continuously. And we expect that 307 // it takes more than 1 second due to the overload protection settings. 308 EXPECT_TRUE(Time::Now() - start_time_ >= one_second); 309 io_message_loop_proxy()->PostTask(FROM_HERE, 310 base::MessageLoop::QuitClosure()); 311 } 312 return CloudPrintURLFetcher::STOP_PROCESSING; 313 } 314 315 CloudPrintURLFetcher::ResponseAction 316 CloudPrintURLFetcherRetryBackoffTest::HandleRawData( 317 const net::URLFetcher* source, 318 const GURL& url, 319 const std::string& data) { 320 response_count_++; 321 // First attempt + 11 retries = 12 total responses. 322 EXPECT_LE(response_count_, 12); 323 return CloudPrintURLFetcher::RETRY_REQUEST; 324 } 325 326 void CloudPrintURLFetcherRetryBackoffTest::OnRequestGiveUp() { 327 // It takes more than 200 ms to finish all 11 requests. 328 EXPECT_TRUE(Time::Now() - start_time_ >= TimeDelta::FromMilliseconds(200)); 329 io_message_loop_proxy()->PostTask(FROM_HERE, 330 base::MessageLoop::QuitClosure()); 331 } 332 333 TEST_F(CloudPrintURLFetcherBasicTest, HandleRawResponse) { 334 net::SpawnedTestServer test_server(net::SpawnedTestServer::TYPE_HTTP, 335 net::SpawnedTestServer::kLocalhost, 336 base::FilePath(kDocRoot)); 337 ASSERT_TRUE(test_server.Start()); 338 SetHandleRawResponse(true); 339 340 CreateFetcher(test_server.GetURL("echo"), 0); 341 base::MessageLoop::current()->Run(); 342 } 343 344 TEST_F(CloudPrintURLFetcherBasicTest, HandleRawData) { 345 net::SpawnedTestServer test_server(net::SpawnedTestServer::TYPE_HTTP, 346 net::SpawnedTestServer::kLocalhost, 347 base::FilePath(kDocRoot)); 348 ASSERT_TRUE(test_server.Start()); 349 350 SetHandleRawData(true); 351 CreateFetcher(test_server.GetURL("echo"), 0); 352 base::MessageLoop::current()->Run(); 353 } 354 355 TEST_F(CloudPrintURLFetcherOverloadTest, Protect) { 356 net::SpawnedTestServer test_server(net::SpawnedTestServer::TYPE_HTTP, 357 net::SpawnedTestServer::kLocalhost, 358 base::FilePath(kDocRoot)); 359 ASSERT_TRUE(test_server.Start()); 360 361 GURL url(test_server.GetURL("defaultresponse")); 362 CreateFetcher(url, 11); 363 364 base::MessageLoop::current()->Run(); 365 } 366 367 TEST_F(CloudPrintURLFetcherRetryBackoffTest, GiveUp) { 368 net::SpawnedTestServer test_server(net::SpawnedTestServer::TYPE_HTTP, 369 net::SpawnedTestServer::kLocalhost, 370 base::FilePath(kDocRoot)); 371 ASSERT_TRUE(test_server.Start()); 372 373 GURL url(test_server.GetURL("defaultresponse")); 374 CreateFetcher(url, 11); 375 376 base::MessageLoop::current()->Run(); 377 } 378 379 } // namespace cloud_print 380