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