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/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 }  // namespace syncer
    409