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