| // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/message_loop_proxy.h" |
| #include "base/threading/thread.h" |
| #include "chrome/browser/sync/glue/http_bridge.h" |
| #include "chrome/common/net/test_url_fetcher_factory.h" |
| #include "chrome/test/test_url_request_context_getter.h" |
| #include "content/browser/browser_thread.h" |
| #include "net/url_request/url_request_test_util.h" |
| #include "net/test/test_server.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using browser_sync::HttpBridge; |
| |
| namespace { |
| // TODO(timsteele): Should use PathService here. See Chromium Issue 3113. |
| const FilePath::CharType kDocRoot[] = FILE_PATH_LITERAL("chrome/test/data"); |
| } |
| |
| class HttpBridgeTest : public testing::Test { |
| public: |
| HttpBridgeTest() |
| : test_server_(net::TestServer::TYPE_HTTP, FilePath(kDocRoot)), |
| fake_default_request_context_getter_(NULL), |
| io_thread_(BrowserThread::IO) { |
| } |
| |
| virtual void SetUp() { |
| base::Thread::Options options; |
| options.message_loop_type = MessageLoop::TYPE_IO; |
| io_thread_.StartWithOptions(options); |
| } |
| |
| virtual void TearDown() { |
| io_thread_loop()->ReleaseSoon(FROM_HERE, |
| fake_default_request_context_getter_); |
| io_thread_.Stop(); |
| fake_default_request_context_getter_ = NULL; |
| } |
| |
| HttpBridge* BuildBridge() { |
| if (!fake_default_request_context_getter_) { |
| fake_default_request_context_getter_ = new TestURLRequestContextGetter(); |
| fake_default_request_context_getter_->AddRef(); |
| } |
| HttpBridge* bridge = new HttpBridge( |
| new HttpBridge::RequestContextGetter( |
| fake_default_request_context_getter_)); |
| return bridge; |
| } |
| |
| static void Abort(HttpBridge* bridge) { |
| bridge->Abort(); |
| } |
| |
| static void TestSameHttpNetworkSession(MessageLoop* main_message_loop, |
| HttpBridgeTest* test) { |
| scoped_refptr<HttpBridge> http_bridge(test->BuildBridge()); |
| EXPECT_TRUE(test->GetTestRequestContextGetter()); |
| net::HttpNetworkSession* test_session = |
| test->GetTestRequestContextGetter()->GetURLRequestContext()-> |
| http_transaction_factory()->GetSession(); |
| EXPECT_EQ(test_session, |
| http_bridge->GetRequestContextGetter()-> |
| GetURLRequestContext()-> |
| http_transaction_factory()->GetSession()); |
| main_message_loop->PostTask(FROM_HERE, new MessageLoop::QuitTask); |
| } |
| |
| MessageLoop* io_thread_loop() { return io_thread_.message_loop(); } |
| |
| // Note this is lazy created, so don't call this before your bridge. |
| TestURLRequestContextGetter* GetTestRequestContextGetter() { |
| return fake_default_request_context_getter_; |
| } |
| |
| net::TestServer test_server_; |
| |
| private: |
| // A make-believe "default" request context, as would be returned by |
| // Profile::GetDefaultRequestContext(). Created lazily by BuildBridge. |
| TestURLRequestContextGetter* fake_default_request_context_getter_; |
| |
| // Separate thread for IO used by the HttpBridge. |
| BrowserThread io_thread_; |
| MessageLoop loop_; |
| }; |
| |
| class DummyURLFetcher : public TestURLFetcher { |
| public: |
| DummyURLFetcher() : TestURLFetcher(0, GURL(), POST, NULL) {} |
| |
| net::HttpResponseHeaders* response_headers() const { |
| return NULL; |
| } |
| }; |
| |
| // An HttpBridge that doesn't actually make network requests and just calls |
| // back with dummy response info. |
| class ShuntedHttpBridge : public HttpBridge { |
| public: |
| // If |never_finishes| is true, the simulated request never actually |
| // returns. |
| ShuntedHttpBridge(net::URLRequestContextGetter* baseline_context_getter, |
| HttpBridgeTest* test, bool never_finishes) |
| : HttpBridge(new HttpBridge::RequestContextGetter( |
| baseline_context_getter)), |
| test_(test), never_finishes_(never_finishes) { } |
| protected: |
| virtual void MakeAsynchronousPost() { |
| ASSERT_TRUE(MessageLoop::current() == test_->io_thread_loop()); |
| if (never_finishes_) |
| return; |
| |
| // We don't actually want to make a request for this test, so just callback |
| // as if it completed. |
| test_->io_thread_loop()->PostTask(FROM_HERE, |
| NewRunnableMethod(this, &ShuntedHttpBridge::CallOnURLFetchComplete)); |
| } |
| private: |
| ~ShuntedHttpBridge() {} |
| |
| void CallOnURLFetchComplete() { |
| ASSERT_TRUE(MessageLoop::current() == test_->io_thread_loop()); |
| // We return no cookies and a dummy content response. |
| ResponseCookies cookies; |
| |
| std::string response_content = "success!"; |
| DummyURLFetcher fetcher; |
| OnURLFetchComplete(&fetcher, GURL("www.google.com"), |
| net::URLRequestStatus(), |
| 200, cookies, response_content); |
| } |
| HttpBridgeTest* test_; |
| bool never_finishes_; |
| }; |
| |
| TEST_F(HttpBridgeTest, TestUsesSameHttpNetworkSession) { |
| // Run this test on the IO thread because we can only call |
| // URLRequestContextGetter::GetURLRequestContext on the IO thread. |
| BrowserThread::PostTask( |
| BrowserThread::IO, FROM_HERE, |
| NewRunnableFunction(&HttpBridgeTest::TestSameHttpNetworkSession, |
| MessageLoop::current(), this)); |
| MessageLoop::current()->Run(); |
| } |
| |
| // Test the HttpBridge without actually making any network requests. |
| TEST_F(HttpBridgeTest, TestMakeSynchronousPostShunted) { |
| scoped_refptr<net::URLRequestContextGetter> ctx_getter( |
| new TestURLRequestContextGetter()); |
| scoped_refptr<HttpBridge> http_bridge(new ShuntedHttpBridge( |
| ctx_getter, this, false)); |
| http_bridge->SetUserAgent("bob"); |
| http_bridge->SetURL("http://www.google.com", 9999); |
| http_bridge->SetPostPayload("text/plain", 2, " "); |
| |
| int os_error = 0; |
| int response_code = 0; |
| bool success = http_bridge->MakeSynchronousPost(&os_error, &response_code); |
| EXPECT_TRUE(success); |
| EXPECT_EQ(200, response_code); |
| EXPECT_EQ(0, os_error); |
| |
| EXPECT_EQ(8, http_bridge->GetResponseContentLength()); |
| EXPECT_EQ(std::string("success!"), |
| std::string(http_bridge->GetResponseContent())); |
| } |
| |
| // Full round-trip test of the HttpBridge, using default UA string and |
| // no request cookies. |
| TEST_F(HttpBridgeTest, TestMakeSynchronousPostLiveWithPayload) { |
| ASSERT_TRUE(test_server_.Start()); |
| |
| scoped_refptr<HttpBridge> http_bridge(BuildBridge()); |
| |
| std::string payload = "this should be echoed back"; |
| GURL echo = test_server_.GetURL("echo"); |
| http_bridge->SetURL(echo.spec().c_str(), echo.IntPort()); |
| http_bridge->SetPostPayload("application/x-www-form-urlencoded", |
| payload.length() + 1, payload.c_str()); |
| int os_error = 0; |
| int response_code = 0; |
| bool success = http_bridge->MakeSynchronousPost(&os_error, &response_code); |
| EXPECT_TRUE(success); |
| EXPECT_EQ(200, response_code); |
| EXPECT_EQ(0, os_error); |
| |
| EXPECT_EQ(payload.length() + 1, |
| static_cast<size_t>(http_bridge->GetResponseContentLength())); |
| EXPECT_EQ(payload, std::string(http_bridge->GetResponseContent())); |
| } |
| |
| // Full round-trip test of the HttpBridge, using custom UA string |
| TEST_F(HttpBridgeTest, TestMakeSynchronousPostLiveComprehensive) { |
| ASSERT_TRUE(test_server_.Start()); |
| |
| scoped_refptr<HttpBridge> http_bridge(BuildBridge()); |
| |
| GURL echo_header = test_server_.GetURL("echoall"); |
| http_bridge->SetUserAgent("bob"); |
| http_bridge->SetURL(echo_header.spec().c_str(), echo_header.IntPort()); |
| |
| std::string test_payload = "###TEST PAYLOAD###"; |
| http_bridge->SetPostPayload("text/html", test_payload.length() + 1, |
| test_payload.c_str()); |
| |
| int os_error = 0; |
| int response_code = 0; |
| bool success = http_bridge->MakeSynchronousPost(&os_error, &response_code); |
| EXPECT_TRUE(success); |
| EXPECT_EQ(200, response_code); |
| EXPECT_EQ(0, os_error); |
| |
| std::string response(http_bridge->GetResponseContent(), |
| http_bridge->GetResponseContentLength()); |
| EXPECT_EQ(std::string::npos, response.find("Cookie:")); |
| EXPECT_NE(std::string::npos, response.find("User-Agent: bob")); |
| EXPECT_NE(std::string::npos, response.find(test_payload.c_str())); |
| } |
| |
| TEST_F(HttpBridgeTest, TestExtraRequestHeaders) { |
| ASSERT_TRUE(test_server_.Start()); |
| |
| scoped_refptr<HttpBridge> http_bridge(BuildBridge()); |
| |
| GURL echo_header = test_server_.GetURL("echoall"); |
| |
| http_bridge->SetURL(echo_header.spec().c_str(), echo_header.IntPort()); |
| http_bridge->SetExtraRequestHeaders("test:fnord"); |
| |
| std::string test_payload = "###TEST PAYLOAD###"; |
| http_bridge->SetPostPayload("text/html", test_payload.length() + 1, |
| test_payload.c_str()); |
| |
| int os_error = 0; |
| int response_code = 0; |
| bool success = http_bridge->MakeSynchronousPost(&os_error, &response_code); |
| EXPECT_TRUE(success); |
| EXPECT_EQ(200, response_code); |
| EXPECT_EQ(0, os_error); |
| |
| std::string response(http_bridge->GetResponseContent(), |
| http_bridge->GetResponseContentLength()); |
| |
| EXPECT_NE(std::string::npos, response.find("fnord")); |
| EXPECT_NE(std::string::npos, response.find(test_payload.c_str())); |
| } |
| |
| TEST_F(HttpBridgeTest, TestResponseHeader) { |
| ASSERT_TRUE(test_server_.Start()); |
| |
| scoped_refptr<HttpBridge> http_bridge(BuildBridge()); |
| |
| GURL echo_header = test_server_.GetURL("echoall"); |
| http_bridge->SetURL(echo_header.spec().c_str(), echo_header.IntPort()); |
| |
| std::string test_payload = "###TEST PAYLOAD###"; |
| http_bridge->SetPostPayload("text/html", test_payload.length() + 1, |
| test_payload.c_str()); |
| |
| int os_error = 0; |
| int response_code = 0; |
| bool success = http_bridge->MakeSynchronousPost(&os_error, &response_code); |
| EXPECT_TRUE(success); |
| EXPECT_EQ(200, response_code); |
| EXPECT_EQ(0, os_error); |
| |
| EXPECT_EQ(http_bridge->GetResponseHeaderValue("Content-type"), "text/html"); |
| EXPECT_TRUE(http_bridge->GetResponseHeaderValue("invalid-header").empty()); |
| } |
| |
| TEST_F(HttpBridgeTest, Abort) { |
| scoped_refptr<net::URLRequestContextGetter> ctx_getter( |
| new TestURLRequestContextGetter()); |
| scoped_refptr<ShuntedHttpBridge> http_bridge(new ShuntedHttpBridge( |
| ctx_getter, this, true)); |
| http_bridge->SetUserAgent("bob"); |
| http_bridge->SetURL("http://www.google.com", 9999); |
| http_bridge->SetPostPayload("text/plain", 2, " "); |
| |
| int os_error = 0; |
| int response_code = 0; |
| |
| BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, NewRunnableFunction( |
| &HttpBridgeTest::Abort, http_bridge)); |
| bool success = http_bridge->MakeSynchronousPost(&os_error, &response_code); |
| EXPECT_FALSE(success); |
| EXPECT_EQ(net::ERR_ABORTED, os_error); |
| } |
| |
| TEST_F(HttpBridgeTest, AbortLate) { |
| scoped_refptr<net::URLRequestContextGetter> ctx_getter( |
| new TestURLRequestContextGetter()); |
| scoped_refptr<ShuntedHttpBridge> http_bridge(new ShuntedHttpBridge( |
| ctx_getter, this, false)); |
| http_bridge->SetUserAgent("bob"); |
| http_bridge->SetURL("http://www.google.com", 9999); |
| http_bridge->SetPostPayload("text/plain", 2, " "); |
| |
| int os_error = 0; |
| int response_code = 0; |
| |
| bool success = http_bridge->MakeSynchronousPost(&os_error, &response_code); |
| ASSERT_TRUE(success); |
| http_bridge->Abort(); |
| // Ensures no double-free of URLFetcher, etc. |
| } |