Allow fallback from https to http for component update checks.

Component updater relies on its custom message signing protocol to
guarantee the integrity of update checks.

As a failover mechanism and to improve the update success rates,
fallback to HTTP is allowed. This is similar to
what Google Update (Omaha) has been doing.

BUG=590070

Review URL: https://codereview.chromium.org/1740333002

Cr-Commit-Position: refs/heads/master@{#384993}
diff --git a/chrome/browser/component_updater/caps_installer_win.cc b/chrome/browser/component_updater/caps_installer_win.cc
index 78942ba..cd4cd9b 100644
--- a/chrome/browser/component_updater/caps_installer_win.cc
+++ b/chrome/browser/component_updater/caps_installer_win.cc
@@ -7,6 +7,7 @@
 #include <stdint.h>
 #include <string>
 #include <utility>
+#include <vector>
 
 #include "base/bind.h"
 #include "base/command_line.h"
@@ -78,6 +79,8 @@
 
   bool CanAutoUpdate() const override { return true; }
 
+  bool RequiresNetworkEncryption() const override { return false; }
+
   bool OnCustomInstall(const base::DictionaryValue& manifest,
                        const base::FilePath& install_dir) override {
     return true;
diff --git a/chrome/browser/component_updater/chrome_component_updater_configurator.cc b/chrome/browser/component_updater/chrome_component_updater_configurator.cc
index f915a8c..97e99e7 100644
--- a/chrome/browser/component_updater/chrome_component_updater_configurator.cc
+++ b/chrome/browser/component_updater/chrome_component_updater_configurator.cc
@@ -64,10 +64,13 @@
   ~ChromeConfigurator() override {}
 };
 
+// Allows the component updater to use non-encrypted communication with the
+// update backend. The security of the update checks is enforced using
+// a custom message signing protocol and it does not depend on using HTTPS.
 ChromeConfigurator::ChromeConfigurator(
     const base::CommandLine* cmdline,
     net::URLRequestContextGetter* url_request_getter)
-    : configurator_impl_(cmdline, url_request_getter) {}
+    : configurator_impl_(cmdline, url_request_getter, false) {}
 
 int ChromeConfigurator::InitialDelay() const {
   return configurator_impl_.InitialDelay();
diff --git a/chrome/browser/component_updater/chrome_component_updater_configurator_unittest.cc b/chrome/browser/component_updater/chrome_component_updater_configurator_unittest.cc
index 996fd0da..dcd57143 100644
--- a/chrome/browser/component_updater/chrome_component_updater_configurator_unittest.cc
+++ b/chrome/browser/component_updater/chrome_component_updater_configurator_unittest.cc
@@ -9,6 +9,8 @@
 #include "base/memory/ref_counted.h"
 #include "chrome/browser/component_updater/chrome_component_updater_configurator.h"
 #include "components/component_updater/component_updater_switches.h"
+#include "components/component_updater/component_updater_url_constants.h"
+#include "components/component_updater/configurator_impl.h"
 #include "components/update_client/configurator.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
@@ -67,7 +69,7 @@
   const auto urls = config->UpdateUrl();
 
   // Expect the default url to be cryptographically secure.
-  EXPECT_GE(urls.size(), 1ul);
+  EXPECT_GE(urls.size(), 1u);
   EXPECT_TRUE(urls.front().SchemeIsCryptographic());
 }
 
@@ -78,4 +80,35 @@
   EXPECT_TRUE(config->UseCupSigning());
 }
 
+TEST(ChromeComponentUpdaterConfiguratorTest, TestUseEncryption) {
+  base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
+  const auto config(MakeChromeComponentUpdaterConfigurator(cmdline, nullptr));
+
+  const auto urls = config->UpdateUrl();
+  ASSERT_EQ(2u, urls.size());
+  ASSERT_STREQ(kUpdaterDefaultUrl, urls[0].spec().c_str());
+  ASSERT_STREQ(kUpdaterFallbackUrl, urls[1].spec().c_str());
+
+  ASSERT_EQ(config->UpdateUrl(), config->PingUrl());
+
+  // Use the configurator implementation to test the filtering of
+  // unencrypted URLs.
+  {
+    const ConfiguratorImpl config(cmdline, nullptr, true);
+    const auto urls = config.UpdateUrl();
+    ASSERT_EQ(1u, urls.size());
+    ASSERT_STREQ(kUpdaterDefaultUrl, urls[0].spec().c_str());
+    ASSERT_EQ(config.UpdateUrl(), config.PingUrl());
+  }
+
+  {
+    const ConfiguratorImpl config(cmdline, nullptr, false);
+    const auto urls = config.UpdateUrl();
+    ASSERT_EQ(2u, urls.size());
+    ASSERT_STREQ(kUpdaterDefaultUrl, urls[0].spec().c_str());
+    ASSERT_STREQ(kUpdaterFallbackUrl, urls[1].spec().c_str());
+    ASSERT_EQ(config.UpdateUrl(), config.PingUrl());
+  }
+}
+
 }  // namespace component_updater
diff --git a/chrome/browser/component_updater/cld_component_installer.cc b/chrome/browser/component_updater/cld_component_installer.cc
index d287f43..e96e001 100644
--- a/chrome/browser/component_updater/cld_component_installer.cc
+++ b/chrome/browser/component_updater/cld_component_installer.cc
@@ -56,6 +56,10 @@
   return true;
 }
 
+bool CldComponentInstallerTraits::RequiresNetworkEncryption() const {
+  return false;
+}
+
 bool CldComponentInstallerTraits::OnCustomInstall(
     const base::DictionaryValue& manifest,
     const base::FilePath& install_dir) {
diff --git a/chrome/browser/component_updater/cld_component_installer.h b/chrome/browser/component_updater/cld_component_installer.h
index 74600bb..7fc25d1 100644
--- a/chrome/browser/component_updater/cld_component_installer.h
+++ b/chrome/browser/component_updater/cld_component_installer.h
@@ -42,6 +42,7 @@
 
   // The following methods override ComponentInstallerTraits.
   bool CanAutoUpdate() const override;
+  bool RequiresNetworkEncryption() const override;
   bool OnCustomInstall(const base::DictionaryValue& manifest,
                        const base::FilePath& install_dir) override;
   bool VerifyInstallation(const base::DictionaryValue& manifest,
diff --git a/chrome/browser/component_updater/ev_whitelist_component_installer.cc b/chrome/browser/component_updater/ev_whitelist_component_installer.cc
index 77e6e9d..db98433 100644
--- a/chrome/browser/component_updater/ev_whitelist_component_installer.cc
+++ b/chrome/browser/component_updater/ev_whitelist_component_installer.cc
@@ -75,6 +75,10 @@
   return true;
 }
 
+bool EVWhitelistComponentInstallerTraits::RequiresNetworkEncryption() const {
+  return false;
+}
+
 bool EVWhitelistComponentInstallerTraits::OnCustomInstall(
     const base::DictionaryValue& manifest,
     const base::FilePath& install_dir) {
diff --git a/chrome/browser/component_updater/ev_whitelist_component_installer.h b/chrome/browser/component_updater/ev_whitelist_component_installer.h
index 9e765d8..933c345 100644
--- a/chrome/browser/component_updater/ev_whitelist_component_installer.h
+++ b/chrome/browser/component_updater/ev_whitelist_component_installer.h
@@ -30,6 +30,7 @@
  private:
   // The following methods override ComponentInstallerTraits.
   bool CanAutoUpdate() const override;
+  bool RequiresNetworkEncryption() const override;
   bool OnCustomInstall(const base::DictionaryValue& manifest,
                        const base::FilePath& install_dir) override;
   bool VerifyInstallation(const base::DictionaryValue& manifest,
diff --git a/chrome/browser/component_updater/supervised_user_whitelist_installer.cc b/chrome/browser/component_updater/supervised_user_whitelist_installer.cc
index 2fe544d6..42f3ed6 100644
--- a/chrome/browser/component_updater/supervised_user_whitelist_installer.cc
+++ b/chrome/browser/component_updater/supervised_user_whitelist_installer.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/component_updater/supervised_user_whitelist_installer.h"
 
 #include <stddef.h>
+#include <map>
 #include <utility>
 
 #include "base/bind.h"
@@ -248,6 +249,7 @@
   bool VerifyInstallation(const base::DictionaryValue& manifest,
                           const base::FilePath& install_dir) const override;
   bool CanAutoUpdate() const override;
+  bool RequiresNetworkEncryption() const override;
   bool OnCustomInstall(const base::DictionaryValue& manifest,
                        const base::FilePath& install_dir) override;
   void ComponentReady(const base::Version& version,
@@ -276,6 +278,11 @@
   return true;
 }
 
+bool SupervisedUserWhitelistComponentInstallerTraits::
+    RequiresNetworkEncryption() const {
+  return true;
+}
+
 bool SupervisedUserWhitelistComponentInstallerTraits::OnCustomInstall(
     const base::DictionaryValue& manifest,
     const base::FilePath& install_dir) {
diff --git a/chrome/browser/component_updater/sw_reporter_installer_win.cc b/chrome/browser/component_updater/sw_reporter_installer_win.cc
index 025eacf..f69c808 100644
--- a/chrome/browser/component_updater/sw_reporter_installer_win.cc
+++ b/chrome/browser/component_updater/sw_reporter_installer_win.cc
@@ -115,6 +115,8 @@
 
   bool CanAutoUpdate() const override { return true; }
 
+  bool RequiresNetworkEncryption() const override { return false; }
+
   bool OnCustomInstall(const base::DictionaryValue& manifest,
                        const base::FilePath& install_dir) override {
     return true;
diff --git a/chrome/browser/component_updater/widevine_cdm_component_installer.cc b/chrome/browser/component_updater/widevine_cdm_component_installer.cc
index 8f74a949b..f7b135e9 100644
--- a/chrome/browser/component_updater/widevine_cdm_component_installer.cc
+++ b/chrome/browser/component_updater/widevine_cdm_component_installer.cc
@@ -236,6 +236,7 @@
  private:
   // The following methods override ComponentInstallerTraits.
   bool CanAutoUpdate() const override;
+  bool RequiresNetworkEncryption() const override;
   bool OnCustomInstall(const base::DictionaryValue& manifest,
                                const base::FilePath& install_dir) override;
   bool VerifyInstallation(
@@ -268,6 +269,10 @@
   return true;
 }
 
+bool WidevineCdmComponentInstallerTraits::RequiresNetworkEncryption() const {
+  return false;
+}
+
 bool WidevineCdmComponentInstallerTraits::OnCustomInstall(
     const base::DictionaryValue& manifest,
     const base::FilePath& install_dir) {
diff --git a/chrome/browser/extensions/updater/chrome_update_client_config.cc b/chrome/browser/extensions/updater/chrome_update_client_config.cc
index 9dcb208..4dfb696 100644
--- a/chrome/browser/extensions/updater/chrome_update_client_config.cc
+++ b/chrome/browser/extensions/updater/chrome_update_client_config.cc
@@ -13,11 +13,13 @@
 
 namespace extensions {
 
+// For privacy reasons, requires encryption of the component updater
+// communication with the update backend.
 ChromeUpdateClientConfig::ChromeUpdateClientConfig(
     content::BrowserContext* context)
     : impl_(base::CommandLine::ForCurrentProcess(),
-            context->GetRequestContext()) {
-}
+            context->GetRequestContext(),
+            true) {}
 
 int ChromeUpdateClientConfig::InitialDelay() const {
   return impl_.InitialDelay();
diff --git a/chrome/browser/net/crl_set_fetcher.cc b/chrome/browser/net/crl_set_fetcher.cc
index f2d9caf..0245433 100644
--- a/chrome/browser/net/crl_set_fetcher.cc
+++ b/chrome/browser/net/crl_set_fetcher.cc
@@ -144,7 +144,8 @@
   component.installer = this;
   component.name = "CRLSet";
   component.version = Version(base::UintToString(sequence_of_loaded_crl));
-  component.allow_background_download = false;
+  component.allows_background_download = false;
+  component.requires_network_encryption = false;
   if (!component.version.IsValid()) {
     NOTREACHED();
     component.version = Version("0");
diff --git a/components/component_updater/component_updater_service.cc b/components/component_updater/component_updater_service.cc
index 8f0515bd..775e780 100644
--- a/components/component_updater/component_updater_service.cc
+++ b/components/component_updater/component_updater_service.cc
@@ -264,16 +264,33 @@
   UMA_HISTOGRAM_ENUMERATION("ComponentUpdater.Calls", UPDATE_TYPE_AUTOMATIC,
                             UPDATE_TYPE_COUNT);
 
-  std::vector<std::string> ids;
+  std::vector<std::string> secure_ids;    // Requires HTTPS for update checks.
+  std::vector<std::string> unsecure_ids;  // Can fallback to HTTP.
   for (const auto id : components_order_) {
     DCHECK(components_.find(id) != components_.end());
-    ids.push_back(id);
+
+    auto component(GetComponent(id));
+    if (!component || component->requires_network_encryption)
+      secure_ids.push_back(id);
+    else
+      unsecure_ids.push_back(id);
   }
 
-  update_client_->Update(
-      ids, base::Bind(&CrxUpdateService::OnUpdate, base::Unretained(this)),
-      base::Bind(&CrxUpdateService::OnUpdateComplete, base::Unretained(this),
-                 base::TimeTicks::Now()));
+  if (!unsecure_ids.empty()) {
+    update_client_->Update(
+        unsecure_ids,
+        base::Bind(&CrxUpdateService::OnUpdate, base::Unretained(this)),
+        base::Bind(&CrxUpdateService::OnUpdateComplete, base::Unretained(this),
+                   base::TimeTicks::Now()));
+  }
+
+  if (!secure_ids.empty()) {
+    update_client_->Update(
+        secure_ids,
+        base::Bind(&CrxUpdateService::OnUpdate, base::Unretained(this)),
+        base::Bind(&CrxUpdateService::OnUpdateComplete, base::Unretained(this),
+                   base::TimeTicks::Now()));
+  }
 
   return true;
 }
diff --git a/components/component_updater/component_updater_url_constants.cc b/components/component_updater/component_updater_url_constants.cc
index 3cb8ef1..c762cc91 100644
--- a/components/component_updater/component_updater_url_constants.cc
+++ b/components/component_updater/component_updater_url_constants.cc
@@ -7,10 +7,15 @@
 namespace component_updater {
 
 // The default URL for the v3 protocol service endpoint. In some cases, the
-// component updater is allowed to fall back to and alternate URL source, if
+// component updater is allowed to fall back to other URL endpoints, if
 // the request to the default URL source fails.
+//
+// The responses to the requests made to these endpoints are always signed.
+//
 // The value of |kDefaultUrlSource| can be overridden with
 // --component-updater=url-source=someurl.
 const char kUpdaterDefaultUrl[] = "https://clients2.google.com/service/update2";
 
+const char kUpdaterFallbackUrl[] = "http://clients2.google.com/service/update2";
+
 }  // namespace component_updater
diff --git a/components/component_updater/component_updater_url_constants.h b/components/component_updater/component_updater_url_constants.h
index 3195dd8..10f3287 100644
--- a/components/component_updater/component_updater_url_constants.h
+++ b/components/component_updater/component_updater_url_constants.h
@@ -8,6 +8,7 @@
 namespace component_updater {
 
 extern const char kUpdaterDefaultUrl[];
+extern const char kUpdaterFallbackUrl[];
 
 }  // namespace component_updater
 
diff --git a/components/component_updater/configurator_impl.cc b/components/component_updater/configurator_impl.cc
index 6372118..8da26fd6 100644
--- a/components/component_updater/configurator_impl.cc
+++ b/components/component_updater/configurator_impl.cc
@@ -15,7 +15,7 @@
 #include "build/build_config.h"
 #include "components/component_updater/component_updater_switches.h"
 #include "components/component_updater/component_updater_url_constants.h"
-#include "components/update_client/configurator.h"
+#include "components/update_client/utils.h"
 #include "components/version_info/version_info.h"
 
 #if defined(OS_WIN)
@@ -82,12 +82,14 @@
 
 ConfiguratorImpl::ConfiguratorImpl(
     const base::CommandLine* cmdline,
-    net::URLRequestContextGetter* url_request_getter)
+    net::URLRequestContextGetter* url_request_getter,
+    bool require_encryption)
     : url_request_getter_(url_request_getter),
       fast_update_(false),
       pings_enabled_(false),
       deltas_enabled_(false),
-      background_downloads_enabled_(false) {
+      background_downloads_enabled_(false),
+      require_encryption_(require_encryption) {
   // Parse comma-delimited debug flags.
   std::vector<std::string> switch_values = base::SplitString(
       cmdline->GetSwitchValueASCII(switches::kComponentUpdater), ",",
@@ -140,9 +142,14 @@
   std::vector<GURL> urls;
   if (url_source_override_.is_valid()) {
     urls.push_back(GURL(url_source_override_));
-  } else {
-    urls.push_back(GURL(kUpdaterDefaultUrl));
+    return urls;
   }
+
+  urls.push_back(GURL(kUpdaterDefaultUrl));
+  urls.push_back(GURL(kUpdaterFallbackUrl));
+  if (require_encryption_)
+    update_client::RemoveUnsecureUrls(&urls);
+
   return urls;
 }
 
diff --git a/components/component_updater/configurator_impl.h b/components/component_updater/configurator_impl.h
index d5f99cf..1fa95f1 100644
--- a/components/component_updater/configurator_impl.h
+++ b/components/component_updater/configurator_impl.h
@@ -27,7 +27,8 @@
 class ConfiguratorImpl {
  public:
   ConfiguratorImpl(const base::CommandLine* cmdline,
-                   net::URLRequestContextGetter* url_request_getter);
+                   net::URLRequestContextGetter* url_request_getter,
+                   bool require_encryption);
 
   ~ConfiguratorImpl();
 
@@ -92,6 +93,7 @@
   bool pings_enabled_;
   bool deltas_enabled_;
   bool background_downloads_enabled_;
+  bool require_encryption_;
 
   DISALLOW_COPY_AND_ASSIGN(ConfiguratorImpl);
 };
diff --git a/components/component_updater/default_component_installer.cc b/components/component_updater/default_component_installer.cc
index b286e29..8f5bba6 100644
--- a/components/component_updater/default_component_installer.cc
+++ b/components/component_updater/default_component_installer.cc
@@ -260,6 +260,8 @@
   if (installer_traits_->CanAutoUpdate()) {
     CrxComponent crx;
     crx.name = installer_traits_->GetName();
+    crx.requires_network_encryption =
+        installer_traits_->RequiresNetworkEncryption();
     crx.installer = this;
     crx.version = current_version_;
     crx.fingerprint = current_fingerprint_;
diff --git a/components/component_updater/default_component_installer.h b/components/component_updater/default_component_installer.h
index 5b99491..9de80d6 100644
--- a/components/component_updater/default_component_installer.h
+++ b/components/component_updater/default_component_installer.h
@@ -49,6 +49,10 @@
   // during component registration from the UI thread.
   virtual bool CanAutoUpdate() const = 0;
 
+  // Returns true if the network communication related to this component
+  // must be encrypted.
+  virtual bool RequiresNetworkEncryption() const = 0;
+
   // OnCustomInstall is called during the installation process. Components that
   // require custom installation operations should implement them here.
   // Returns false if a custom operation failed, and true otherwise.
diff --git a/components/update_client/action_update.cc b/components/update_client/action_update.cc
index ea0ad6e..c8045a2 100644
--- a/components/update_client/action_update.cc
+++ b/components/update_client/action_update.cc
@@ -294,7 +294,7 @@
   DCHECK(thread_checker_.CalledOnValidThread());
 
   // On demand component updates are always downloaded in foreground.
-  return !item->on_demand && item->component.allow_background_download &&
+  return !item->on_demand && item->component.allows_background_download &&
          update_context_->config->UseBackgroundDownloader();
 }
 
diff --git a/components/update_client/action_update_check.cc b/components/update_client/action_update_check.cc
index 9e31eb6..13455f5 100644
--- a/components/update_client/action_update_check.cc
+++ b/components/update_client/action_update_check.cc
@@ -97,9 +97,12 @@
 
 void ActionUpdateCheck::UpdateCheckComplete(
     int error,
-    const UpdateResponse::Results& results) {
+    const UpdateResponse::Results& results,
+    int retry_after_sec) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
+  update_context_->retry_after_sec_ = retry_after_sec;
+
   if (!error)
     OnUpdateCheckSucceeded(results);
   else
diff --git a/components/update_client/action_update_check.h b/components/update_client/action_update_check.h
index 7576d64..67fc4d1 100644
--- a/components/update_client/action_update_check.h
+++ b/components/update_client/action_update_check.h
@@ -36,7 +36,9 @@
   void Run(UpdateContext* update_context, Callback callback) override;
 
  private:
-  void UpdateCheckComplete(int error, const UpdateResponse::Results& results);
+  void UpdateCheckComplete(int error,
+                           const UpdateResponse::Results& results,
+                           int retry_after_sec);
 
   void OnUpdateCheckSucceeded(const UpdateResponse::Results& results);
   void OnUpdateCheckFailed(int error);
diff --git a/components/update_client/ping_manager.cc b/components/update_client/ping_manager.cc
index 19188145..2d665933 100644
--- a/components/update_client/ping_manager.cc
+++ b/components/update_client/ping_manager.cc
@@ -179,7 +179,9 @@
   bool SendPing(const CrxUpdateItem* item);
 
  private:
-  void OnRequestSenderComplete(int error, const std::string& response);
+  void OnRequestSenderComplete(int error,
+                               const std::string& response,
+                               int retry_after_sec);
 
   const scoped_refptr<Configurator> config_;
   scoped_ptr<RequestSender> request_sender_;
@@ -196,7 +198,8 @@
 }
 
 void PingSender::OnRequestSenderComplete(int error,
-                                         const std::string& response) {
+                                         const std::string& response,
+                                         int retry_after_sec) {
   DCHECK(thread_checker_.CalledOnValidThread());
   delete this;
 }
@@ -205,7 +208,9 @@
   DCHECK(item);
   DCHECK(thread_checker_.CalledOnValidThread());
 
-  std::vector<GURL> urls(config_->PingUrl());
+  auto urls(config_->PingUrl());
+  if (item->component.requires_network_encryption)
+    RemoveUnsecureUrls(&urls);
 
   if (urls.empty())
     return false;
@@ -225,12 +230,14 @@
 PingManager::~PingManager() {
 }
 
-// Sends a fire and forget ping when the updates are complete. The ping
-// sender object self-deletes after sending the ping has completed asynchrously.
-void PingManager::SendPing(const CrxUpdateItem* item) {
-  PingSender* ping_sender(new PingSender(config_));
+bool PingManager::SendPing(const CrxUpdateItem* item) {
+  scoped_ptr<PingSender> ping_sender(new PingSender(config_));
   if (!ping_sender->SendPing(item))
-    delete ping_sender;
+    return false;
+
+  // The ping sender object self-deletes after sending the ping asynchrously.
+  ping_sender.release();
+  return true;
 }
 
 }  // namespace update_client
diff --git a/components/update_client/ping_manager.h b/components/update_client/ping_manager.h
index d5a6311d..c907e7d 100644
--- a/components/update_client/ping_manager.h
+++ b/components/update_client/ping_manager.h
@@ -19,7 +19,11 @@
   explicit PingManager(const scoped_refptr<Configurator>& config);
   virtual ~PingManager();
 
-  virtual void SendPing(const CrxUpdateItem* item);
+  // Sends a fire and forget ping for the |item|. Returns true if the
+  // ping is queued up and may be sent in the future, or false, if an error
+  // occurs right away. The ping itself is not persisted and it will be
+  // discarded if it can't be sent for any reason.
+  virtual bool SendPing(const CrxUpdateItem* item);
 
  private:
   const scoped_refptr<Configurator> config_;
diff --git a/components/update_client/ping_manager_unittest.cc b/components/update_client/ping_manager_unittest.cc
index cf1d147..e25dc94 100644
--- a/components/update_client/ping_manager_unittest.cc
+++ b/components/update_client/ping_manager_unittest.cc
@@ -177,4 +177,23 @@
   interceptor->Reset();
 }
 
+// Tests that sending the ping fails when the component requires encryption but
+// the ping URL is unsecure.
+TEST_F(ComponentUpdaterPingManagerTest, PingManagerRequiresEncryptionTest) {
+  config_->SetPingUrl(GURL("http:\\foo\bar"));
+
+  {
+    CrxUpdateItem item;
+    item.component.requires_network_encryption = true;
+
+    EXPECT_FALSE(ping_manager_->SendPing(&item));
+  }
+
+  {
+    // Tests that the default for |requires_network_encryption| is true.
+    CrxUpdateItem item;
+    EXPECT_FALSE(ping_manager_->SendPing(&item));
+  }
+}
+
 }  // namespace update_client
diff --git a/components/update_client/request_sender.cc b/components/update_client/request_sender.cc
index e8fd4660..f3e5814 100644
--- a/components/update_client/request_sender.cc
+++ b/components/update_client/request_sender.cc
@@ -4,6 +4,8 @@
 
 #include "components/update_client/request_sender.h"
 
+#include <algorithm>
+
 #include "base/base64.h"
 #include "base/bind.h"
 #include "base/bind_helpers.h"
@@ -17,6 +19,7 @@
 #include "components/update_client/utils.h"
 #include "net/http/http_response_headers.h"
 #include "net/url_request/url_fetcher.h"
+#include "net/url_request/url_request_status.h"
 
 namespace update_client {
 
@@ -28,6 +31,25 @@
     "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEB+Yi+3SdJKCyFJmm+suW3CyXygvVsbDbPnJgoC"
     "X4GeTtoL8Q/WjPx7CGtXOL1Xjbx0VPPN3DrvqZSL/oXy9hVw==";
 
+// The ETag header carries the ECSDA signature of the protocol response, if
+// signing has been used.
+const char kHeaderEtag[] = "ETag";
+
+// The server uses the optional X-Retry-After header to indicate that the
+// current request should not be attempted again. Any response received along
+// with the X-Retry-After header should be interpreted as it would have been
+// without the X-Retry-After header.
+//
+// In addition to the presence of the header, the value of the header is
+// used as a signal for when to do future update checks, but only when the
+// response is over https. Values over http are not trusted and are ignored.
+//
+// The value of the header is the number of seconds to wait before trying to do
+// a subsequent update check. The upper bound for the number of seconds to wait
+// before trying to do a subsequent update check is capped at 24 hours.
+const char kHeaderXRetryAfter[] = "X-Retry-After";
+const int64_t kMaxRetryAfterSec = 24 * 60 * 60;
+
 }  // namespace
 
 // This value is chosen not to conflict with network errors defined by
@@ -55,7 +77,7 @@
   request_sender_callback_ = request_sender_callback;
 
   if (urls_.empty()) {
-    return HandleSendError(-1);
+    return HandleSendError(-1, 0);
   }
 
   cur_url_ = urls_.begin();
@@ -63,7 +85,7 @@
   if (use_signing_) {
     public_key_ = GetKey(kKeyPubBytesBase64);
     if (public_key_.empty())
-      return HandleSendError(-1);
+      return HandleSendError(-1, 0);
   }
 
   SendInternal();
@@ -91,16 +113,18 @@
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE,
         base::Bind(&RequestSender::SendInternalComplete, base::Unretained(this),
-                   -1, std::string(), std::string()));
+                   -1, std::string(), std::string(), 0));
 }
 
 void RequestSender::SendInternalComplete(int error,
                                          const std::string& response_body,
-                                         const std::string& response_etag) {
+                                         const std::string& response_etag,
+                                         int retry_after_sec) {
   if (!error) {
     if (!use_signing_) {
       base::ThreadTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE, base::Bind(request_sender_callback_, 0, response_body));
+          FROM_HERE, base::Bind(request_sender_callback_, 0, response_body,
+                                retry_after_sec));
       return;
     }
 
@@ -108,7 +132,8 @@
     DCHECK(signer_.get());
     if (signer_->ValidateResponse(response_body, response_etag)) {
       base::ThreadTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE, base::Bind(request_sender_callback_, 0, response_body));
+          FROM_HERE, base::Bind(request_sender_callback_, 0, response_body,
+                                retry_after_sec));
       return;
     }
 
@@ -116,35 +141,49 @@
   }
 
   DCHECK(error);
-  if (++cur_url_ != urls_.end() &&
+
+  // A positive |retry_after_sec| is a hint from the server that the client
+  // should not send further request until the cooldown has expired.
+  if (retry_after_sec <= 0 && ++cur_url_ != urls_.end() &&
       base::ThreadTaskRunnerHandle::Get()->PostTask(
           FROM_HERE,
           base::Bind(&RequestSender::SendInternal, base::Unretained(this)))) {
     return;
   }
 
-  HandleSendError(error);
+  HandleSendError(error, retry_after_sec);
 }
 
 void RequestSender::OnURLFetchComplete(const net::URLFetcher* source) {
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(source);
 
-  VLOG(1) << "request completed from url: " << source->GetOriginalURL().spec();
+  const GURL original_url(source->GetOriginalURL());
+  VLOG(1) << "request completed from url: " << original_url.spec();
 
   const int fetch_error(GetFetchError(*source));
   std::string response_body;
   CHECK(source->GetResponseAsString(&response_body));
 
+  int64_t retry_after_sec(-1);
+  const auto status(source->GetStatus().status());
+  if (original_url.SchemeIsCryptographic() &&
+      status == net::URLRequestStatus::SUCCESS) {
+    retry_after_sec = GetInt64HeaderValue(source, kHeaderXRetryAfter);
+    retry_after_sec = std::min(retry_after_sec, kMaxRetryAfterSec);
+  }
+
   base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE,
-      base::Bind(&RequestSender::SendInternalComplete, base::Unretained(this),
-                 fetch_error, response_body, GetServerETag(source)));
+      FROM_HERE, base::Bind(&RequestSender::SendInternalComplete,
+                            base::Unretained(this), fetch_error, response_body,
+                            GetStringHeaderValue(source, kHeaderEtag),
+                            static_cast<int>(retry_after_sec)));
 }
 
-void RequestSender::HandleSendError(int error) {
+void RequestSender::HandleSendError(int error, int retry_after_sec) {
   base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::Bind(request_sender_callback_, error, std::string()));
+      FROM_HERE, base::Bind(request_sender_callback_, error, std::string(),
+                            retry_after_sec));
 }
 
 std::string RequestSender::GetKey(const char* key_bytes_base64) {
@@ -166,15 +205,23 @@
   return url.ReplaceComponents(replacements);
 }
 
-std::string RequestSender::GetServerETag(const net::URLFetcher* source) {
+std::string RequestSender::GetStringHeaderValue(const net::URLFetcher* source,
+                                                const char* header_name) {
   const auto response_headers(source->GetResponseHeaders());
   if (!response_headers)
     return std::string();
 
   std::string etag;
-  return response_headers->EnumerateHeader(nullptr, "ETag", &etag)
+  return response_headers->EnumerateHeader(nullptr, header_name, &etag)
              ? etag
              : std::string();
 }
 
+int64_t RequestSender::GetInt64HeaderValue(const net::URLFetcher* source,
+                                           const char* header_name) {
+  const auto response_headers(source->GetResponseHeaders());
+  return response_headers ? response_headers->GetInt64HeaderValue(header_name)
+                          : -1;
+}
+
 }  // namespace update_client
diff --git a/components/update_client/request_sender.h b/components/update_client/request_sender.h
index ddf5cca..b5daaa2e 100644
--- a/components/update_client/request_sender.h
+++ b/components/update_client/request_sender.h
@@ -5,6 +5,8 @@
 #ifndef COMPONENTS_UPDATE_CLIENT_REQUEST_SENDER_H_
 #define COMPONENTS_UPDATE_CLIENT_REQUEST_SENDER_H_
 
+#include <stdint.h>
+
 #include <string>
 #include <vector>
 
@@ -35,8 +37,13 @@
 class RequestSender : public net::URLFetcherDelegate {
  public:
   // If |error| is 0, then the response is provided in the |response| parameter.
-  using RequestSenderCallback =
-      base::Callback<void(int error, const std::string& response)>;
+  // |retry_after_sec| contains the value of the X-Retry-After response header,
+  // when the response was received from a cryptographically secure URL. The
+  // range for this value is [-1, 86400]. If |retry_after_sec| is -1 it means
+  // that the header could not be found, or trusted, or had an invalid value.
+  // The upper bound represents a delay of one day.
+  using RequestSenderCallback = base::Callback<
+      void(int error, const std::string& response, int retry_after_sec)>;
 
   static int kErrorResponseNotTrusted;
 
@@ -57,9 +64,15 @@
   // Decodes and returns the public key used by CUP.
   static std::string GetKey(const char* key_bytes_base64);
 
-  // Returns the Etag of the server response or an empty string if the
-  // Etag is not available.
-  static std::string GetServerETag(const net::URLFetcher* source);
+  // Returns the string value of a header of the server response or an empty
+  // string if the header is not available.
+  static std::string GetStringHeaderValue(const net::URLFetcher* source,
+                                          const char* header_name);
+
+  // Returns the integral value of a header of the server response or -1 if
+  // if the header is not available or a conversion error has occured.
+  static int64_t GetInt64HeaderValue(const net::URLFetcher* source,
+                                     const char* header_name);
 
   // Overrides for URLFetcherDelegate.
   void OnURLFetchComplete(const net::URLFetcher* source) override;
@@ -67,19 +80,20 @@
   // Implements the error handling and url fallback mechanism.
   void SendInternal();
 
-  // Called when SendInternal complets. |response_body|and |response_etag|
+  // Called when SendInternal completes. |response_body| and |response_etag|
   // contain the body and the etag associated with the HTTP response.
   void SendInternalComplete(int error,
                             const std::string& response_body,
-                            const std::string& response_etag);
+                            const std::string& response_etag,
+                            int retry_after_sec);
 
   // Helper function to handle a non-continuable error in Send.
-  void HandleSendError(int error);
+  void HandleSendError(int error, int retry_after_sec);
 
   base::ThreadChecker thread_checker_;
 
   const scoped_refptr<Configurator> config_;
-  bool use_signing_;
+  bool use_signing_;  // True if CUP signing is used.
   std::vector<GURL> urls_;
   std::string request_body_;
   RequestSenderCallback request_sender_callback_;
diff --git a/components/update_client/request_sender_unittest.cc b/components/update_client/request_sender_unittest.cc
index 5132fd9..5582132 100644
--- a/components/update_client/request_sender_unittest.cc
+++ b/components/update_client/request_sender_unittest.cc
@@ -45,7 +45,9 @@
   void SetUp() override;
   void TearDown() override;
 
-  void RequestSenderComplete(int error, const std::string& response);
+  void RequestSenderComplete(int error,
+                             const std::string& response,
+                             int retry_after_sec);
 
  protected:
   void Quit();
@@ -124,7 +126,8 @@
 }
 
 void RequestSenderTest::RequestSenderComplete(int error,
-                                              const std::string& response) {
+                                              const std::string& response,
+                                              int retry_after_sec) {
   error_ = error;
   response_ = response;
 
diff --git a/components/update_client/test_configurator.cc b/components/update_client/test_configurator.cc
index 93279c4..6ba5392 100644
--- a/components/update_client/test_configurator.cc
+++ b/components/update_client/test_configurator.cc
@@ -55,10 +55,16 @@
 }
 
 std::vector<GURL> TestConfigurator::UpdateUrl() const {
+  if (!update_check_url_.is_empty())
+    return std::vector<GURL>(1, update_check_url_);
+
   return MakeDefaultUrls();
 }
 
 std::vector<GURL> TestConfigurator::PingUrl() const {
+  if (!ping_url_.is_empty())
+    return std::vector<GURL>(1, ping_url_);
+
   return UpdateUrl();
 }
 
@@ -133,6 +139,14 @@
   download_preference_ = download_preference;
 }
 
+void TestConfigurator::SetUpdateCheckUrl(const GURL& url) {
+  update_check_url_ = url;
+}
+
+void TestConfigurator::SetPingUrl(const GURL& url) {
+  ping_url_ = url;
+}
+
 scoped_refptr<base::SequencedTaskRunner>
 TestConfigurator::GetSequencedTaskRunner() const {
   DCHECK(worker_task_runner_.get());
diff --git a/components/update_client/test_configurator.h b/components/update_client/test_configurator.h
index 391ebc94..ed5bf32bc 100644
--- a/components/update_client/test_configurator.h
+++ b/components/update_client/test_configurator.h
@@ -83,6 +83,8 @@
   void SetInitialDelay(int seconds);
   void SetDownloadPreference(const std::string& download_preference);
   void SetUseCupSigning(bool use_cup_signing);
+  void SetUpdateCheckUrl(const GURL& url);
+  void SetPingUrl(const GURL& url);
 
  private:
   friend class base::RefCountedThreadSafe<TestConfigurator>;
@@ -96,6 +98,8 @@
   int ondemand_time_;
   std::string download_preference_;
   bool use_cup_signing_;
+  GURL update_check_url_;
+  GURL ping_url_;
 
   scoped_refptr<net::TestURLRequestContextGetter> context_;
 
diff --git a/components/update_client/update_checker.cc b/components/update_client/update_checker.cc
index dc98251..234ef22 100644
--- a/components/update_client/update_checker.cc
+++ b/components/update_client/update_checker.cc
@@ -33,6 +33,15 @@
   return IsValidBrand(brand) ? brand : std::string("");
 }
 
+// Returns true if at least one item requires network encryption.
+bool IsEncryptionRequired(const std::vector<CrxUpdateItem*>& items) {
+  for (const auto& item : items) {
+    if (item->component.requires_network_encryption)
+      return true;
+  }
+  return false;
+}
+
 // Builds an update check request for |components|. |additional_attributes| is
 // serialized as part of the <request> element of the request to customize it
 // with data that is not platform or component specific. For each |item|, a
@@ -93,7 +102,9 @@
       const UpdateCheckCallback& update_check_callback) override;
 
  private:
-  void OnRequestSenderComplete(int error, const std::string& response);
+  void OnRequestSenderComplete(int error,
+                               const std::string& response,
+                               int retry_after_sec);
   base::ThreadChecker thread_checker_;
 
   const scoped_refptr<Configurator> config_;
@@ -123,26 +134,30 @@
 
   update_check_callback_ = update_check_callback;
 
+  auto urls(config_->UpdateUrl());
+  if (IsEncryptionRequired(items_to_check))
+    RemoveUnsecureUrls(&urls);
+
   request_sender_.reset(new RequestSender(config_));
   request_sender_->Send(
       config_->UseCupSigning(),
       BuildUpdateCheckRequest(*config_, items_to_check, additional_attributes),
-      config_->UpdateUrl(),
-      base::Bind(&UpdateCheckerImpl::OnRequestSenderComplete,
-                 base::Unretained(this)));
+      urls, base::Bind(&UpdateCheckerImpl::OnRequestSenderComplete,
+                       base::Unretained(this)));
   return true;
 }
 
 void UpdateCheckerImpl::OnRequestSenderComplete(int error,
-                                                const std::string& response) {
+                                                const std::string& response,
+                                                int retry_after_sec) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
   if (!error) {
     UpdateResponse update_response;
     if (update_response.Parse(response)) {
       base::ThreadTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE,
-          base::Bind(update_check_callback_, error, update_response.results()));
+          FROM_HERE, base::Bind(update_check_callback_, error,
+                                update_response.results(), retry_after_sec));
       return;
     }
 
@@ -151,8 +166,8 @@
   }
 
   base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE,
-      base::Bind(update_check_callback_, error, UpdateResponse::Results()));
+      FROM_HERE, base::Bind(update_check_callback_, error,
+                            UpdateResponse::Results(), retry_after_sec));
 }
 
 }  // namespace
diff --git a/components/update_client/update_checker.h b/components/update_client/update_checker.h
index 1de7f0c..1f0eb993 100644
--- a/components/update_client/update_checker.h
+++ b/components/update_client/update_checker.h
@@ -25,7 +25,9 @@
 class UpdateChecker {
  public:
   using UpdateCheckCallback =
-      base::Callback<void(int error, const UpdateResponse::Results& results)>;
+      base::Callback<void(int error,
+                          const UpdateResponse::Results& results,
+                          int retry_after_sec)>;
 
   using Factory =
       scoped_ptr<UpdateChecker> (*)(const scoped_refptr<Configurator>& config);
diff --git a/components/update_client/update_checker_unittest.cc b/components/update_client/update_checker_unittest.cc
index 26873c7..3348996 100644
--- a/components/update_client/update_checker_unittest.cc
+++ b/components/update_client/update_checker_unittest.cc
@@ -47,7 +47,9 @@
   void SetUp() override;
   void TearDown() override;
 
-  void UpdateCheckComplete(int error, const UpdateResponse::Results& results);
+  void UpdateCheckComplete(int error,
+                           const UpdateResponse::Results& results,
+                           int retry_after_sec);
 
  protected:
   void Quit();
@@ -129,7 +131,8 @@
 
 void UpdateCheckerTest::UpdateCheckComplete(
     int error,
-    const UpdateResponse::Results& results) {
+    const UpdateResponse::Results& results,
+    int retry_after_sec) {
   error_ = error;
   results_ = results;
   Quit();
@@ -308,4 +311,25 @@
   EXPECT_EQ(0ul, results_.list.size());
 }
 
+// Tests that the UpdateCheckers will not make an update check for a
+// component that requires encryption when the update check URL is unsecure.
+TEST_F(UpdateCheckerTest, UpdateCheckRequiresEncryptionError) {
+  config_->SetUpdateCheckUrl(GURL("http:\\foo\bar"));
+
+  update_checker_ = UpdateChecker::Create(config_);
+
+  CrxUpdateItem item(BuildCrxUpdateItem());
+  item.component.requires_network_encryption = true;
+  std::vector<CrxUpdateItem*> items_to_check;
+  items_to_check.push_back(&item);
+
+  update_checker_->CheckForUpdates(
+      items_to_check, "", base::Bind(&UpdateCheckerTest::UpdateCheckComplete,
+                                     base::Unretained(this)));
+  RunThreads();
+
+  EXPECT_EQ(-1, error_);
+  EXPECT_EQ(0u, results_.list.size());
+}
+
 }  // namespace update_client
diff --git a/components/update_client/update_client.cc b/components/update_client/update_client.cc
index 86b3dfc..8fa551d 100644
--- a/components/update_client/update_client.cc
+++ b/components/update_client/update_client.cc
@@ -52,8 +52,8 @@
 CrxUpdateItem::~CrxUpdateItem() {
 }
 
-CrxComponent::CrxComponent() : allow_background_download(true) {
-}
+CrxComponent::CrxComponent()
+    : allows_background_download(true), requires_network_encryption(true) {}
 
 CrxComponent::CrxComponent(const CrxComponent& other) = default;
 
diff --git a/components/update_client/update_client.h b/components/update_client/update_client.h
index 12d19c9..e354fb6 100644
--- a/components/update_client/update_client.h
+++ b/components/update_client/update_client.h
@@ -143,6 +143,7 @@
   ERROR_UPDATE_INVALID_ARGUMENT = -1,
   ERROR_UPDATE_IN_PROGRESS = 1,
   ERROR_UPDATE_CANCELED = 2,
+  ERROR_UPDATE_RETRY_LATER = 3,
 };
 
 // Defines an interface for a generic CRX installer.
@@ -197,10 +198,14 @@
   std::string name;         // Optional.
 
   // Specifies that the CRX can be background-downloaded in some cases.
-  // The default for this value is |true| and the value can be overriden at
-  // the registration time. This is a temporary change until the issue
-  // crbug/340448 is resolved.
-  bool allow_background_download;
+  // The default for this value is |true|.
+  bool allows_background_download;
+
+  // Specifies that the update checks and pings associated with this component
+  // require confidentiality. The default for this value is |true|. As a side
+  // note, the confidentiality of the downloads is enforced by the server,
+  // which only returns secure download URLs in this case.
+  bool requires_network_encryption;
 };
 
 // All methods are safe to call only from the browser's main thread. Once an
diff --git a/components/update_client/update_client_unittest.cc b/components/update_client/update_client_unittest.cc
index a0bee0f..957f6973 100644
--- a/components/update_client/update_client_unittest.cc
+++ b/components/update_client/update_client_unittest.cc
@@ -97,7 +97,7 @@
   explicit FakePingManagerImpl(const scoped_refptr<Configurator>& config);
   ~FakePingManagerImpl() override;
 
-  void SendPing(const CrxUpdateItem* item) override;
+  bool SendPing(const CrxUpdateItem* item) override;
 
   const std::vector<CrxUpdateItem>& items() const;
 
@@ -113,8 +113,9 @@
 FakePingManagerImpl::~FakePingManagerImpl() {
 }
 
-void FakePingManagerImpl::SendPing(const CrxUpdateItem* item) {
+bool FakePingManagerImpl::SendPing(const CrxUpdateItem* item) {
   items_.push_back(*item);
+  return true;
 }
 
 const std::vector<CrxUpdateItem>& FakePingManagerImpl::items() const {
@@ -227,7 +228,7 @@
         const UpdateCheckCallback& update_check_callback) override {
       base::ThreadTaskRunnerHandle::Get()->PostTask(
           FROM_HERE,
-          base::Bind(update_check_callback, 0, UpdateResponse::Results()));
+          base::Bind(update_check_callback, 0, UpdateResponse::Results(), 0));
       return true;
     }
   };
@@ -368,7 +369,7 @@
       results.list.push_back(result);
 
       base::ThreadTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE, base::Bind(update_check_callback, 0, results));
+          FROM_HERE, base::Bind(update_check_callback, 0, results, 0));
       return true;
     }
   };
@@ -580,7 +581,7 @@
       results.list.push_back(result2);
 
       base::ThreadTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE, base::Bind(update_check_callback, 0, results));
+          FROM_HERE, base::Bind(update_check_callback, 0, results, 0));
       return true;
     }
   };
@@ -827,7 +828,7 @@
       results.list.push_back(result2);
 
       base::ThreadTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE, base::Bind(update_check_callback, 0, results));
+          FROM_HERE, base::Bind(update_check_callback, 0, results, 0));
       return true;
     }
   };
@@ -1099,7 +1100,7 @@
       }
 
       base::ThreadTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE, base::Bind(update_check_callback, 0, results));
+          FROM_HERE, base::Bind(update_check_callback, 0, results, 0));
       return true;
     }
   };
@@ -1340,7 +1341,7 @@
       results.list.push_back(result);
 
       base::ThreadTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE, base::Bind(update_check_callback, 0, results));
+          FROM_HERE, base::Bind(update_check_callback, 0, results, 0));
       return true;
     }
   };
@@ -1573,7 +1574,7 @@
       }
 
       base::ThreadTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE, base::Bind(update_check_callback, 0, results));
+          FROM_HERE, base::Bind(update_check_callback, 0, results, 0));
       return true;
     }
   };
@@ -1778,7 +1779,7 @@
         const UpdateCheckCallback& update_check_callback) override {
       base::ThreadTaskRunnerHandle::Get()->PostTask(
           FROM_HERE,
-          base::Bind(update_check_callback, 0, UpdateResponse::Results()));
+          base::Bind(update_check_callback, 0, UpdateResponse::Results(), 0));
       return true;
     }
   };
@@ -1911,7 +1912,7 @@
       results.list.push_back(result);
 
       base::ThreadTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE, base::Bind(update_check_callback, 0, results));
+          FROM_HERE, base::Bind(update_check_callback, 0, results, 0));
       return true;
     }
   };
@@ -2063,7 +2064,7 @@
         const UpdateCheckCallback& update_check_callback) override {
       base::ThreadTaskRunnerHandle::Get()->PostTask(
           FROM_HERE,
-          base::Bind(update_check_callback, 0, UpdateResponse::Results()));
+          base::Bind(update_check_callback, 0, UpdateResponse::Results(), 0));
       return true;
     }
   };
@@ -2243,4 +2244,174 @@
                                    base::Version("1.0"), 10);
 }
 
+TEST_F(UpdateClientTest, RetryAfter) {
+  class DataCallbackFake {
+   public:
+    static void Callback(const std::vector<std::string>& ids,
+                         std::vector<CrxComponent>* components) {
+      CrxComponent crx;
+      crx.name = "test_jebg";
+      crx.pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash));
+      crx.version = Version("0.9");
+      crx.installer = new TestInstaller;
+      components->push_back(crx);
+    }
+  };
+
+  class CompletionCallbackFake {
+   public:
+    static void Callback(const base::Closure& quit_closure, int error) {
+      static int num_call = 0;
+      ++num_call;
+
+      EXPECT_LE(num_call, 4);
+
+      if (num_call == 1) {
+        EXPECT_EQ(0, error);
+      } else if (num_call == 2) {
+        // This request is throttled since the update engine received a
+        // positive |retry_after_sec| value in the update check response.
+        EXPECT_EQ(Error::ERROR_UPDATE_RETRY_LATER, error);
+      } else if (num_call == 3) {
+        // This request is a foreground Install, which is never throttled.
+        // The update engine received a |retry_after_sec| value of 0, which
+        // resets the throttling.
+        EXPECT_EQ(0, error);
+      } else if (num_call == 4) {
+        // This request succeeds since there is no throttling in effect.
+        EXPECT_EQ(0, error);
+      }
+
+      quit_closure.Run();
+    }
+  };
+
+  class FakeUpdateChecker : public UpdateChecker {
+   public:
+    static scoped_ptr<UpdateChecker> Create(
+        const scoped_refptr<Configurator>& config) {
+      return scoped_ptr<UpdateChecker>(new FakeUpdateChecker());
+    }
+
+    bool CheckForUpdates(
+        const std::vector<CrxUpdateItem*>& items_to_check,
+        const std::string& additional_attributes,
+        const UpdateCheckCallback& update_check_callback) override {
+      static int num_call = 0;
+      ++num_call;
+
+      EXPECT_LE(num_call, 3);
+
+      int retry_after_sec(0);
+      if (num_call == 1) {
+        // Throttle the next call.
+        retry_after_sec = 60 * 60;  // 1 hour.
+      }
+
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE, base::Bind(update_check_callback, 0,
+                                UpdateResponse::Results(), retry_after_sec));
+      return true;
+    }
+  };
+
+  class FakeCrxDownloader : public CrxDownloader {
+   public:
+    static scoped_ptr<CrxDownloader> Create(
+        bool is_background_download,
+        net::URLRequestContextGetter* context_getter,
+        const scoped_refptr<base::SequencedTaskRunner>& task_runner) {
+      return scoped_ptr<CrxDownloader>(new FakeCrxDownloader());
+    }
+
+   private:
+    FakeCrxDownloader()
+        : CrxDownloader(base::ThreadTaskRunnerHandle::Get(), nullptr) {}
+    ~FakeCrxDownloader() override {}
+
+    void DoStartDownload(const GURL& url) override { EXPECT_TRUE(false); }
+  };
+
+  class FakePingManager : public FakePingManagerImpl {
+   public:
+    explicit FakePingManager(const scoped_refptr<Configurator>& config)
+        : FakePingManagerImpl(config) {}
+    ~FakePingManager() override { EXPECT_TRUE(items().empty()); }
+  };
+
+  scoped_ptr<PingManager> ping_manager(new FakePingManager(config()));
+  scoped_refptr<UpdateClient> update_client(new UpdateClientImpl(
+      config(), std::move(ping_manager), &FakeUpdateChecker::Create,
+      &FakeCrxDownloader::Create));
+
+  MockObserver observer;
+
+  InSequence seq;
+  EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES,
+                                "jebgalgnebhfojomionfpkfelancnnkf"))
+      .Times(1);
+  EXPECT_CALL(observer, OnEvent(Events::COMPONENT_NOT_UPDATED,
+                                "jebgalgnebhfojomionfpkfelancnnkf"))
+      .Times(1);
+  EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES,
+                                "jebgalgnebhfojomionfpkfelancnnkf"))
+      .Times(1);
+  EXPECT_CALL(observer, OnEvent(Events::COMPONENT_NOT_UPDATED,
+                                "jebgalgnebhfojomionfpkfelancnnkf"))
+      .Times(1);
+  EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES,
+                                "jebgalgnebhfojomionfpkfelancnnkf"))
+      .Times(1);
+  EXPECT_CALL(observer, OnEvent(Events::COMPONENT_NOT_UPDATED,
+                                "jebgalgnebhfojomionfpkfelancnnkf"))
+      .Times(1);
+
+  update_client->AddObserver(&observer);
+
+  std::vector<std::string> ids;
+  ids.push_back(std::string("jebgalgnebhfojomionfpkfelancnnkf"));
+
+  {
+    // The engine handles this Update call but responds with a valid
+    // |retry_after_sec|, which causes subsequent calls to fail.
+    base::RunLoop runloop;
+    update_client->Update(
+        ids, base::Bind(&DataCallbackFake::Callback),
+        base::Bind(&CompletionCallbackFake::Callback, runloop.QuitClosure()));
+    runloop.Run();
+  }
+
+  {
+    // This call will result in a completion callback invoked with
+    // Error::ERROR_UPDATE_RETRY_LATER.
+    base::RunLoop runloop;
+    update_client->Update(
+        ids, base::Bind(&DataCallbackFake::Callback),
+        base::Bind(&CompletionCallbackFake::Callback, runloop.QuitClosure()));
+    runloop.Run();
+  }
+
+  {
+    // The Install call is handled, and the throttling is reset due to
+    // the value of |retry_after_sec| in the completion callback.
+    base::RunLoop runloop;
+    update_client->Install(
+        std::string("jebgalgnebhfojomionfpkfelancnnkf"),
+        base::Bind(&DataCallbackFake::Callback),
+        base::Bind(&CompletionCallbackFake::Callback, runloop.QuitClosure()));
+    runloop.Run();
+  }
+
+  {
+    // This call succeeds.
+    base::RunLoop runloop;
+    update_client->Update(
+        ids, base::Bind(&DataCallbackFake::Callback),
+        base::Bind(&CompletionCallbackFake::Callback, runloop.QuitClosure()));
+    runloop.Run();
+  }
+
+  update_client->RemoveObserver(&observer);
+}
+
 }  // namespace update_client
diff --git a/components/update_client/update_engine.cc b/components/update_client/update_engine.cc
index 06cf6eb..20f9a6a 100644
--- a/components/update_client/update_engine.cc
+++ b/components/update_client/update_engine.cc
@@ -9,6 +9,7 @@
 #include "base/location.h"
 #include "base/stl_util.h"
 #include "base/thread_task_runner_handle.h"
+#include "base/time/time.h"
 #include "components/update_client/action_update_check.h"
 #include "components/update_client/configurator.h"
 #include "components/update_client/crx_update_item.h"
@@ -36,8 +37,8 @@
       blocking_task_runner(config->GetSequencedTaskRunner()),
       update_checker_factory(update_checker_factory),
       crx_downloader_factory(crx_downloader_factory),
-      ping_manager(ping_manager) {
-}
+      ping_manager(ping_manager),
+      retry_after_sec_(0) {}
 
 UpdateContext::~UpdateContext() {
   STLDeleteElements(&update_items);
@@ -84,6 +85,12 @@
     const CompletionCallback& callback) {
   DCHECK(thread_checker_.CalledOnValidThread());
 
+  if (IsThrottled(is_foreground)) {
+    base::ThreadTaskRunnerHandle::Get()->PostTask(
+        FROM_HERE, base::Bind(callback, Error::ERROR_UPDATE_RETRY_LATER));
+    return;
+  }
+
   scoped_ptr<UpdateContext> update_context(new UpdateContext(
       config_, is_foreground, ids, crx_data_callback,
       notify_observers_callback_, callback, update_checker_factory_,
@@ -109,6 +116,18 @@
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(update_contexts_.find(update_context) != update_contexts_.end());
 
+  const int throttle_sec(update_context->retry_after_sec_);
+  DCHECK_LE(throttle_sec, 24 * 60 * 60);
+
+  // Only positive values for throttle_sec are effective. 0 means that no
+  // throttling occurs and has the effect of resetting the member.
+  // Negative values are not trusted and are ignored.
+  if (throttle_sec >= 0)
+    throttle_updates_until_ =
+        throttle_sec
+            ? base::Time::Now() + base::TimeDelta::FromSeconds(throttle_sec)
+            : base::Time();
+
   auto callback = update_context->callback;
 
   update_contexts_.erase(update_context);
@@ -117,4 +136,16 @@
   callback.Run(error);
 }
 
+bool UpdateEngine::IsThrottled(bool is_foreground) const {
+  if (is_foreground || throttle_updates_until_.is_null())
+    return false;
+
+  const auto now(base::Time::Now());
+
+  // Throttle the calls in the interval (t - 1 day, t) to limit the effect of
+  // unset clocks or clock drift.
+  return throttle_updates_until_ - base::TimeDelta::FromDays(1) < now &&
+         now < throttle_updates_until_;
+}
+
 }  // namespace update_client
diff --git a/components/update_client/update_engine.h b/components/update_client/update_engine.h
index f2a577a3..1d69ece 100644
--- a/components/update_client/update_engine.h
+++ b/components/update_client/update_engine.h
@@ -63,6 +63,10 @@
  private:
   void UpdateComplete(UpdateContext* update_context, int error);
 
+  // Returns true if the update engine rejects this update call because it
+  // occurs too soon.
+  bool IsThrottled(bool is_foreground) const;
+
   base::ThreadChecker thread_checker_;
 
   scoped_refptr<Configurator> config_;
@@ -79,6 +83,12 @@
   // Contains the contexts associated with each update in progress.
   std::set<UpdateContext*> update_contexts_;
 
+  // Implements a rate limiting mechanism for background update checks. Has the
+  // effect of rejecting the update call if the update call occurs before
+  // a certain time, which is negotiated with the server as part of the
+  // update protocol. See the comments for X-Retry-After header.
+  base::Time throttle_updates_until_;
+
   DISALLOW_COPY_AND_ASSIGN(UpdateEngine);
 };
 
@@ -135,6 +145,9 @@
 
   // Contains the ids of the items to update.
   std::queue<std::string> queue;
+
+  // The time in seconds to wait until doing further update checks.
+  int retry_after_sec_;
 };
 
 }  // namespace update_client
diff --git a/components/update_client/utils.cc b/components/update_client/utils.cc
index a76e073..caa00cea 100644
--- a/components/update_client/utils.cc
+++ b/components/update_client/utils.cc
@@ -34,6 +34,7 @@
 #include "net/url_request/url_fetcher.h"
 #include "net/url_request/url_request_context_getter.h"
 #include "net/url_request/url_request_status.h"
+#include "url/gurl.h"
 
 namespace update_client {
 
@@ -238,4 +239,12 @@
          }) == brand.end();
 }
 
+void RemoveUnsecureUrls(std::vector<GURL>* urls) {
+  DCHECK(urls);
+  urls->erase(std::remove_if(
+                  urls->begin(), urls->end(),
+                  [](const GURL& url) { return !url.SchemeIsCryptographic(); }),
+              urls->end());
+}
+
 }  // namespace update_client
diff --git a/components/update_client/utils.h b/components/update_client/utils.h
index bd428ca..af353f7 100644
--- a/components/update_client/utils.h
+++ b/components/update_client/utils.h
@@ -6,6 +6,7 @@
 #define COMPONENTS_UPDATE_CLIENT_UTILS_H_
 
 #include <string>
+#include <vector>
 
 #include "base/memory/scoped_ptr.h"
 
@@ -98,6 +99,9 @@
 // Returns true if the |brand| parameter matches ^([a-zA-Z]{4})?$ .
 bool IsValidBrand(const std::string& brand);
 
+// Removes the unsecure urls in the |urls| parameter.
+void RemoveUnsecureUrls(std::vector<GURL>* urls);
+
 }  // namespace update_client
 
 #endif  // COMPONENTS_UPDATE_CLIENT_UTILS_H_
diff --git a/components/update_client/utils_unittest.cc b/components/update_client/utils_unittest.cc
index 81530705..b15fcac4 100644
--- a/components/update_client/utils_unittest.cc
+++ b/components/update_client/utils_unittest.cc
@@ -81,4 +81,35 @@
   EXPECT_FALSE(IsValidBrand(std::string("\xaa")));   // Has non-ASCII char.
 }
 
+TEST(UpdateClientUtils, RemoveUnsecureUrls) {
+  const GURL test1[] = {GURL("http://foo"), GURL("https://foo")};
+  std::vector<GURL> urls(std::begin(test1), std::end(test1));
+  RemoveUnsecureUrls(&urls);
+  EXPECT_EQ(1u, urls.size());
+  EXPECT_EQ(urls[0], GURL("https://foo"));
+
+  const GURL test2[] = {GURL("https://foo"), GURL("http://foo")};
+  urls.assign(std::begin(test2), std::end(test2));
+  RemoveUnsecureUrls(&urls);
+  EXPECT_EQ(1u, urls.size());
+  EXPECT_EQ(urls[0], GURL("https://foo"));
+
+  const GURL test3[] = {GURL("https://foo"), GURL("https://bar")};
+  urls.assign(std::begin(test3), std::end(test3));
+  RemoveUnsecureUrls(&urls);
+  EXPECT_EQ(2u, urls.size());
+  EXPECT_EQ(urls[0], GURL("https://foo"));
+  EXPECT_EQ(urls[1], GURL("https://bar"));
+
+  const GURL test4[] = {GURL("http://foo")};
+  urls.assign(std::begin(test4), std::end(test4));
+  RemoveUnsecureUrls(&urls);
+  EXPECT_EQ(0u, urls.size());
+
+  const GURL test5[] = {GURL("http://foo"), GURL("http://bar")};
+  urls.assign(std::begin(test5), std::end(test5));
+  RemoveUnsecureUrls(&urls);
+  EXPECT_EQ(0u, urls.size());
+}
+
 }  // namespace update_client
diff --git a/extensions/browser/updater/update_data_provider.cc b/extensions/browser/updater/update_data_provider.cc
index 1adb8f9..b86eaa8 100644
--- a/extensions/browser/updater/update_data_provider.cc
+++ b/extensions/browser/updater/update_data_provider.cc
@@ -48,8 +48,8 @@
     crypto::SHA256HashString(pubkey_bytes, info->pk_hash.data(),
                              info->pk_hash.size());
     info->version = *extension->version();
-    info->allow_background_download = false;
-
+    info->allows_background_download = false;
+    info->requires_network_encryption = true;
     info->installer = new UpdateInstallShim(
         id, extension->path(),
         base::Bind(&UpdateDataProvider::RunInstallCallback, this));
diff --git a/ios/chrome/browser/component_updater/ios_component_updater_configurator.cc b/ios/chrome/browser/component_updater/ios_component_updater_configurator.cc
index 0ad785b5..c92046e 100644
--- a/ios/chrome/browser/component_updater/ios_component_updater_configurator.cc
+++ b/ios/chrome/browser/component_updater/ios_component_updater_configurator.cc
@@ -57,10 +57,13 @@
   ~IOSConfigurator() override {}
 };
 
+// Allows the component updater to use non-encrypted communication with the
+// update backend. The security of the update checks is enforced using
+// a custom message signing protocol and it does not depend on using HTTPS.
 IOSConfigurator::IOSConfigurator(
     const base::CommandLine* cmdline,
     net::URLRequestContextGetter* url_request_getter)
-    : configurator_impl_(cmdline, url_request_getter) {}
+    : configurator_impl_(cmdline, url_request_getter, false) {}
 
 int IOSConfigurator::InitialDelay() const {
   return configurator_impl_.InitialDelay();
diff --git a/ios/chrome/browser/net/crl_set_fetcher.cc b/ios/chrome/browser/net/crl_set_fetcher.cc
index 71d16a23..a7e78a23 100644
--- a/ios/chrome/browser/net/crl_set_fetcher.cc
+++ b/ios/chrome/browser/net/crl_set_fetcher.cc
@@ -136,7 +136,8 @@
   component.installer = this;
   component.name = "CRLSet";
   component.version = Version(base::UintToString(sequence_of_loaded_crl));
-  component.allow_background_download = false;
+  component.allows_background_download = false;
+  component.requires_network_encryption = false;
   if (!component.version.IsValid()) {
     NOTREACHED();
     component.version = Version("0");