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.
5 #include "base/synchronization/waitable_event.h"
6 #include "base/threading/thread.h"
7 #include "net/test/spawned_test_server/spawned_test_server.h"
8 #include "net/url_request/test_url_fetcher_factory.h"
9 #include "net/url_request/url_fetcher_delegate.h"
10 #include "net/url_request/url_request_test_util.h"
11 #include "sync/internal_api/public/base/cancelation_signal.h"
12 #include "sync/internal_api/public/http_bridge.h"
13 #include "testing/gtest/include/gtest/gtest.h"
18 // TODO(timsteele): Should use PathService here. See Chromium Issue 3113.
19 const base::FilePath::CharType kDocRoot
[] =
20 FILE_PATH_LITERAL("chrome/test/data");
23 class SyncHttpBridgeTest
: public testing::Test
{
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") {
34 void SetUp() override
{
35 base::Thread::Options options
;
36 options
.message_loop_type
= base::MessageLoop::TYPE_IO
;
37 io_thread_
.StartWithOptions(options
);
40 void TearDown() override
{
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
;
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();
55 HttpBridge
* bridge
= new HttpBridge(
56 new HttpBridge::RequestContextGetter(
57 fake_default_request_context_getter_
,
59 NetworkTimeUpdateCallback());
63 static void Abort(HttpBridge
* bridge
) {
67 // Used by AbortAndReleaseBeforeFetchCompletes to test an interesting race
69 void RunSyncThreadBridgeUseTest(base::WaitableEvent
* signal_when_created
,
70 base::WaitableEvent
* signal_when_released
);
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());
86 base::MessageLoop
* GetIOThreadLoop() { return io_thread_
.message_loop(); }
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_
;
93 net::SpawnedTestServer test_server_
;
95 base::Thread
* io_thread() { return &io_thread_
; }
97 HttpBridge
* bridge_for_race_test() { return bridge_for_race_test_
; }
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_
;
104 HttpBridge
* bridge_for_race_test_
;
106 // Separate thread for IO used by the HttpBridge.
107 base::Thread io_thread_
;
108 base::MessageLoop loop_
;
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
{
117 // If |never_finishes| is true, the simulated request never actually
119 ShuntedHttpBridge(net::URLRequestContextGetter
* baseline_context_getter
,
120 SyncHttpBridgeTest
* test
, bool never_finishes
)
122 new HttpBridge::RequestContextGetter(
123 baseline_context_getter
, "user agent"),
124 NetworkTimeUpdateCallback()),
125 test_(test
), never_finishes_(never_finishes
) { }
127 void MakeAsynchronousPost() override
{
128 ASSERT_TRUE(base::MessageLoop::current() == test_
->GetIOThreadLoop());
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));
138 ~ShuntedHttpBridge() override
{}
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
;
145 std::string response_content
= "success!";
146 net::TestURLFetcher
fetcher(0, GURL("http://www.google.com"), NULL
);
147 fetcher
.set_response_code(200);
148 fetcher
.set_cookies(cookies
);
149 fetcher
.SetResponseString(response_content
);
150 OnURLFetchComplete(&fetcher
);
152 SyncHttpBridgeTest
* test_
;
153 bool never_finishes_
;
156 void SyncHttpBridgeTest::RunSyncThreadBridgeUseTest(
157 base::WaitableEvent
* signal_when_created
,
158 base::WaitableEvent
* signal_when_released
) {
159 scoped_refptr
<net::URLRequestContextGetter
> ctx_getter(
160 new net::TestURLRequestContextGetter(io_thread_
.message_loop_proxy()));
162 scoped_refptr
<ShuntedHttpBridge
> bridge(
163 new ShuntedHttpBridge(ctx_getter
.get(), this, true));
164 bridge
->SetURL("http://www.google.com", 9999);
165 bridge
->SetPostPayload("text/plain", 2, " ");
166 bridge_for_race_test_
= bridge
.get();
167 signal_when_created
->Signal();
170 int response_code
= 0;
171 bridge
->MakeSynchronousPost(&os_error
, &response_code
);
172 bridge_for_race_test_
= NULL
;
174 signal_when_released
->Signal();
177 TEST_F(SyncHttpBridgeTest
, TestUsesSameHttpNetworkSession
) {
178 // Run this test on the IO thread because we can only call
179 // URLRequestContextGetter::GetURLRequestContext on the IO thread.
180 io_thread()->message_loop()
181 ->PostTask(FROM_HERE
,
182 base::Bind(&SyncHttpBridgeTest::TestSameHttpNetworkSession
,
183 base::MessageLoop::current(),
185 base::MessageLoop::current()->Run();
188 // Test the HttpBridge without actually making any network requests.
189 TEST_F(SyncHttpBridgeTest
, TestMakeSynchronousPostShunted
) {
190 scoped_refptr
<net::URLRequestContextGetter
> ctx_getter(
191 new net::TestURLRequestContextGetter(io_thread()->message_loop_proxy()));
192 scoped_refptr
<HttpBridge
> http_bridge(
193 new ShuntedHttpBridge(ctx_getter
.get(), this, false));
194 http_bridge
->SetURL("http://www.google.com", 9999);
195 http_bridge
->SetPostPayload("text/plain", 2, " ");
198 int response_code
= 0;
199 bool success
= http_bridge
->MakeSynchronousPost(&os_error
, &response_code
);
200 EXPECT_TRUE(success
);
201 EXPECT_EQ(200, response_code
);
202 EXPECT_EQ(0, os_error
);
204 EXPECT_EQ(8, http_bridge
->GetResponseContentLength());
205 EXPECT_EQ(std::string("success!"),
206 std::string(http_bridge
->GetResponseContent()));
209 // Full round-trip test of the HttpBridge, using default UA string and
210 // no request cookies.
211 TEST_F(SyncHttpBridgeTest
, TestMakeSynchronousPostLiveWithPayload
) {
212 ASSERT_TRUE(test_server_
.Start());
214 scoped_refptr
<HttpBridge
> http_bridge(BuildBridge());
216 std::string payload
= "this should be echoed back";
217 GURL echo
= test_server_
.GetURL("echo");
218 http_bridge
->SetURL(echo
.spec().c_str(), echo
.IntPort());
219 http_bridge
->SetPostPayload("application/x-www-form-urlencoded",
220 payload
.length() + 1, payload
.c_str());
222 int response_code
= 0;
223 bool success
= http_bridge
->MakeSynchronousPost(&os_error
, &response_code
);
224 EXPECT_TRUE(success
);
225 EXPECT_EQ(200, response_code
);
226 EXPECT_EQ(0, os_error
);
228 EXPECT_EQ(payload
.length() + 1,
229 static_cast<size_t>(http_bridge
->GetResponseContentLength()));
230 EXPECT_EQ(payload
, std::string(http_bridge
->GetResponseContent()));
233 // Full round-trip test of the HttpBridge.
234 TEST_F(SyncHttpBridgeTest
, TestMakeSynchronousPostLiveComprehensive
) {
235 ASSERT_TRUE(test_server_
.Start());
237 scoped_refptr
<HttpBridge
> http_bridge(BuildBridge());
239 GURL echo_header
= test_server_
.GetURL("echoall");
240 http_bridge
->SetURL(echo_header
.spec().c_str(), echo_header
.IntPort());
242 std::string test_payload
= "###TEST PAYLOAD###";
243 http_bridge
->SetPostPayload("text/html", test_payload
.length() + 1,
244 test_payload
.c_str());
247 int response_code
= 0;
248 bool success
= http_bridge
->MakeSynchronousPost(&os_error
, &response_code
);
249 EXPECT_TRUE(success
);
250 EXPECT_EQ(200, response_code
);
251 EXPECT_EQ(0, os_error
);
253 std::string
response(http_bridge
->GetResponseContent(),
254 http_bridge
->GetResponseContentLength());
255 EXPECT_EQ(std::string::npos
, response
.find("Cookie:"));
256 EXPECT_NE(std::string::npos
, response
.find("User-Agent: user agent"));
257 EXPECT_NE(std::string::npos
, response
.find(test_payload
.c_str()));
260 TEST_F(SyncHttpBridgeTest
, TestExtraRequestHeaders
) {
261 ASSERT_TRUE(test_server_
.Start());
263 scoped_refptr
<HttpBridge
> http_bridge(BuildBridge());
265 GURL echo_header
= test_server_
.GetURL("echoall");
267 http_bridge
->SetURL(echo_header
.spec().c_str(), echo_header
.IntPort());
268 http_bridge
->SetExtraRequestHeaders("test:fnord");
270 std::string test_payload
= "###TEST PAYLOAD###";
271 http_bridge
->SetPostPayload("text/html", test_payload
.length() + 1,
272 test_payload
.c_str());
275 int response_code
= 0;
276 bool success
= http_bridge
->MakeSynchronousPost(&os_error
, &response_code
);
277 EXPECT_TRUE(success
);
278 EXPECT_EQ(200, response_code
);
279 EXPECT_EQ(0, os_error
);
281 std::string
response(http_bridge
->GetResponseContent(),
282 http_bridge
->GetResponseContentLength());
284 EXPECT_NE(std::string::npos
, response
.find("fnord"));
285 EXPECT_NE(std::string::npos
, response
.find(test_payload
.c_str()));
288 TEST_F(SyncHttpBridgeTest
, TestResponseHeader
) {
289 ASSERT_TRUE(test_server_
.Start());
291 scoped_refptr
<HttpBridge
> http_bridge(BuildBridge());
293 GURL echo_header
= test_server_
.GetURL("echoall");
294 http_bridge
->SetURL(echo_header
.spec().c_str(), echo_header
.IntPort());
296 std::string test_payload
= "###TEST PAYLOAD###";
297 http_bridge
->SetPostPayload("text/html", test_payload
.length() + 1,
298 test_payload
.c_str());
301 int response_code
= 0;
302 bool success
= http_bridge
->MakeSynchronousPost(&os_error
, &response_code
);
303 EXPECT_TRUE(success
);
304 EXPECT_EQ(200, response_code
);
305 EXPECT_EQ(0, os_error
);
307 EXPECT_EQ(http_bridge
->GetResponseHeaderValue("Content-type"), "text/html");
308 EXPECT_TRUE(http_bridge
->GetResponseHeaderValue("invalid-header").empty());
311 TEST_F(SyncHttpBridgeTest
, Abort
) {
312 scoped_refptr
<net::URLRequestContextGetter
> ctx_getter(
313 new net::TestURLRequestContextGetter(io_thread()->message_loop_proxy()));
314 scoped_refptr
<ShuntedHttpBridge
> http_bridge(
315 new ShuntedHttpBridge(ctx_getter
.get(), this, true));
316 http_bridge
->SetURL("http://www.google.com", 9999);
317 http_bridge
->SetPostPayload("text/plain", 2, " ");
320 int response_code
= 0;
322 io_thread()->message_loop_proxy()->PostTask(
324 base::Bind(&SyncHttpBridgeTest::Abort
, http_bridge
));
325 bool success
= http_bridge
->MakeSynchronousPost(&os_error
, &response_code
);
326 EXPECT_FALSE(success
);
327 EXPECT_EQ(net::ERR_ABORTED
, os_error
);
330 TEST_F(SyncHttpBridgeTest
, AbortLate
) {
331 scoped_refptr
<net::URLRequestContextGetter
> ctx_getter(
332 new net::TestURLRequestContextGetter(io_thread()->message_loop_proxy()));
333 scoped_refptr
<ShuntedHttpBridge
> http_bridge(
334 new ShuntedHttpBridge(ctx_getter
.get(), this, false));
335 http_bridge
->SetURL("http://www.google.com", 9999);
336 http_bridge
->SetPostPayload("text/plain", 2, " ");
339 int response_code
= 0;
341 bool success
= http_bridge
->MakeSynchronousPost(&os_error
, &response_code
);
342 ASSERT_TRUE(success
);
343 http_bridge
->Abort();
344 // Ensures no double-free of URLFetcher, etc.
347 // Tests an interesting case where code using the HttpBridge aborts the fetch
348 // and releases ownership before a pending fetch completed callback is issued by
349 // the underlying URLFetcher (and before that URLFetcher is destroyed, which
350 // would cancel the callback).
351 TEST_F(SyncHttpBridgeTest
, AbortAndReleaseBeforeFetchComplete
) {
352 base::Thread
sync_thread("SyncThread");
355 // First, block the sync thread on the post.
356 base::WaitableEvent
signal_when_created(false, false);
357 base::WaitableEvent
signal_when_released(false, false);
358 sync_thread
.message_loop()->PostTask(FROM_HERE
,
359 base::Bind(&SyncHttpBridgeTest::RunSyncThreadBridgeUseTest
,
360 base::Unretained(this),
361 &signal_when_created
,
362 &signal_when_released
));
364 // Stop IO so we can control order of operations.
365 base::WaitableEvent
io_waiter(false, false);
366 ASSERT_TRUE(io_thread()->message_loop_proxy()->PostTask(
368 base::Bind(&base::WaitableEvent::Wait
, base::Unretained(&io_waiter
))));
370 signal_when_created
.Wait(); // Wait till we have a bridge to abort.
371 ASSERT_TRUE(bridge_for_race_test());
373 // Schedule the fetch completion callback (but don't run it yet). Don't take
374 // a reference to the bridge to mimic URLFetcher's handling of the delegate.
375 net::URLFetcherDelegate
* delegate
=
376 static_cast<net::URLFetcherDelegate
*>(bridge_for_race_test());
377 net::ResponseCookies cookies
;
378 std::string response_content
= "success!";
379 net::TestURLFetcher
fetcher(0, GURL("http://www.google.com"), NULL
);
380 fetcher
.set_response_code(200);
381 fetcher
.set_cookies(cookies
);
382 fetcher
.SetResponseString(response_content
);
383 ASSERT_TRUE(io_thread()->message_loop_proxy()->PostTask(
385 base::Bind(&net::URLFetcherDelegate::OnURLFetchComplete
,
386 base::Unretained(delegate
), &fetcher
)));
388 // Abort the fetch. This should be smart enough to handle the case where
389 // the bridge is destroyed before the callback scheduled above completes.
390 bridge_for_race_test()->Abort();
392 // Wait until the sync thread releases its ref on the bridge.
393 signal_when_released
.Wait();
394 ASSERT_FALSE(bridge_for_race_test());
396 // Unleash the hounds. The fetch completion callback should fire first, and
397 // succeed even though we Release()d the bridge above because the call to
398 // Abort should have held a reference.
406 void HttpBridgeRunOnSyncThread(
407 net::URLRequestContextGetter
* baseline_context_getter
,
408 CancelationSignal
* factory_cancelation_signal
,
409 syncer::HttpPostProviderFactory
** bridge_factory_out
,
410 syncer::HttpPostProviderInterface
** bridge_out
,
411 base::WaitableEvent
* signal_when_created
,
412 base::WaitableEvent
* wait_for_shutdown
) {
413 scoped_ptr
<syncer::HttpBridgeFactory
> bridge_factory(
414 new syncer::HttpBridgeFactory(baseline_context_getter
,
415 NetworkTimeUpdateCallback(),
416 factory_cancelation_signal
));
417 bridge_factory
->Init("test");
418 *bridge_factory_out
= bridge_factory
.get();
420 HttpPostProviderInterface
* bridge
= bridge_factory
->Create();
421 *bridge_out
= bridge
;
423 signal_when_created
->Signal();
424 wait_for_shutdown
->Wait();
426 bridge_factory
->Destroy(bridge
);
429 void WaitOnIOThread(base::WaitableEvent
* signal_wait_start
,
430 base::WaitableEvent
* wait_done
) {
431 signal_wait_start
->Signal();
435 // Tests RequestContextGetter is properly released on IO thread even when
436 // IO thread stops before sync thread.
437 TEST_F(SyncHttpBridgeTest
, RequestContextGetterReleaseOrder
) {
438 base::Thread
sync_thread("SyncThread");
441 syncer::HttpPostProviderFactory
* factory
= NULL
;
442 syncer::HttpPostProviderInterface
* bridge
= NULL
;
444 scoped_refptr
<net::URLRequestContextGetter
> baseline_context_getter(
445 new net::TestURLRequestContextGetter(io_thread()->message_loop_proxy()));
447 base::WaitableEvent
signal_when_created(false, false);
448 base::WaitableEvent
wait_for_shutdown(false, false);
450 CancelationSignal release_request_context_signal
;
452 // Create bridge factory and factory on sync thread and wait for the creation
454 sync_thread
.message_loop()->PostTask(FROM_HERE
,
455 base::Bind(&HttpBridgeRunOnSyncThread
,
456 base::Unretained(baseline_context_getter
.get()),
457 &release_request_context_signal
,&factory
, &bridge
,
458 &signal_when_created
, &wait_for_shutdown
));
459 signal_when_created
.Wait();
461 // Simulate sync shutdown by aborting bridge and shutting down factory on
464 release_request_context_signal
.Signal();
466 // Wait for sync's RequestContextGetter to be cleared on IO thread and
467 // check for reference count.
468 base::WaitableEvent
signal_wait_start(false, false);
469 base::WaitableEvent
wait_done(false, false);
470 io_thread()->message_loop()->PostTask(
472 base::Bind(&WaitOnIOThread
, &signal_wait_start
, &wait_done
));
473 signal_wait_start
.Wait();
474 // |baseline_context_getter| should have only one reference from local
476 EXPECT_TRUE(baseline_context_getter
->HasOneRef());
477 baseline_context_getter
= NULL
;
479 // Unblock and stop IO thread before sync thread.
483 // Unblock and stop sync thread.
484 wait_for_shutdown
.Signal();
488 // Attempt to release the URLRequestContextGetter before the HttpBridgeFactory
490 TEST_F(SyncHttpBridgeTest
, EarlyAbortFactory
) {
491 // In a real scenario, the following would happen on many threads. For
492 // simplicity, this test uses only one thread.
494 scoped_refptr
<net::URLRequestContextGetter
> baseline_context_getter(
495 new net::TestURLRequestContextGetter(io_thread()->message_loop_proxy()));
496 CancelationSignal release_request_context_signal
;
498 // UI Thread: Initialize the HttpBridgeFactory. The next step would be to
499 // post a task to SBH::Core to have it initialized.
500 scoped_ptr
<syncer::HttpBridgeFactory
> factory(
501 new HttpBridgeFactory(baseline_context_getter
.get(),
502 NetworkTimeUpdateCallback(),
503 &release_request_context_signal
));
505 // UI Thread: A very early shutdown request arrives and executes on the UI
506 // thread before the posted sync thread task is run.
507 release_request_context_signal
.Signal();
509 // Sync thread: Finally run the posted task, only to find that our
510 // HttpBridgeFactory has been neutered. Should not crash.
511 factory
->Init("TestUserAgent");
513 // At this point, attempting to use the factory would trigger a crash. Both
514 // this test and the real world code should make sure this never happens.
517 } // namespace syncer