• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 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/run_loop.h"
6 #include "base/strings/string_util.h"
7 #include "components/invalidation/gcm_network_channel.h"
8 #include "google_apis/gaia/google_service_auth_error.h"
9 #include "net/url_request/test_url_fetcher_factory.h"
10 #include "net/url_request/url_request_test_util.h"
11 #include "testing/gtest/include/gtest/gtest.h"
12 
13 namespace syncer {
14 
15 class TestGCMNetworkChannelDelegate : public GCMNetworkChannelDelegate {
16  public:
TestGCMNetworkChannelDelegate()17   TestGCMNetworkChannelDelegate()
18       : register_call_count_(0) {}
19 
Initialize(GCMNetworkChannelDelegate::ConnectionStateCallback callback)20   virtual void Initialize(
21       GCMNetworkChannelDelegate::ConnectionStateCallback callback) OVERRIDE {
22     connection_state_callback = callback;
23   }
24 
RequestToken(RequestTokenCallback callback)25   virtual void RequestToken(RequestTokenCallback callback) OVERRIDE {
26     request_token_callback = callback;
27   }
28 
InvalidateToken(const std::string & token)29   virtual void InvalidateToken(const std::string& token) OVERRIDE {
30     invalidated_token = token;
31   }
32 
Register(RegisterCallback callback)33   virtual void Register(RegisterCallback callback) OVERRIDE {
34     ++register_call_count_;
35     register_callback = callback;
36   }
37 
SetMessageReceiver(MessageCallback callback)38   virtual void SetMessageReceiver(MessageCallback callback) OVERRIDE {
39     message_callback = callback;
40   }
41 
42   RequestTokenCallback request_token_callback;
43   std::string invalidated_token;
44   RegisterCallback register_callback;
45   int register_call_count_;
46   MessageCallback message_callback;
47   ConnectionStateCallback connection_state_callback;
48 };
49 
50 // Backoff policy for test. Run first 5 retries without delay.
51 const net::BackoffEntry::Policy kTestBackoffPolicy = {
52   // Number of initial errors (in sequence) to ignore before applying
53   // exponential back-off rules.
54   5,
55 
56   // Initial delay for exponential back-off in ms.
57   2000, // 2 seconds.
58 
59   // Factor by which the waiting time will be multiplied.
60   2,
61 
62   // Fuzzing percentage. ex: 10% will spread requests randomly
63   // between 90%-100% of the calculated time.
64   0.2, // 20%.
65 
66   // Maximum amount of time we are willing to delay our request in ms.
67   1000 * 3600 * 4, // 4 hours.
68 
69   // Time to keep an entry from being discarded even when it
70   // has no significant state, -1 to never discard.
71   -1,
72 
73   // Don't use initial delay unless the last request was an error.
74   false,
75 };
76 
77 class TestGCMNetworkChannel : public GCMNetworkChannel {
78  public:
TestGCMNetworkChannel(scoped_refptr<net::URLRequestContextGetter> request_context_getter,scoped_ptr<GCMNetworkChannelDelegate> delegate)79   TestGCMNetworkChannel(
80       scoped_refptr<net::URLRequestContextGetter> request_context_getter,
81       scoped_ptr<GCMNetworkChannelDelegate> delegate)
82       :  GCMNetworkChannel(request_context_getter, delegate.Pass()) {
83     ResetRegisterBackoffEntryForTest(&kTestBackoffPolicy);
84   }
85 
86  protected:
87   // On Android GCMNetworkChannel::BuildUrl hits NOTREACHED(). I still want
88   // tests to run.
BuildUrl(const std::string & registration_id)89   virtual GURL BuildUrl(const std::string& registration_id) OVERRIDE {
90     return GURL("http://test.url.com");
91   }
92 };
93 
94 class GCMNetworkChannelTest;
95 
96 // Test needs to capture setting echo-token header on http request.
97 // This class is going to do that.
98 class TestNetworkChannelURLFetcher : public net::FakeURLFetcher {
99  public:
TestNetworkChannelURLFetcher(GCMNetworkChannelTest * test,const GURL & url,net::URLFetcherDelegate * delegate,const std::string & response_data,net::HttpStatusCode response_code,net::URLRequestStatus::Status status)100   TestNetworkChannelURLFetcher(GCMNetworkChannelTest* test,
101                                const GURL& url,
102                                net::URLFetcherDelegate* delegate,
103                                const std::string& response_data,
104                                net::HttpStatusCode response_code,
105                                net::URLRequestStatus::Status status)
106       : net::FakeURLFetcher(url,
107                             delegate,
108                             response_data,
109                             response_code,
110                             status),
111         test_(test) {}
112 
113   virtual void AddExtraRequestHeader(const std::string& header_line) OVERRIDE;
114 
115  private:
116   GCMNetworkChannelTest* test_;
117 };
118 
119 class GCMNetworkChannelTest
120     : public ::testing::Test,
121       public SyncNetworkChannel::Observer {
122  public:
GCMNetworkChannelTest()123   GCMNetworkChannelTest()
124       : delegate_(NULL),
125         url_fetchers_created_count_(0),
126         last_invalidator_state_(TRANSIENT_INVALIDATION_ERROR) {}
127 
~GCMNetworkChannelTest()128   virtual ~GCMNetworkChannelTest() {
129   }
130 
SetUp()131   virtual void SetUp() {
132     request_context_getter_ = new net::TestURLRequestContextGetter(
133         base::MessageLoopProxy::current());
134     // Ownership of delegate goes to GCNMentworkChannel but test needs pointer
135     // to it.
136     delegate_ = new TestGCMNetworkChannelDelegate();
137     scoped_ptr<GCMNetworkChannelDelegate> delegate(delegate_);
138     gcm_network_channel_.reset(new TestGCMNetworkChannel(
139         request_context_getter_,
140         delegate.Pass()));
141     gcm_network_channel_->AddObserver(this);
142     gcm_network_channel_->SetMessageReceiver(
143         invalidation::NewPermanentCallback(
144             this, &GCMNetworkChannelTest::OnIncomingMessage));
145     url_fetcher_factory_.reset(new net::FakeURLFetcherFactory(NULL,
146         base::Bind(&GCMNetworkChannelTest::CreateURLFetcher,
147             base::Unretained(this))));
148   }
149 
TearDown()150   virtual void TearDown() {
151     gcm_network_channel_->RemoveObserver(this);
152   }
153 
154   // Helper functions to call private methods from test
BuildUrl(const std::string & registration_id)155   GURL BuildUrl(const std::string& registration_id) {
156     return gcm_network_channel_->GCMNetworkChannel::BuildUrl(registration_id);
157   }
158 
Base64EncodeURLSafe(const std::string & input,std::string * output)159   static void Base64EncodeURLSafe(const std::string& input,
160                                   std::string* output) {
161     GCMNetworkChannel::Base64EncodeURLSafe(input, output);
162   }
163 
Base64DecodeURLSafe(const std::string & input,std::string * output)164   static bool Base64DecodeURLSafe(const std::string& input,
165                                   std::string* output) {
166     return GCMNetworkChannel::Base64DecodeURLSafe(input, output);
167   }
168 
OnNetworkChannelStateChanged(InvalidatorState invalidator_state)169   virtual void OnNetworkChannelStateChanged(
170       InvalidatorState invalidator_state) OVERRIDE {
171     last_invalidator_state_ = invalidator_state;
172   }
173 
OnIncomingMessage(std::string incoming_message)174   void OnIncomingMessage(std::string incoming_message) {
175   }
176 
network_channel()177   GCMNetworkChannel* network_channel() {
178     return gcm_network_channel_.get();
179   }
180 
delegate()181   TestGCMNetworkChannelDelegate* delegate() {
182     return delegate_;
183   }
184 
url_fetchers_created_count()185   int url_fetchers_created_count() {
186     return url_fetchers_created_count_;
187   }
188 
url_fetcher_factory()189   net::FakeURLFetcherFactory* url_fetcher_factory() {
190     return url_fetcher_factory_.get();
191   }
192 
CreateURLFetcher(const GURL & url,net::URLFetcherDelegate * delegate,const std::string & response_data,net::HttpStatusCode response_code,net::URLRequestStatus::Status status)193   scoped_ptr<net::FakeURLFetcher> CreateURLFetcher(
194       const GURL& url,
195       net::URLFetcherDelegate* delegate,
196       const std::string& response_data,
197       net::HttpStatusCode response_code,
198       net::URLRequestStatus::Status status) {
199     ++url_fetchers_created_count_;
200     return scoped_ptr<net::FakeURLFetcher>(new TestNetworkChannelURLFetcher(
201         this, url, delegate, response_data, response_code, status));
202   }
203 
set_last_echo_token(const std::string & echo_token)204   void set_last_echo_token(const std::string& echo_token) {
205     last_echo_token_ = echo_token;
206   }
207 
get_last_echo_token()208   const std::string& get_last_echo_token() {
209     return last_echo_token_;
210   }
211 
get_last_invalidator_state()212   InvalidatorState get_last_invalidator_state() {
213     return last_invalidator_state_;
214   }
215 
RunLoopUntilIdle()216   void RunLoopUntilIdle() {
217     base::RunLoop run_loop;
218     run_loop.RunUntilIdle();
219   }
220 
221  private:
222   base::MessageLoop message_loop_;
223   TestGCMNetworkChannelDelegate* delegate_;
224   scoped_ptr<GCMNetworkChannel> gcm_network_channel_;
225   scoped_refptr<net::TestURLRequestContextGetter> request_context_getter_;
226   scoped_ptr<net::FakeURLFetcherFactory> url_fetcher_factory_;
227   int url_fetchers_created_count_;
228   std::string last_echo_token_;
229   InvalidatorState last_invalidator_state_;
230 };
231 
AddExtraRequestHeader(const std::string & header_line)232 void TestNetworkChannelURLFetcher::AddExtraRequestHeader(
233     const std::string& header_line) {
234   net::FakeURLFetcher::AddExtraRequestHeader(header_line);
235   std::string header_name("echo-token: ");
236   std::string echo_token;
237   if (StartsWithASCII(header_line, header_name, false)) {
238     echo_token = header_line;
239     ReplaceFirstSubstringAfterOffset(
240         &echo_token, 0, header_name, std::string());
241     test_->set_last_echo_token(echo_token);
242   }
243 }
244 
TEST_F(GCMNetworkChannelTest,HappyCase)245 TEST_F(GCMNetworkChannelTest, HappyCase) {
246   EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, get_last_invalidator_state());
247   EXPECT_FALSE(delegate()->message_callback.is_null());
248   url_fetcher_factory()->SetFakeResponse(GURL("http://test.url.com"),
249                                          std::string(),
250                                          net::HTTP_NO_CONTENT,
251                                          net::URLRequestStatus::SUCCESS);
252 
253   // Emulate gcm connection state to be online.
254   delegate()->connection_state_callback.Run(true);
255   // After construction GCMNetworkChannel should have called Register.
256   EXPECT_FALSE(delegate()->register_callback.is_null());
257   // Return valid registration id.
258   delegate()->register_callback.Run("registration.id", gcm::GCMClient::SUCCESS);
259 
260   network_channel()->SendMessage("abra.cadabra");
261   // SendMessage should have triggered RequestToken. No HTTP request should be
262   // started yet.
263   EXPECT_FALSE(delegate()->request_token_callback.is_null());
264   EXPECT_EQ(url_fetchers_created_count(), 0);
265   // Return valid access token. This should trigger HTTP request.
266   delegate()->request_token_callback.Run(
267       GoogleServiceAuthError::AuthErrorNone(), "access.token");
268   RunLoopUntilIdle();
269   EXPECT_EQ(url_fetchers_created_count(), 1);
270 
271   // Return another access token. Message should be cleared by now and shouldn't
272   // be sent.
273   delegate()->request_token_callback.Run(
274       GoogleServiceAuthError::AuthErrorNone(), "access.token2");
275   RunLoopUntilIdle();
276   EXPECT_EQ(url_fetchers_created_count(), 1);
277   EXPECT_EQ(INVALIDATIONS_ENABLED, get_last_invalidator_state());
278 }
279 
TEST_F(GCMNetworkChannelTest,FailedRegister)280 TEST_F(GCMNetworkChannelTest, FailedRegister) {
281   // After construction GCMNetworkChannel should have called Register.
282   EXPECT_FALSE(delegate()->register_callback.is_null());
283   EXPECT_EQ(1, delegate()->register_call_count_);
284   // Return transient error from Register call.
285   delegate()->register_callback.Run("", gcm::GCMClient::NETWORK_ERROR);
286   RunLoopUntilIdle();
287   // GcmNetworkChannel should have scheduled Register retry.
288   EXPECT_EQ(2, delegate()->register_call_count_);
289   // Return persistent error from Register call.
290   delegate()->register_callback.Run("", gcm::GCMClient::NOT_SIGNED_IN);
291   RunLoopUntilIdle();
292   // GcmNetworkChannel should give up trying.
293   EXPECT_EQ(2, delegate()->register_call_count_);
294 
295   network_channel()->SendMessage("abra.cadabra");
296   // SendMessage shouldn't trigger RequestToken.
297   EXPECT_TRUE(delegate()->request_token_callback.is_null());
298   EXPECT_EQ(0, url_fetchers_created_count());
299 }
300 
TEST_F(GCMNetworkChannelTest,RegisterFinishesAfterSendMessage)301 TEST_F(GCMNetworkChannelTest, RegisterFinishesAfterSendMessage) {
302   url_fetcher_factory()->SetFakeResponse(GURL("http://test.url.com"),
303                                          "",
304                                          net::HTTP_NO_CONTENT,
305                                          net::URLRequestStatus::SUCCESS);
306 
307   // After construction GCMNetworkChannel should have called Register.
308   EXPECT_FALSE(delegate()->register_callback.is_null());
309 
310   network_channel()->SendMessage("abra.cadabra");
311   // SendMessage shouldn't trigger RequestToken.
312   EXPECT_TRUE(delegate()->request_token_callback.is_null());
313   EXPECT_EQ(url_fetchers_created_count(), 0);
314 
315   // Return valid registration id.
316   delegate()->register_callback.Run("registration.id", gcm::GCMClient::SUCCESS);
317 
318   EXPECT_FALSE(delegate()->request_token_callback.is_null());
319   EXPECT_EQ(url_fetchers_created_count(), 0);
320   // Return valid access token. This should trigger HTTP request.
321   delegate()->request_token_callback.Run(
322       GoogleServiceAuthError::AuthErrorNone(), "access.token");
323   RunLoopUntilIdle();
324   EXPECT_EQ(url_fetchers_created_count(), 1);
325 }
326 
TEST_F(GCMNetworkChannelTest,RequestTokenFailure)327 TEST_F(GCMNetworkChannelTest, RequestTokenFailure) {
328   // After construction GCMNetworkChannel should have called Register.
329   EXPECT_FALSE(delegate()->register_callback.is_null());
330   // Return valid registration id.
331   delegate()->register_callback.Run("registration.id", gcm::GCMClient::SUCCESS);
332 
333   network_channel()->SendMessage("abra.cadabra");
334   // SendMessage should have triggered RequestToken. No HTTP request should be
335   // started yet.
336   EXPECT_FALSE(delegate()->request_token_callback.is_null());
337   EXPECT_EQ(url_fetchers_created_count(), 0);
338   // RequestToken returns failure.
339   delegate()->request_token_callback.Run(
340       GoogleServiceAuthError::FromConnectionError(1), "");
341 
342   // Should be no HTTP requests.
343   EXPECT_EQ(url_fetchers_created_count(), 0);
344 }
345 
TEST_F(GCMNetworkChannelTest,AuthErrorFromServer)346 TEST_F(GCMNetworkChannelTest, AuthErrorFromServer) {
347   // Setup fake response to return AUTH_ERROR.
348   url_fetcher_factory()->SetFakeResponse(GURL("http://test.url.com"),
349                                          "",
350                                          net::HTTP_UNAUTHORIZED,
351                                          net::URLRequestStatus::SUCCESS);
352 
353   // After construction GCMNetworkChannel should have called Register.
354   EXPECT_FALSE(delegate()->register_callback.is_null());
355   // Return valid registration id.
356   delegate()->register_callback.Run("registration.id", gcm::GCMClient::SUCCESS);
357 
358   network_channel()->SendMessage("abra.cadabra");
359   // SendMessage should have triggered RequestToken. No HTTP request should be
360   // started yet.
361   EXPECT_FALSE(delegate()->request_token_callback.is_null());
362   EXPECT_EQ(url_fetchers_created_count(), 0);
363   // Return valid access token. This should trigger HTTP request.
364   delegate()->request_token_callback.Run(
365       GoogleServiceAuthError::AuthErrorNone(), "access.token");
366   RunLoopUntilIdle();
367   EXPECT_EQ(url_fetchers_created_count(), 1);
368   EXPECT_EQ(delegate()->invalidated_token, "access.token");
369 }
370 
371 // Following two tests are to check for memory leaks/crashes when Register and
372 // RequestToken don't complete by the teardown.
TEST_F(GCMNetworkChannelTest,RegisterNeverCompletes)373 TEST_F(GCMNetworkChannelTest, RegisterNeverCompletes) {
374   network_channel()->SendMessage("abra.cadabra");
375   // Register should be called by now. Let's not complete and see what happens.
376   EXPECT_FALSE(delegate()->register_callback.is_null());
377 }
378 
TEST_F(GCMNetworkChannelTest,RequestTokenNeverCompletes)379 TEST_F(GCMNetworkChannelTest, RequestTokenNeverCompletes) {
380   network_channel()->SendMessage("abra.cadabra");
381   // Return valid registration id.
382   delegate()->register_callback.Run("registration.id", gcm::GCMClient::SUCCESS);
383   // RequestToken should be called by now. Let's not complete and see what
384   // happens.
385   EXPECT_FALSE(delegate()->request_token_callback.is_null());
386 }
387 
TEST_F(GCMNetworkChannelTest,Base64EncodeDecode)388 TEST_F(GCMNetworkChannelTest, Base64EncodeDecode) {
389   std::string input;
390   std::string plain;
391   std::string base64;
392   // Empty string.
393   Base64EncodeURLSafe(input, &base64);
394   EXPECT_TRUE(base64.empty());
395   EXPECT_TRUE(Base64DecodeURLSafe(base64, &plain));
396   EXPECT_EQ(input, plain);
397   // String length: 1..7.
398   for (int length = 1; length < 8; length++) {
399     input = "abra.cadabra";
400     input.resize(length);
401     Base64EncodeURLSafe(input, &base64);
402     // Ensure no padding at the end.
403     EXPECT_NE(base64[base64.size() - 1], '=');
404     EXPECT_TRUE(Base64DecodeURLSafe(base64, &plain));
405     EXPECT_EQ(input, plain);
406   }
407   // Presence of '-', '_'.
408   input = "\xfb\xff";
409   Base64EncodeURLSafe(input, &base64);
410   EXPECT_EQ("-_8", base64);
411   EXPECT_TRUE(Base64DecodeURLSafe(base64, &plain));
412   EXPECT_EQ(input, plain);
413 }
414 
TEST_F(GCMNetworkChannelTest,ChannelState)415 TEST_F(GCMNetworkChannelTest, ChannelState) {
416   EXPECT_FALSE(delegate()->message_callback.is_null());
417   // POST will fail.
418   url_fetcher_factory()->SetFakeResponse(GURL("http://test.url.com"),
419                                          std::string(),
420                                          net::HTTP_SERVICE_UNAVAILABLE,
421                                          net::URLRequestStatus::SUCCESS);
422 
423   delegate()->connection_state_callback.Run(true);
424   delegate()->register_callback.Run("registration.id", gcm::GCMClient::SUCCESS);
425 
426   network_channel()->SendMessage("abra.cadabra");
427   EXPECT_FALSE(delegate()->request_token_callback.is_null());
428   delegate()->request_token_callback.Run(
429       GoogleServiceAuthError::AuthErrorNone(), "access.token");
430   RunLoopUntilIdle();
431   EXPECT_EQ(url_fetchers_created_count(), 1);
432   // Failing HTTP POST should cause TRANSIENT_INVALIDATION_ERROR.
433   EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, get_last_invalidator_state());
434 
435   // Setup POST to succeed.
436   url_fetcher_factory()->SetFakeResponse(GURL("http://test.url.com"),
437                                          "",
438                                          net::HTTP_NO_CONTENT,
439                                          net::URLRequestStatus::SUCCESS);
440   network_channel()->SendMessage("abra.cadabra");
441   EXPECT_FALSE(delegate()->request_token_callback.is_null());
442   delegate()->request_token_callback.Run(
443       GoogleServiceAuthError::AuthErrorNone(), "access.token");
444   RunLoopUntilIdle();
445   EXPECT_EQ(url_fetchers_created_count(), 2);
446   // Successful post should set invalidator state to enabled.
447   EXPECT_EQ(INVALIDATIONS_ENABLED, get_last_invalidator_state());
448   // Network changed event shouldn't affect invalidator state.
449   network_channel()->OnNetworkChanged(
450       net::NetworkChangeNotifier::CONNECTION_NONE);
451   EXPECT_EQ(INVALIDATIONS_ENABLED, get_last_invalidator_state());
452 
453   // GCM connection state should affect invalidator state.
454   delegate()->connection_state_callback.Run(false);
455   EXPECT_EQ(TRANSIENT_INVALIDATION_ERROR, get_last_invalidator_state());
456   delegate()->connection_state_callback.Run(true);
457   EXPECT_EQ(INVALIDATIONS_ENABLED, get_last_invalidator_state());
458 }
459 
460 #if !defined(OS_ANDROID)
TEST_F(GCMNetworkChannelTest,BuildUrl)461 TEST_F(GCMNetworkChannelTest, BuildUrl) {
462   GURL url = BuildUrl("registration.id");
463   EXPECT_TRUE(url.SchemeIsHTTPOrHTTPS());
464   EXPECT_FALSE(url.host().empty());
465   EXPECT_FALSE(url.path().empty());
466   std::vector<std::string> parts;
467   Tokenize(url.path(), "/", &parts);
468   std::string buffer;
469   EXPECT_TRUE(Base64DecodeURLSafe(parts[parts.size() - 1], &buffer));
470 }
471 
TEST_F(GCMNetworkChannelTest,EchoToken)472 TEST_F(GCMNetworkChannelTest, EchoToken) {
473   url_fetcher_factory()->SetFakeResponse(GURL("http://test.url.com"),
474                                          std::string(),
475                                          net::HTTP_OK,
476                                          net::URLRequestStatus::SUCCESS);
477   // After construction GCMNetworkChannel should have called Register.
478   // Return valid registration id.
479   delegate()->register_callback.Run("registration.id", gcm::GCMClient::SUCCESS);
480 
481   network_channel()->SendMessage("abra.cadabra");
482   // Return valid access token. This should trigger HTTP request.
483   delegate()->request_token_callback.Run(
484       GoogleServiceAuthError::AuthErrorNone(), "access.token");
485   RunLoopUntilIdle();
486   EXPECT_EQ(url_fetchers_created_count(), 1);
487   EXPECT_TRUE(get_last_echo_token().empty());
488 
489   // Trigger response.
490   delegate()->message_callback.Run("abra.cadabra", "echo.token");
491   // Send another message.
492   network_channel()->SendMessage("abra.cadabra");
493   // Return valid access token. This should trigger HTTP request.
494   delegate()->request_token_callback.Run(
495       GoogleServiceAuthError::AuthErrorNone(), "access.token");
496   RunLoopUntilIdle();
497   EXPECT_EQ(url_fetchers_created_count(), 2);
498   EXPECT_EQ("echo.token", get_last_echo_token());
499 
500   // Trigger response with empty echo token.
501   delegate()->message_callback.Run("abra.cadabra", "");
502   // Send another message.
503   network_channel()->SendMessage("abra.cadabra");
504   // Return valid access token. This should trigger HTTP request.
505   delegate()->request_token_callback.Run(
506       GoogleServiceAuthError::AuthErrorNone(), "access.token");
507   RunLoopUntilIdle();
508   EXPECT_EQ(url_fetchers_created_count(), 3);
509   // Echo_token should be from second message.
510   EXPECT_EQ("echo.token", get_last_echo_token());
511 }
512 #endif
513 
514 }  // namespace syncer
515