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:
SyncHttpBridgeTest()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
SetUp()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
TearDown()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
BuildBridge()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
Abort(HttpBridge * bridge)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
TestSameHttpNetworkSession(base::MessageLoop * main_message_loop,SyncHttpBridgeTest * test)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
GetIOThreadLoop()87 base::MessageLoop* GetIOThreadLoop() { return io_thread_.message_loop(); }
88
89 // Note this is lazy created, so don't call this before your bridge.
GetTestRequestContextGetter()90 net::TestURLRequestContextGetter* GetTestRequestContextGetter() {
91 return fake_default_request_context_getter_;
92 }
93
94 net::SpawnedTestServer test_server_;
95
io_thread()96 base::Thread* io_thread() { return &io_thread_; }
97
bridge_for_race_test()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.
ShuntedHttpBridge(net::URLRequestContextGetter * baseline_context_getter,SyncHttpBridgeTest * test,bool never_finishes)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:
MakeAsynchronousPost()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:
~ShuntedHttpBridge()139 virtual ~ShuntedHttpBridge() {}
140
CallOnURLFetchComplete()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
RunSyncThreadBridgeUseTest(base::WaitableEvent * signal_when_created,base::WaitableEvent * signal_when_released)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
TEST_F(SyncHttpBridgeTest,TestUsesSameHttpNetworkSession)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.
TEST_F(SyncHttpBridgeTest,TestMakeSynchronousPostShunted)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.
TEST_F(SyncHttpBridgeTest,TestMakeSynchronousPostLiveWithPayload)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.
TEST_F(SyncHttpBridgeTest,TestMakeSynchronousPostLiveComprehensive)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
TEST_F(SyncHttpBridgeTest,TestExtraRequestHeaders)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
TEST_F(SyncHttpBridgeTest,TestResponseHeader)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
TEST_F(SyncHttpBridgeTest,Abort)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
TEST_F(SyncHttpBridgeTest,AbortLate)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).
TEST_F(SyncHttpBridgeTest,AbortAndReleaseBeforeFetchComplete)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
HttpBridgeRunOnSyncThread(net::URLRequestContextGetter * baseline_context_getter,CancelationSignal * factory_cancelation_signal,syncer::HttpPostProviderFactory ** bridge_factory_out,syncer::HttpPostProviderInterface ** bridge_out,base::WaitableEvent * signal_when_created,base::WaitableEvent * wait_for_shutdown)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
WaitOnIOThread(base::WaitableEvent * signal_wait_start,base::WaitableEvent * wait_done)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.
TEST_F(SyncHttpBridgeTest,RequestContextGetterReleaseOrder)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.
TEST_F(SyncHttpBridgeTest,EarlyAbortFactory)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