AU: Proxy Resolver classes

A collection of classes (abstract ProxyResolver interface and two
concrete implementations). The abstraction is one that should suit us
moving forward: a string URL is provided and a collection of proxy
servers is returned. One implementation always returns [no
proxy]. Another returns [manual settings from chrome, if applicable,
..., no proxy].

A future concrete implementation will consult Chrome via DBus with the
URL in question, however this is delayed until Chrome exposes such an
API.

As a result of this API missing from Chrome, this CL only resolves
proxies for a URL with manually input proxy settings.

Future CLs will integrate this into the rest of the update system.

BUG=3167
TEST=unit tests, (with future CLs) working proxy support on device

Review URL: http://codereview.chromium.org/5151005

Change-Id: If9dc6d09da681bca6f6ae74c896ba946ab81cb4d
diff --git a/SConstruct b/SConstruct
index f772882..97cc6e3 100644
--- a/SConstruct
+++ b/SConstruct
@@ -236,6 +236,7 @@
                    buffered_file_writer.cc
                    bzip.cc
                    bzip_extent_writer.cc
+                   chrome_proxy_resolver.cc
                    cycle_breaker.cc
                    dbus_service.cc
                    decompressing_file_writer.cc
@@ -261,6 +262,7 @@
                    payload_signer.cc
                    postinstall_runner_action.cc
                    prefs.cc
+                   proxy_resolver.cc
                    simple_key_value_store.cc
                    split_file_writer.cc
                    subprocess.cc
@@ -278,6 +280,7 @@
                             action_processor_unittest.cc
                             buffered_file_writer_unittest.cc
                             bzip_extent_writer_unittest.cc
+                            chrome_proxy_resolver_unittest.cc
                             cycle_breaker_unittest.cc
                             decompressing_file_writer_unittest.cc
                             delta_diff_generator_unittest.cc
diff --git a/chrome_proxy_resolver.cc b/chrome_proxy_resolver.cc
new file mode 100644
index 0000000..eaa213a
--- /dev/null
+++ b/chrome_proxy_resolver.cc
@@ -0,0 +1,185 @@
+// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "update_engine/chrome_proxy_resolver.h"
+
+#include <base/json/json_reader.h>
+#include <base/scoped_ptr.h>
+#include <base/values.h>
+
+#include "update_engine/utils.h"
+
+using std::string;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+const char kSessionManagerService[] = "org.chromium.SessionManager";
+const char kSessionManagerPath[] = "/org/chromium/SessionManager";
+const char kSessionManagerInterface[] = "org.chromium.SessionManagerInterface";
+const char kSessionManagerRetrievePropertyMethod[] =
+    "RetrieveProperty";
+const char kSessionManagerProxySettingsKey[] = "cros.proxy.everywhere";
+
+bool ChromeProxyResolver::GetProxiesForUrl(
+    const std::string& url,
+    std::vector<std::string>* out_proxies) {
+  // First, query dbus for the currently stored settings
+  DBusGProxy* proxy = DbusProxy();
+  TEST_AND_RETURN_FALSE(proxy);
+  string json_settings;
+  TEST_AND_RETURN_FALSE(GetJsonProxySettings(proxy, &json_settings));
+  LOG(INFO) << "got settings:" << json_settings;
+  TEST_AND_RETURN_FALSE(
+      GetProxiesForUrlWithSettings(url, json_settings, out_proxies));
+  return true;
+}
+
+bool ChromeProxyResolver::GetJsonProxySettings(DBusGProxy* proxy,
+                                               std::string* out_json) {
+  gchar* value = NULL;
+  GArray* sig = NULL;
+  GError* error = NULL;
+  TEST_AND_RETURN_FALSE(
+      dbus_->ProxyCall(proxy,
+                       kSessionManagerRetrievePropertyMethod,
+                       &error,
+                       G_TYPE_STRING, kSessionManagerProxySettingsKey,
+                       G_TYPE_INVALID,
+                       G_TYPE_STRING, &value,
+                       DBUS_TYPE_G_UCHAR_ARRAY, &sig,
+                       G_TYPE_INVALID));
+  g_array_free(sig, false);
+  out_json->assign(value);
+  g_free(value);
+  return true;
+}
+
+DBusGProxy* ChromeProxyResolver::DbusProxy() {
+  GError* error = NULL;
+  DBusGConnection* bus = dbus_->BusGet(DBUS_BUS_SYSTEM, &error);
+  TEST_AND_RETURN_FALSE(bus);
+  DBusGProxy* proxy = dbus_->ProxyNewForNameOwner(bus,
+                                                  kSessionManagerService,
+                                                  kSessionManagerPath,
+                                                  kSessionManagerInterface,
+                                                  &error);
+  if (!proxy) {
+    LOG(ERROR) << "Error getting FlimFlam proxy: "
+               << utils::GetGErrorMessage(error);
+  }
+  return proxy;
+}
+
+namespace {
+enum ProxyMode {
+  kProxyModeDirect = 0,
+  kProxyModeAutoDetect,
+  kProxyModePACScript,
+  kProxyModeSingle,
+  kProxyModeProxyPerScheme
+}; 
+}  // namespace {}
+
+bool ChromeProxyResolver::GetProxiesForUrlWithSettings(
+    const string& url,
+    const string& json_settings,
+    std::vector<std::string>* out_proxies) {
+  base::JSONReader parser;
+
+  scoped_ptr<Value> root(
+      parser.JsonToValue(json_settings,
+                         true,  // check root is obj/arr
+                         false));  // false = disallow trailing comma
+  if (!root.get()) {
+    LOG(ERROR) << "Unable to parse \"" << json_settings << "\": "
+               << parser.GetErrorMessage();
+    return false;
+  }
+
+  TEST_AND_RETURN_FALSE(root->IsType(Value::TYPE_DICTIONARY));
+
+  DictionaryValue* root_dict = dynamic_cast<DictionaryValue*>(root.get());
+  TEST_AND_RETURN_FALSE(root_dict);
+  int mode = -1;
+  TEST_AND_RETURN_FALSE(root_dict->GetInteger("mode", &mode));
+
+  LOG(INFO) << "proxy mode: " << mode;
+  if (mode != kProxyModeSingle &&
+      mode != kProxyModeProxyPerScheme) {
+    LOG(INFO) << "unsupported proxy scheme";
+    out_proxies->clear();
+    out_proxies->push_back(kNoProxy);
+    return true;
+  }
+  if (mode == kProxyModeSingle) {
+    LOG(INFO) << "single proxy mode";
+    string proxy_string;
+    TEST_AND_RETURN_FALSE(root_dict->GetString("single.server", &proxy_string));
+    if (proxy_string.find("://") == string::npos) {
+      // missing protocol, assume http.
+      proxy_string = string("http://") + proxy_string;
+    }
+    out_proxies->clear();
+    out_proxies->push_back(proxy_string);
+    LOG(INFO) << "single proxy: " << (*out_proxies)[0];
+    out_proxies->push_back(kNoProxy);
+    return true;
+  }
+  // Proxy per scheme mode.
+  LOG(INFO) << "proxy per scheme mode";
+
+  // Find which scheme we are
+  bool url_is_http = utils::StringHasPrefix(url, "http://");
+  if (!url_is_http)
+    TEST_AND_RETURN_FALSE(utils::StringHasPrefix(url, "https://"));
+
+  // Using "proto_*" variables to refer to http or https
+  const string proto_path = url_is_http ? "http.server" : "https.server";
+  const string socks_path = "socks.server";
+
+  out_proxies->clear();
+
+  string proto_server, socks_server;
+  if (root_dict->GetString(proto_path, &proto_server)) {
+    if (proto_server.find("://") == string::npos) {
+      // missing protocol, assume http.
+      proto_server = string("http://") + proto_server;
+    }
+    out_proxies->push_back(proto_server);
+    LOG(INFO) << "got http/https server: " << proto_server;
+  }
+  if (root_dict->GetString(socks_path, &socks_server)) {
+    out_proxies->push_back(socks_server);
+    LOG(INFO) << "got socks server: " << proto_server;
+  }
+  out_proxies->push_back(kNoProxy);
+  return true;
+}
+
+bool ChromeProxyResolver::GetProxyType(const std::string& proxy,
+                                       curl_proxytype* out_type) {
+  if (utils::StringHasPrefix(proxy, "socks5://") ||
+      utils::StringHasPrefix(proxy, "socks://")) {
+    *out_type = CURLPROXY_SOCKS5_HOSTNAME;
+    return true;
+  }
+  if (utils::StringHasPrefix(proxy, "socks4://")) {
+    *out_type = CURLPROXY_SOCKS4A;
+    return true;
+  }
+  if (utils::StringHasPrefix(proxy, "http://") ||
+      utils::StringHasPrefix(proxy, "https://")) {
+    *out_type = CURLPROXY_HTTP;
+    return true;
+  }
+  if (utils::StringHasPrefix(proxy, kNoProxy)) {
+    // known failure case. don't log.
+    return false;
+  }
+  LOG(INFO) << "Unknown proxy type: " << proxy;
+  return false;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/chrome_proxy_resolver.h b/chrome_proxy_resolver.h
new file mode 100644
index 0000000..796f8fb
--- /dev/null
+++ b/chrome_proxy_resolver.h
@@ -0,0 +1,66 @@
+// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_CHROME_PROXY_RESOLVER_H__
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_CHROME_PROXY_RESOLVER_H__
+
+#include <string>
+#include <vector>
+
+#include <curl/curl.h>
+#include <gtest/gtest_prod.h>  // for FRIEND_TEST
+
+#include "update_engine/dbus_interface.h"
+#include "update_engine/proxy_resolver.h"
+
+namespace chromeos_update_engine {
+
+extern const char kSessionManagerService[];
+extern const char kSessionManagerPath[];
+extern const char kSessionManagerInterface[];
+extern const char kSessionManagerRetrievePropertyMethod[];
+extern const char kSessionManagerProxySettingsKey[];
+
+// Class to resolve proxy for a url based on Chrome's proxy settings.
+
+// Currently only supports manual settings, not PAC files or autodetected
+// settings.
+
+class ChromeProxyResolver : public ProxyResolver {
+ public:
+  explicit ChromeProxyResolver(DbusGlibInterface* dbus) : dbus_(dbus) {}
+  virtual ~ChromeProxyResolver() {}
+
+  virtual bool GetProxiesForUrl(const std::string& url,
+                                std::vector<std::string>* out_proxies);
+
+  // Get the curl proxy type for a given proxy url. Returns true on success.
+  // Note: if proxy is kNoProxy, this will return false.
+  static bool GetProxyType(const std::string& proxy,
+                              curl_proxytype* out_type);
+
+ private:
+  FRIEND_TEST(ChromeProxyResolverTest, GetProxiesForUrlWithSettingsTest);
+
+  // Fetches a dbus proxy to session manager. Returns NULL on failure.
+  DBusGProxy* DbusProxy();
+
+  // Fetches the json-encoded proxy settings string from the session manager.
+  bool GetJsonProxySettings(DBusGProxy* proxy, std::string* out_json);
+
+  // Given a |url| and the json encoded settings |json_settings|,
+  // returns the proper proxy servers in |out_proxies|. Returns true on
+  // success.
+  bool GetProxiesForUrlWithSettings(const std::string& url,
+                                    const std::string& json_settings,
+                                    std::vector<std::string>* out_proxies);
+
+  DbusGlibInterface* dbus_;
+  
+  DISALLOW_COPY_AND_ASSIGN(ChromeProxyResolver);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // CHROMEOS_PLATFORM_UPDATE_ENGINE_CHROME_PROXY_RESOLVER_H__
diff --git a/chrome_proxy_resolver_unittest.cc b/chrome_proxy_resolver_unittest.cc
new file mode 100644
index 0000000..66b2401
--- /dev/null
+++ b/chrome_proxy_resolver_unittest.cc
@@ -0,0 +1,147 @@
+// Copyright (c) 2009 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "update_engine/chrome_proxy_resolver.h"
+#include "update_engine/mock_dbus_interface.h"
+
+using std::string;
+using std::vector;
+using ::testing::_;
+using ::testing::Return;
+using ::testing::SetArgumentPointee;
+using ::testing::StrEq;
+
+namespace chromeos_update_engine {
+
+class ChromeProxyResolverTest : public ::testing::Test { };
+
+TEST(ChromeProxyResolverTest, GetProxiesForUrlWithSettingsTest) {
+  const string kSingle =
+      "{\"mode\":3,\"single\":{\"server\":\"192.168.42.86:80\",\"src\":0}}";
+  const string kSocks =
+      "{\"mode\":4,\"socks\":{\"server\":\"socks5://192.168.42.83:5555\","
+      "\"src\":0}}";
+  const string kAll =
+      "{\"http\":{\"server\":\"http_proxy:11\",\"src\":0},"
+      "\"https\":{\"server\":\"https://https_proxy:22\",\"src\":0},"
+      "\"mode\":4,"
+      "\"socks\":{\"server\":\"socks5://socks:55\",\"src\":0}}";
+  const string kHttpHttps =
+      "{\"http\":{\"server\":\"http_proxy:11\",\"src\":0},"
+      "\"https\":{\"server\":\"https://https_proxy:22\",\"src\":0},"
+      "\"mode\":4}";
+
+  ChromeProxyResolver resolver(NULL);
+  vector<string> out;
+  string urls[] = {"http://foo.com/update", "https://bar.com/foo.gz"};
+  string multi_settings[] = {kAll, kHttpHttps};
+  for (size_t i = 0; i < arraysize(urls); i++) {
+    const string& url = urls[i];
+    LOG(INFO) << "url: " << url;
+    EXPECT_TRUE(resolver.GetProxiesForUrlWithSettings(url, kSingle, &out));
+    EXPECT_EQ(2, out.size());
+    EXPECT_EQ("http://192.168.42.86:80", out[0]);
+    EXPECT_EQ(kNoProxy, out[1]);
+
+    EXPECT_TRUE(resolver.GetProxiesForUrlWithSettings(url, kSocks, &out));
+    EXPECT_EQ(2, out.size());
+    EXPECT_EQ("socks5://192.168.42.83:5555", out[0]);
+    EXPECT_EQ(kNoProxy, out[1]);
+  
+    for (size_t j = 0; j < arraysize(multi_settings); j++) {
+      const string& settings = multi_settings[j];
+      EXPECT_TRUE(resolver.GetProxiesForUrlWithSettings(url, settings, &out));
+      EXPECT_EQ(j == 0 ? 3 : 2, out.size());
+      if (i == 0)
+        EXPECT_EQ("http://http_proxy:11", out[0]);
+      else
+        EXPECT_EQ("https://https_proxy:22", out[0]);
+      if (j == 0)
+        EXPECT_EQ("socks5://socks:55", out[1]);
+      EXPECT_EQ(kNoProxy, out.back());
+    }
+  }
+  
+  // Bad JSON
+  EXPECT_FALSE(resolver.GetProxiesForUrlWithSettings("http://foo.com",
+                                                     "}",
+                                                     &out));
+  
+  // Bad proxy scheme
+  EXPECT_TRUE(resolver.GetProxiesForUrlWithSettings("http://foo.com",
+                                                    "{\"mode\":1}",
+                                                    &out));
+  EXPECT_EQ(1, out.size());
+  EXPECT_EQ(kNoProxy, out[0]);
+}
+
+TEST(ChromeProxyResolverTest, DbusInterfaceTest) {
+  long number = 1;
+  DBusGConnection* kMockSystemBus =
+      reinterpret_cast<DBusGConnection*>(number++);
+  DBusGProxy* kMockSessionManagerProxy =
+      reinterpret_cast<DBusGProxy*>(number++);
+
+  const char settings[] = 
+      "{\"mode\":4,\"socks\":{\"server\":\"socks5://192.168.52.83:5555\","
+      "\"src\":0}}";
+
+  MockDbusGlib dbus_iface;
+  ChromeProxyResolver resolver(&dbus_iface);
+
+  GArray* ret_array = g_array_new(FALSE, FALSE, 1);
+
+  EXPECT_CALL(dbus_iface, BusGet(DBUS_BUS_SYSTEM, _))
+      .Times(1)
+      .WillRepeatedly(Return(kMockSystemBus));
+  EXPECT_CALL(dbus_iface,
+              ProxyNewForNameOwner(kMockSystemBus,
+                                   StrEq(kSessionManagerService),
+                                   StrEq(kSessionManagerPath),
+                                   StrEq(kSessionManagerInterface),
+                                   _))
+      .WillOnce(Return(kMockSessionManagerProxy));
+  EXPECT_CALL(dbus_iface, ProxyCall(
+      kMockSessionManagerProxy,
+      StrEq(kSessionManagerRetrievePropertyMethod),
+      _,
+      G_TYPE_STRING, StrEq(kSessionManagerProxySettingsKey),
+      G_TYPE_INVALID,
+      G_TYPE_STRING, _,
+      DBUS_TYPE_G_UCHAR_ARRAY, _))
+      .WillOnce(DoAll(SetArgumentPointee<7>(strdup(settings)),
+                      SetArgumentPointee<9>(ret_array),
+                      Return(TRUE)));
+
+  vector<string> proxies;
+  EXPECT_TRUE(resolver.GetProxiesForUrl("http://user:pass@foo.com:22",
+                                        &proxies));
+  EXPECT_EQ(2, proxies.size());
+  EXPECT_EQ("socks5://192.168.52.83:5555", proxies[0]);
+  EXPECT_EQ(kNoProxy, proxies[1]);
+}
+
+TEST(ChromeProxyResolverTest, GetProxyTypeTest) {
+  curl_proxytype type;
+  EXPECT_TRUE(ChromeProxyResolver::GetProxyType("socks://f.ru", &type));
+  EXPECT_EQ(CURLPROXY_SOCKS5_HOSTNAME, type);
+  EXPECT_TRUE(ChromeProxyResolver::GetProxyType("socks5://f.ru:9", &type));
+  EXPECT_EQ(CURLPROXY_SOCKS5_HOSTNAME, type);
+  EXPECT_TRUE(ChromeProxyResolver::GetProxyType("socks4://f.ru:9", &type));
+  EXPECT_EQ(CURLPROXY_SOCKS4A, type);
+  EXPECT_TRUE(ChromeProxyResolver::GetProxyType("http://f.no:88", &type));
+  EXPECT_EQ(CURLPROXY_HTTP, type);
+  EXPECT_TRUE(ChromeProxyResolver::GetProxyType("https://f.no:88", &type));
+  EXPECT_EQ(CURLPROXY_HTTP, type);
+  EXPECT_FALSE(ChromeProxyResolver::GetProxyType(kNoProxy, &type));
+  EXPECT_FALSE(ChromeProxyResolver::GetProxyType("foo://", &type));
+  EXPECT_FALSE(ChromeProxyResolver::GetProxyType("missing.com:8", &type));
+}
+
+}  // namespace chromeos_update_engine
diff --git a/dbus_interface.h b/dbus_interface.h
index 1b5c953..de09fff 100644
--- a/dbus_interface.h
+++ b/dbus_interface.h
@@ -7,6 +7,7 @@
 
 // This class interfaces with DBus. The interface allows it to be mocked.
 
+#include <base/logging.h>
 #include <dbus/dbus-glib.h>
 
 namespace chromeos_update_engine {
@@ -25,7 +26,7 @@
 
   // wraps dbus_g_bus_get
   virtual DBusGConnection*  BusGet(DBusBusType type, GError** error) = 0;
-  
+
   // wraps dbus_g_proxy_call
   virtual gboolean ProxyCall(DBusGProxy* proxy,
                              const char* method,
@@ -34,6 +35,15 @@
                              GType var_arg1,
                              GHashTable** var_arg2,
                              GType var_arg3) = 0;
+
+  virtual gboolean ProxyCall(DBusGProxy* proxy,
+                             const char* method,
+                             GError** error,
+                             GType var_arg1, const char* var_arg2,
+                             GType var_arg3,
+                             GType var_arg4, gchar** var_arg5,
+                             GType var_arg6, GArray** var_arg7,
+                             GType var_arg8) = 0;
 };
 
 class ConcreteDbusGlib : public DbusGlibInterface {
@@ -67,6 +77,19 @@
     return dbus_g_proxy_call(
         proxy, method, error, first_arg_type, var_arg1, var_arg2, var_arg3);
   }
+
+  virtual gboolean ProxyCall(DBusGProxy* proxy,
+                             const char* method,
+                             GError** error,
+                             GType var_arg1, const char* var_arg2,
+                             GType var_arg3,
+                             GType var_arg4, gchar** var_arg5,
+                             GType var_arg6, GArray** var_arg7,
+                             GType var_arg8) {
+    return dbus_g_proxy_call(
+        proxy, method, error, var_arg1, var_arg2, var_arg3,
+        var_arg4, var_arg5, var_arg6, var_arg7, var_arg8);
+  }
 };
 
 }  // namespace chromeos_update_engine
diff --git a/mock_dbus_interface.h b/mock_dbus_interface.h
index bf0d609..c817109 100644
--- a/mock_dbus_interface.h
+++ b/mock_dbus_interface.h
@@ -30,6 +30,34 @@
                                    GType var_arg1,
                                    GHashTable** var_arg2,
                                    GType var_arg3));
+  MOCK_METHOD10(ProxyCall, gboolean(DBusGProxy* proxy,
+                                    const char* method,
+                                    GError** error,
+                                    GType var_arg1, const char* var_arg2,
+                                    GType var_arg3,
+                                    GType var_arg4, gchar** var_arg5,
+                                    GType var_arg6, GArray** var_arg7));
+
+  // Since gmock only supports mocking functions up to 10 args, we
+  // take the 11-arg function we'd like to mock, drop the last arg
+  // and call the 10-arg version. Dropping the last arg isn't ideal,
+  // but this is a lot better than nothing.
+  gboolean ProxyCall(DBusGProxy* proxy,
+                       const char* method,
+                       GError** error,
+                       GType var_arg1, const char* var_arg2,
+                       GType var_arg3,
+                       GType var_arg4, gchar** var_arg5,
+                       GType var_arg6, GArray** var_arg7,
+                       GType var_arg8) {
+    return ProxyCall(proxy,
+                     method,
+                     error,
+                     var_arg1, var_arg2,
+                     var_arg3,
+                     var_arg4, var_arg5,
+                     var_arg6, var_arg7);
+  }
 };
 
 }  // namespace chromeos_update_engine
diff --git a/proxy_resolver.cc b/proxy_resolver.cc
new file mode 100644
index 0000000..6b3bd93
--- /dev/null
+++ b/proxy_resolver.cc
@@ -0,0 +1,21 @@
+// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "update_engine/proxy_resolver.h"
+
+using std::string;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+const char kNoProxy[] = "direct://";
+
+bool DirectProxyResolver::GetProxiesForUrl(const string& url,
+                                           vector<string>* out_proxies) {
+  out_proxies->clear();
+  out_proxies->push_back(kNoProxy);
+  return true;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/proxy_resolver.h b/proxy_resolver.h
new file mode 100644
index 0000000..5d9064c
--- /dev/null
+++ b/proxy_resolver.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_PROXY_RESOLVER_H__
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_PROXY_RESOLVER_H__
+
+#include <base/logging.h>
+
+#include <string>
+#include <vector>
+
+namespace chromeos_update_engine {
+
+extern const char kNoProxy[];
+
+class ProxyResolver {
+ public:
+  ProxyResolver() {}
+  virtual ~ProxyResolver() {}
+
+  // Stores a list of proxies for a given |url| in |out_proxy|.
+  // Returns true on success. The resultant proxy will be in one of the
+  // following forms:
+  // http://<host>[:<port>] - HTTP proxy
+  // socks{4,5}://<host>[:<port>] - SOCKS4/5 proxy
+  // kNoProxy - no proxy
+  virtual bool GetProxiesForUrl(const std::string& url,
+                                std::vector<std::string>* out_proxies) = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ProxyResolver);
+};
+
+// Always says to not use a proxy
+class DirectProxyResolver : public ProxyResolver {
+ public:
+  virtual bool GetProxiesForUrl(const std::string& url,
+                                std::vector<std::string>* out_proxies);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(DirectProxyResolver);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // CHROMEOS_PLATFORM_UPDATE_ENGINE_PROXY_RESOLVER_H__