Home | History | Annotate | Download | only in safe_browsing
      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 #include "chrome/browser/safe_browsing/protocol_manager.h"
      6 
      7 #include "base/environment.h"
      8 #include "base/logging.h"
      9 #include "base/memory/scoped_vector.h"
     10 #include "base/metrics/histogram.h"
     11 #include "base/rand_util.h"
     12 #include "base/stl_util.h"
     13 #include "base/strings/string_util.h"
     14 #include "base/strings/stringprintf.h"
     15 #include "base/timer/timer.h"
     16 #include "chrome/browser/safe_browsing/protocol_parser.h"
     17 #include "chrome/common/chrome_version_info.h"
     18 #include "chrome/common/env_vars.h"
     19 #include "google_apis/google_api_keys.h"
     20 #include "net/base/escape.h"
     21 #include "net/base/load_flags.h"
     22 #include "net/base/net_errors.h"
     23 #include "net/url_request/url_fetcher.h"
     24 #include "net/url_request/url_request_context_getter.h"
     25 #include "net/url_request/url_request_status.h"
     26 
     27 #if defined(OS_ANDROID)
     28 #include "net/base/network_change_notifier.h"
     29 #endif
     30 
     31 using base::Time;
     32 using base::TimeDelta;
     33 
     34 namespace {
     35 
     36 // UpdateResult indicates what happened with the primary and/or backup update
     37 // requests. The ordering of the values must stay the same for UMA consistency,
     38 // and is also ordered in this way to match ProtocolManager::BackupUpdateReason.
     39 enum UpdateResult {
     40   UPDATE_RESULT_FAIL,
     41   UPDATE_RESULT_SUCCESS,
     42   UPDATE_RESULT_BACKUP_CONNECT_FAIL,
     43   UPDATE_RESULT_BACKUP_CONNECT_SUCCESS,
     44   UPDATE_RESULT_BACKUP_HTTP_FAIL,
     45   UPDATE_RESULT_BACKUP_HTTP_SUCCESS,
     46   UPDATE_RESULT_BACKUP_NETWORK_FAIL,
     47   UPDATE_RESULT_BACKUP_NETWORK_SUCCESS,
     48   UPDATE_RESULT_MAX,
     49   UPDATE_RESULT_BACKUP_START = UPDATE_RESULT_BACKUP_CONNECT_FAIL,
     50 };
     51 
     52 void RecordUpdateResult(UpdateResult result) {
     53   DCHECK(result >= 0 && result < UPDATE_RESULT_MAX);
     54   UMA_HISTOGRAM_ENUMERATION("SB2.UpdateResult", result, UPDATE_RESULT_MAX);
     55 }
     56 
     57 }  // namespace
     58 
     59 // Minimum time, in seconds, from start up before we must issue an update query.
     60 static const int kSbTimerStartIntervalSecMin = 60;
     61 
     62 // Maximum time, in seconds, from start up before we must issue an update query.
     63 static const int kSbTimerStartIntervalSecMax = 300;
     64 
     65 // The maximum time, in seconds, to wait for a response to an update request.
     66 static const int kSbMaxUpdateWaitSec = 30;
     67 
     68 // Maximum back off multiplier.
     69 static const size_t kSbMaxBackOff = 8;
     70 
     71 // The default SBProtocolManagerFactory.
     72 class SBProtocolManagerFactoryImpl : public SBProtocolManagerFactory {
     73  public:
     74   SBProtocolManagerFactoryImpl() { }
     75   virtual ~SBProtocolManagerFactoryImpl() { }
     76   virtual SafeBrowsingProtocolManager* CreateProtocolManager(
     77       SafeBrowsingProtocolManagerDelegate* delegate,
     78       net::URLRequestContextGetter* request_context_getter,
     79       const SafeBrowsingProtocolConfig& config) OVERRIDE {
     80     return new SafeBrowsingProtocolManager(
     81         delegate, request_context_getter, config);
     82   }
     83  private:
     84   DISALLOW_COPY_AND_ASSIGN(SBProtocolManagerFactoryImpl);
     85 };
     86 
     87 // SafeBrowsingProtocolManager implementation ----------------------------------
     88 
     89 // static
     90 SBProtocolManagerFactory* SafeBrowsingProtocolManager::factory_ = NULL;
     91 
     92 // static
     93 SafeBrowsingProtocolManager* SafeBrowsingProtocolManager::Create(
     94     SafeBrowsingProtocolManagerDelegate* delegate,
     95     net::URLRequestContextGetter* request_context_getter,
     96     const SafeBrowsingProtocolConfig& config) {
     97   if (!factory_)
     98     factory_ = new SBProtocolManagerFactoryImpl();
     99   return factory_->CreateProtocolManager(
    100       delegate, request_context_getter, config);
    101 }
    102 
    103 SafeBrowsingProtocolManager::SafeBrowsingProtocolManager(
    104     SafeBrowsingProtocolManagerDelegate* delegate,
    105     net::URLRequestContextGetter* request_context_getter,
    106     const SafeBrowsingProtocolConfig& config)
    107     : delegate_(delegate),
    108       request_type_(NO_REQUEST),
    109       update_error_count_(0),
    110       gethash_error_count_(0),
    111       update_back_off_mult_(1),
    112       gethash_back_off_mult_(1),
    113       next_update_interval_(base::TimeDelta::FromSeconds(
    114           base::RandInt(kSbTimerStartIntervalSecMin,
    115                         kSbTimerStartIntervalSecMax))),
    116       update_state_(FIRST_REQUEST),
    117       chunk_pending_to_write_(false),
    118       version_(config.version),
    119       update_size_(0),
    120       client_name_(config.client_name),
    121       request_context_getter_(request_context_getter),
    122       url_prefix_(config.url_prefix),
    123       backup_update_reason_(BACKUP_UPDATE_REASON_MAX),
    124       disable_auto_update_(config.disable_auto_update),
    125       url_fetcher_id_(0),
    126       app_in_foreground_(true) {
    127   DCHECK(!url_prefix_.empty());
    128 
    129   backup_url_prefixes_[BACKUP_UPDATE_REASON_CONNECT] =
    130       config.backup_connect_error_url_prefix;
    131   backup_url_prefixes_[BACKUP_UPDATE_REASON_HTTP] =
    132       config.backup_http_error_url_prefix;
    133   backup_url_prefixes_[BACKUP_UPDATE_REASON_NETWORK] =
    134       config.backup_network_error_url_prefix;
    135 
    136   // Set the backoff multiplier fuzz to a random value between 0 and 1.
    137   back_off_fuzz_ = static_cast<float>(base::RandDouble());
    138   if (version_.empty())
    139     version_ = SafeBrowsingProtocolManagerHelper::Version();
    140 }
    141 
    142 // static
    143 void SafeBrowsingProtocolManager::RecordGetHashResult(
    144     bool is_download, ResultType result_type) {
    145   if (is_download) {
    146     UMA_HISTOGRAM_ENUMERATION("SB2.GetHashResultDownload", result_type,
    147                               GET_HASH_RESULT_MAX);
    148   } else {
    149     UMA_HISTOGRAM_ENUMERATION("SB2.GetHashResult", result_type,
    150                               GET_HASH_RESULT_MAX);
    151   }
    152 }
    153 
    154 bool SafeBrowsingProtocolManager::IsUpdateScheduled() const {
    155   return update_timer_.IsRunning();
    156 }
    157 
    158 SafeBrowsingProtocolManager::~SafeBrowsingProtocolManager() {
    159   // Delete in-progress SafeBrowsing requests.
    160   STLDeleteContainerPairFirstPointers(hash_requests_.begin(),
    161                                       hash_requests_.end());
    162   hash_requests_.clear();
    163 }
    164 
    165 // We can only have one update or chunk request outstanding, but there may be
    166 // multiple GetHash requests pending since we don't want to serialize them and
    167 // slow down the user.
    168 void SafeBrowsingProtocolManager::GetFullHash(
    169     const std::vector<SBPrefix>& prefixes,
    170     FullHashCallback callback,
    171     bool is_download) {
    172   DCHECK(CalledOnValidThread());
    173   // If we are in GetHash backoff, we need to check if we're past the next
    174   // allowed time. If we are, we can proceed with the request. If not, we are
    175   // required to return empty results (i.e. treat the page as safe).
    176   if (gethash_error_count_ && Time::Now() <= next_gethash_time_) {
    177     RecordGetHashResult(is_download, GET_HASH_BACKOFF_ERROR);
    178     std::vector<SBFullHashResult> full_hashes;
    179     callback.Run(full_hashes, base::TimeDelta());
    180     return;
    181   }
    182   GURL gethash_url = GetHashUrl();
    183   net::URLFetcher* fetcher = net::URLFetcher::Create(
    184       url_fetcher_id_++, gethash_url, net::URLFetcher::POST, this);
    185   hash_requests_[fetcher] = FullHashDetails(callback, is_download);
    186 
    187   const std::string get_hash = safe_browsing::FormatGetHash(prefixes);
    188 
    189   fetcher->SetLoadFlags(net::LOAD_DISABLE_CACHE);
    190   fetcher->SetRequestContext(request_context_getter_.get());
    191   fetcher->SetUploadData("text/plain", get_hash);
    192   fetcher->Start();
    193 }
    194 
    195 void SafeBrowsingProtocolManager::GetNextUpdate() {
    196   DCHECK(CalledOnValidThread());
    197   if (request_.get() || request_type_ != NO_REQUEST)
    198     return;
    199 
    200 #if defined(OS_ANDROID)
    201   net::NetworkChangeNotifier::ConnectionType type =
    202     net::NetworkChangeNotifier::GetConnectionType();
    203   if (type != net::NetworkChangeNotifier::CONNECTION_WIFI) {
    204     ScheduleNextUpdate(false /* no back off */);
    205     return;
    206   }
    207 #endif
    208 
    209   IssueUpdateRequest();
    210 }
    211 
    212 // net::URLFetcherDelegate implementation ----------------------------------
    213 
    214 // All SafeBrowsing request responses are handled here.
    215 // TODO(paulg): Clarify with the SafeBrowsing team whether a failed parse of a
    216 //              chunk should retry the download and parse of that chunk (and
    217 //              what back off / how many times to try), and if that effects the
    218 //              update back off. For now, a failed parse of the chunk means we
    219 //              drop it. This isn't so bad because the next UPDATE_REQUEST we
    220 //              do will report all the chunks we have. If that chunk is still
    221 //              required, the SafeBrowsing servers will tell us to get it again.
    222 void SafeBrowsingProtocolManager::OnURLFetchComplete(
    223     const net::URLFetcher* source) {
    224   DCHECK(CalledOnValidThread());
    225   scoped_ptr<const net::URLFetcher> fetcher;
    226 
    227   HashRequests::iterator it = hash_requests_.find(source);
    228   if (it != hash_requests_.end()) {
    229     // GetHash response.
    230     fetcher.reset(it->first);
    231     const FullHashDetails& details = it->second;
    232     std::vector<SBFullHashResult> full_hashes;
    233     base::TimeDelta cache_lifetime;
    234     if (source->GetStatus().is_success() &&
    235         (source->GetResponseCode() == 200 ||
    236          source->GetResponseCode() == 204)) {
    237       // For tracking our GetHash false positive (204) rate, compared to real
    238       // (200) responses.
    239       if (source->GetResponseCode() == 200)
    240         RecordGetHashResult(details.is_download, GET_HASH_STATUS_200);
    241       else
    242         RecordGetHashResult(details.is_download, GET_HASH_STATUS_204);
    243 
    244       gethash_error_count_ = 0;
    245       gethash_back_off_mult_ = 1;
    246       std::string data;
    247       source->GetResponseAsString(&data);
    248       if (!safe_browsing::ParseGetHash(
    249               data.data(), data.length(), &cache_lifetime, &full_hashes)) {
    250         full_hashes.clear();
    251         RecordGetHashResult(details.is_download, GET_HASH_PARSE_ERROR);
    252         // TODO(cbentzel): Should cache_lifetime be set to 0 here? (See
    253         // http://crbug.com/360232.)
    254       }
    255     } else {
    256       HandleGetHashError(Time::Now());
    257       if (source->GetStatus().status() == net::URLRequestStatus::FAILED) {
    258         RecordGetHashResult(details.is_download, GET_HASH_NETWORK_ERROR);
    259         VLOG(1) << "SafeBrowsing GetHash request for: " << source->GetURL()
    260                 << " failed with error: " << source->GetStatus().error();
    261       } else {
    262         RecordGetHashResult(details.is_download, GET_HASH_HTTP_ERROR);
    263         VLOG(1) << "SafeBrowsing GetHash request for: " << source->GetURL()
    264                 << " failed with error: " << source->GetResponseCode();
    265       }
    266     }
    267 
    268     // Invoke the callback with full_hashes, even if there was a parse error or
    269     // an error response code (in which case full_hashes will be empty). The
    270     // caller can't be blocked indefinitely.
    271     details.callback.Run(full_hashes, cache_lifetime);
    272 
    273     hash_requests_.erase(it);
    274   } else {
    275     // Update or chunk response.
    276     fetcher.reset(request_.release());
    277 
    278     if (request_type_ == UPDATE_REQUEST ||
    279         request_type_ == BACKUP_UPDATE_REQUEST) {
    280       if (!fetcher.get()) {
    281         // We've timed out waiting for an update response, so we've cancelled
    282         // the update request and scheduled a new one. Ignore this response.
    283         return;
    284       }
    285 
    286       // Cancel the update response timeout now that we have the response.
    287       timeout_timer_.Stop();
    288     }
    289 
    290     net::URLRequestStatus status = source->GetStatus();
    291     if (status.is_success() && source->GetResponseCode() == 200) {
    292       // We have data from the SafeBrowsing service.
    293       std::string data;
    294       source->GetResponseAsString(&data);
    295 
    296       // TODO(shess): Cleanup the flow of this code so that |parsed_ok| can be
    297       // removed or omitted.
    298       const bool parsed_ok = HandleServiceResponse(
    299           source->GetURL(), data.data(), data.length());
    300       if (!parsed_ok) {
    301         VLOG(1) << "SafeBrowsing request for: " << source->GetURL()
    302                 << " failed parse.";
    303         chunk_request_urls_.clear();
    304         if (request_type_ == UPDATE_REQUEST &&
    305             IssueBackupUpdateRequest(BACKUP_UPDATE_REASON_HTTP)) {
    306           return;
    307         }
    308         UpdateFinished(false);
    309       }
    310 
    311       switch (request_type_) {
    312         case CHUNK_REQUEST:
    313           if (parsed_ok) {
    314             chunk_request_urls_.pop_front();
    315             if (chunk_request_urls_.empty() && !chunk_pending_to_write_)
    316               UpdateFinished(true);
    317           }
    318           break;
    319         case UPDATE_REQUEST:
    320         case BACKUP_UPDATE_REQUEST:
    321           if (chunk_request_urls_.empty() && parsed_ok) {
    322             // We are up to date since the servers gave us nothing new, so we
    323             // are done with this update cycle.
    324             UpdateFinished(true);
    325           }
    326           break;
    327         case NO_REQUEST:
    328           // This can happen if HandleServiceResponse fails above.
    329           break;
    330         default:
    331           NOTREACHED();
    332           break;
    333       }
    334     } else {
    335       if (status.status() == net::URLRequestStatus::FAILED) {
    336         VLOG(1) << "SafeBrowsing request for: " << source->GetURL()
    337                 << " failed with error: " << source->GetStatus().error();
    338       } else {
    339         VLOG(1) << "SafeBrowsing request for: " << source->GetURL()
    340                 << " failed with error: " << source->GetResponseCode();
    341       }
    342       if (request_type_ == CHUNK_REQUEST) {
    343         // The SafeBrowsing service error, or very bad response code: back off.
    344         chunk_request_urls_.clear();
    345       } else if (request_type_ == UPDATE_REQUEST) {
    346         BackupUpdateReason backup_update_reason = BACKUP_UPDATE_REASON_MAX;
    347         if (status.is_success()) {
    348           backup_update_reason = BACKUP_UPDATE_REASON_HTTP;
    349         } else {
    350           switch (status.error()) {
    351             case net::ERR_INTERNET_DISCONNECTED:
    352             case net::ERR_NETWORK_CHANGED:
    353               backup_update_reason = BACKUP_UPDATE_REASON_NETWORK;
    354               break;
    355             default:
    356               backup_update_reason = BACKUP_UPDATE_REASON_CONNECT;
    357               break;
    358           }
    359         }
    360         if (backup_update_reason != BACKUP_UPDATE_REASON_MAX &&
    361             IssueBackupUpdateRequest(backup_update_reason)) {
    362           return;
    363         }
    364       }
    365       UpdateFinished(false);
    366     }
    367   }
    368 
    369   // Get the next chunk if available.
    370   IssueChunkRequest();
    371 }
    372 
    373 bool SafeBrowsingProtocolManager::HandleServiceResponse(
    374     const GURL& url, const char* data, size_t length) {
    375   DCHECK(CalledOnValidThread());
    376 
    377   switch (request_type_) {
    378     case UPDATE_REQUEST:
    379     case BACKUP_UPDATE_REQUEST: {
    380       size_t next_update_sec = 0;
    381       bool reset = false;
    382       scoped_ptr<std::vector<SBChunkDelete> > chunk_deletes(
    383           new std::vector<SBChunkDelete>);
    384       std::vector<ChunkUrl> chunk_urls;
    385       if (!safe_browsing::ParseUpdate(data, length, &next_update_sec, &reset,
    386                                       chunk_deletes.get(), &chunk_urls)) {
    387         return false;
    388       }
    389 
    390       base::TimeDelta next_update_interval =
    391           base::TimeDelta::FromSeconds(next_update_sec);
    392       last_update_ = Time::Now();
    393 
    394       if (update_state_ == FIRST_REQUEST)
    395         update_state_ = SECOND_REQUEST;
    396       else if (update_state_ == SECOND_REQUEST)
    397         update_state_ = NORMAL_REQUEST;
    398 
    399       // New time for the next update.
    400       if (next_update_interval > base::TimeDelta()) {
    401         next_update_interval_ = next_update_interval;
    402       } else if (update_state_ == SECOND_REQUEST) {
    403         next_update_interval_ = base::TimeDelta::FromSeconds(
    404             base::RandInt(15, 45));
    405       }
    406 
    407       // New chunks to download.
    408       if (!chunk_urls.empty()) {
    409         UMA_HISTOGRAM_COUNTS("SB2.UpdateUrls", chunk_urls.size());
    410         for (size_t i = 0; i < chunk_urls.size(); ++i)
    411           chunk_request_urls_.push_back(chunk_urls[i]);
    412       }
    413 
    414       // Handle the case were the SafeBrowsing service tells us to dump our
    415       // database.
    416       if (reset) {
    417         delegate_->ResetDatabase();
    418         return true;
    419       }
    420 
    421       // Chunks to delete from our storage.
    422       if (!chunk_deletes->empty())
    423         delegate_->DeleteChunks(chunk_deletes.Pass());
    424 
    425       break;
    426     }
    427     case CHUNK_REQUEST: {
    428       UMA_HISTOGRAM_TIMES("SB2.ChunkRequest",
    429                           base::Time::Now() - chunk_request_start_);
    430 
    431       const ChunkUrl chunk_url = chunk_request_urls_.front();
    432       scoped_ptr<ScopedVector<SBChunkData> >
    433           chunks(new ScopedVector<SBChunkData>);
    434       UMA_HISTOGRAM_COUNTS("SB2.ChunkSize", length);
    435       update_size_ += length;
    436       if (!safe_browsing::ParseChunk(data, length, chunks.get()))
    437         return false;
    438 
    439       // Chunks to add to storage.  Pass ownership of |chunks|.
    440       if (!chunks->empty()) {
    441         chunk_pending_to_write_ = true;
    442         delegate_->AddChunks(
    443             chunk_url.list_name, chunks.Pass(),
    444             base::Bind(&SafeBrowsingProtocolManager::OnAddChunksComplete,
    445                        base::Unretained(this)));
    446       }
    447 
    448       break;
    449     }
    450 
    451     default:
    452       return false;
    453   }
    454 
    455   return true;
    456 }
    457 
    458 void SafeBrowsingProtocolManager::Initialize() {
    459   DCHECK(CalledOnValidThread());
    460   // Don't want to hit the safe browsing servers on build/chrome bots.
    461   scoped_ptr<base::Environment> env(base::Environment::Create());
    462   if (env->HasVar(env_vars::kHeadless))
    463     return;
    464   ScheduleNextUpdate(false /* no back off */);
    465 }
    466 
    467 void SafeBrowsingProtocolManager::ScheduleNextUpdate(bool back_off) {
    468   DCHECK(CalledOnValidThread());
    469   if (disable_auto_update_) {
    470     // Unschedule any current timer.
    471     update_timer_.Stop();
    472     return;
    473   }
    474   // Reschedule with the new update.
    475   base::TimeDelta next_update_interval = GetNextUpdateInterval(back_off);
    476   ForceScheduleNextUpdate(next_update_interval);
    477 }
    478 
    479 void SafeBrowsingProtocolManager::ForceScheduleNextUpdate(
    480     base::TimeDelta interval) {
    481   DCHECK(CalledOnValidThread());
    482   DCHECK(interval >= base::TimeDelta());
    483   // Unschedule any current timer.
    484   update_timer_.Stop();
    485   update_timer_.Start(FROM_HERE, interval, this,
    486                       &SafeBrowsingProtocolManager::GetNextUpdate);
    487 }
    488 
    489 // According to section 5 of the SafeBrowsing protocol specification, we must
    490 // back off after a certain number of errors. We only change |next_update_sec_|
    491 // when we receive a response from the SafeBrowsing service.
    492 base::TimeDelta SafeBrowsingProtocolManager::GetNextUpdateInterval(
    493     bool back_off) {
    494   DCHECK(CalledOnValidThread());
    495   DCHECK(next_update_interval_ > base::TimeDelta());
    496   base::TimeDelta next = next_update_interval_;
    497   if (back_off) {
    498     next = GetNextBackOffInterval(&update_error_count_, &update_back_off_mult_);
    499   } else {
    500     // Successful response means error reset.
    501     update_error_count_ = 0;
    502     update_back_off_mult_ = 1;
    503   }
    504   return next;
    505 }
    506 
    507 base::TimeDelta SafeBrowsingProtocolManager::GetNextBackOffInterval(
    508     size_t* error_count, size_t* multiplier) const {
    509   DCHECK(CalledOnValidThread());
    510   DCHECK(multiplier && error_count);
    511   (*error_count)++;
    512   if (*error_count > 1 && *error_count < 6) {
    513     base::TimeDelta next = base::TimeDelta::FromMinutes(
    514         *multiplier * (1 + back_off_fuzz_) * 30);
    515     *multiplier *= 2;
    516     if (*multiplier > kSbMaxBackOff)
    517       *multiplier = kSbMaxBackOff;
    518     return next;
    519   }
    520   if (*error_count >= 6)
    521     return base::TimeDelta::FromHours(8);
    522   return base::TimeDelta::FromMinutes(1);
    523 }
    524 
    525 // This request requires getting a list of all the chunks for each list from the
    526 // database asynchronously. The request will be issued when we're called back in
    527 // OnGetChunksComplete.
    528 // TODO(paulg): We should get this at start up and maintain a ChunkRange cache
    529 //              to avoid hitting the database with each update request. On the
    530 //              otherhand, this request will only occur ~20-30 minutes so there
    531 //              isn't that much overhead. Measure!
    532 void SafeBrowsingProtocolManager::IssueUpdateRequest() {
    533   DCHECK(CalledOnValidThread());
    534   request_type_ = UPDATE_REQUEST;
    535   delegate_->UpdateStarted();
    536   delegate_->GetChunks(
    537       base::Bind(&SafeBrowsingProtocolManager::OnGetChunksComplete,
    538                  base::Unretained(this)));
    539 }
    540 
    541 // The backup request can run immediately since the chunks have already been
    542 // retrieved from the DB.
    543 bool SafeBrowsingProtocolManager::IssueBackupUpdateRequest(
    544     BackupUpdateReason backup_update_reason) {
    545   DCHECK(CalledOnValidThread());
    546   DCHECK_EQ(request_type_, UPDATE_REQUEST);
    547   DCHECK(backup_update_reason >= 0 &&
    548          backup_update_reason < BACKUP_UPDATE_REASON_MAX);
    549   if (backup_url_prefixes_[backup_update_reason].empty())
    550     return false;
    551   request_type_ = BACKUP_UPDATE_REQUEST;
    552   backup_update_reason_ = backup_update_reason;
    553 
    554   GURL backup_update_url = BackupUpdateUrl(backup_update_reason);
    555   request_.reset(net::URLFetcher::Create(
    556       url_fetcher_id_++, backup_update_url, net::URLFetcher::POST, this));
    557   request_->SetLoadFlags(net::LOAD_DISABLE_CACHE);
    558   request_->SetRequestContext(request_context_getter_.get());
    559   request_->SetUploadData("text/plain", update_list_data_);
    560   request_->Start();
    561 
    562   // Begin the update request timeout.
    563   timeout_timer_.Start(FROM_HERE, TimeDelta::FromSeconds(kSbMaxUpdateWaitSec),
    564                        this,
    565                        &SafeBrowsingProtocolManager::UpdateResponseTimeout);
    566 
    567   return true;
    568 }
    569 
    570 void SafeBrowsingProtocolManager::IssueChunkRequest() {
    571   DCHECK(CalledOnValidThread());
    572   // We are only allowed to have one request outstanding at any time.  Also,
    573   // don't get the next url until the previous one has been written to disk so
    574   // that we don't use too much memory.
    575   if (request_.get() || chunk_request_urls_.empty() || chunk_pending_to_write_)
    576     return;
    577 
    578   ChunkUrl next_chunk = chunk_request_urls_.front();
    579   DCHECK(!next_chunk.url.empty());
    580   GURL chunk_url = NextChunkUrl(next_chunk.url);
    581   request_type_ = CHUNK_REQUEST;
    582   request_.reset(net::URLFetcher::Create(
    583       url_fetcher_id_++, chunk_url, net::URLFetcher::GET, this));
    584   request_->SetLoadFlags(net::LOAD_DISABLE_CACHE);
    585   request_->SetRequestContext(request_context_getter_.get());
    586   chunk_request_start_ = base::Time::Now();
    587   request_->Start();
    588 }
    589 
    590 void SafeBrowsingProtocolManager::OnGetChunksComplete(
    591     const std::vector<SBListChunkRanges>& lists, bool database_error) {
    592   DCHECK(CalledOnValidThread());
    593   DCHECK_EQ(request_type_, UPDATE_REQUEST);
    594   DCHECK(update_list_data_.empty());
    595   if (database_error) {
    596     // The update was not successful, but don't back off.
    597     UpdateFinished(false, false);
    598     return;
    599   }
    600 
    601   // Format our stored chunks:
    602   bool found_malware = false;
    603   bool found_phishing = false;
    604   for (size_t i = 0; i < lists.size(); ++i) {
    605     update_list_data_.append(safe_browsing::FormatList(lists[i]));
    606     if (lists[i].name == safe_browsing_util::kPhishingList)
    607       found_phishing = true;
    608 
    609     if (lists[i].name == safe_browsing_util::kMalwareList)
    610       found_malware = true;
    611   }
    612 
    613   // If we have an empty database, let the server know we want data for these
    614   // lists.
    615   // TODO(shess): These cases never happen because the database fills in the
    616   // lists in GetChunks().  Refactor the unit tests so that this code can be
    617   // removed.
    618   if (!found_phishing) {
    619     update_list_data_.append(safe_browsing::FormatList(
    620         SBListChunkRanges(safe_browsing_util::kPhishingList)));
    621   }
    622   if (!found_malware) {
    623     update_list_data_.append(safe_browsing::FormatList(
    624         SBListChunkRanges(safe_browsing_util::kMalwareList)));
    625   }
    626 
    627   // Large requests are (probably) a sign of database corruption.
    628   // Record stats to inform decisions about whether to automate
    629   // deletion of such databases.  http://crbug.com/120219
    630   UMA_HISTOGRAM_COUNTS("SB2.UpdateRequestSize", update_list_data_.size());
    631 
    632   GURL update_url = UpdateUrl();
    633   request_.reset(net::URLFetcher::Create(
    634       url_fetcher_id_++, update_url, net::URLFetcher::POST, this));
    635   request_->SetLoadFlags(net::LOAD_DISABLE_CACHE);
    636   request_->SetRequestContext(request_context_getter_.get());
    637   request_->SetUploadData("text/plain", update_list_data_);
    638   request_->Start();
    639 
    640   // Begin the update request timeout.
    641   timeout_timer_.Start(FROM_HERE, TimeDelta::FromSeconds(kSbMaxUpdateWaitSec),
    642                        this,
    643                        &SafeBrowsingProtocolManager::UpdateResponseTimeout);
    644 }
    645 
    646 // If we haven't heard back from the server with an update response, this method
    647 // will run. Close the current update session and schedule another update.
    648 void SafeBrowsingProtocolManager::UpdateResponseTimeout() {
    649   DCHECK(CalledOnValidThread());
    650   DCHECK(request_type_ == UPDATE_REQUEST ||
    651          request_type_ == BACKUP_UPDATE_REQUEST);
    652   request_.reset();
    653   if (request_type_ == UPDATE_REQUEST &&
    654       IssueBackupUpdateRequest(BACKUP_UPDATE_REASON_CONNECT)) {
    655     return;
    656   }
    657   UpdateFinished(false);
    658 }
    659 
    660 void SafeBrowsingProtocolManager::OnAddChunksComplete() {
    661   DCHECK(CalledOnValidThread());
    662   chunk_pending_to_write_ = false;
    663 
    664   if (chunk_request_urls_.empty()) {
    665     UMA_HISTOGRAM_LONG_TIMES("SB2.Update", Time::Now() - last_update_);
    666     UpdateFinished(true);
    667   } else {
    668     IssueChunkRequest();
    669   }
    670 }
    671 
    672 void SafeBrowsingProtocolManager::HandleGetHashError(const Time& now) {
    673   DCHECK(CalledOnValidThread());
    674   base::TimeDelta next = GetNextBackOffInterval(
    675       &gethash_error_count_, &gethash_back_off_mult_);
    676   next_gethash_time_ = now + next;
    677 }
    678 
    679 void SafeBrowsingProtocolManager::UpdateFinished(bool success) {
    680   UpdateFinished(success, !success);
    681 }
    682 
    683 void SafeBrowsingProtocolManager::UpdateFinished(bool success, bool back_off) {
    684   DCHECK(CalledOnValidThread());
    685 #if defined(OS_ANDROID)
    686   if (app_in_foreground_)
    687     UMA_HISTOGRAM_COUNTS("SB2.UpdateSizeForeground", update_size_);
    688   else
    689     UMA_HISTOGRAM_COUNTS("SB2.UpdateSizeBackground", update_size_);
    690 #endif
    691   UMA_HISTOGRAM_COUNTS("SB2.UpdateSize", update_size_);
    692   update_size_ = 0;
    693   bool update_success = success || request_type_ == CHUNK_REQUEST;
    694   if (backup_update_reason_ == BACKUP_UPDATE_REASON_MAX) {
    695     RecordUpdateResult(
    696         update_success ? UPDATE_RESULT_SUCCESS : UPDATE_RESULT_FAIL);
    697   } else {
    698     UpdateResult update_result = static_cast<UpdateResult>(
    699           UPDATE_RESULT_BACKUP_START +
    700           (static_cast<int>(backup_update_reason_) * 2) +
    701           update_success);
    702     RecordUpdateResult(update_result);
    703   }
    704   backup_update_reason_ = BACKUP_UPDATE_REASON_MAX;
    705   request_type_ = NO_REQUEST;
    706   update_list_data_.clear();
    707   delegate_->UpdateFinished(success);
    708   ScheduleNextUpdate(back_off);
    709 }
    710 
    711 GURL SafeBrowsingProtocolManager::UpdateUrl() const {
    712   std::string url = SafeBrowsingProtocolManagerHelper::ComposeUrl(
    713       url_prefix_, "downloads", client_name_, version_, additional_query_);
    714   return GURL(url);
    715 }
    716 
    717 GURL SafeBrowsingProtocolManager::BackupUpdateUrl(
    718     BackupUpdateReason backup_update_reason) const {
    719   DCHECK(backup_update_reason >= 0 &&
    720          backup_update_reason < BACKUP_UPDATE_REASON_MAX);
    721   DCHECK(!backup_url_prefixes_[backup_update_reason].empty());
    722   std::string url = SafeBrowsingProtocolManagerHelper::ComposeUrl(
    723       backup_url_prefixes_[backup_update_reason], "downloads", client_name_,
    724       version_, additional_query_);
    725   return GURL(url);
    726 }
    727 
    728 GURL SafeBrowsingProtocolManager::GetHashUrl() const {
    729   std::string url = SafeBrowsingProtocolManagerHelper::ComposeUrl(
    730       url_prefix_, "gethash", client_name_, version_, additional_query_);
    731   return GURL(url);
    732 }
    733 
    734 GURL SafeBrowsingProtocolManager::NextChunkUrl(const std::string& url) const {
    735   DCHECK(CalledOnValidThread());
    736   std::string next_url;
    737   if (!StartsWithASCII(url, "http://", false) &&
    738       !StartsWithASCII(url, "https://", false)) {
    739     // Use https if we updated via https, otherwise http (useful for testing).
    740     if (StartsWithASCII(url_prefix_, "https://", false))
    741       next_url.append("https://");
    742     else
    743       next_url.append("http://");
    744     next_url.append(url);
    745   } else {
    746     next_url = url;
    747   }
    748   if (!additional_query_.empty()) {
    749     if (next_url.find("?") != std::string::npos) {
    750       next_url.append("&");
    751     } else {
    752       next_url.append("?");
    753     }
    754     next_url.append(additional_query_);
    755   }
    756   return GURL(next_url);
    757 }
    758 
    759 SafeBrowsingProtocolManager::FullHashDetails::FullHashDetails()
    760     : callback(),
    761       is_download(false) {
    762 }
    763 
    764 SafeBrowsingProtocolManager::FullHashDetails::FullHashDetails(
    765     FullHashCallback callback, bool is_download)
    766     : callback(callback),
    767       is_download(is_download) {
    768 }
    769 
    770 SafeBrowsingProtocolManager::FullHashDetails::~FullHashDetails() {
    771 }
    772 
    773 SafeBrowsingProtocolManagerDelegate::~SafeBrowsingProtocolManagerDelegate() {
    774 }
    775