1// Copyright (c) 2011 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_proxy.h"
6#include "base/threading/thread.h"
7#include "chrome/browser/sync/glue/http_bridge.h"
8#include "chrome/common/net/test_url_fetcher_factory.h"
9#include "chrome/test/test_url_request_context_getter.h"
10#include "content/browser/browser_thread.h"
11#include "net/url_request/url_request_test_util.h"
12#include "net/test/test_server.h"
13#include "testing/gtest/include/gtest/gtest.h"
14
15using browser_sync::HttpBridge;
16
17namespace {
18// TODO(timsteele): Should use PathService here. See Chromium Issue 3113.
19const FilePath::CharType kDocRoot[] = FILE_PATH_LITERAL("chrome/test/data");
20}
21
22class HttpBridgeTest : public testing::Test {
23 public:
24  HttpBridgeTest()
25      : test_server_(net::TestServer::TYPE_HTTP, FilePath(kDocRoot)),
26        fake_default_request_context_getter_(NULL),
27        io_thread_(BrowserThread::IO) {
28  }
29
30  virtual void SetUp() {
31    base::Thread::Options options;
32    options.message_loop_type = MessageLoop::TYPE_IO;
33    io_thread_.StartWithOptions(options);
34  }
35
36  virtual void TearDown() {
37    io_thread_loop()->ReleaseSoon(FROM_HERE,
38        fake_default_request_context_getter_);
39    io_thread_.Stop();
40    fake_default_request_context_getter_ = NULL;
41  }
42
43  HttpBridge* BuildBridge() {
44    if (!fake_default_request_context_getter_) {
45      fake_default_request_context_getter_ = new TestURLRequestContextGetter();
46      fake_default_request_context_getter_->AddRef();
47    }
48    HttpBridge* bridge = new HttpBridge(
49        new HttpBridge::RequestContextGetter(
50            fake_default_request_context_getter_));
51    return bridge;
52  }
53
54  static void Abort(HttpBridge* bridge) {
55    bridge->Abort();
56  }
57
58  static void TestSameHttpNetworkSession(MessageLoop* main_message_loop,
59                                         HttpBridgeTest* test) {
60    scoped_refptr<HttpBridge> http_bridge(test->BuildBridge());
61    EXPECT_TRUE(test->GetTestRequestContextGetter());
62    net::HttpNetworkSession* test_session =
63        test->GetTestRequestContextGetter()->GetURLRequestContext()->
64        http_transaction_factory()->GetSession();
65    EXPECT_EQ(test_session,
66              http_bridge->GetRequestContextGetter()->
67                  GetURLRequestContext()->
68                  http_transaction_factory()->GetSession());
69    main_message_loop->PostTask(FROM_HERE, new MessageLoop::QuitTask);
70  }
71
72  MessageLoop* io_thread_loop() { return io_thread_.message_loop(); }
73
74  // Note this is lazy created, so don't call this before your bridge.
75  TestURLRequestContextGetter* GetTestRequestContextGetter() {
76    return fake_default_request_context_getter_;
77  }
78
79  net::TestServer test_server_;
80
81 private:
82  // A make-believe "default" request context, as would be returned by
83  // Profile::GetDefaultRequestContext().  Created lazily by BuildBridge.
84  TestURLRequestContextGetter* fake_default_request_context_getter_;
85
86  // Separate thread for IO used by the HttpBridge.
87  BrowserThread io_thread_;
88  MessageLoop loop_;
89};
90
91class DummyURLFetcher : public TestURLFetcher {
92 public:
93  DummyURLFetcher() : TestURLFetcher(0, GURL(), POST, NULL) {}
94
95  net::HttpResponseHeaders* response_headers() const {
96    return NULL;
97  }
98};
99
100// An HttpBridge that doesn't actually make network requests and just calls
101// back with dummy response info.
102class ShuntedHttpBridge : public HttpBridge {
103 public:
104  // If |never_finishes| is true, the simulated request never actually
105  // returns.
106  ShuntedHttpBridge(net::URLRequestContextGetter* baseline_context_getter,
107                    HttpBridgeTest* test, bool never_finishes)
108      : HttpBridge(new HttpBridge::RequestContextGetter(
109                       baseline_context_getter)),
110                   test_(test), never_finishes_(never_finishes) { }
111 protected:
112  virtual void MakeAsynchronousPost() {
113    ASSERT_TRUE(MessageLoop::current() == test_->io_thread_loop());
114    if (never_finishes_)
115      return;
116
117    // We don't actually want to make a request for this test, so just callback
118    // as if it completed.
119    test_->io_thread_loop()->PostTask(FROM_HERE,
120        NewRunnableMethod(this, &ShuntedHttpBridge::CallOnURLFetchComplete));
121  }
122 private:
123  ~ShuntedHttpBridge() {}
124
125  void CallOnURLFetchComplete() {
126    ASSERT_TRUE(MessageLoop::current() == test_->io_thread_loop());
127    // We return no cookies and a dummy content response.
128    ResponseCookies cookies;
129
130    std::string response_content = "success!";
131    DummyURLFetcher fetcher;
132    OnURLFetchComplete(&fetcher, GURL("www.google.com"),
133                       net::URLRequestStatus(),
134                       200, cookies, response_content);
135  }
136  HttpBridgeTest* test_;
137  bool never_finishes_;
138};
139
140TEST_F(HttpBridgeTest, TestUsesSameHttpNetworkSession) {
141  // Run this test on the IO thread because we can only call
142  // URLRequestContextGetter::GetURLRequestContext on the IO thread.
143  BrowserThread::PostTask(
144      BrowserThread::IO, FROM_HERE,
145      NewRunnableFunction(&HttpBridgeTest::TestSameHttpNetworkSession,
146                          MessageLoop::current(), this));
147  MessageLoop::current()->Run();
148}
149
150// Test the HttpBridge without actually making any network requests.
151TEST_F(HttpBridgeTest, TestMakeSynchronousPostShunted) {
152  scoped_refptr<net::URLRequestContextGetter> ctx_getter(
153      new TestURLRequestContextGetter());
154  scoped_refptr<HttpBridge> http_bridge(new ShuntedHttpBridge(
155      ctx_getter, this, false));
156  http_bridge->SetUserAgent("bob");
157  http_bridge->SetURL("http://www.google.com", 9999);
158  http_bridge->SetPostPayload("text/plain", 2, " ");
159
160  int os_error = 0;
161  int response_code = 0;
162  bool success = http_bridge->MakeSynchronousPost(&os_error, &response_code);
163  EXPECT_TRUE(success);
164  EXPECT_EQ(200, response_code);
165  EXPECT_EQ(0, os_error);
166
167  EXPECT_EQ(8, http_bridge->GetResponseContentLength());
168  EXPECT_EQ(std::string("success!"),
169            std::string(http_bridge->GetResponseContent()));
170}
171
172// Full round-trip test of the HttpBridge, using default UA string and
173// no request cookies.
174TEST_F(HttpBridgeTest, TestMakeSynchronousPostLiveWithPayload) {
175  ASSERT_TRUE(test_server_.Start());
176
177  scoped_refptr<HttpBridge> http_bridge(BuildBridge());
178
179  std::string payload = "this should be echoed back";
180  GURL echo = test_server_.GetURL("echo");
181  http_bridge->SetURL(echo.spec().c_str(), echo.IntPort());
182  http_bridge->SetPostPayload("application/x-www-form-urlencoded",
183                              payload.length() + 1, payload.c_str());
184  int os_error = 0;
185  int response_code = 0;
186  bool success = http_bridge->MakeSynchronousPost(&os_error, &response_code);
187  EXPECT_TRUE(success);
188  EXPECT_EQ(200, response_code);
189  EXPECT_EQ(0, os_error);
190
191  EXPECT_EQ(payload.length() + 1,
192            static_cast<size_t>(http_bridge->GetResponseContentLength()));
193  EXPECT_EQ(payload, std::string(http_bridge->GetResponseContent()));
194}
195
196// Full round-trip test of the HttpBridge, using custom UA string
197TEST_F(HttpBridgeTest, TestMakeSynchronousPostLiveComprehensive) {
198  ASSERT_TRUE(test_server_.Start());
199
200  scoped_refptr<HttpBridge> http_bridge(BuildBridge());
201
202  GURL echo_header = test_server_.GetURL("echoall");
203  http_bridge->SetUserAgent("bob");
204  http_bridge->SetURL(echo_header.spec().c_str(), echo_header.IntPort());
205
206  std::string test_payload = "###TEST PAYLOAD###";
207  http_bridge->SetPostPayload("text/html", test_payload.length() + 1,
208                              test_payload.c_str());
209
210  int os_error = 0;
211  int response_code = 0;
212  bool success = http_bridge->MakeSynchronousPost(&os_error, &response_code);
213  EXPECT_TRUE(success);
214  EXPECT_EQ(200, response_code);
215  EXPECT_EQ(0, os_error);
216
217  std::string response(http_bridge->GetResponseContent(),
218                       http_bridge->GetResponseContentLength());
219  EXPECT_EQ(std::string::npos, response.find("Cookie:"));
220  EXPECT_NE(std::string::npos, response.find("User-Agent: bob"));
221  EXPECT_NE(std::string::npos, response.find(test_payload.c_str()));
222}
223
224TEST_F(HttpBridgeTest, TestExtraRequestHeaders) {
225  ASSERT_TRUE(test_server_.Start());
226
227  scoped_refptr<HttpBridge> http_bridge(BuildBridge());
228
229  GURL echo_header = test_server_.GetURL("echoall");
230
231  http_bridge->SetURL(echo_header.spec().c_str(), echo_header.IntPort());
232  http_bridge->SetExtraRequestHeaders("test:fnord");
233
234  std::string test_payload = "###TEST PAYLOAD###";
235  http_bridge->SetPostPayload("text/html", test_payload.length() + 1,
236                              test_payload.c_str());
237
238  int os_error = 0;
239  int response_code = 0;
240  bool success = http_bridge->MakeSynchronousPost(&os_error, &response_code);
241  EXPECT_TRUE(success);
242  EXPECT_EQ(200, response_code);
243  EXPECT_EQ(0, os_error);
244
245  std::string response(http_bridge->GetResponseContent(),
246                       http_bridge->GetResponseContentLength());
247
248  EXPECT_NE(std::string::npos, response.find("fnord"));
249  EXPECT_NE(std::string::npos, response.find(test_payload.c_str()));
250}
251
252TEST_F(HttpBridgeTest, TestResponseHeader) {
253  ASSERT_TRUE(test_server_.Start());
254
255  scoped_refptr<HttpBridge> http_bridge(BuildBridge());
256
257  GURL echo_header = test_server_.GetURL("echoall");
258  http_bridge->SetURL(echo_header.spec().c_str(), echo_header.IntPort());
259
260  std::string test_payload = "###TEST PAYLOAD###";
261  http_bridge->SetPostPayload("text/html", test_payload.length() + 1,
262                              test_payload.c_str());
263
264  int os_error = 0;
265  int response_code = 0;
266  bool success = http_bridge->MakeSynchronousPost(&os_error, &response_code);
267  EXPECT_TRUE(success);
268  EXPECT_EQ(200, response_code);
269  EXPECT_EQ(0, os_error);
270
271  EXPECT_EQ(http_bridge->GetResponseHeaderValue("Content-type"), "text/html");
272  EXPECT_TRUE(http_bridge->GetResponseHeaderValue("invalid-header").empty());
273}
274
275TEST_F(HttpBridgeTest, Abort) {
276  scoped_refptr<net::URLRequestContextGetter> ctx_getter(
277      new TestURLRequestContextGetter());
278  scoped_refptr<ShuntedHttpBridge> http_bridge(new ShuntedHttpBridge(
279      ctx_getter, this, true));
280  http_bridge->SetUserAgent("bob");
281  http_bridge->SetURL("http://www.google.com", 9999);
282  http_bridge->SetPostPayload("text/plain", 2, " ");
283
284  int os_error = 0;
285  int response_code = 0;
286
287  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, NewRunnableFunction(
288                          &HttpBridgeTest::Abort, http_bridge));
289  bool success = http_bridge->MakeSynchronousPost(&os_error, &response_code);
290  EXPECT_FALSE(success);
291  EXPECT_EQ(net::ERR_ABORTED, os_error);
292}
293
294TEST_F(HttpBridgeTest, AbortLate) {
295  scoped_refptr<net::URLRequestContextGetter> ctx_getter(
296      new TestURLRequestContextGetter());
297  scoped_refptr<ShuntedHttpBridge> http_bridge(new ShuntedHttpBridge(
298      ctx_getter, this, false));
299  http_bridge->SetUserAgent("bob");
300  http_bridge->SetURL("http://www.google.com", 9999);
301  http_bridge->SetPostPayload("text/plain", 2, " ");
302
303  int os_error = 0;
304  int response_code = 0;
305
306  bool success = http_bridge->MakeSynchronousPost(&os_error, &response_code);
307  ASSERT_TRUE(success);
308  http_bridge->Abort();
309  // Ensures no double-free of URLFetcher, etc.
310}
311