Home | History | Annotate | Download | only in internal_api
      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