AU: Full proxy support
This CL Introduces a new proxy resolver that queries Chrome browser
for all URLs. This means the updater now works with Proxy
AutoConfiguration (PAC) files and automatic proxy detection.
BUG=chromium-os:9478
TEST=performed update, unittests
Review URL: http://codereview.chromium.org/6594025
Change-Id: I2c169aec06109e3eabc4011e0b536685dfd13f79
diff --git a/SConstruct b/SConstruct
index f75b256..dea012d 100644
--- a/SConstruct
+++ b/SConstruct
@@ -238,6 +238,7 @@
buffered_file_writer.cc
bzip.cc
bzip_extent_writer.cc
+ chrome_browser_proxy_resolver.cc
chrome_proxy_resolver.cc
cycle_breaker.cc
dbus_service.cc
@@ -285,6 +286,7 @@
action_processor_unittest.cc
buffered_file_writer_unittest.cc
bzip_extent_writer_unittest.cc
+ chrome_browser_proxy_resolver_unittest.cc
chrome_proxy_resolver_unittest.cc
cycle_breaker_unittest.cc
decompressing_file_writer_unittest.cc
diff --git a/UpdateEngine.conf b/UpdateEngine.conf
index 534fade..949e5bb 100644
--- a/UpdateEngine.conf
+++ b/UpdateEngine.conf
@@ -28,6 +28,7 @@
<allow send_destination="org.chromium.UpdateEngine"
send_interface="org.chromium.UpdateEngineInterface"
send_member="SetTrack"/>
+ <allow send_interface="org.chromium.UpdateEngineLibcrosProxyResolvedInterface" />
</policy>
<policy context="default">
<deny send_destination="org.chromium.UpdateEngine" />
diff --git a/chrome_browser_proxy_resolver.cc b/chrome_browser_proxy_resolver.cc
new file mode 100644
index 0000000..a481f4f
--- /dev/null
+++ b/chrome_browser_proxy_resolver.cc
@@ -0,0 +1,268 @@
+// Copyright (c) 2011 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_browser_proxy_resolver.h"
+
+#include <map>
+#include <string>
+
+#include <base/string_tokenizer.h>
+#include <base/string_util.h>
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+#include <google/protobuf/stubs/common.h>
+
+#include "update_engine/dbus_constants.h"
+#include "update_engine/utils.h"
+
+namespace chromeos_update_engine {
+
+using google::protobuf::Closure;
+using google::protobuf::NewCallback;
+using std::deque;
+using std::make_pair;
+using std::multimap;
+using std::pair;
+using std::string;
+
+#define LIB_CROS_PROXY_RESOLVE_NAME "ProxyResolved"
+#define LIB_CROS_PROXY_RESOLVE_SIGNAL_INTERFACE \
+ "org.chromium.UpdateEngineLibcrosProxyResolvedInterface"
+const char kLibCrosServiceName[] = "org.chromium.LibCrosService";
+const char kLibCrosServicePath[] = "/org/chromium/LibCrosService";
+const char kLibCrosServiceInterface[] = "org.chromium.LibCrosServiceInterface";
+const char kLibCrosServiceResolveNetworkProxyMethodName[] =
+ "ResolveNetworkProxy";
+const char kLibCrosProxyResolveName[] = LIB_CROS_PROXY_RESOLVE_NAME;
+const char kLibCrosProxyResolveSignalInterface[] =
+ LIB_CROS_PROXY_RESOLVE_SIGNAL_INTERFACE;
+const char kLibCrosProxyResolveSignalFilter[] = "type='signal', "
+ "interface='" LIB_CROS_PROXY_RESOLVE_SIGNAL_INTERFACE "', "
+ "path='/', "
+ "member='" LIB_CROS_PROXY_RESOLVE_NAME "'";
+#undef LIB_CROS_PROXY_RESOLVE_SIGNAL_INTERFACE
+#undef LIB_CROS_PROXY_RESOLVE_NAME
+
+namespace {
+const int kTimeout = 5; // seconds
+
+DBusGProxy* GetProxy(DbusGlibInterface* dbus) {
+ GError* error = NULL;
+
+ DBusGConnection* bus = dbus->BusGet(DBUS_BUS_SYSTEM, &error);
+ if (!bus) {
+ LOG(ERROR) << "Failed to get bus";
+ return NULL;
+ }
+ DBusGProxy* proxy = dbus->ProxyNewForNameOwner(bus,
+ kLibCrosServiceName,
+ kLibCrosServicePath,
+ kLibCrosServiceInterface,
+ &error);
+ if (!proxy) {
+ LOG(ERROR) << "Error getting dbus proxy for "
+ << kLibCrosServiceName << ": " << utils::GetGErrorMessage(error);
+ return NULL;
+ }
+ return proxy;
+}
+
+} // namespace {}
+
+ChromeBrowserProxyResolver::ChromeBrowserProxyResolver(DbusGlibInterface* dbus)
+ : dbus_(dbus), proxy_(NULL), timeout_(kTimeout) {}
+
+bool ChromeBrowserProxyResolver::Init() {
+ // Set up signal handler. Code lifted from libcros
+ if (proxy_) {
+ // Already initialized
+ return true;
+ }
+ GError* gerror = NULL;
+ DBusGConnection* gbus = dbus_->BusGet(DBUS_BUS_SYSTEM, &gerror);
+ TEST_AND_RETURN_FALSE(gbus);
+ DBusConnection* connection = dbus_->ConnectionGetConnection(gbus);
+ TEST_AND_RETURN_FALSE(connection);
+ DBusError error;
+ dbus_error_init(&error);
+ dbus_->DbusBusAddMatch(connection, kLibCrosProxyResolveSignalFilter, &error);
+ TEST_AND_RETURN_FALSE(!dbus_error_is_set(&error));
+ TEST_AND_RETURN_FALSE(dbus_->DbusConnectionAddFilter(
+ connection,
+ &ChromeBrowserProxyResolver::StaticFilterMessage,
+ this,
+ NULL));
+
+ proxy_ = GetProxy(dbus_);
+ if (!proxy_) {
+ dbus_->DbusConnectionRemoveFilter(
+ connection,
+ &ChromeBrowserProxyResolver::StaticFilterMessage,
+ this);
+ }
+ TEST_AND_RETURN_FALSE(proxy_); // For the error log
+ return true;
+}
+
+ChromeBrowserProxyResolver::~ChromeBrowserProxyResolver() {
+ if (proxy_) {
+ GError* gerror = NULL;
+ DBusGConnection* gbus = dbus_->BusGet(DBUS_BUS_SYSTEM, &gerror);
+ TEST_AND_RETURN(gbus);
+ DBusConnection* connection = dbus_->ConnectionGetConnection(gbus);
+ dbus_->DbusConnectionRemoveFilter(
+ connection,
+ &ChromeBrowserProxyResolver::StaticFilterMessage,
+ this);
+ }
+ // Kill outstanding timers
+ for (TimeoutsMap::iterator it = timers_.begin(), e = timers_.end(); it != e;
+ ++it) {
+ g_source_destroy(it->second);
+ it->second = NULL;
+ }
+}
+
+bool ChromeBrowserProxyResolver::GetProxiesForUrl(const string& url,
+ ProxiesResolvedFn callback,
+ void* data) {
+ GError* error = NULL;
+ TEST_AND_RETURN_FALSE(proxy_);
+ if (!dbus_->ProxyCall(
+ proxy_,
+ kLibCrosServiceResolveNetworkProxyMethodName,
+ &error,
+ G_TYPE_STRING, url.c_str(),
+ G_TYPE_STRING, kLibCrosProxyResolveSignalInterface,
+ G_TYPE_STRING, kLibCrosProxyResolveName,
+ G_TYPE_INVALID, G_TYPE_INVALID)) {
+ LOG(ERROR) << "dbus_g_proxy_call failed: "
+ << utils::GetGErrorMessage(error);
+ return false;
+ }
+ callbacks_.insert(make_pair(url, make_pair(callback, data)));
+ Closure* closure = NewCallback(this,
+ &ChromeBrowserProxyResolver::HandleTimeout,
+ url);
+ GSource* timer = g_timeout_source_new_seconds(timeout_);
+ g_source_set_callback(timer, &utils::GlibRunClosure, closure, NULL);
+ g_source_attach(timer, NULL);
+ timers_.insert(make_pair(url, timer));
+ return true;
+}
+
+DBusHandlerResult ChromeBrowserProxyResolver::FilterMessage(
+ DBusConnection* connection,
+ DBusMessage* message) {
+ // Code lifted from libcros.
+ if (!dbus_->DbusMessageIsSignal(message,
+ kLibCrosProxyResolveSignalInterface,
+ kLibCrosProxyResolveName)) {
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+ // Get args
+ char* source_url = NULL;
+ char* proxy_list = NULL;
+ char* error = NULL;
+ DBusError arg_error;
+ dbus_error_init(&arg_error);
+ if (!dbus_->DbusMessageGetArgs(message, &arg_error,
+ DBUS_TYPE_STRING, &source_url,
+ DBUS_TYPE_STRING, &proxy_list,
+ DBUS_TYPE_STRING, &error,
+ DBUS_TYPE_INVALID)) {
+ LOG(ERROR) << "Error reading dbus signal.";
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+ if (!source_url || !proxy_list) {
+ LOG(ERROR) << "Error getting url, proxy list from dbus signal.";
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+ HandleReply(source_url, proxy_list);
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+bool ChromeBrowserProxyResolver::DeleteUrlState(
+ const string& source_url,
+ bool delete_timer,
+ pair<ProxiesResolvedFn, void*>* callback) {
+ {
+ CallbacksMap::iterator it = callbacks_.lower_bound(source_url);
+ TEST_AND_RETURN_FALSE(it != callbacks_.end());
+ TEST_AND_RETURN_FALSE(it->first == source_url);
+ if (callback)
+ *callback = it->second;
+ callbacks_.erase(it);
+ }
+ {
+ TimeoutsMap::iterator it = timers_.lower_bound(source_url);
+ TEST_AND_RETURN_FALSE(it != timers_.end());
+ TEST_AND_RETURN_FALSE(it->first == source_url);
+ if (delete_timer)
+ g_source_destroy(it->second);
+ timers_.erase(it);
+ }
+ return true;
+}
+
+void ChromeBrowserProxyResolver::HandleReply(const string& source_url,
+ const string& proxy_list) {
+ pair<ProxiesResolvedFn, void*> callback;
+ TEST_AND_RETURN(DeleteUrlState(source_url, true, &callback));
+ (*callback.first)(ParseProxyString(proxy_list), callback.second);
+}
+
+void ChromeBrowserProxyResolver::HandleTimeout(string source_url) {
+ LOG(INFO) << "Timeout handler called. Seems Chrome isn't responding.";
+ pair<ProxiesResolvedFn, void*> callback;
+ TEST_AND_RETURN(DeleteUrlState(source_url, false, &callback));
+ deque<string> proxies;
+ proxies.push_back(kNoProxy);
+ (*callback.first)(proxies, callback.second);
+}
+
+deque<string> ChromeBrowserProxyResolver::ParseProxyString(
+ const string& input) {
+ deque<string> ret;
+ // Some of this code taken from
+ // http://src.chromium.org/svn/trunk/src/net/proxy/proxy_server.cc and
+ // http://src.chromium.org/svn/trunk/src/net/proxy/proxy_list.cc
+ StringTokenizer entry_tok(input, ";");
+ while (entry_tok.GetNext()) {
+ string token = entry_tok.token();
+ TrimWhitespaceASCII(token, TRIM_ALL, &token);
+
+ // Start by finding the first space (if any).
+ std::string::iterator space;
+ for (space = token.begin(); space != token.end(); ++space) {
+ if (IsAsciiWhitespace(*space)) {
+ break;
+ }
+ }
+
+ string scheme = string(token.begin(), space);
+ StringToLowerASCII(&scheme);
+ // Chrome uses "socks" to mean socks4 and "proxy" to mean http.
+ if (scheme == "socks")
+ scheme += "4";
+ else if (scheme == "proxy")
+ scheme = "http";
+ else if (scheme != "https" &&
+ scheme != "socks4" &&
+ scheme != "socks5" &&
+ scheme != "direct")
+ continue; // Invalid proxy scheme
+
+ string host_and_port = string(space, token.end());
+ TrimWhitespaceASCII(host_and_port, TRIM_ALL, &host_and_port);
+ if (scheme != "direct" && host_and_port.empty())
+ continue; // Must supply host/port when non-direct proxy used.
+ ret.push_back(scheme + "://" + host_and_port);
+ }
+ if (ret.empty() || *ret.rbegin() != kNoProxy)
+ ret.push_back(kNoProxy);
+ return ret;
+}
+
+} // namespace chromeos_update_engine
diff --git a/chrome_browser_proxy_resolver.h b/chrome_browser_proxy_resolver.h
new file mode 100644
index 0000000..429c772
--- /dev/null
+++ b/chrome_browser_proxy_resolver.h
@@ -0,0 +1,86 @@
+// Copyright (c) 2011 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_BROWSER_PROXY_RESOLVER_H__
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_CHROME_BROWSER_PROXY_RESOLVER_H__
+
+#include <map>
+#include <string>
+
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.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 kLibCrosServiceName[];
+extern const char kLibCrosServicePath[];
+extern const char kLibCrosServiceInterface[];
+extern const char kLibCrosServiceResolveNetworkProxyMethodName[];
+extern const char kLibCrosProxyResolveName[];
+extern const char kLibCrosProxyResolveSignalInterface[];
+extern const char kLibCrosProxyResolveSignalFilter[];
+
+class ChromeBrowserProxyResolver : public ProxyResolver {
+ public:
+ explicit ChromeBrowserProxyResolver(DbusGlibInterface* dbus);
+ virtual ~ChromeBrowserProxyResolver();
+ bool Init();
+
+ virtual bool GetProxiesForUrl(const std::string& url,
+ ProxiesResolvedFn callback,
+ void* data);
+ void set_timeout(int seconds) { timeout_ = seconds; }
+
+ // Public for test
+ static DBusHandlerResult StaticFilterMessage(
+ DBusConnection* connection,
+ DBusMessage* message,
+ void* data) {
+ return reinterpret_cast<ChromeBrowserProxyResolver*>(data)->FilterMessage(
+ connection, message);
+ }
+
+ private:
+ FRIEND_TEST(ChromeBrowserProxyResolverTest, ParseTest);
+ FRIEND_TEST(ChromeBrowserProxyResolverTest, SuccessTest);
+ typedef std::multimap<std::string, std::pair<ProxiesResolvedFn, void*> >
+ CallbacksMap;
+ typedef std::multimap<std::string, GSource*> TimeoutsMap;
+
+ // Handle a reply from Chrome:
+ void HandleReply(const std::string& source_url,
+ const std::string& proxy_list);
+ DBusHandlerResult FilterMessage(
+ DBusConnection* connection,
+ DBusMessage* message);
+ // Handle no reply:
+ void HandleTimeout(std::string source_url);
+
+ // Parses a string-encoded list of proxies and returns a deque
+ // of individual proxies. The last one will always be kNoProxy.
+ static std::deque<std::string> ParseProxyString(const std::string& input);
+
+ // Deletes internal state for the first instance of url in the state.
+ // If delete_timer is set, calls g_source_destroy on the timer source.
+ // Returns the callback in an out parameter. Returns true on success.
+ bool DeleteUrlState(const std::string& url,
+ bool delete_timer,
+ std::pair<ProxiesResolvedFn, void*>* callback);
+
+ DbusGlibInterface* dbus_;
+ DBusGProxy* proxy_;
+ DBusGProxy* peer_proxy_;
+ int timeout_;
+ TimeoutsMap timers_;
+ CallbacksMap callbacks_;
+ DISALLOW_COPY_AND_ASSIGN(ChromeBrowserProxyResolver);
+};
+
+} // namespace chromeos_update_engine
+
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_CHROME_BROWSER_PROXY_RESOLVER_H__
diff --git a/chrome_browser_proxy_resolver_unittest.cc b/chrome_browser_proxy_resolver_unittest.cc
new file mode 100644
index 0000000..09a8be7
--- /dev/null
+++ b/chrome_browser_proxy_resolver_unittest.cc
@@ -0,0 +1,238 @@
+// Copyright (c) 2011 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 <deque>
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include "update_engine/chrome_browser_proxy_resolver.h"
+#include "update_engine/mock_dbus_interface.h"
+
+using std::deque;
+using std::string;
+using ::testing::_;
+using ::testing::Return;
+using ::testing::SetArgumentPointee;
+using ::testing::StrEq;
+
+namespace chromeos_update_engine {
+
+class ChromeBrowserProxyResolverTest : public ::testing::Test { };
+
+TEST(ChromeBrowserProxyResolverTest, ParseTest) {
+ // Test ideas from
+ // http://src.chromium.org/svn/trunk/src/net/proxy/proxy_list_unittest.cc
+ const char* inputs[] = {
+ "PROXY foopy:10",
+ " DIRECT", // leading space.
+ "PROXY foopy1 ; proxy foopy2;\t DIRECT",
+ "proxy foopy1 ; SOCKS foopy2",
+ "DIRECT ; proxy foopy1 ; DIRECT ; SOCKS5 foopy2;DIRECT ",
+ "DIRECT ; proxy foopy1:80; DIRECT ; DIRECT",
+ "PROXY-foopy:10",
+ "PROXY",
+ "PROXY foopy1 ; JUNK ; JUNK ; SOCKS5 foopy2 ; ;",
+ "HTTP foopy1; SOCKS5 foopy2"
+ };
+ deque<string> outputs[arraysize(inputs)];
+ outputs[0].push_back("http://foopy:10");
+ outputs[0].push_back(kNoProxy);
+ outputs[1].push_back(kNoProxy);
+ outputs[2].push_back("http://foopy1");
+ outputs[2].push_back("http://foopy2");
+ outputs[2].push_back(kNoProxy);
+ outputs[3].push_back("http://foopy1");
+ outputs[3].push_back("socks4://foopy2");
+ outputs[3].push_back(kNoProxy);
+ outputs[4].push_back(kNoProxy);
+ outputs[4].push_back("http://foopy1");
+ outputs[4].push_back(kNoProxy);
+ outputs[4].push_back("socks5://foopy2");
+ outputs[4].push_back(kNoProxy);
+ outputs[5].push_back(kNoProxy);
+ outputs[5].push_back("http://foopy1:80");
+ outputs[5].push_back(kNoProxy);
+ outputs[5].push_back(kNoProxy);
+ outputs[6].push_back(kNoProxy);
+ outputs[7].push_back(kNoProxy);
+ outputs[8].push_back("http://foopy1");
+ outputs[8].push_back("socks5://foopy2");
+ outputs[8].push_back(kNoProxy);
+ outputs[9].push_back("socks5://foopy2");
+ outputs[9].push_back(kNoProxy);
+
+ for (size_t i = 0; i < arraysize(inputs); i++) {
+ deque<string> results =
+ ChromeBrowserProxyResolver::ParseProxyString(inputs[i]);
+ deque<string>& expected = outputs[i];
+ EXPECT_EQ(results.size(), expected.size()) << "i = " << i;
+ if (expected.size() != results.size())
+ continue;
+ for (size_t j = 0; j < expected.size(); j++) {
+ EXPECT_EQ(expected[j], results[j]) << "i = " << i;
+ }
+ }
+}
+
+namespace {
+void DbusInterfaceTestResolved(const std::deque<std::string>& proxies,
+ void* data) {
+ EXPECT_EQ(2, proxies.size());
+ EXPECT_EQ("socks5://192.168.52.83:5555", proxies[0]);
+ EXPECT_EQ(kNoProxy, proxies[1]);
+ g_main_loop_quit(reinterpret_cast<GMainLoop*>(data));
+}
+void DbusInterfaceTestResolvedNoReply(const std::deque<std::string>& proxies,
+ void* data) {
+ EXPECT_EQ(1, proxies.size());
+ EXPECT_EQ(kNoProxy, proxies[0]);
+ g_main_loop_quit(reinterpret_cast<GMainLoop*>(data));
+}
+struct SendReplyArgs {
+ DBusConnection* connection;
+ DBusMessage* message;
+ ChromeBrowserProxyResolver* resolver;
+};
+gboolean SendReply(gpointer data) {
+ LOG(INFO) << "Calling SendReply";
+ SendReplyArgs* args = reinterpret_cast<SendReplyArgs*>(data);
+ ChromeBrowserProxyResolver::StaticFilterMessage(
+ args->connection,
+ args->message,
+ args->resolver);
+ return FALSE; // Don't keep calling this function
+}
+
+// chrome_replies should be set to whether or not we fake a reply from
+// chrome. If there's no reply, the resolver should time out.
+void RunTest(bool chrome_replies) {
+ long number = 1;
+ DBusGConnection* kMockSystemGBus =
+ reinterpret_cast<DBusGConnection*>(number++);
+ DBusConnection* kMockSystemBus =
+ reinterpret_cast<DBusConnection*>(number++);
+ DBusGProxy* kMockDbusProxy =
+ reinterpret_cast<DBusGProxy*>(number++);
+ DBusMessage* kMockDbusMessage =
+ reinterpret_cast<DBusMessage*>(number++);
+
+ const char kUrl[] = "http://example.com/blah";
+
+ MockDbusGlib dbus_iface;
+
+ EXPECT_CALL(dbus_iface, BusGet(_, _))
+ .Times(3)
+ .WillRepeatedly(Return(kMockSystemGBus));
+ EXPECT_CALL(dbus_iface,
+ ConnectionGetConnection(kMockSystemGBus))
+ .Times(2)
+ .WillRepeatedly(Return(kMockSystemBus));
+ EXPECT_CALL(dbus_iface, DbusBusAddMatch(kMockSystemBus, _, _));
+ EXPECT_CALL(dbus_iface,
+ DbusConnectionAddFilter(kMockSystemBus, _, _, _))
+ .WillOnce(Return(1));
+ EXPECT_CALL(dbus_iface,
+ ProxyNewForNameOwner(kMockSystemGBus,
+ StrEq(kLibCrosServiceName),
+ StrEq(kLibCrosServicePath),
+ StrEq(kLibCrosServiceInterface),
+ _))
+ .WillOnce(Return(kMockDbusProxy));
+ EXPECT_CALL(dbus_iface, ProxyCall(
+ kMockDbusProxy,
+ StrEq(kLibCrosServiceResolveNetworkProxyMethodName),
+ _,
+ G_TYPE_STRING, StrEq(kUrl),
+ G_TYPE_STRING, StrEq(kLibCrosProxyResolveSignalInterface),
+ G_TYPE_STRING, StrEq(kLibCrosProxyResolveName),
+ G_TYPE_INVALID))
+ .WillOnce(Return(TRUE));
+ EXPECT_CALL(dbus_iface,
+ DbusConnectionRemoveFilter(kMockSystemBus, _, _));
+ if (chrome_replies) {
+ EXPECT_CALL(dbus_iface,
+ DbusMessageIsSignal(kMockDbusMessage,
+ kLibCrosProxyResolveSignalInterface,
+ kLibCrosProxyResolveName))
+ .WillOnce(Return(1));
+ EXPECT_CALL(dbus_iface,
+ DbusMessageGetArgs(kMockDbusMessage, _,
+ DBUS_TYPE_STRING, _,
+ DBUS_TYPE_STRING, _,
+ DBUS_TYPE_STRING, _,
+ DBUS_TYPE_INVALID))
+ .WillOnce(DoAll(SetArgumentPointee<3>(strdup(kUrl)),
+ SetArgumentPointee<5>(
+ strdup("SOCKS5 192.168.52.83:5555;DIRECT")),
+ Return(TRUE)));
+ }
+
+ GMainLoop* loop = g_main_loop_new(g_main_context_default(), FALSE);
+
+ ChromeBrowserProxyResolver resolver(&dbus_iface);
+ EXPECT_TRUE(resolver.Init());
+ resolver.set_timeout(1);
+ SendReplyArgs args = {
+ kMockSystemBus,
+ kMockDbusMessage,
+ &resolver
+ };
+ if (chrome_replies)
+ g_idle_add(SendReply, &args);
+ EXPECT_TRUE(resolver.GetProxiesForUrl(kUrl,
+ chrome_replies ?
+ &DbusInterfaceTestResolved :
+ &DbusInterfaceTestResolvedNoReply,
+ loop));
+ g_main_loop_run(loop);
+ g_main_loop_unref(loop);
+}
+} // namespace {}
+
+TEST(ChromeBrowserProxyResolverTest, SuccessTest) {
+ RunTest(true);
+}
+
+TEST(ChromeBrowserProxyResolverTest, NoReplyTest) {
+ RunTest(false);
+}
+
+TEST(ChromeBrowserProxyResolverTest, NoChromeTest) {
+ long number = 1;
+ DBusGConnection* kMockSystemGBus =
+ reinterpret_cast<DBusGConnection*>(number++);
+ DBusConnection* kMockSystemBus =
+ reinterpret_cast<DBusConnection*>(number++);
+
+ const char kUrl[] = "http://example.com/blah";
+
+ MockDbusGlib dbus_iface;
+
+ EXPECT_CALL(dbus_iface, BusGet(_, _))
+ .Times(2)
+ .WillRepeatedly(Return(kMockSystemGBus));
+ EXPECT_CALL(dbus_iface,
+ ConnectionGetConnection(kMockSystemGBus))
+ .Times(1)
+ .WillOnce(Return(kMockSystemBus));
+ EXPECT_CALL(dbus_iface, DbusBusAddMatch(kMockSystemBus, _, _));
+ EXPECT_CALL(dbus_iface,
+ DbusConnectionAddFilter(kMockSystemBus, _, _, _))
+ .WillOnce(Return(1));
+ EXPECT_CALL(dbus_iface,
+ ProxyNewForNameOwner(kMockSystemGBus,
+ StrEq(kLibCrosServiceName),
+ StrEq(kLibCrosServicePath),
+ StrEq(kLibCrosServiceInterface),
+ _))
+ .WillOnce(Return(static_cast<DBusGProxy*>(NULL)));
+ EXPECT_CALL(dbus_iface,
+ DbusConnectionRemoveFilter(kMockSystemBus, _, _));
+ ChromeBrowserProxyResolver resolver(&dbus_iface);
+ EXPECT_FALSE(resolver.Init());
+ EXPECT_FALSE(resolver.GetProxiesForUrl(kUrl, NULL, NULL));
+}
+
+} // namespace chromeos_update_engine
diff --git a/dbus_constants.h b/dbus_constants.h
index bd5b551..e4173f0 100644
--- a/dbus_constants.h
+++ b/dbus_constants.h
@@ -12,7 +12,6 @@
"/org/chromium/UpdateEngine";
static const char* const kUpdateEngineServiceInterface =
"org.chromium.UpdateEngineInterface";
-
} // namespace chromeos_update_engine
#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_DBUS_CONSTANTS_H__
diff --git a/dbus_interface.h b/dbus_interface.h
index de09fff..ec266b5 100644
--- a/dbus_interface.h
+++ b/dbus_interface.h
@@ -9,6 +9,7 @@
#include <base/logging.h>
#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
namespace chromeos_update_engine {
@@ -25,7 +26,7 @@
virtual void ProxyUnref(DBusGProxy* proxy) = 0;
// wraps dbus_g_bus_get
- virtual DBusGConnection* BusGet(DBusBusType type, GError** error) = 0;
+ virtual DBusGConnection* BusGet(DBusBusType type, GError** error) = 0;
// wraps dbus_g_proxy_call
virtual gboolean ProxyCall(DBusGProxy* proxy,
@@ -44,6 +45,38 @@
GType var_arg4, gchar** var_arg5,
GType var_arg6, GArray** var_arg7,
GType var_arg8) = 0;
+
+ virtual gboolean ProxyCall(DBusGProxy* proxy,
+ const char* method,
+ GError** error,
+ GType var_arg1, const char* var_arg2,
+ GType var_arg3, const char* var_arg4,
+ GType var_arg5, const char* var_arg6,
+ GType var_arg7, GType var_arg8) = 0;
+
+ virtual DBusConnection* ConnectionGetConnection(DBusGConnection* gbus) = 0;
+
+ virtual void DbusBusAddMatch(DBusConnection* connection,
+ const char* rule,
+ DBusError* error) = 0;
+
+ virtual dbus_bool_t DbusConnectionAddFilter(
+ DBusConnection* connection,
+ DBusHandleMessageFunction function,
+ void* user_data,
+ DBusFreeFunction free_data_function) = 0;
+ virtual void DbusConnectionRemoveFilter(DBusConnection* connection,
+ DBusHandleMessageFunction function,
+ void* user_data) = 0;
+ virtual dbus_bool_t DbusMessageIsSignal(DBusMessage* message,
+ const char* interface,
+ const char* signal_name) = 0;
+ virtual dbus_bool_t DbusMessageGetArgs(DBusMessage* message,
+ DBusError* error,
+ GType var_arg1, char** var_arg2,
+ GType var_arg3, char** var_arg4,
+ GType var_arg5, char** var_arg6,
+ GType var_arg7) = 0;
};
class ConcreteDbusGlib : public DbusGlibInterface {
@@ -63,7 +96,7 @@
g_object_unref(proxy);
}
- virtual DBusGConnection* BusGet(DBusBusType type, GError** error) {
+ virtual DBusGConnection* BusGet(DBusBusType type, GError** error) {
return dbus_g_bus_get(type, error);
}
@@ -90,6 +123,62 @@
proxy, method, error, var_arg1, var_arg2, var_arg3,
var_arg4, var_arg5, var_arg6, var_arg7, var_arg8);
}
+
+ virtual gboolean ProxyCall(DBusGProxy* proxy,
+ const char* method,
+ GError** error,
+ GType var_arg1, const char* var_arg2,
+ GType var_arg3, const char* var_arg4,
+ GType var_arg5, const char* var_arg6,
+ GType 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);
+ }
+
+
+ virtual DBusConnection* ConnectionGetConnection(DBusGConnection* gbus) {
+ return dbus_g_connection_get_connection(gbus);
+ }
+
+ virtual void DbusBusAddMatch(DBusConnection* connection,
+ const char* rule,
+ DBusError* error) {
+ dbus_bus_add_match(connection, rule, error);
+ }
+
+ virtual dbus_bool_t DbusConnectionAddFilter(
+ DBusConnection* connection,
+ DBusHandleMessageFunction function,
+ void* user_data,
+ DBusFreeFunction free_data_function) {
+ return dbus_connection_add_filter(connection,
+ function,
+ user_data,
+ free_data_function);
+ }
+ virtual void DbusConnectionRemoveFilter(DBusConnection* connection,
+ DBusHandleMessageFunction function,
+ void* user_data) {
+ dbus_connection_remove_filter(connection, function, user_data);
+ }
+ dbus_bool_t DbusMessageIsSignal(DBusMessage* message,
+ const char* interface,
+ const char* signal_name) {
+ return dbus_message_is_signal(message, interface, signal_name);
+ }
+ virtual dbus_bool_t DbusMessageGetArgs(DBusMessage* message,
+ DBusError* error,
+ GType var_arg1, char** var_arg2,
+ GType var_arg3, char** var_arg4,
+ GType var_arg5, char** var_arg6,
+ GType var_arg7) {
+ return dbus_message_get_args(message, error,
+ var_arg1, var_arg2,
+ var_arg3, var_arg4,
+ var_arg5, var_arg6,
+ var_arg7);
+ }
};
} // namespace chromeos_update_engine
diff --git a/main.cc b/main.cc
index 9dae81a..48ea9f1 100755
--- a/main.cc
+++ b/main.cc
@@ -74,7 +74,6 @@
DBUS_SERVICE_DBUS,
DBUS_PATH_DBUS,
DBUS_INTERFACE_DBUS);
-
guint32 request_name_ret;
if (!org_freedesktop_DBus_request_name(proxy,
kUpdateEngineServiceName,
@@ -146,6 +145,7 @@
logging::DONT_LOCK_LOG_FILE,
logging::APPEND_TO_OLD_LOG_FILE);
}
+
} // namespace {}
} // namespace chromeos_update_engine
diff --git a/mock_dbus_interface.h b/mock_dbus_interface.h
index c817109..781b3f3 100644
--- a/mock_dbus_interface.h
+++ b/mock_dbus_interface.h
@@ -37,19 +37,70 @@
GType var_arg3,
GType var_arg4, gchar** var_arg5,
GType var_arg6, GArray** var_arg7));
+ MOCK_METHOD10(ProxyCall, gboolean(DBusGProxy* proxy,
+ const char* method,
+ GError** error,
+ GType var_arg1, const char* var_arg2,
+ GType var_arg3, const char* var_arg4,
+ GType var_arg5, const char* var_arg6,
+ GType var_arg7));
+
+ MOCK_METHOD1(ConnectionGetConnection, DBusConnection*(DBusGConnection* gbus));
+
+ MOCK_METHOD3(DbusBusAddMatch, void(DBusConnection* connection,
+ const char* rule,
+ DBusError* error));
+
+ MOCK_METHOD4(DbusConnectionAddFilter, dbus_bool_t(
+ DBusConnection* connection,
+ DBusHandleMessageFunction function,
+ void* user_data,
+ DBusFreeFunction free_data_function));
+
+ MOCK_METHOD3(DbusConnectionRemoveFilter, void(
+ DBusConnection* connection,
+ DBusHandleMessageFunction function,
+ void* user_data));
+
+ MOCK_METHOD3(DbusMessageIsSignal, dbus_bool_t(DBusMessage* message,
+ const char* interface,
+ const char* signal_name));
+
+ MOCK_METHOD9(DbusMessageGetArgs, dbus_bool_t(
+ DBusMessage* message,
+ DBusError* error,
+ GType var_arg1, char** var_arg2,
+ GType var_arg3, char** var_arg4,
+ GType var_arg5, char** var_arg6,
+ GType 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) {
+ 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);
+ }
+ gboolean ProxyCall(DBusGProxy* proxy,
+ const char* method,
+ GError** error,
+ GType var_arg1, const char* var_arg2,
+ GType var_arg3, const char* var_arg4,
+ GType var_arg5, const char* var_arg6,
+ GType var_arg7, GType var_arg8) {
return ProxyCall(proxy,
method,
error,
diff --git a/update_attempter.cc b/update_attempter.cc
index 949434e..e49a551 100644
--- a/update_attempter.cc
+++ b/update_attempter.cc
@@ -125,6 +125,7 @@
void UpdateAttempter::Update(const std::string& app_version,
const std::string& omaha_url,
bool obey_proxies) {
+ chrome_proxy_resolver_.Init();
if (status_ == UPDATE_STATUS_UPDATED_NEED_REBOOT) {
LOG(INFO) << "Not updating b/c we already updated and we're waiting for "
<< "reboot";
diff --git a/update_attempter.h b/update_attempter.h
index 1709a3a..0afe7f6 100644
--- a/update_attempter.h
+++ b/update_attempter.h
@@ -16,7 +16,7 @@
#include <gtest/gtest_prod.h> // for FRIEND_TEST
#include "update_engine/action_processor.h"
-#include "update_engine/chrome_proxy_resolver.h"
+#include "update_engine/chrome_browser_proxy_resolver.h"
#include "update_engine/download_action.h"
#include "update_engine/omaha_request_params.h"
#include "update_engine/omaha_response_handler_action.h"
@@ -235,7 +235,7 @@
// Our two proxy resolvers
DirectProxyResolver direct_proxy_resolver_;
- ChromeProxyResolver chrome_proxy_resolver_;
+ ChromeBrowserProxyResolver chrome_proxy_resolver_;
DISALLOW_COPY_AND_ASSIGN(UpdateAttempter);
};
diff --git a/update_attempter_mock.h b/update_attempter_mock.h
index 908faeb..16c0e11 100644
--- a/update_attempter_mock.h
+++ b/update_attempter_mock.h
@@ -14,12 +14,12 @@
class UpdateAttempterMock : public UpdateAttempter {
public:
- UpdateAttempterMock() : UpdateAttempter(NULL, NULL, &dbus_) {}
+ explicit UpdateAttempterMock(MockDbusGlib* dbus)
+ : UpdateAttempter(NULL, NULL, dbus) {}
MOCK_METHOD3(Update, void(const std::string& app_version,
const std::string& omaha_url,
bool obey_proxies));
- MockDbusGlib dbus_;
};
} // namespace chromeos_update_engine
diff --git a/update_attempter_unittest.cc b/update_attempter_unittest.cc
index 2f60609..76068fd 100644
--- a/update_attempter_unittest.cc
+++ b/update_attempter_unittest.cc
@@ -33,13 +33,13 @@
// methods.
class UpdateAttempterUnderTest : public UpdateAttempter {
public:
- UpdateAttempterUnderTest()
- : UpdateAttempter(NULL, NULL, &dbus_) {}
- MockDbusGlib dbus_;
+ explicit UpdateAttempterUnderTest(MockDbusGlib* dbus)
+ : UpdateAttempter(NULL, NULL, dbus) {}
};
class UpdateAttempterTest : public ::testing::Test {
protected:
+ UpdateAttempterTest() : attempter_(&dbus_) {}
virtual void SetUp() {
EXPECT_EQ(NULL, attempter_.dbus_service_);
EXPECT_EQ(NULL, attempter_.prefs_);
@@ -60,6 +60,7 @@
attempter_.prefs_ = &prefs_;
}
+ MockDbusGlib dbus_;
UpdateAttempterUnderTest attempter_;
ActionProcessorMock* processor_;
NiceMock<PrefsMock> prefs_;
@@ -110,7 +111,8 @@
extern const char* kUpdateCompletedMarker;
const FilePath kMarker(kUpdateCompletedMarker);
EXPECT_EQ(0, file_util::WriteFile(kMarker, "", 0));
- UpdateAttempterUnderTest attempter;
+ MockDbusGlib dbus;
+ UpdateAttempterUnderTest attempter(&dbus);
EXPECT_EQ(UPDATE_STATUS_UPDATED_NEED_REBOOT, attempter.status());
EXPECT_TRUE(file_util::Delete(kMarker, false));
}
diff --git a/update_check_scheduler_unittest.cc b/update_check_scheduler_unittest.cc
index c267108..c1dd330 100644
--- a/update_check_scheduler_unittest.cc
+++ b/update_check_scheduler_unittest.cc
@@ -40,7 +40,7 @@
class UpdateCheckSchedulerTest : public ::testing::Test {
public:
- UpdateCheckSchedulerTest() : scheduler_(&attempter_) {}
+ UpdateCheckSchedulerTest() : scheduler_(&attempter_), attempter_(&dbus_) {}
protected:
virtual void SetUp() {
@@ -65,6 +65,7 @@
}
UpdateCheckSchedulerUnderTest scheduler_;
+ MockDbusGlib dbus_;
UpdateAttempterMock attempter_;
MockFunction<gboolean(gpointer data)> source_callback_;
GMainLoop* loop_;