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/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