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
13namespace syncer {
14
15class TestGCMNetworkChannelDelegate : public GCMNetworkChannelDelegate {
16 public:
17  TestGCMNetworkChannelDelegate()
18      : register_call_count_(0) {}
19
20  virtual void Initialize(
21      GCMNetworkChannelDelegate::ConnectionStateCallback callback) OVERRIDE {
22    connection_state_callback = callback;
23  }
24
25  virtual void RequestToken(RequestTokenCallback callback) OVERRIDE {
26    request_token_callback = callback;
27  }
28
29  virtual void InvalidateToken(const std::string& token) OVERRIDE {
30    invalidated_token = token;
31  }
32
33  virtual void Register(RegisterCallback callback) OVERRIDE {
34    ++register_call_count_;
35    register_callback = callback;
36  }
37
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.
51const 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
77class TestGCMNetworkChannel : public GCMNetworkChannel {
78 public:
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.
89  virtual GURL BuildUrl(const std::string& registration_id) OVERRIDE {
90    return GURL("http://test.url.com");
91  }
92};
93
94class GCMNetworkChannelTest;
95
96// Test needs to capture setting echo-token header on http request.
97// This class is going to do that.
98class TestNetworkChannelURLFetcher : public net::FakeURLFetcher {
99 public:
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
119class GCMNetworkChannelTest
120    : public ::testing::Test,
121      public SyncNetworkChannel::Observer {
122 public:
123  GCMNetworkChannelTest()
124      : delegate_(NULL),
125        url_fetchers_created_count_(0),
126        last_invalidator_state_(TRANSIENT_INVALIDATION_ERROR) {}
127
128  virtual ~GCMNetworkChannelTest() {
129  }
130
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
150  virtual void TearDown() {
151    gcm_network_channel_->RemoveObserver(this);
152  }
153
154  // Helper functions to call private methods from test
155  GURL BuildUrl(const std::string& registration_id) {
156    return gcm_network_channel_->GCMNetworkChannel::BuildUrl(registration_id);
157  }
158
159  static void Base64EncodeURLSafe(const std::string& input,
160                                  std::string* output) {
161    GCMNetworkChannel::Base64EncodeURLSafe(input, output);
162  }
163
164  static bool Base64DecodeURLSafe(const std::string& input,
165                                  std::string* output) {
166    return GCMNetworkChannel::Base64DecodeURLSafe(input, output);
167  }
168
169  virtual void OnNetworkChannelStateChanged(
170      InvalidatorState invalidator_state) OVERRIDE {
171    last_invalidator_state_ = invalidator_state;
172  }
173
174  void OnIncomingMessage(std::string incoming_message) {
175  }
176
177  GCMNetworkChannel* network_channel() {
178    return gcm_network_channel_.get();
179  }
180
181  TestGCMNetworkChannelDelegate* delegate() {
182    return delegate_;
183  }
184
185  int url_fetchers_created_count() {
186    return url_fetchers_created_count_;
187  }
188
189  net::FakeURLFetcherFactory* url_fetcher_factory() {
190    return url_fetcher_factory_.get();
191  }
192
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
204  void set_last_echo_token(const std::string& echo_token) {
205    last_echo_token_ = echo_token;
206  }
207
208  const std::string& get_last_echo_token() {
209    return last_echo_token_;
210  }
211
212  InvalidatorState get_last_invalidator_state() {
213    return last_invalidator_state_;
214  }
215
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
232void 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
245TEST_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
280TEST_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
301TEST_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
327TEST_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
346TEST_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.
373TEST_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
379TEST_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
388TEST_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
415TEST_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)
461TEST_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
472TEST_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