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: 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. 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: 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 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: 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: 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 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 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 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 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 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 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. 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 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 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 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) 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 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