protocol_manager_unittest.cc revision eb525c5499e34cc9c4b825d6d9e75bb07cc06ace
1// Copyright (c) 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.
4//
5
6#include "base/strings/stringprintf.h"
7#include "base/test/test_simple_task_runner.h"
8#include "base/thread_task_runner_handle.h"
9#include "base/time/time.h"
10#include "chrome/browser/safe_browsing/protocol_manager.h"
11#include "google_apis/google_api_keys.h"
12#include "net/base/escape.h"
13#include "net/base/load_flags.h"
14#include "net/base/net_errors.h"
15#include "net/url_request/test_url_fetcher_factory.h"
16#include "testing/gmock/include/gmock/gmock.h"
17#include "testing/gmock_mutant.h"
18#include "testing/gtest/include/gtest/gtest.h"
19
20using base::Time;
21using base::TimeDelta;
22using testing::_;
23using testing::Invoke;
24
25static const char kUrlPrefix[] = "https://prefix.com/foo";
26static const char kBackupConnectUrlPrefix[] = "https://alt1-prefix.com/foo";
27static const char kBackupHttpUrlPrefix[] = "https://alt2-prefix.com/foo";
28static const char kBackupNetworkUrlPrefix[] = "https://alt3-prefix.com/foo";
29static const char kClient[] = "unittest";
30static const char kAppVer[] = "1.0";
31static const char kAdditionalQuery[] = "additional_query";
32
33class SafeBrowsingProtocolManagerTest : public testing::Test {
34 protected:
35  std::string key_param_;
36
37  virtual void SetUp() {
38    std::string key = google_apis::GetAPIKey();
39    if (!key.empty()) {
40      key_param_ = base::StringPrintf(
41          "&key=%s",
42          net::EscapeQueryParamValue(key, true).c_str());
43    }
44  }
45
46  scoped_ptr<SafeBrowsingProtocolManager> CreateProtocolManager(
47      SafeBrowsingProtocolManagerDelegate* delegate) {
48    SafeBrowsingProtocolConfig config;
49    config.client_name = kClient;
50    config.url_prefix = kUrlPrefix;
51    config.backup_connect_error_url_prefix = kBackupConnectUrlPrefix;
52    config.backup_http_error_url_prefix = kBackupHttpUrlPrefix;
53    config.backup_network_error_url_prefix = kBackupNetworkUrlPrefix;
54    config.version = kAppVer;
55
56    return scoped_ptr<SafeBrowsingProtocolManager>(
57        SafeBrowsingProtocolManager::Create(delegate, NULL, config));
58  }
59
60  void ValidateUpdateFetcherRequest(
61      const net::TestURLFetcher* url_fetcher,
62      const std::string& expected_prefix) {
63    ASSERT_TRUE(url_fetcher);
64    EXPECT_EQ(net::LOAD_DISABLE_CACHE, url_fetcher->GetLoadFlags());
65    EXPECT_EQ("goog-phish-shavar;\ngoog-malware-shavar;\n",
66              url_fetcher->upload_data());
67    EXPECT_EQ(GURL(expected_prefix + "/downloads?client=unittest&appver=1.0"
68                   "&pver=2.2" + key_param_),
69              url_fetcher->GetOriginalURL());
70  }
71
72  void ValidateUpdateFetcherRequest(const net::TestURLFetcher* url_fetcher) {
73    ValidateUpdateFetcherRequest(url_fetcher, kUrlPrefix);
74  }
75
76  void ValidateRedirectFetcherRequest(const net::TestURLFetcher* url_fetcher,
77                                      const std::string& expected_url) {
78    ASSERT_TRUE(url_fetcher);
79    EXPECT_EQ(net::LOAD_DISABLE_CACHE, url_fetcher->GetLoadFlags());
80    EXPECT_EQ("", url_fetcher->upload_data());
81    EXPECT_EQ(GURL(expected_url), url_fetcher->GetOriginalURL());
82  }
83};
84
85// Ensure that we respect section 5 of the SafeBrowsing protocol specification.
86TEST_F(SafeBrowsingProtocolManagerTest, TestBackOffTimes) {
87  scoped_ptr<SafeBrowsingProtocolManager> pm(CreateProtocolManager(NULL));
88
89  pm->next_update_interval_ = TimeDelta::FromSeconds(1800);
90  ASSERT_TRUE(pm->back_off_fuzz_ >= 0.0 && pm->back_off_fuzz_ <= 1.0);
91
92  TimeDelta next;
93
94  // No errors received so far.
95  next = pm->GetNextUpdateInterval(false);
96  EXPECT_EQ(next, TimeDelta::FromSeconds(1800));
97
98  // 1 error.
99  next = pm->GetNextUpdateInterval(true);
100  EXPECT_EQ(next, TimeDelta::FromSeconds(60));
101
102  // 2 errors.
103  next = pm->GetNextUpdateInterval(true);
104  EXPECT_TRUE(next >= TimeDelta::FromMinutes(30) &&
105              next <= TimeDelta::FromMinutes(60));
106
107  // 3 errors.
108  next = pm->GetNextUpdateInterval(true);
109  EXPECT_TRUE(next >= TimeDelta::FromMinutes(60) &&
110              next <= TimeDelta::FromMinutes(120));
111
112  // 4 errors.
113  next = pm->GetNextUpdateInterval(true);
114  EXPECT_TRUE(next >= TimeDelta::FromMinutes(120) &&
115              next <= TimeDelta::FromMinutes(240));
116
117  // 5 errors.
118  next = pm->GetNextUpdateInterval(true);
119  EXPECT_TRUE(next >= TimeDelta::FromMinutes(240) &&
120              next <= TimeDelta::FromMinutes(480));
121
122  // 6 errors, reached max backoff.
123  next = pm->GetNextUpdateInterval(true);
124  EXPECT_EQ(next, TimeDelta::FromMinutes(480));
125
126  // 7 errors.
127  next = pm->GetNextUpdateInterval(true);
128  EXPECT_EQ(next, TimeDelta::FromMinutes(480));
129
130  // Received a successful response.
131  next = pm->GetNextUpdateInterval(false);
132  EXPECT_EQ(next, TimeDelta::FromSeconds(1800));
133}
134
135TEST_F(SafeBrowsingProtocolManagerTest, TestChunkStrings) {
136  scoped_ptr<SafeBrowsingProtocolManager> pm(CreateProtocolManager(NULL));
137
138  // Add and Sub chunks.
139  SBListChunkRanges phish("goog-phish-shavar");
140  phish.adds = "1,4,6,8-20,99";
141  phish.subs = "16,32,64-96";
142  EXPECT_EQ(pm->FormatList(phish),
143            "goog-phish-shavar;a:1,4,6,8-20,99:s:16,32,64-96\n");
144
145  // Add chunks only.
146  phish.subs = "";
147  EXPECT_EQ(pm->FormatList(phish), "goog-phish-shavar;a:1,4,6,8-20,99\n");
148
149  // Sub chunks only.
150  phish.adds = "";
151  phish.subs = "16,32,64-96";
152  EXPECT_EQ(pm->FormatList(phish), "goog-phish-shavar;s:16,32,64-96\n");
153
154  // No chunks of either type.
155  phish.adds = "";
156  phish.subs = "";
157  EXPECT_EQ(pm->FormatList(phish), "goog-phish-shavar;\n");
158}
159
160TEST_F(SafeBrowsingProtocolManagerTest, TestGetHashBackOffTimes) {
161  scoped_ptr<SafeBrowsingProtocolManager> pm(CreateProtocolManager(NULL));
162
163  // No errors or back off time yet.
164  EXPECT_EQ(pm->gethash_error_count_, 0);
165  EXPECT_TRUE(pm->next_gethash_time_.is_null());
166
167  Time now = Time::Now();
168
169  // 1 error.
170  pm->HandleGetHashError(now);
171  EXPECT_EQ(pm->gethash_error_count_, 1);
172  TimeDelta margin = TimeDelta::FromSeconds(5);  // Fudge factor.
173  Time future = now + TimeDelta::FromMinutes(1);
174  EXPECT_TRUE(pm->next_gethash_time_ >= future - margin &&
175              pm->next_gethash_time_ <= future + margin);
176
177  // 2 errors.
178  pm->HandleGetHashError(now);
179  EXPECT_EQ(pm->gethash_error_count_, 2);
180  EXPECT_TRUE(pm->next_gethash_time_ >= now + TimeDelta::FromMinutes(30));
181  EXPECT_TRUE(pm->next_gethash_time_ <= now + TimeDelta::FromMinutes(60));
182
183  // 3 errors.
184  pm->HandleGetHashError(now);
185  EXPECT_EQ(pm->gethash_error_count_, 3);
186  EXPECT_TRUE(pm->next_gethash_time_ >= now + TimeDelta::FromMinutes(60));
187  EXPECT_TRUE(pm->next_gethash_time_ <= now + TimeDelta::FromMinutes(120));
188
189  // 4 errors.
190  pm->HandleGetHashError(now);
191  EXPECT_EQ(pm->gethash_error_count_, 4);
192  EXPECT_TRUE(pm->next_gethash_time_ >= now + TimeDelta::FromMinutes(120));
193  EXPECT_TRUE(pm->next_gethash_time_ <= now + TimeDelta::FromMinutes(240));
194
195  // 5 errors.
196  pm->HandleGetHashError(now);
197  EXPECT_EQ(pm->gethash_error_count_, 5);
198  EXPECT_TRUE(pm->next_gethash_time_ >= now + TimeDelta::FromMinutes(240));
199  EXPECT_TRUE(pm->next_gethash_time_ <= now + TimeDelta::FromMinutes(480));
200
201  // 6 errors, reached max backoff.
202  pm->HandleGetHashError(now);
203  EXPECT_EQ(pm->gethash_error_count_, 6);
204  EXPECT_TRUE(pm->next_gethash_time_ == now + TimeDelta::FromMinutes(480));
205
206  // 7 errors.
207  pm->HandleGetHashError(now);
208  EXPECT_EQ(pm->gethash_error_count_, 7);
209  EXPECT_TRUE(pm->next_gethash_time_== now + TimeDelta::FromMinutes(480));
210}
211
212TEST_F(SafeBrowsingProtocolManagerTest, TestGetHashUrl) {
213  scoped_ptr<SafeBrowsingProtocolManager> pm(CreateProtocolManager(NULL));
214
215  EXPECT_EQ("https://prefix.com/foo/gethash?client=unittest&appver=1.0&"
216            "pver=2.2" + key_param_, pm->GetHashUrl().spec());
217
218  pm->set_additional_query(kAdditionalQuery);
219  EXPECT_EQ("https://prefix.com/foo/gethash?client=unittest&appver=1.0&"
220            "pver=2.2" + key_param_ + "&additional_query",
221            pm->GetHashUrl().spec());
222}
223
224TEST_F(SafeBrowsingProtocolManagerTest, TestUpdateUrl) {
225  scoped_ptr<SafeBrowsingProtocolManager> pm(CreateProtocolManager(NULL));
226
227  EXPECT_EQ("https://prefix.com/foo/downloads?client=unittest&appver=1.0&"
228            "pver=2.2" + key_param_, pm->UpdateUrl().spec());
229
230  pm->set_additional_query(kAdditionalQuery);
231  EXPECT_EQ("https://prefix.com/foo/downloads?client=unittest&appver=1.0&"
232            "pver=2.2" + key_param_ + "&additional_query",
233            pm->UpdateUrl().spec());
234}
235
236TEST_F(SafeBrowsingProtocolManagerTest, TestNextChunkUrl) {
237  scoped_ptr<SafeBrowsingProtocolManager> pm(CreateProtocolManager(NULL));
238
239  std::string url_partial = "localhost:1234/foo/bar?foo";
240  std::string url_http_full = "http://localhost:1234/foo/bar?foo";
241  std::string url_https_full = "https://localhost:1234/foo/bar?foo";
242  std::string url_https_no_query = "https://localhost:1234/foo/bar";
243
244  EXPECT_EQ("https://localhost:1234/foo/bar?foo",
245            pm->NextChunkUrl(url_partial).spec());
246  EXPECT_EQ("http://localhost:1234/foo/bar?foo",
247            pm->NextChunkUrl(url_http_full).spec());
248  EXPECT_EQ("https://localhost:1234/foo/bar?foo",
249            pm->NextChunkUrl(url_https_full).spec());
250  EXPECT_EQ("https://localhost:1234/foo/bar",
251            pm->NextChunkUrl(url_https_no_query).spec());
252
253  pm->set_additional_query(kAdditionalQuery);
254  EXPECT_EQ("https://localhost:1234/foo/bar?foo&additional_query",
255            pm->NextChunkUrl(url_partial).spec());
256  EXPECT_EQ("http://localhost:1234/foo/bar?foo&additional_query",
257            pm->NextChunkUrl(url_http_full).spec());
258  EXPECT_EQ("https://localhost:1234/foo/bar?foo&additional_query",
259            pm->NextChunkUrl(url_https_full).spec());
260  EXPECT_EQ("https://localhost:1234/foo/bar?additional_query",
261            pm->NextChunkUrl(url_https_no_query).spec());
262}
263
264namespace {
265
266class MockProtocolDelegate : public SafeBrowsingProtocolManagerDelegate {
267 public:
268  MockProtocolDelegate() {}
269  virtual ~MockProtocolDelegate() {}
270
271  MOCK_METHOD0(UpdateStarted, void());
272  MOCK_METHOD1(UpdateFinished, void(bool));
273  MOCK_METHOD0(ResetDatabase, void());
274  MOCK_METHOD1(GetChunks, void(GetChunksCallback));
275  MOCK_METHOD3(AddChunks, void(const std::string&, SBChunkList*,
276                               AddChunksCallback));
277  MOCK_METHOD1(DeleteChunks, void(std::vector<SBChunkDelete>*));
278};
279
280// |InvokeGetChunksCallback| is required because GMock's InvokeArgument action
281// expects to use operator(), and a Callback only provides Run().
282// TODO(cbentzel): Use ACTION or ACTION_TEMPLATE instead?
283void InvokeGetChunksCallback(
284    const std::vector<SBListChunkRanges>& ranges,
285    bool database_error,
286    SafeBrowsingProtocolManagerDelegate::GetChunksCallback callback) {
287  callback.Run(ranges, database_error);
288}
289
290// |HandleAddChunks| deletes the chunks and asynchronously invokes
291// |callback| since SafeBrowsingProtocolManager is not re-entrant at the time
292// this is called. This guarantee is part of the
293// SafeBrowsingProtocolManagerDelegate contract.
294void HandleAddChunks(
295    const std::string& unused_list,
296    SBChunkList* chunks,
297    SafeBrowsingProtocolManagerDelegate::AddChunksCallback callback) {
298  delete chunks;
299  scoped_refptr<base::SingleThreadTaskRunner> task_runner(
300      base::ThreadTaskRunnerHandle::Get());
301  if (!task_runner.get())
302    return;
303  task_runner->PostTask(FROM_HERE, callback);
304}
305
306}  // namespace
307
308// Tests that the Update protocol will be skipped if there are problems
309// accessing the database.
310TEST_F(SafeBrowsingProtocolManagerTest, ProblemAccessingDatabase) {
311  scoped_refptr<base::TestSimpleTaskRunner> runner(
312      new base::TestSimpleTaskRunner());
313  base::ThreadTaskRunnerHandle runner_handler(runner);
314
315  testing::StrictMock<MockProtocolDelegate> test_delegate;
316  EXPECT_CALL(test_delegate, UpdateStarted()).Times(1);
317  EXPECT_CALL(test_delegate, GetChunks(_)).WillOnce(
318      Invoke(testing::CreateFunctor(InvokeGetChunksCallback,
319                                    std::vector<SBListChunkRanges>(),
320                                    true)));
321  EXPECT_CALL(test_delegate, UpdateFinished(false)).Times(1);
322
323  scoped_ptr<SafeBrowsingProtocolManager> pm(
324      CreateProtocolManager(&test_delegate));
325
326  pm->ForceScheduleNextUpdate(TimeDelta());
327  runner->RunPendingTasks();
328
329  EXPECT_TRUE(pm->IsUpdateScheduled());
330}
331
332// Tests the contents of the POST body when there are contents in the
333// local database. This is not exhaustive, as the actual list formatting
334// is covered by SafeBrowsingProtocolManagerTest.TestChunkStrings.
335TEST_F(SafeBrowsingProtocolManagerTest, ExistingDatabase) {
336  scoped_refptr<base::TestSimpleTaskRunner> runner(
337      new base::TestSimpleTaskRunner());
338  base::ThreadTaskRunnerHandle runner_handler(runner);
339  net::TestURLFetcherFactory url_fetcher_factory;
340
341  std::vector<SBListChunkRanges> ranges;
342  SBListChunkRanges range_phish(safe_browsing_util::kPhishingList);
343  range_phish.adds = "adds_phish";
344  range_phish.subs = "subs_phish";
345  ranges.push_back(range_phish);
346
347  SBListChunkRanges range_unknown("unknown_list");
348  range_unknown.adds = "adds_unknown";
349  range_unknown.subs = "subs_unknown";
350  ranges.push_back(range_unknown);
351
352  testing::StrictMock<MockProtocolDelegate> test_delegate;
353  EXPECT_CALL(test_delegate, UpdateStarted()).Times(1);
354  EXPECT_CALL(test_delegate, GetChunks(_)).WillOnce(
355      Invoke(testing::CreateFunctor(InvokeGetChunksCallback,
356                                    ranges,
357                                    false)));
358  EXPECT_CALL(test_delegate, UpdateFinished(true)).Times(1);
359
360  scoped_ptr<SafeBrowsingProtocolManager> pm(
361      CreateProtocolManager(&test_delegate));
362
363  // Kick off initialization. This returns chunks from the DB synchronously.
364  pm->ForceScheduleNextUpdate(TimeDelta());
365  runner->RunPendingTasks();
366
367  // We should have an URLFetcher at this point in time.
368  net::TestURLFetcher* url_fetcher = url_fetcher_factory.GetFetcherByID(0);
369  ASSERT_TRUE(url_fetcher);
370  EXPECT_EQ(net::LOAD_DISABLE_CACHE, url_fetcher->GetLoadFlags());
371  EXPECT_EQ("goog-phish-shavar;a:adds_phish:s:subs_phish\n"
372            "unknown_list;a:adds_unknown:s:subs_unknown\n"
373            "goog-malware-shavar;\n",
374            url_fetcher->upload_data());
375  EXPECT_EQ(GURL("https://prefix.com/foo/downloads?client=unittest&appver=1.0"
376                 "&pver=2.2" + key_param_),
377            url_fetcher->GetOriginalURL());
378
379  url_fetcher->set_status(net::URLRequestStatus());
380  url_fetcher->set_response_code(200);
381  url_fetcher->SetResponseString(std::string());
382  url_fetcher->delegate()->OnURLFetchComplete(url_fetcher);
383
384  EXPECT_TRUE(pm->IsUpdateScheduled());
385}
386
387TEST_F(SafeBrowsingProtocolManagerTest, UpdateResponseBadBodyBackupSuccess) {
388  scoped_refptr<base::TestSimpleTaskRunner> runner(
389      new base::TestSimpleTaskRunner());
390  base::ThreadTaskRunnerHandle runner_handler(runner);
391  net::TestURLFetcherFactory url_fetcher_factory;
392
393  testing::StrictMock<MockProtocolDelegate> test_delegate;
394  EXPECT_CALL(test_delegate, UpdateStarted()).Times(1);
395  EXPECT_CALL(test_delegate, GetChunks(_)).WillOnce(
396      Invoke(testing::CreateFunctor(InvokeGetChunksCallback,
397                                    std::vector<SBListChunkRanges>(),
398                                    false)));
399  EXPECT_CALL(test_delegate, UpdateFinished(true)).Times(1);
400
401  scoped_ptr<SafeBrowsingProtocolManager> pm(
402      CreateProtocolManager(&test_delegate));
403
404  // Kick off initialization. This returns chunks from the DB synchronously.
405  pm->ForceScheduleNextUpdate(TimeDelta());
406  runner->RunPendingTasks();
407
408  // We should have an URLFetcher at this point in time.
409  net::TestURLFetcher* url_fetcher = url_fetcher_factory.GetFetcherByID(0);
410  ValidateUpdateFetcherRequest(url_fetcher);
411
412  // The update response is successful, but an invalid body.
413  url_fetcher->set_status(net::URLRequestStatus());
414  url_fetcher->set_response_code(200);
415  url_fetcher->SetResponseString("THIS_IS_A_BAD_RESPONSE");
416  url_fetcher->delegate()->OnURLFetchComplete(url_fetcher);
417
418  // There should now be a backup request.
419  net::TestURLFetcher* backup_url_fetcher =
420      url_fetcher_factory.GetFetcherByID(1);
421  ValidateUpdateFetcherRequest(backup_url_fetcher,
422                               kBackupHttpUrlPrefix);
423
424  // Respond to the backup successfully.
425  backup_url_fetcher->set_status(net::URLRequestStatus());
426  backup_url_fetcher->set_response_code(200);
427  backup_url_fetcher->SetResponseString(std::string());
428  backup_url_fetcher->delegate()->OnURLFetchComplete(backup_url_fetcher);
429
430  EXPECT_TRUE(pm->IsUpdateScheduled());
431}
432
433// Tests what happens when there is an HTTP error response to the update
434// request, as well as an error response to the backup update request.
435TEST_F(SafeBrowsingProtocolManagerTest, UpdateResponseHttpErrorBackupError) {
436  scoped_refptr<base::TestSimpleTaskRunner> runner(
437      new base::TestSimpleTaskRunner());
438  base::ThreadTaskRunnerHandle runner_handler(runner);
439  net::TestURLFetcherFactory url_fetcher_factory;
440
441  testing::StrictMock<MockProtocolDelegate> test_delegate;
442  EXPECT_CALL(test_delegate, UpdateStarted()).Times(1);
443  EXPECT_CALL(test_delegate, GetChunks(_)).WillOnce(
444      Invoke(testing::CreateFunctor(InvokeGetChunksCallback,
445                                    std::vector<SBListChunkRanges>(),
446                                    false)));
447  EXPECT_CALL(test_delegate, UpdateFinished(false)).Times(1);
448
449  scoped_ptr<SafeBrowsingProtocolManager> pm(
450      CreateProtocolManager(&test_delegate));
451
452  // Kick off initialization. This returns chunks from the DB synchronously.
453  pm->ForceScheduleNextUpdate(TimeDelta());
454  runner->RunPendingTasks();
455
456  // We should have an URLFetcher at this point in time.
457  net::TestURLFetcher* url_fetcher = url_fetcher_factory.GetFetcherByID(0);
458  ValidateUpdateFetcherRequest(url_fetcher);
459
460  // Go ahead and respond to it.
461  url_fetcher->set_status(net::URLRequestStatus());
462  url_fetcher->set_response_code(404);
463  url_fetcher->SetResponseString(std::string());
464  url_fetcher->delegate()->OnURLFetchComplete(url_fetcher);
465
466  // There should now be a backup request.
467  net::TestURLFetcher* backup_url_fetcher =
468      url_fetcher_factory.GetFetcherByID(1);
469  ValidateUpdateFetcherRequest(backup_url_fetcher, kBackupHttpUrlPrefix);
470
471  // Respond to the backup unsuccessfully.
472  backup_url_fetcher->set_status(net::URLRequestStatus());
473  backup_url_fetcher->set_response_code(404);
474  backup_url_fetcher->SetResponseString(std::string());
475  backup_url_fetcher->delegate()->OnURLFetchComplete(backup_url_fetcher);
476
477  EXPECT_TRUE(pm->IsUpdateScheduled());
478}
479
480// Tests what happens when there is an HTTP error response to the update
481// request, followed by a successful response to the backup update request.
482TEST_F(SafeBrowsingProtocolManagerTest, UpdateResponseHttpErrorBackupSuccess) {
483  scoped_refptr<base::TestSimpleTaskRunner> runner(
484      new base::TestSimpleTaskRunner());
485  base::ThreadTaskRunnerHandle runner_handler(runner);
486  net::TestURLFetcherFactory url_fetcher_factory;
487
488  testing::StrictMock<MockProtocolDelegate> test_delegate;
489  EXPECT_CALL(test_delegate, UpdateStarted()).Times(1);
490  EXPECT_CALL(test_delegate, GetChunks(_)).WillOnce(
491      Invoke(testing::CreateFunctor(InvokeGetChunksCallback,
492                                    std::vector<SBListChunkRanges>(),
493                                    false)));
494  EXPECT_CALL(test_delegate, UpdateFinished(true)).Times(1);
495
496  scoped_ptr<SafeBrowsingProtocolManager> pm(
497      CreateProtocolManager(&test_delegate));
498
499  // Kick off initialization. This returns chunks from the DB synchronously.
500  pm->ForceScheduleNextUpdate(TimeDelta());
501  runner->RunPendingTasks();
502
503  // We should have an URLFetcher at this point in time.
504  net::TestURLFetcher* url_fetcher = url_fetcher_factory.GetFetcherByID(0);
505  ValidateUpdateFetcherRequest(url_fetcher);
506
507  // Go ahead and respond to it.
508  url_fetcher->set_status(net::URLRequestStatus());
509  url_fetcher->set_response_code(404);
510  url_fetcher->SetResponseString(std::string());
511  url_fetcher->delegate()->OnURLFetchComplete(url_fetcher);
512
513  // There should now be a backup request.
514  net::TestURLFetcher* backup_url_fetcher =
515      url_fetcher_factory.GetFetcherByID(1);
516  ValidateUpdateFetcherRequest(backup_url_fetcher,
517                               kBackupHttpUrlPrefix);
518
519  // Respond to the backup successfully.
520  backup_url_fetcher->set_status(net::URLRequestStatus());
521  backup_url_fetcher->set_response_code(200);
522  backup_url_fetcher->SetResponseString(std::string());
523  backup_url_fetcher->delegate()->OnURLFetchComplete(backup_url_fetcher);
524
525  EXPECT_TRUE(pm->IsUpdateScheduled());
526}
527
528// Tests what happens when there is an HTTP error response to the update
529// request, and a timeout on the backup update request.
530TEST_F(SafeBrowsingProtocolManagerTest, UpdateResponseHttpErrorBackupTimeout) {
531  scoped_refptr<base::TestSimpleTaskRunner> runner(
532      new base::TestSimpleTaskRunner());
533  base::ThreadTaskRunnerHandle runner_handler(runner);
534  net::TestURLFetcherFactory url_fetcher_factory;
535
536  testing::StrictMock<MockProtocolDelegate> test_delegate;
537  EXPECT_CALL(test_delegate, UpdateStarted()).Times(1);
538  EXPECT_CALL(test_delegate, GetChunks(_)).WillOnce(
539      Invoke(testing::CreateFunctor(InvokeGetChunksCallback,
540                                    std::vector<SBListChunkRanges>(),
541                                    false)));
542  EXPECT_CALL(test_delegate, UpdateFinished(false)).Times(1);
543
544  scoped_ptr<SafeBrowsingProtocolManager> pm(
545      CreateProtocolManager(&test_delegate));
546
547  // Kick off initialization. This returns chunks from the DB synchronously.
548  pm->ForceScheduleNextUpdate(TimeDelta());
549  runner->RunPendingTasks();
550
551  // We should have an URLFetcher at this point in time.
552  net::TestURLFetcher* url_fetcher = url_fetcher_factory.GetFetcherByID(0);
553  ValidateUpdateFetcherRequest(url_fetcher);
554
555  // Go ahead and respond to it.
556  url_fetcher->set_status(net::URLRequestStatus());
557  url_fetcher->set_response_code(404);
558  url_fetcher->SetResponseString(std::string());
559  url_fetcher->delegate()->OnURLFetchComplete(url_fetcher);
560
561  // There should now be a backup request.
562  net::TestURLFetcher* backup_url_fetcher =
563      url_fetcher_factory.GetFetcherByID(1);
564  ValidateUpdateFetcherRequest(backup_url_fetcher, kBackupHttpUrlPrefix);
565
566  // Either one or two calls to RunPendingTasks are needed here. The first run
567  // of RunPendingTasks will run the canceled timeout task associated with
568  // the first Update request. Depending on timing, this will either directly
569  // call the timeout task from the backup request, or schedule another task
570  // to run that in the future.
571  // TODO(cbentzel): Less fragile approach.
572  runner->RunPendingTasks();
573  if (!pm->IsUpdateScheduled())
574    runner->RunPendingTasks();
575  EXPECT_TRUE(pm->IsUpdateScheduled());
576}
577
578// Tests what happens when there is a connection error when issuing the update
579// request, and an error with the backup update request.
580TEST_F(SafeBrowsingProtocolManagerTest,
581       UpdateResponseConnectionErrorBackupError) {
582  scoped_refptr<base::TestSimpleTaskRunner> runner(
583      new base::TestSimpleTaskRunner());
584  base::ThreadTaskRunnerHandle runner_handler(runner);
585  net::TestURLFetcherFactory url_fetcher_factory;
586
587  testing::StrictMock<MockProtocolDelegate> test_delegate;
588  EXPECT_CALL(test_delegate, UpdateStarted()).Times(1);
589  EXPECT_CALL(test_delegate, GetChunks(_)).WillOnce(
590      Invoke(testing::CreateFunctor(InvokeGetChunksCallback,
591                                    std::vector<SBListChunkRanges>(),
592                                    false)));
593  EXPECT_CALL(test_delegate, UpdateFinished(false)).Times(1);
594
595  scoped_ptr<SafeBrowsingProtocolManager> pm(
596      CreateProtocolManager(&test_delegate));
597
598  // Kick off initialization. This returns chunks from the DB synchronously.
599  pm->ForceScheduleNextUpdate(TimeDelta());
600  runner->RunPendingTasks();
601
602  // We should have an URLFetcher at this point in time.
603  net::TestURLFetcher* url_fetcher = url_fetcher_factory.GetFetcherByID(0);
604  ValidateUpdateFetcherRequest(url_fetcher);
605
606  // Go ahead and respond to it.
607  url_fetcher->set_status(net::URLRequestStatus(net::URLRequestStatus::FAILED,
608                                                net::ERR_CONNECTION_RESET));
609  url_fetcher->delegate()->OnURLFetchComplete(url_fetcher);
610
611  // There should be a backup URLFetcher now.
612  net::TestURLFetcher* backup_url_fetcher =
613      url_fetcher_factory.GetFetcherByID(1);
614  ValidateUpdateFetcherRequest(backup_url_fetcher,
615                               kBackupConnectUrlPrefix);
616
617  // Respond to the backup unsuccessfully.
618  backup_url_fetcher->set_status(net::URLRequestStatus());
619  backup_url_fetcher->set_response_code(404);
620  backup_url_fetcher->SetResponseString(std::string());
621  backup_url_fetcher->delegate()->OnURLFetchComplete(backup_url_fetcher);
622
623  EXPECT_TRUE(pm->IsUpdateScheduled());
624}
625
626// Tests what happens when there is a connection error when issuing the update
627// request, and a successful response to the backup update request.
628TEST_F(SafeBrowsingProtocolManagerTest,
629       UpdateResponseConnectionErrorBackupSuccess) {
630  scoped_refptr<base::TestSimpleTaskRunner> runner(
631      new base::TestSimpleTaskRunner());
632  base::ThreadTaskRunnerHandle runner_handler(runner);
633  net::TestURLFetcherFactory url_fetcher_factory;
634
635  testing::StrictMock<MockProtocolDelegate> test_delegate;
636  EXPECT_CALL(test_delegate, UpdateStarted()).Times(1);
637  EXPECT_CALL(test_delegate, GetChunks(_)).WillOnce(
638      Invoke(testing::CreateFunctor(InvokeGetChunksCallback,
639                                    std::vector<SBListChunkRanges>(),
640                                    false)));
641  EXPECT_CALL(test_delegate, UpdateFinished(true)).Times(1);
642
643  scoped_ptr<SafeBrowsingProtocolManager> pm(
644      CreateProtocolManager(&test_delegate));
645
646  // Kick off initialization. This returns chunks from the DB synchronously.
647  pm->ForceScheduleNextUpdate(TimeDelta());
648  runner->RunPendingTasks();
649
650  // We should have an URLFetcher at this point in time.
651  net::TestURLFetcher* url_fetcher = url_fetcher_factory.GetFetcherByID(0);
652  ValidateUpdateFetcherRequest(url_fetcher);
653
654  // Go ahead and respond to it.
655  url_fetcher->set_status(net::URLRequestStatus(net::URLRequestStatus::FAILED,
656                                                net::ERR_CONNECTION_RESET));
657  url_fetcher->delegate()->OnURLFetchComplete(url_fetcher);
658
659  // There should be a backup URLFetcher now.
660  net::TestURLFetcher* backup_url_fetcher =
661      url_fetcher_factory.GetFetcherByID(1);
662  ValidateUpdateFetcherRequest(backup_url_fetcher,
663                               kBackupConnectUrlPrefix);
664
665  // Respond to the backup unsuccessfully.
666  backup_url_fetcher->set_status(net::URLRequestStatus());
667  backup_url_fetcher->set_response_code(200);
668  backup_url_fetcher->SetResponseString(std::string());
669  backup_url_fetcher->delegate()->OnURLFetchComplete(backup_url_fetcher);
670
671  EXPECT_TRUE(pm->IsUpdateScheduled());
672}
673// Tests what happens when there is a network state error when issuing the
674// update request, and an error with the backup update request.
675TEST_F(SafeBrowsingProtocolManagerTest,
676       UpdateResponseNetworkErrorBackupError) {
677  scoped_refptr<base::TestSimpleTaskRunner> runner(
678      new base::TestSimpleTaskRunner());
679  base::ThreadTaskRunnerHandle runner_handler(runner);
680  net::TestURLFetcherFactory url_fetcher_factory;
681
682  testing::StrictMock<MockProtocolDelegate> test_delegate;
683  EXPECT_CALL(test_delegate, UpdateStarted()).Times(1);
684  EXPECT_CALL(test_delegate, GetChunks(_)).WillOnce(
685      Invoke(testing::CreateFunctor(InvokeGetChunksCallback,
686                                    std::vector<SBListChunkRanges>(),
687                                    false)));
688  EXPECT_CALL(test_delegate, UpdateFinished(false)).Times(1);
689
690  scoped_ptr<SafeBrowsingProtocolManager> pm(
691      CreateProtocolManager(&test_delegate));
692
693  // Kick off initialization. This returns chunks from the DB synchronously.
694  pm->ForceScheduleNextUpdate(TimeDelta());
695  runner->RunPendingTasks();
696
697  // We should have an URLFetcher at this point in time.
698  net::TestURLFetcher* url_fetcher = url_fetcher_factory.GetFetcherByID(0);
699  ValidateUpdateFetcherRequest(url_fetcher);
700
701  // Go ahead and respond to it.
702  url_fetcher->set_status(
703      net::URLRequestStatus(net::URLRequestStatus::FAILED,
704                            net::ERR_INTERNET_DISCONNECTED));
705  url_fetcher->delegate()->OnURLFetchComplete(url_fetcher);
706
707  // There should be a backup URLFetcher now.
708  net::TestURLFetcher* backup_url_fetcher =
709      url_fetcher_factory.GetFetcherByID(1);
710  ValidateUpdateFetcherRequest(backup_url_fetcher,
711                               kBackupNetworkUrlPrefix);
712
713  // Respond to the backup unsuccessfully.
714  backup_url_fetcher->set_status(net::URLRequestStatus());
715  backup_url_fetcher->set_response_code(404);
716  backup_url_fetcher->SetResponseString(std::string());
717  backup_url_fetcher->delegate()->OnURLFetchComplete(backup_url_fetcher);
718
719  EXPECT_TRUE(pm->IsUpdateScheduled());
720}
721
722// Tests what happens when there is a network state error when issuing the
723// update request, and a successful response to the backup update request.
724TEST_F(SafeBrowsingProtocolManagerTest,
725       UpdateResponseNetworkErrorBackupSuccess) {
726  scoped_refptr<base::TestSimpleTaskRunner> runner(
727      new base::TestSimpleTaskRunner());
728  base::ThreadTaskRunnerHandle runner_handler(runner);
729  net::TestURLFetcherFactory url_fetcher_factory;
730
731  testing::StrictMock<MockProtocolDelegate> test_delegate;
732  EXPECT_CALL(test_delegate, UpdateStarted()).Times(1);
733  EXPECT_CALL(test_delegate, GetChunks(_)).WillOnce(
734      Invoke(testing::CreateFunctor(InvokeGetChunksCallback,
735                                    std::vector<SBListChunkRanges>(),
736                                    false)));
737  EXPECT_CALL(test_delegate, UpdateFinished(true)).Times(1);
738
739  scoped_ptr<SafeBrowsingProtocolManager> pm(
740      CreateProtocolManager(&test_delegate));
741
742  // Kick off initialization. This returns chunks from the DB synchronously.
743  pm->ForceScheduleNextUpdate(TimeDelta());
744  runner->RunPendingTasks();
745
746  // We should have an URLFetcher at this point in time.
747  net::TestURLFetcher* url_fetcher = url_fetcher_factory.GetFetcherByID(0);
748  ValidateUpdateFetcherRequest(url_fetcher);
749
750  // Go ahead and respond to it.
751  url_fetcher->set_status(
752      net::URLRequestStatus(net::URLRequestStatus::FAILED,
753                            net::ERR_INTERNET_DISCONNECTED));
754  url_fetcher->delegate()->OnURLFetchComplete(url_fetcher);
755
756  // There should be a backup URLFetcher now.
757  net::TestURLFetcher* backup_url_fetcher =
758      url_fetcher_factory.GetFetcherByID(1);
759  ValidateUpdateFetcherRequest(backup_url_fetcher,
760                               kBackupNetworkUrlPrefix);
761
762  // Respond to the backup unsuccessfully.
763  backup_url_fetcher->set_status(net::URLRequestStatus());
764  backup_url_fetcher->set_response_code(200);
765  backup_url_fetcher->SetResponseString(std::string());
766  backup_url_fetcher->delegate()->OnURLFetchComplete(backup_url_fetcher);
767
768  EXPECT_TRUE(pm->IsUpdateScheduled());
769}
770
771// Tests what happens when there is a timeout before an update response.
772TEST_F(SafeBrowsingProtocolManagerTest, UpdateResponseTimeoutBackupSuccess) {
773  scoped_refptr<base::TestSimpleTaskRunner> runner(
774      new base::TestSimpleTaskRunner());
775  base::ThreadTaskRunnerHandle runner_handler(runner);
776  net::TestURLFetcherFactory url_fetcher_factory;
777
778  testing::StrictMock<MockProtocolDelegate> test_delegate;
779  EXPECT_CALL(test_delegate, UpdateStarted()).Times(1);
780  EXPECT_CALL(test_delegate, GetChunks(_)).WillOnce(
781      Invoke(testing::CreateFunctor(InvokeGetChunksCallback,
782                                    std::vector<SBListChunkRanges>(),
783                                    false)));
784  EXPECT_CALL(test_delegate, UpdateFinished(true)).Times(1);
785
786  scoped_ptr<SafeBrowsingProtocolManager> pm(
787      CreateProtocolManager(&test_delegate));
788
789  // Kick off initialization. This returns chunks from the DB synchronously.
790  pm->ForceScheduleNextUpdate(TimeDelta());
791  runner->RunPendingTasks();
792
793  // We should have an URLFetcher at this point in time.
794  net::TestURLFetcher* url_fetcher = url_fetcher_factory.GetFetcherByID(0);
795  ValidateUpdateFetcherRequest(url_fetcher);
796
797  // The first time RunPendingTasks is called above, the update timeout timer is
798  // not handled. This call of RunPendingTasks will handle the update.
799  runner->RunPendingTasks();
800
801  // There should be a backup URLFetcher now.
802  net::TestURLFetcher* backup_url_fetcher =
803      url_fetcher_factory.GetFetcherByID(1);
804  ValidateUpdateFetcherRequest(backup_url_fetcher,
805                               kBackupConnectUrlPrefix);
806
807  // Respond to the backup unsuccessfully.
808  backup_url_fetcher->set_status(net::URLRequestStatus());
809  backup_url_fetcher->set_response_code(200);
810  backup_url_fetcher->SetResponseString(std::string());
811  backup_url_fetcher->delegate()->OnURLFetchComplete(backup_url_fetcher);
812
813  EXPECT_TRUE(pm->IsUpdateScheduled());
814}
815
816// Tests what happens when there is a reset command in the response.
817TEST_F(SafeBrowsingProtocolManagerTest, UpdateResponseReset) {
818  scoped_refptr<base::TestSimpleTaskRunner> runner(
819      new base::TestSimpleTaskRunner());
820  base::ThreadTaskRunnerHandle runner_handler(runner);
821  net::TestURLFetcherFactory url_fetcher_factory;
822
823  testing::StrictMock<MockProtocolDelegate> test_delegate;
824  EXPECT_CALL(test_delegate, UpdateStarted()).Times(1);
825  EXPECT_CALL(test_delegate, GetChunks(_)).WillOnce(
826      Invoke(testing::CreateFunctor(InvokeGetChunksCallback,
827                                    std::vector<SBListChunkRanges>(),
828                                    false)));
829  EXPECT_CALL(test_delegate, ResetDatabase()).Times(1);
830  EXPECT_CALL(test_delegate, UpdateFinished(true)).Times(1);
831
832  scoped_ptr<SafeBrowsingProtocolManager> pm(
833      CreateProtocolManager(&test_delegate));
834
835  // Kick off initialization. This returns chunks from the DB synchronously.
836  pm->ForceScheduleNextUpdate(TimeDelta());
837  runner->RunPendingTasks();
838
839  net::TestURLFetcher* url_fetcher = url_fetcher_factory.GetFetcherByID(0);
840  ValidateUpdateFetcherRequest(url_fetcher);
841
842  // The update response is successful, and has a reset command.
843  url_fetcher->set_status(net::URLRequestStatus());
844  url_fetcher->set_response_code(200);
845  url_fetcher->SetResponseString("r:pleasereset\n");
846  url_fetcher->delegate()->OnURLFetchComplete(url_fetcher);
847
848  EXPECT_TRUE(pm->IsUpdateScheduled());
849}
850
851// Tests a single valid update response, followed by a single redirect response
852// that has an valid, but empty body.
853TEST_F(SafeBrowsingProtocolManagerTest, EmptyRedirectResponse) {
854  scoped_refptr<base::TestSimpleTaskRunner> runner(
855      new base::TestSimpleTaskRunner());
856  base::ThreadTaskRunnerHandle runner_handler(runner);
857  net::TestURLFetcherFactory url_fetcher_factory;
858
859  testing::StrictMock<MockProtocolDelegate> test_delegate;
860  EXPECT_CALL(test_delegate, UpdateStarted()).Times(1);
861  EXPECT_CALL(test_delegate, GetChunks(_)).WillOnce(
862      Invoke(testing::CreateFunctor(InvokeGetChunksCallback,
863                                    std::vector<SBListChunkRanges>(),
864                                    false)));
865  EXPECT_CALL(test_delegate, UpdateFinished(true)).Times(1);
866
867  scoped_ptr<SafeBrowsingProtocolManager> pm(
868      CreateProtocolManager(&test_delegate));
869
870  // Kick off initialization. This returns chunks from the DB synchronously.
871  pm->ForceScheduleNextUpdate(TimeDelta());
872  runner->RunPendingTasks();
873
874  // The update response contains a single redirect command.
875  net::TestURLFetcher* url_fetcher = url_fetcher_factory.GetFetcherByID(0);
876  ValidateUpdateFetcherRequest(url_fetcher);
877  url_fetcher->set_status(net::URLRequestStatus());
878  url_fetcher->set_response_code(200);
879  url_fetcher->SetResponseString(
880      "i:goog-phish-shavar\n"
881      "u:redirect-server.example.com/path\n");
882  url_fetcher->delegate()->OnURLFetchComplete(url_fetcher);
883
884  // The redirect response contains an empty body.
885  net::TestURLFetcher* chunk_url_fetcher =
886      url_fetcher_factory.GetFetcherByID(1);
887  ValidateRedirectFetcherRequest(
888      chunk_url_fetcher, "https://redirect-server.example.com/path");
889  chunk_url_fetcher->set_status(net::URLRequestStatus());
890  chunk_url_fetcher->set_response_code(200);
891  chunk_url_fetcher->SetResponseString(std::string());
892  chunk_url_fetcher->delegate()->OnURLFetchComplete(chunk_url_fetcher);
893
894  EXPECT_TRUE(pm->IsUpdateScheduled());
895}
896
897// Tests a single valid update response, followed by a single redirect response
898// that has an invalid body.
899TEST_F(SafeBrowsingProtocolManagerTest, InvalidRedirectResponse) {
900  scoped_refptr<base::TestSimpleTaskRunner> runner(
901      new base::TestSimpleTaskRunner());
902  base::ThreadTaskRunnerHandle runner_handler(runner);
903  net::TestURLFetcherFactory url_fetcher_factory;
904
905  testing::StrictMock<MockProtocolDelegate> test_delegate;
906  EXPECT_CALL(test_delegate, UpdateStarted()).Times(1);
907  EXPECT_CALL(test_delegate, GetChunks(_)).WillOnce(
908      Invoke(testing::CreateFunctor(InvokeGetChunksCallback,
909                                    std::vector<SBListChunkRanges>(),
910                                    false)));
911  EXPECT_CALL(test_delegate, UpdateFinished(false)).Times(1);
912
913  scoped_ptr<SafeBrowsingProtocolManager> pm(
914      CreateProtocolManager(&test_delegate));
915
916  // Kick off initialization. This returns chunks from the DB synchronously.
917  pm->ForceScheduleNextUpdate(TimeDelta());
918  runner->RunPendingTasks();
919
920  // The update response contains a single redirect command.
921  net::TestURLFetcher* url_fetcher = url_fetcher_factory.GetFetcherByID(0);
922  ValidateUpdateFetcherRequest(url_fetcher);
923  url_fetcher->set_status(net::URLRequestStatus());
924  url_fetcher->set_response_code(200);
925  url_fetcher->SetResponseString(
926      "i:goog-phish-shavar\n"
927      "u:redirect-server.example.com/path\n");
928  url_fetcher->delegate()->OnURLFetchComplete(url_fetcher);
929
930  // The redirect response contains an invalid body.
931  net::TestURLFetcher* chunk_url_fetcher =
932      url_fetcher_factory.GetFetcherByID(1);
933  ValidateRedirectFetcherRequest(
934      chunk_url_fetcher, "https://redirect-server.example.com/path");
935  chunk_url_fetcher->set_status(net::URLRequestStatus());
936  chunk_url_fetcher->set_response_code(200);
937  chunk_url_fetcher->SetResponseString("THIS IS AN INVALID RESPONSE");
938  chunk_url_fetcher->delegate()->OnURLFetchComplete(chunk_url_fetcher);
939
940  EXPECT_TRUE(pm->IsUpdateScheduled());
941}
942
943// Tests a single valid update response, followed by a single redirect response
944// containing chunks.
945TEST_F(SafeBrowsingProtocolManagerTest, SingleRedirectResponseWithChunks) {
946  scoped_refptr<base::TestSimpleTaskRunner> runner(
947      new base::TestSimpleTaskRunner());
948  base::ThreadTaskRunnerHandle runner_handler(runner);
949  net::TestURLFetcherFactory url_fetcher_factory;
950
951  testing::StrictMock<MockProtocolDelegate> test_delegate;
952  EXPECT_CALL(test_delegate, UpdateStarted()).Times(1);
953  EXPECT_CALL(test_delegate, GetChunks(_)).WillOnce(
954      Invoke(testing::CreateFunctor(InvokeGetChunksCallback,
955                                    std::vector<SBListChunkRanges>(),
956                                    false)));
957  EXPECT_CALL(test_delegate, AddChunks("goog-phish-shavar", _, _)).WillOnce(
958      Invoke(HandleAddChunks));
959  EXPECT_CALL(test_delegate, UpdateFinished(true)).Times(1);
960
961  scoped_ptr<SafeBrowsingProtocolManager> pm(
962      CreateProtocolManager(&test_delegate));
963
964  // Kick off initialization. This returns chunks from the DB synchronously.
965  pm->ForceScheduleNextUpdate(TimeDelta());
966  runner->RunPendingTasks();
967
968  // The update response contains a single redirect command.
969  net::TestURLFetcher* url_fetcher = url_fetcher_factory.GetFetcherByID(0);
970  ValidateUpdateFetcherRequest(url_fetcher);
971  url_fetcher->set_status(net::URLRequestStatus());
972  url_fetcher->set_response_code(200);
973  url_fetcher->SetResponseString(
974      "i:goog-phish-shavar\n"
975      "u:redirect-server.example.com/path\n");
976  url_fetcher->delegate()->OnURLFetchComplete(url_fetcher);
977
978  // The redirect response contains a single chunk.
979  net::TestURLFetcher* chunk_url_fetcher =
980      url_fetcher_factory.GetFetcherByID(1);
981  ValidateRedirectFetcherRequest(
982      chunk_url_fetcher, "https://redirect-server.example.com/path");
983  chunk_url_fetcher->set_status(net::URLRequestStatus());
984  chunk_url_fetcher->set_response_code(200);
985  chunk_url_fetcher->SetResponseString(
986      "a:4:4:9\n"
987      "host\1fdaf");
988  chunk_url_fetcher->delegate()->OnURLFetchComplete(chunk_url_fetcher);
989
990  EXPECT_FALSE(pm->IsUpdateScheduled());
991
992  // The AddChunksCallback needs to be invoked.
993  runner->RunPendingTasks();
994
995  EXPECT_TRUE(pm->IsUpdateScheduled());
996}
997
998// Tests a single valid update response, followed by multiple redirect responses
999// containing chunks.
1000TEST_F(SafeBrowsingProtocolManagerTest, MultipleRedirectResponsesWithChunks) {
1001  scoped_refptr<base::TestSimpleTaskRunner> runner(
1002      new base::TestSimpleTaskRunner());
1003  base::ThreadTaskRunnerHandle runner_handler(runner);
1004  net::TestURLFetcherFactory url_fetcher_factory;
1005
1006  testing::StrictMock<MockProtocolDelegate> test_delegate;
1007  EXPECT_CALL(test_delegate, UpdateStarted()).Times(1);
1008  EXPECT_CALL(test_delegate, GetChunks(_)).WillOnce(
1009      Invoke(testing::CreateFunctor(InvokeGetChunksCallback,
1010                                    std::vector<SBListChunkRanges>(),
1011                                    false)));
1012  EXPECT_CALL(test_delegate, AddChunks("goog-phish-shavar", _, _)).
1013      WillRepeatedly(Invoke(HandleAddChunks));
1014  EXPECT_CALL(test_delegate, UpdateFinished(true)).Times(1);
1015
1016  scoped_ptr<SafeBrowsingProtocolManager> pm(
1017      CreateProtocolManager(&test_delegate));
1018
1019  // Kick off initialization. This returns chunks from the DB synchronously.
1020  pm->ForceScheduleNextUpdate(TimeDelta());
1021  runner->RunPendingTasks();
1022
1023  // The update response contains multiple redirect commands.
1024  net::TestURLFetcher* url_fetcher = url_fetcher_factory.GetFetcherByID(0);
1025  ValidateUpdateFetcherRequest(url_fetcher);
1026  url_fetcher->set_status(net::URLRequestStatus());
1027  url_fetcher->set_response_code(200);
1028  url_fetcher->SetResponseString(
1029      "i:goog-phish-shavar\n"
1030      "u:redirect-server.example.com/one\n"
1031      "u:redirect-server.example.com/two\n");
1032  url_fetcher->delegate()->OnURLFetchComplete(url_fetcher);
1033
1034  // The first redirect response contains a single chunk.
1035  net::TestURLFetcher* first_chunk_url_fetcher =
1036      url_fetcher_factory.GetFetcherByID(1);
1037  ValidateRedirectFetcherRequest(
1038      first_chunk_url_fetcher, "https://redirect-server.example.com/one");
1039  first_chunk_url_fetcher->set_status(net::URLRequestStatus());
1040  first_chunk_url_fetcher->set_response_code(200);
1041  first_chunk_url_fetcher->SetResponseString(
1042      "a:4:4:9\n"
1043      "host\1aaaa");
1044  first_chunk_url_fetcher->delegate()->OnURLFetchComplete(
1045      first_chunk_url_fetcher);
1046
1047  // Invoke the AddChunksCallback to trigger the second request.
1048  runner->RunPendingTasks();
1049
1050  EXPECT_FALSE(pm->IsUpdateScheduled());
1051
1052  // The second redirect response contains a single chunk.
1053  net::TestURLFetcher* second_chunk_url_fetcher =
1054      url_fetcher_factory.GetFetcherByID(2);
1055  ValidateRedirectFetcherRequest(
1056      second_chunk_url_fetcher, "https://redirect-server.example.com/two");
1057  second_chunk_url_fetcher->set_status(net::URLRequestStatus());
1058  second_chunk_url_fetcher->set_response_code(200);
1059  second_chunk_url_fetcher->SetResponseString(
1060      "a:5:4:9\n"
1061      "host\1bbbb");
1062  second_chunk_url_fetcher->delegate()->OnURLFetchComplete(
1063      second_chunk_url_fetcher);
1064
1065  EXPECT_FALSE(pm->IsUpdateScheduled());
1066
1067  // Invoke the AddChunksCallback to finish the update.
1068  runner->RunPendingTasks();
1069
1070  EXPECT_TRUE(pm->IsUpdateScheduled());
1071}
1072