1 // Copyright 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/synchronization/waitable_event.h" 6 #include "base/threading/thread.h" 7 #include "net/test/spawned_test_server/spawned_test_server.h" 8 #include "net/url_request/test_url_fetcher_factory.h" 9 #include "net/url_request/url_fetcher_delegate.h" 10 #include "net/url_request/url_request_test_util.h" 11 #include "sync/internal_api/public/base/cancelation_signal.h" 12 #include "sync/internal_api/public/http_bridge.h" 13 #include "testing/gtest/include/gtest/gtest.h" 14 15 namespace syncer { 16 17 namespace { 18 // TODO(timsteele): Should use PathService here. See Chromium Issue 3113. 19 const base::FilePath::CharType kDocRoot[] = 20 FILE_PATH_LITERAL("chrome/test/data"); 21 } 22 23 class SyncHttpBridgeTest : public testing::Test { 24 public: 25 SyncHttpBridgeTest() 26 : test_server_(net::SpawnedTestServer::TYPE_HTTP, 27 net::SpawnedTestServer::kLocalhost, 28 base::FilePath(kDocRoot)), 29 fake_default_request_context_getter_(NULL), 30 bridge_for_race_test_(NULL), 31 io_thread_("IO thread") { 32 } 33 34 virtual void SetUp() { 35 base::Thread::Options options; 36 options.message_loop_type = base::MessageLoop::TYPE_IO; 37 io_thread_.StartWithOptions(options); 38 } 39 40 virtual void TearDown() { 41 if (fake_default_request_context_getter_) { 42 GetIOThreadLoop()->ReleaseSoon(FROM_HERE, 43 fake_default_request_context_getter_); 44 fake_default_request_context_getter_ = NULL; 45 } 46 io_thread_.Stop(); 47 } 48 49 HttpBridge* BuildBridge() { 50 if (!fake_default_request_context_getter_) { 51 fake_default_request_context_getter_ = 52 new net::TestURLRequestContextGetter(io_thread_.message_loop_proxy()); 53 fake_default_request_context_getter_->AddRef(); 54 } 55 HttpBridge* bridge = new HttpBridge( 56 new HttpBridge::RequestContextGetter( 57 fake_default_request_context_getter_, 58 "user agent"), 59 NetworkTimeUpdateCallback()); 60 return bridge; 61 } 62 63 static void Abort(HttpBridge* bridge) { 64 bridge->Abort(); 65 } 66 67 // Used by AbortAndReleaseBeforeFetchCompletes to test an interesting race 68 // condition. 69 void RunSyncThreadBridgeUseTest(base::WaitableEvent* signal_when_created, 70 base::WaitableEvent* signal_when_released); 71 72 static void TestSameHttpNetworkSession(base::MessageLoop* main_message_loop, 73 SyncHttpBridgeTest* test) { 74 scoped_refptr<HttpBridge> http_bridge(test->BuildBridge()); 75 EXPECT_TRUE(test->GetTestRequestContextGetter()); 76 net::HttpNetworkSession* test_session = 77 test->GetTestRequestContextGetter()->GetURLRequestContext()-> 78 http_transaction_factory()->GetSession(); 79 EXPECT_EQ(test_session, 80 http_bridge->GetRequestContextGetterForTest()-> 81 GetURLRequestContext()-> 82 http_transaction_factory()->GetSession()); 83 main_message_loop->PostTask(FROM_HERE, base::MessageLoop::QuitClosure()); 84 } 85 86 base::MessageLoop* GetIOThreadLoop() { return io_thread_.message_loop(); } 87 88 // Note this is lazy created, so don't call this before your bridge. 89 net::TestURLRequestContextGetter* GetTestRequestContextGetter() { 90 return fake_default_request_context_getter_; 91 } 92 93 net::SpawnedTestServer test_server_; 94 95 base::Thread* io_thread() { return &io_thread_; } 96 97 HttpBridge* bridge_for_race_test() { return bridge_for_race_test_; } 98 99 private: 100 // A make-believe "default" request context, as would be returned by 101 // Profile::GetDefaultRequestContext(). Created lazily by BuildBridge. 102 net::TestURLRequestContextGetter* fake_default_request_context_getter_; 103 104 HttpBridge* bridge_for_race_test_; 105 106 // Separate thread for IO used by the HttpBridge. 107 base::Thread io_thread_; 108 base::MessageLoop loop_; 109 }; 110 111 // An HttpBridge that doesn't actually make network requests and just calls 112 // back with dummy response info. 113 // TODO(tim): Instead of inheriting here we should inject a component 114 // responsible for the MakeAsynchronousPost bit. 115 class ShuntedHttpBridge : public HttpBridge { 116 public: 117 // If |never_finishes| is true, the simulated request never actually 118 // returns. 119 ShuntedHttpBridge(net::URLRequestContextGetter* baseline_context_getter, 120 SyncHttpBridgeTest* test, bool never_finishes) 121 : HttpBridge( 122 new HttpBridge::RequestContextGetter( 123 baseline_context_getter, "user agent"), 124 NetworkTimeUpdateCallback()), 125 test_(test), never_finishes_(never_finishes) { } 126 protected: 127 virtual void MakeAsynchronousPost() OVERRIDE { 128 ASSERT_TRUE(base::MessageLoop::current() == test_->GetIOThreadLoop()); 129 if (never_finishes_) 130 return; 131 132 // We don't actually want to make a request for this test, so just callback 133 // as if it completed. 134 test_->GetIOThreadLoop()->PostTask(FROM_HERE, 135 base::Bind(&ShuntedHttpBridge::CallOnURLFetchComplete, this)); 136 } 137 private: 138 virtual ~ShuntedHttpBridge() {} 139 140 void CallOnURLFetchComplete() { 141 ASSERT_TRUE(base::MessageLoop::current() == test_->GetIOThreadLoop()); 142 // We return no cookies and a dummy content response. 143 net::ResponseCookies cookies; 144 145 std::string response_content = "success!"; 146 net::TestURLFetcher fetcher(0, GURL(), NULL); 147 fetcher.set_url(GURL("www.google.com")); 148 fetcher.set_response_code(200); 149 fetcher.set_cookies(cookies); 150 fetcher.SetResponseString(response_content); 151 OnURLFetchComplete(&fetcher); 152 } 153 SyncHttpBridgeTest* test_; 154 bool never_finishes_; 155 }; 156 157 void SyncHttpBridgeTest::RunSyncThreadBridgeUseTest( 158 base::WaitableEvent* signal_when_created, 159 base::WaitableEvent* signal_when_released) { 160 scoped_refptr<net::URLRequestContextGetter> ctx_getter( 161 new net::TestURLRequestContextGetter(io_thread_.message_loop_proxy())); 162 { 163 scoped_refptr<ShuntedHttpBridge> bridge( 164 new ShuntedHttpBridge(ctx_getter.get(), this, true)); 165 bridge->SetURL("http://www.google.com", 9999); 166 bridge->SetPostPayload("text/plain", 2, " "); 167 bridge_for_race_test_ = bridge.get(); 168 signal_when_created->Signal(); 169 170 int os_error = 0; 171 int response_code = 0; 172 bridge->MakeSynchronousPost(&os_error, &response_code); 173 bridge_for_race_test_ = NULL; 174 } 175 signal_when_released->Signal(); 176 } 177 178 TEST_F(SyncHttpBridgeTest, TestUsesSameHttpNetworkSession) { 179 // Run this test on the IO thread because we can only call 180 // URLRequestContextGetter::GetURLRequestContext on the IO thread. 181 io_thread()->message_loop() 182 ->PostTask(FROM_HERE, 183 base::Bind(&SyncHttpBridgeTest::TestSameHttpNetworkSession, 184 base::MessageLoop::current(), 185 this)); 186 base::MessageLoop::current()->Run(); 187 } 188 189 // Test the HttpBridge without actually making any network requests. 190 TEST_F(SyncHttpBridgeTest, TestMakeSynchronousPostShunted) { 191 scoped_refptr<net::URLRequestContextGetter> ctx_getter( 192 new net::TestURLRequestContextGetter(io_thread()->message_loop_proxy())); 193 scoped_refptr<HttpBridge> http_bridge( 194 new ShuntedHttpBridge(ctx_getter.get(), this, false)); 195 http_bridge->SetURL("http://www.google.com", 9999); 196 http_bridge->SetPostPayload("text/plain", 2, " "); 197 198 int os_error = 0; 199 int response_code = 0; 200 bool success = http_bridge->MakeSynchronousPost(&os_error, &response_code); 201 EXPECT_TRUE(success); 202 EXPECT_EQ(200, response_code); 203 EXPECT_EQ(0, os_error); 204 205 EXPECT_EQ(8, http_bridge->GetResponseContentLength()); 206 EXPECT_EQ(std::string("success!"), 207 std::string(http_bridge->GetResponseContent())); 208 } 209 210 // Full round-trip test of the HttpBridge, using default UA string and 211 // no request cookies. 212 TEST_F(SyncHttpBridgeTest, TestMakeSynchronousPostLiveWithPayload) { 213 ASSERT_TRUE(test_server_.Start()); 214 215 scoped_refptr<HttpBridge> http_bridge(BuildBridge()); 216 217 std::string payload = "this should be echoed back"; 218 GURL echo = test_server_.GetURL("echo"); 219 http_bridge->SetURL(echo.spec().c_str(), echo.IntPort()); 220 http_bridge->SetPostPayload("application/x-www-form-urlencoded", 221 payload.length() + 1, payload.c_str()); 222 int os_error = 0; 223 int response_code = 0; 224 bool success = http_bridge->MakeSynchronousPost(&os_error, &response_code); 225 EXPECT_TRUE(success); 226 EXPECT_EQ(200, response_code); 227 EXPECT_EQ(0, os_error); 228 229 EXPECT_EQ(payload.length() + 1, 230 static_cast<size_t>(http_bridge->GetResponseContentLength())); 231 EXPECT_EQ(payload, std::string(http_bridge->GetResponseContent())); 232 } 233 234 // Full round-trip test of the HttpBridge. 235 TEST_F(SyncHttpBridgeTest, TestMakeSynchronousPostLiveComprehensive) { 236 ASSERT_TRUE(test_server_.Start()); 237 238 scoped_refptr<HttpBridge> http_bridge(BuildBridge()); 239 240 GURL echo_header = test_server_.GetURL("echoall"); 241 http_bridge->SetURL(echo_header.spec().c_str(), echo_header.IntPort()); 242 243 std::string test_payload = "###TEST PAYLOAD###"; 244 http_bridge->SetPostPayload("text/html", test_payload.length() + 1, 245 test_payload.c_str()); 246 247 int os_error = 0; 248 int response_code = 0; 249 bool success = http_bridge->MakeSynchronousPost(&os_error, &response_code); 250 EXPECT_TRUE(success); 251 EXPECT_EQ(200, response_code); 252 EXPECT_EQ(0, os_error); 253 254 std::string response(http_bridge->GetResponseContent(), 255 http_bridge->GetResponseContentLength()); 256 EXPECT_EQ(std::string::npos, response.find("Cookie:")); 257 EXPECT_NE(std::string::npos, response.find("User-Agent: user agent")); 258 EXPECT_NE(std::string::npos, response.find(test_payload.c_str())); 259 } 260 261 TEST_F(SyncHttpBridgeTest, TestExtraRequestHeaders) { 262 ASSERT_TRUE(test_server_.Start()); 263 264 scoped_refptr<HttpBridge> http_bridge(BuildBridge()); 265 266 GURL echo_header = test_server_.GetURL("echoall"); 267 268 http_bridge->SetURL(echo_header.spec().c_str(), echo_header.IntPort()); 269 http_bridge->SetExtraRequestHeaders("test:fnord"); 270 271 std::string test_payload = "###TEST PAYLOAD###"; 272 http_bridge->SetPostPayload("text/html", test_payload.length() + 1, 273 test_payload.c_str()); 274 275 int os_error = 0; 276 int response_code = 0; 277 bool success = http_bridge->MakeSynchronousPost(&os_error, &response_code); 278 EXPECT_TRUE(success); 279 EXPECT_EQ(200, response_code); 280 EXPECT_EQ(0, os_error); 281 282 std::string response(http_bridge->GetResponseContent(), 283 http_bridge->GetResponseContentLength()); 284 285 EXPECT_NE(std::string::npos, response.find("fnord")); 286 EXPECT_NE(std::string::npos, response.find(test_payload.c_str())); 287 } 288 289 TEST_F(SyncHttpBridgeTest, TestResponseHeader) { 290 ASSERT_TRUE(test_server_.Start()); 291 292 scoped_refptr<HttpBridge> http_bridge(BuildBridge()); 293 294 GURL echo_header = test_server_.GetURL("echoall"); 295 http_bridge->SetURL(echo_header.spec().c_str(), echo_header.IntPort()); 296 297 std::string test_payload = "###TEST PAYLOAD###"; 298 http_bridge->SetPostPayload("text/html", test_payload.length() + 1, 299 test_payload.c_str()); 300 301 int os_error = 0; 302 int response_code = 0; 303 bool success = http_bridge->MakeSynchronousPost(&os_error, &response_code); 304 EXPECT_TRUE(success); 305 EXPECT_EQ(200, response_code); 306 EXPECT_EQ(0, os_error); 307 308 EXPECT_EQ(http_bridge->GetResponseHeaderValue("Content-type"), "text/html"); 309 EXPECT_TRUE(http_bridge->GetResponseHeaderValue("invalid-header").empty()); 310 } 311 312 TEST_F(SyncHttpBridgeTest, Abort) { 313 scoped_refptr<net::URLRequestContextGetter> ctx_getter( 314 new net::TestURLRequestContextGetter(io_thread()->message_loop_proxy())); 315 scoped_refptr<ShuntedHttpBridge> http_bridge( 316 new ShuntedHttpBridge(ctx_getter.get(), this, true)); 317 http_bridge->SetURL("http://www.google.com", 9999); 318 http_bridge->SetPostPayload("text/plain", 2, " "); 319 320 int os_error = 0; 321 int response_code = 0; 322 323 io_thread()->message_loop_proxy()->PostTask( 324 FROM_HERE, 325 base::Bind(&SyncHttpBridgeTest::Abort, http_bridge)); 326 bool success = http_bridge->MakeSynchronousPost(&os_error, &response_code); 327 EXPECT_FALSE(success); 328 EXPECT_EQ(net::ERR_ABORTED, os_error); 329 } 330 331 TEST_F(SyncHttpBridgeTest, AbortLate) { 332 scoped_refptr<net::URLRequestContextGetter> ctx_getter( 333 new net::TestURLRequestContextGetter(io_thread()->message_loop_proxy())); 334 scoped_refptr<ShuntedHttpBridge> http_bridge( 335 new ShuntedHttpBridge(ctx_getter.get(), this, false)); 336 http_bridge->SetURL("http://www.google.com", 9999); 337 http_bridge->SetPostPayload("text/plain", 2, " "); 338 339 int os_error = 0; 340 int response_code = 0; 341 342 bool success = http_bridge->MakeSynchronousPost(&os_error, &response_code); 343 ASSERT_TRUE(success); 344 http_bridge->Abort(); 345 // Ensures no double-free of URLFetcher, etc. 346 } 347 348 // Tests an interesting case where code using the HttpBridge aborts the fetch 349 // and releases ownership before a pending fetch completed callback is issued by 350 // the underlying URLFetcher (and before that URLFetcher is destroyed, which 351 // would cancel the callback). 352 TEST_F(SyncHttpBridgeTest, AbortAndReleaseBeforeFetchComplete) { 353 base::Thread sync_thread("SyncThread"); 354 sync_thread.Start(); 355 356 // First, block the sync thread on the post. 357 base::WaitableEvent signal_when_created(false, false); 358 base::WaitableEvent signal_when_released(false, false); 359 sync_thread.message_loop()->PostTask(FROM_HERE, 360 base::Bind(&SyncHttpBridgeTest::RunSyncThreadBridgeUseTest, 361 base::Unretained(this), 362 &signal_when_created, 363 &signal_when_released)); 364 365 // Stop IO so we can control order of operations. 366 base::WaitableEvent io_waiter(false, false); 367 ASSERT_TRUE(io_thread()->message_loop_proxy()->PostTask( 368 FROM_HERE, 369 base::Bind(&base::WaitableEvent::Wait, base::Unretained(&io_waiter)))); 370 371 signal_when_created.Wait(); // Wait till we have a bridge to abort. 372 ASSERT_TRUE(bridge_for_race_test()); 373 374 // Schedule the fetch completion callback (but don't run it yet). Don't take 375 // a reference to the bridge to mimic URLFetcher's handling of the delegate. 376 net::URLFetcherDelegate* delegate = 377 static_cast<net::URLFetcherDelegate*>(bridge_for_race_test()); 378 net::ResponseCookies cookies; 379 std::string response_content = "success!"; 380 net::TestURLFetcher fetcher(0, GURL(), NULL); 381 fetcher.set_url(GURL("www.google.com")); 382 fetcher.set_response_code(200); 383 fetcher.set_cookies(cookies); 384 fetcher.SetResponseString(response_content); 385 ASSERT_TRUE(io_thread()->message_loop_proxy()->PostTask( 386 FROM_HERE, 387 base::Bind(&net::URLFetcherDelegate::OnURLFetchComplete, 388 base::Unretained(delegate), &fetcher))); 389 390 // Abort the fetch. This should be smart enough to handle the case where 391 // the bridge is destroyed before the callback scheduled above completes. 392 bridge_for_race_test()->Abort(); 393 394 // Wait until the sync thread releases its ref on the bridge. 395 signal_when_released.Wait(); 396 ASSERT_FALSE(bridge_for_race_test()); 397 398 // Unleash the hounds. The fetch completion callback should fire first, and 399 // succeed even though we Release()d the bridge above because the call to 400 // Abort should have held a reference. 401 io_waiter.Signal(); 402 403 // Done. 404 sync_thread.Stop(); 405 io_thread()->Stop(); 406 } 407 408 void HttpBridgeRunOnSyncThread( 409 net::URLRequestContextGetter* baseline_context_getter, 410 CancelationSignal* factory_cancelation_signal, 411 syncer::HttpPostProviderFactory** bridge_factory_out, 412 syncer::HttpPostProviderInterface** bridge_out, 413 base::WaitableEvent* signal_when_created, 414 base::WaitableEvent* wait_for_shutdown) { 415 scoped_ptr<syncer::HttpBridgeFactory> bridge_factory( 416 new syncer::HttpBridgeFactory(baseline_context_getter, 417 NetworkTimeUpdateCallback(), 418 factory_cancelation_signal)); 419 bridge_factory->Init("test"); 420 *bridge_factory_out = bridge_factory.get(); 421 422 HttpPostProviderInterface* bridge = bridge_factory->Create(); 423 *bridge_out = bridge; 424 425 signal_when_created->Signal(); 426 wait_for_shutdown->Wait(); 427 428 bridge_factory->Destroy(bridge); 429 } 430 431 void WaitOnIOThread(base::WaitableEvent* signal_wait_start, 432 base::WaitableEvent* wait_done) { 433 signal_wait_start->Signal(); 434 wait_done->Wait(); 435 } 436 437 // Tests RequestContextGetter is properly released on IO thread even when 438 // IO thread stops before sync thread. 439 TEST_F(SyncHttpBridgeTest, RequestContextGetterReleaseOrder) { 440 base::Thread sync_thread("SyncThread"); 441 sync_thread.Start(); 442 443 syncer::HttpPostProviderFactory* factory = NULL; 444 syncer::HttpPostProviderInterface* bridge = NULL; 445 446 scoped_refptr<net::URLRequestContextGetter> baseline_context_getter( 447 new net::TestURLRequestContextGetter(io_thread()->message_loop_proxy())); 448 449 base::WaitableEvent signal_when_created(false, false); 450 base::WaitableEvent wait_for_shutdown(false, false); 451 452 CancelationSignal release_request_context_signal; 453 454 // Create bridge factory and factory on sync thread and wait for the creation 455 // to finish. 456 sync_thread.message_loop()->PostTask(FROM_HERE, 457 base::Bind(&HttpBridgeRunOnSyncThread, 458 base::Unretained(baseline_context_getter.get()), 459 &release_request_context_signal ,&factory, &bridge, 460 &signal_when_created, &wait_for_shutdown)); 461 signal_when_created.Wait(); 462 463 // Simulate sync shutdown by aborting bridge and shutting down factory on 464 // frontend. 465 bridge->Abort(); 466 release_request_context_signal.Signal(); 467 468 // Wait for sync's RequestContextGetter to be cleared on IO thread and 469 // check for reference count. 470 base::WaitableEvent signal_wait_start(false, false); 471 base::WaitableEvent wait_done(false, false); 472 io_thread()->message_loop()->PostTask( 473 FROM_HERE, 474 base::Bind(&WaitOnIOThread, &signal_wait_start, &wait_done)); 475 signal_wait_start.Wait(); 476 // |baseline_context_getter| should have only one reference from local 477 // variable. 478 EXPECT_TRUE(baseline_context_getter->HasOneRef()); 479 baseline_context_getter = NULL; 480 481 // Unblock and stop IO thread before sync thread. 482 wait_done.Signal(); 483 io_thread()->Stop(); 484 485 // Unblock and stop sync thread. 486 wait_for_shutdown.Signal(); 487 sync_thread.Stop(); 488 } 489 490 // Attempt to release the URLRequestContextGetter before the HttpBridgeFactory 491 // is initialized. 492 TEST_F(SyncHttpBridgeTest, EarlyAbortFactory) { 493 // In a real scenario, the following would happen on many threads. For 494 // simplicity, this test uses only one thread. 495 496 scoped_refptr<net::URLRequestContextGetter> baseline_context_getter( 497 new net::TestURLRequestContextGetter(io_thread()->message_loop_proxy())); 498 CancelationSignal release_request_context_signal; 499 500 // UI Thread: Initialize the HttpBridgeFactory. The next step would be to 501 // post a task to SBH::Core to have it initialized. 502 scoped_ptr<syncer::HttpBridgeFactory> factory( 503 new HttpBridgeFactory(baseline_context_getter.get(), 504 NetworkTimeUpdateCallback(), 505 &release_request_context_signal)); 506 507 // UI Thread: A very early shutdown request arrives and executes on the UI 508 // thread before the posted sync thread task is run. 509 release_request_context_signal.Signal(); 510 511 // Sync thread: Finally run the posted task, only to find that our 512 // HttpBridgeFactory has been neutered. Should not crash. 513 factory->Init("TestUserAgent"); 514 515 // At this point, attempting to use the factory would trigger a crash. Both 516 // this test and the real world code should make sure this never happens. 517 }; 518 519 } // namespace syncer 520