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 <stdint.h>
6 #include <map>
7 #include <string>
8 #include <utility>
9 #include <vector>
10 
11 #include "base/bind.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/string_tokenizer.h"
14 #include "base/threading/thread_task_runner_handle.h"
15 #include "google_apis/gcm/engine/gcm_request_test_base.h"
16 #include "google_apis/gcm/engine/gcm_unregistration_request_handler.h"
17 #include "google_apis/gcm/engine/instance_id_delete_token_request_handler.h"
18 #include "google_apis/gcm/monitoring/fake_gcm_stats_recorder.h"
19 #include "net/base/load_flags.h"
20 #include "services/network/public/cpp/shared_url_loader_factory.h"
21 
22 namespace gcm {
23 
24 namespace {
25 const int kMaxRetries = 2;
26 const uint64_t kAndroidId = 42UL;
27 const char kLoginHeader[] = "AidLogin";
28 const char kAppId[] = "TestAppId";
29 const char kDeletedAppId[] = "deleted=TestAppId";
30 const char kDeletedToken[] = "token=SomeToken";
31 const char kProductCategoryForSubtypes[] = "com.chrome.macosx";
32 const char kRegistrationURL[] = "http://foo.bar/register";
33 const uint64_t kSecurityToken = 77UL;
34 const int kGCMVersion = 40;
35 const char kInstanceId[] = "IID1";
36 const char kDeveloperId[] = "Project1";
37 const char kScope[] = "GCM";
38 
39 }  // namespace
40 
41 class UnregistrationRequestTest : public GCMRequestTestBase {
42  public:
43   UnregistrationRequestTest();
44   ~UnregistrationRequestTest() override;
45 
46   void UnregistrationCallback(UnregistrationRequest::Status status);
47 
48   void OnAboutToCompleteFetch() override;
49 
max_retry_count() const50   int max_retry_count() const { return max_retry_count_; }
set_max_retry_count(int max_retry_count)51   void set_max_retry_count(int max_retry_count) {
52     max_retry_count_ = max_retry_count;
53   }
54 
55  protected:
56   int max_retry_count_;
57   bool callback_called_;
58   UnregistrationRequest::Status status_;
59   std::unique_ptr<UnregistrationRequest> request_;
60   FakeGCMStatsRecorder recorder_;
61 };
62 
UnregistrationRequestTest()63 UnregistrationRequestTest::UnregistrationRequestTest()
64     : max_retry_count_(kMaxRetries),
65       callback_called_(false),
66       status_(UnregistrationRequest::UNREGISTRATION_STATUS_COUNT) {}
67 
~UnregistrationRequestTest()68 UnregistrationRequestTest::~UnregistrationRequestTest() {}
69 
UnregistrationCallback(UnregistrationRequest::Status status)70 void UnregistrationRequestTest::UnregistrationCallback(
71     UnregistrationRequest::Status status) {
72   callback_called_ = true;
73   status_ = status;
74 }
75 
OnAboutToCompleteFetch()76 void UnregistrationRequestTest::OnAboutToCompleteFetch() {
77   status_ = UnregistrationRequest::UNREGISTRATION_STATUS_COUNT;
78   callback_called_ = false;
79 }
80 
81 class GCMUnregistrationRequestTest : public UnregistrationRequestTest {
82  public:
83   GCMUnregistrationRequestTest();
84   ~GCMUnregistrationRequestTest() override;
85 
86   void CreateRequest();
87 };
88 
GCMUnregistrationRequestTest()89 GCMUnregistrationRequestTest::GCMUnregistrationRequestTest() {
90 }
91 
~GCMUnregistrationRequestTest()92 GCMUnregistrationRequestTest::~GCMUnregistrationRequestTest() {
93 }
94 
CreateRequest()95 void GCMUnregistrationRequestTest::CreateRequest() {
96   UnregistrationRequest::RequestInfo request_info(kAndroidId, kSecurityToken,
97                                                   kAppId /* category */,
98                                                   std::string() /* subtype */);
99   std::unique_ptr<GCMUnregistrationRequestHandler> request_handler(
100       new GCMUnregistrationRequestHandler(kAppId));
101   request_.reset(new UnregistrationRequest(
102       GURL(kRegistrationURL), request_info, std::move(request_handler),
103       GetBackoffPolicy(),
104       base::BindOnce(&UnregistrationRequestTest::UnregistrationCallback,
105                      base::Unretained(this)),
106       max_retry_count_, url_loader_factory(),
107       base::ThreadTaskRunnerHandle::Get(), &recorder_, std::string()));
108 }
109 
TEST_F(GCMUnregistrationRequestTest,RequestDataPassedToFetcher)110 TEST_F(GCMUnregistrationRequestTest, RequestDataPassedToFetcher) {
111   CreateRequest();
112   request_->Start();
113 
114   // Verify that the no-cookie flag is set.
115   const network::ResourceRequest* pending_request;
116   ASSERT_TRUE(
117       test_url_loader_factory()->IsPending(kRegistrationURL, &pending_request));
118   EXPECT_EQ(network::mojom::CredentialsMode::kOmit,
119             pending_request->credentials_mode);
120 
121   // Verify that authorization header was put together properly.
122   const net::HttpRequestHeaders* headers =
123       GetExtraHeadersForURL(kRegistrationURL);
124   ASSERT_TRUE(headers);
125   std::string auth_header;
126   headers->GetHeader(net::HttpRequestHeaders::kAuthorization, &auth_header);
127   base::StringTokenizer auth_tokenizer(auth_header, " :");
128   ASSERT_TRUE(auth_tokenizer.GetNext());
129   EXPECT_EQ(kLoginHeader, auth_tokenizer.token());
130   ASSERT_TRUE(auth_tokenizer.GetNext());
131   EXPECT_EQ(base::NumberToString(kAndroidId), auth_tokenizer.token());
132   ASSERT_TRUE(auth_tokenizer.GetNext());
133   EXPECT_EQ(base::NumberToString(kSecurityToken), auth_tokenizer.token());
134 
135   std::map<std::string, std::string> expected_pairs;
136   expected_pairs["app"] = kAppId;
137   expected_pairs["device"] = base::NumberToString(kAndroidId);
138   expected_pairs["delete"] = "true";
139   expected_pairs["gcm_unreg_caller"] = "false";
140 
141   ASSERT_NO_FATAL_FAILURE(
142       VerifyFetcherUploadDataForURL(kRegistrationURL, &expected_pairs));
143 }
144 
TEST_F(GCMUnregistrationRequestTest,SuccessfulUnregistration)145 TEST_F(GCMUnregistrationRequestTest, SuccessfulUnregistration) {
146   set_max_retry_count(0);
147   CreateRequest();
148   request_->Start();
149 
150   SetResponseForURLAndComplete(kRegistrationURL, net::HTTP_OK, kDeletedAppId);
151   EXPECT_TRUE(callback_called_);
152   EXPECT_EQ(UnregistrationRequest::SUCCESS, status_);
153 }
154 
TEST_F(GCMUnregistrationRequestTest,ResponseHttpStatusNotOK)155 TEST_F(GCMUnregistrationRequestTest, ResponseHttpStatusNotOK) {
156   CreateRequest();
157   request_->Start();
158 
159   SetResponseForURLAndComplete(kRegistrationURL, net::HTTP_UNAUTHORIZED, "");
160   EXPECT_FALSE(callback_called_);
161 
162   SetResponseForURLAndComplete(kRegistrationURL, net::HTTP_OK, kDeletedAppId);
163   EXPECT_TRUE(callback_called_);
164   EXPECT_EQ(UnregistrationRequest::SUCCESS, status_);
165 }
166 
TEST_F(GCMUnregistrationRequestTest,ResponseEmpty)167 TEST_F(GCMUnregistrationRequestTest, ResponseEmpty) {
168   CreateRequest();
169   request_->Start();
170 
171   SetResponseForURLAndComplete(kRegistrationURL, net::HTTP_OK, "");
172   EXPECT_FALSE(callback_called_);
173 
174   SetResponseForURLAndComplete(kRegistrationURL, net::HTTP_OK, kDeletedAppId);
175   EXPECT_TRUE(callback_called_);
176   EXPECT_EQ(UnregistrationRequest::SUCCESS, status_);
177 }
178 
TEST_F(GCMUnregistrationRequestTest,InvalidParametersError)179 TEST_F(GCMUnregistrationRequestTest, InvalidParametersError) {
180   CreateRequest();
181   request_->Start();
182 
183   SetResponseForURLAndComplete(kRegistrationURL, net::HTTP_OK,
184                                "Error=INVALID_PARAMETERS");
185   EXPECT_TRUE(callback_called_);
186   EXPECT_EQ(UnregistrationRequest::INVALID_PARAMETERS, status_);
187 }
188 
TEST_F(GCMUnregistrationRequestTest,DeviceRegistrationError)189 TEST_F(GCMUnregistrationRequestTest, DeviceRegistrationError) {
190   CreateRequest();
191   request_->Start();
192 
193   SetResponseForURLAndComplete(kRegistrationURL, net::HTTP_OK,
194                                "Error=PHONE_REGISTRATION_ERROR");
195   EXPECT_TRUE(callback_called_);
196   EXPECT_EQ(UnregistrationRequest::DEVICE_REGISTRATION_ERROR, status_);
197 }
198 
TEST_F(GCMUnregistrationRequestTest,UnkwnownError)199 TEST_F(GCMUnregistrationRequestTest, UnkwnownError) {
200   CreateRequest();
201   request_->Start();
202 
203   SetResponseForURLAndComplete(kRegistrationURL, net::HTTP_OK, "Error=XXX");
204   EXPECT_TRUE(callback_called_);
205   EXPECT_EQ(UnregistrationRequest::UNKNOWN_ERROR, status_);
206 }
207 
TEST_F(GCMUnregistrationRequestTest,ServiceUnavailable)208 TEST_F(GCMUnregistrationRequestTest, ServiceUnavailable) {
209   CreateRequest();
210   request_->Start();
211 
212   SetResponseForURLAndComplete(kRegistrationURL, net::HTTP_SERVICE_UNAVAILABLE,
213                                "");
214   EXPECT_FALSE(callback_called_);
215 
216   SetResponseForURLAndComplete(kRegistrationURL, net::HTTP_OK, kDeletedAppId);
217   EXPECT_TRUE(callback_called_);
218   EXPECT_EQ(UnregistrationRequest::SUCCESS, status_);
219 }
220 
TEST_F(GCMUnregistrationRequestTest,InternalServerError)221 TEST_F(GCMUnregistrationRequestTest, InternalServerError) {
222   CreateRequest();
223   request_->Start();
224 
225   SetResponseForURLAndComplete(kRegistrationURL,
226                                net::HTTP_INTERNAL_SERVER_ERROR, "");
227   EXPECT_FALSE(callback_called_);
228 
229   SetResponseForURLAndComplete(kRegistrationURL, net::HTTP_OK, kDeletedAppId);
230   EXPECT_TRUE(callback_called_);
231   EXPECT_EQ(UnregistrationRequest::SUCCESS, status_);
232 }
233 
TEST_F(GCMUnregistrationRequestTest,IncorrectAppId)234 TEST_F(GCMUnregistrationRequestTest, IncorrectAppId) {
235   CreateRequest();
236   request_->Start();
237 
238   SetResponseForURLAndComplete(kRegistrationURL, net::HTTP_OK,
239                                "deleted=OtherTestAppId");
240   EXPECT_FALSE(callback_called_);
241 
242   SetResponseForURLAndComplete(kRegistrationURL, net::HTTP_OK, kDeletedAppId);
243   EXPECT_TRUE(callback_called_);
244   EXPECT_EQ(UnregistrationRequest::SUCCESS, status_);
245 }
246 
TEST_F(GCMUnregistrationRequestTest,ResponseParsingFailed)247 TEST_F(GCMUnregistrationRequestTest, ResponseParsingFailed) {
248   CreateRequest();
249   request_->Start();
250 
251   SetResponseForURLAndComplete(kRegistrationURL, net::HTTP_OK,
252                                "some malformed response");
253   EXPECT_FALSE(callback_called_);
254 
255   SetResponseForURLAndComplete(kRegistrationURL, net::HTTP_OK, kDeletedAppId);
256   EXPECT_TRUE(callback_called_);
257   EXPECT_EQ(UnregistrationRequest::SUCCESS, status_);
258 }
259 
TEST_F(GCMUnregistrationRequestTest,MaximumAttemptsReachedWithZeroRetries)260 TEST_F(GCMUnregistrationRequestTest, MaximumAttemptsReachedWithZeroRetries) {
261   set_max_retry_count(0);
262   CreateRequest();
263   request_->Start();
264 
265   SetResponseForURLAndComplete(kRegistrationURL, net::HTTP_GATEWAY_TIMEOUT,
266                                "bad response");
267   EXPECT_TRUE(callback_called_);
268   EXPECT_EQ(UnregistrationRequest::REACHED_MAX_RETRIES, status_);
269 }
270 
TEST_F(GCMUnregistrationRequestTest,MaximumAttemptsReached)271 TEST_F(GCMUnregistrationRequestTest, MaximumAttemptsReached) {
272   CreateRequest();
273   request_->Start();
274 
275   SetResponseForURLAndComplete(kRegistrationURL, net::HTTP_GATEWAY_TIMEOUT,
276                                "bad response");
277   EXPECT_FALSE(callback_called_);
278 
279   SetResponseForURLAndComplete(kRegistrationURL, net::HTTP_GATEWAY_TIMEOUT,
280                                "bad response");
281   EXPECT_FALSE(callback_called_);
282 
283   SetResponseForURLAndComplete(kRegistrationURL, net::HTTP_GATEWAY_TIMEOUT,
284                                "bad response");
285   EXPECT_TRUE(callback_called_);
286   EXPECT_EQ(UnregistrationRequest::REACHED_MAX_RETRIES, status_);
287 }
288 
289 class InstaceIDDeleteTokenRequestTest : public UnregistrationRequestTest {
290  public:
291   InstaceIDDeleteTokenRequestTest();
292   ~InstaceIDDeleteTokenRequestTest() override;
293 
294   void CreateRequest(bool use_subtype,
295                      const std::string& instance_id,
296                      const std::string& authorized_entity,
297                      const std::string& scope);
298 };
299 
InstaceIDDeleteTokenRequestTest()300 InstaceIDDeleteTokenRequestTest::InstaceIDDeleteTokenRequestTest() {
301 }
302 
~InstaceIDDeleteTokenRequestTest()303 InstaceIDDeleteTokenRequestTest::~InstaceIDDeleteTokenRequestTest() {
304 }
305 
CreateRequest(bool use_subtype,const std::string & instance_id,const std::string & authorized_entity,const std::string & scope)306 void InstaceIDDeleteTokenRequestTest::CreateRequest(
307     bool use_subtype,
308     const std::string& instance_id,
309     const std::string& authorized_entity,
310     const std::string& scope) {
311   std::string category = use_subtype ? kProductCategoryForSubtypes : kAppId;
312   std::string subtype = use_subtype ? kAppId : std::string();
313   UnregistrationRequest::RequestInfo request_info(kAndroidId, kSecurityToken,
314                                                   category, subtype);
315   std::unique_ptr<InstanceIDDeleteTokenRequestHandler> request_handler(
316       new InstanceIDDeleteTokenRequestHandler(instance_id, authorized_entity,
317                                               scope, kGCMVersion));
318   request_.reset(new UnregistrationRequest(
319       GURL(kRegistrationURL), request_info, std::move(request_handler),
320       GetBackoffPolicy(),
321       base::BindOnce(&UnregistrationRequestTest::UnregistrationCallback,
322                      base::Unretained(this)),
323       max_retry_count(), url_loader_factory(),
324       base::ThreadTaskRunnerHandle::Get(), &recorder_, std::string()));
325 }
326 
TEST_F(InstaceIDDeleteTokenRequestTest,RequestDataPassedToFetcher)327 TEST_F(InstaceIDDeleteTokenRequestTest, RequestDataPassedToFetcher) {
328   CreateRequest(false /* use_subtype */, kInstanceId, kDeveloperId, kScope);
329   request_->Start();
330 
331   // Verify that authorization header was put together properly.
332   const net::HttpRequestHeaders* headers =
333       GetExtraHeadersForURL(kRegistrationURL);
334   ASSERT_TRUE(headers);
335   std::string auth_header;
336   headers->GetHeader(net::HttpRequestHeaders::kAuthorization, &auth_header);
337   base::StringTokenizer auth_tokenizer(auth_header, " :");
338   ASSERT_TRUE(auth_tokenizer.GetNext());
339   EXPECT_EQ(kLoginHeader, auth_tokenizer.token());
340   ASSERT_TRUE(auth_tokenizer.GetNext());
341   EXPECT_EQ(base::NumberToString(kAndroidId), auth_tokenizer.token());
342   ASSERT_TRUE(auth_tokenizer.GetNext());
343   EXPECT_EQ(base::NumberToString(kSecurityToken), auth_tokenizer.token());
344 
345   std::map<std::string, std::string> expected_pairs;
346   expected_pairs["gmsv"] = base::NumberToString(kGCMVersion);
347   expected_pairs["app"] = kAppId;
348   expected_pairs["device"] = base::NumberToString(kAndroidId);
349   expected_pairs["delete"] = "true";
350   expected_pairs["appid"] = kInstanceId;
351   expected_pairs["sender"] = kDeveloperId;
352   expected_pairs["scope"] = kScope;
353   expected_pairs["X-scope"] = kScope;
354 
355   ASSERT_NO_FATAL_FAILURE(
356       VerifyFetcherUploadDataForURL(kRegistrationURL, &expected_pairs));
357 }
358 
TEST_F(InstaceIDDeleteTokenRequestTest,RequestDataWithSubtype)359 TEST_F(InstaceIDDeleteTokenRequestTest, RequestDataWithSubtype) {
360   CreateRequest(true /* use_subtype */, kInstanceId, kDeveloperId, kScope);
361   request_->Start();
362 
363   // Same as RequestDataPassedToFetcher except "app" and "X-subtype".
364   std::map<std::string, std::string> expected_pairs;
365   expected_pairs["gmsv"] = base::NumberToString(kGCMVersion);
366   expected_pairs["app"] = kProductCategoryForSubtypes;
367   expected_pairs["X-subtype"] = kAppId;
368   expected_pairs["device"] = base::NumberToString(kAndroidId);
369   expected_pairs["delete"] = "true";
370   expected_pairs["appid"] = kInstanceId;
371   expected_pairs["sender"] = kDeveloperId;
372   expected_pairs["scope"] = kScope;
373   expected_pairs["X-scope"] = kScope;
374 
375   ASSERT_NO_FATAL_FAILURE(
376       VerifyFetcherUploadDataForURL(kRegistrationURL, &expected_pairs));
377 }
378 
TEST_F(InstaceIDDeleteTokenRequestTest,SuccessfulUnregistration)379 TEST_F(InstaceIDDeleteTokenRequestTest, SuccessfulUnregistration) {
380   CreateRequest(false /* use_subtype */, kInstanceId, kDeveloperId, kScope);
381   request_->Start();
382 
383   SetResponseForURLAndComplete(kRegistrationURL, net::HTTP_OK, kDeletedToken);
384   EXPECT_TRUE(callback_called_);
385   EXPECT_EQ(UnregistrationRequest::SUCCESS, status_);
386 }
387 
TEST_F(InstaceIDDeleteTokenRequestTest,ResponseHttpStatusNotOK)388 TEST_F(InstaceIDDeleteTokenRequestTest, ResponseHttpStatusNotOK) {
389   CreateRequest(false /* use_subtype */, kInstanceId, kDeveloperId, kScope);
390   request_->Start();
391 
392   SetResponseForURLAndComplete(kRegistrationURL, net::HTTP_UNAUTHORIZED, "");
393   EXPECT_FALSE(callback_called_);
394 
395   SetResponseForURLAndComplete(kRegistrationURL, net::HTTP_OK, kDeletedToken);
396   EXPECT_TRUE(callback_called_);
397   EXPECT_EQ(UnregistrationRequest::SUCCESS, status_);
398 }
399 
TEST_F(InstaceIDDeleteTokenRequestTest,InvalidParametersError)400 TEST_F(InstaceIDDeleteTokenRequestTest, InvalidParametersError) {
401   CreateRequest(false /* use_subtype */, kInstanceId, kDeveloperId, kScope);
402   request_->Start();
403 
404   SetResponseForURLAndComplete(kRegistrationURL, net::HTTP_OK,
405                                "Error=INVALID_PARAMETERS");
406   EXPECT_TRUE(callback_called_);
407   EXPECT_EQ(UnregistrationRequest::INVALID_PARAMETERS, status_);
408 }
409 
TEST_F(InstaceIDDeleteTokenRequestTest,UnkwnownError)410 TEST_F(InstaceIDDeleteTokenRequestTest, UnkwnownError) {
411   CreateRequest(false /* use_subtype */, kInstanceId, kDeveloperId, kScope);
412   request_->Start();
413 
414   SetResponseForURLAndComplete(kRegistrationURL, net::HTTP_OK, "Error=XXX");
415   EXPECT_TRUE(callback_called_);
416   EXPECT_EQ(UnregistrationRequest::UNKNOWN_ERROR, status_);
417 }
418 
419 }  // namespace gcm
420