AU: Don't use network on expensive connection types
Specifically:
- FlimFlam proxy class to query the current network status and find
out if it's expensive.
- Dbus Interface, so dbus can be mocked.
- Libcurl change to redirect the URL if we are on an expensive
connection. This may seem hacky, but the reason I avoided retooling
the whole class is that we may decide that some network usage is
okay on pricy connections. Perhaps it's okay to throttle. So, for
now this is a more minimal change.
BUG=chromium-os:2397
TEST=Unit tests, tested that flimflam proxy works on the device.
Review URL: http://codereview.chromium.org/4029002
Change-Id: Ic4dcde1ca863bda890bc46a55c552e2b32d9433d
diff --git a/SConstruct b/SConstruct
index 1a73466..bd2a5d8 100644
--- a/SConstruct
+++ b/SConstruct
@@ -248,6 +248,7 @@
filesystem_copier_action.cc
filesystem_iterator.cc
file_writer.cc
+ flimflam_proxy.cc
graph_utils.cc
gzip.cc
libcurl_http_fetcher.cc
@@ -288,6 +289,7 @@
file_writer_unittest.cc
filesystem_copier_action_unittest.cc
filesystem_iterator_unittest.cc
+ flimflam_proxy_unittest.cc
graph_utils_unittest.cc
http_fetcher_unittest.cc
mock_http_fetcher.cc
diff --git a/dbus_interface.h b/dbus_interface.h
new file mode 100644
index 0000000..1b5c953
--- /dev/null
+++ b/dbus_interface.h
@@ -0,0 +1,74 @@
+// 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_DBUS_INTERFACE_H__
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_DBUS_INTERFACE_H__
+
+// This class interfaces with DBus. The interface allows it to be mocked.
+
+#include <dbus/dbus-glib.h>
+
+namespace chromeos_update_engine {
+
+class DbusGlibInterface {
+ public:
+ // wraps dbus_g_proxy_new_for_name_owner
+ virtual DBusGProxy* ProxyNewForNameOwner(DBusGConnection* connection,
+ const char* name,
+ const char* path,
+ const char* interface,
+ GError** error) = 0;
+
+ // wraps g_object_unref
+ virtual void ProxyUnref(DBusGProxy* proxy) = 0;
+
+ // 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,
+ GError** error,
+ GType first_arg_type,
+ GType var_arg1,
+ GHashTable** var_arg2,
+ GType var_arg3) = 0;
+};
+
+class ConcreteDbusGlib : public DbusGlibInterface {
+ virtual DBusGProxy* ProxyNewForNameOwner(DBusGConnection* connection,
+ const char* name,
+ const char* path,
+ const char* interface,
+ GError** error) {
+ return dbus_g_proxy_new_for_name_owner(connection,
+ name,
+ path,
+ interface,
+ error);
+ }
+
+ virtual void ProxyUnref(DBusGProxy* proxy) {
+ g_object_unref(proxy);
+ }
+
+ virtual DBusGConnection* BusGet(DBusBusType type, GError** error) {
+ return dbus_g_bus_get(type, error);
+ }
+
+ virtual gboolean ProxyCall(DBusGProxy* proxy,
+ const char* method,
+ GError** error,
+ GType first_arg_type,
+ GType var_arg1,
+ GHashTable** var_arg2,
+ GType var_arg3) {
+ return dbus_g_proxy_call(
+ proxy, method, error, first_arg_type, var_arg1, var_arg2, var_arg3);
+ }
+};
+
+} // namespace chromeos_update_engine
+
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_DBUS_INTERFACE_H__
diff --git a/flimflam_proxy.cc b/flimflam_proxy.cc
new file mode 100644
index 0000000..27be084
--- /dev/null
+++ b/flimflam_proxy.cc
@@ -0,0 +1,178 @@
+// 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/flimflam_proxy.h"
+
+#include <string>
+
+#include <base/string_util.h>
+#include <dbus/dbus-glib.h>
+#include <glib.h>
+
+#include "update_engine/utils.h"
+
+using std::string;
+
+namespace chromeos_update_engine {
+
+namespace {
+
+// Gets the DbusGProxy for FlimFlam. Must be free'd with ProxyUnref()
+bool GetFlimFlamProxy(DbusGlibInterface* dbus_iface,
+ const char* path,
+ const char* interface,
+ DBusGProxy** out_proxy) {
+ DBusGConnection* bus;
+ DBusGProxy* proxy;
+ GError* error = NULL;
+
+ bus = dbus_iface->BusGet(DBUS_BUS_SYSTEM, &error);
+ if (!bus) {
+ LOG(ERROR) << "Failed to get system bus";
+ return false;
+ }
+ proxy = dbus_iface->ProxyNewForNameOwner(bus,
+ kFlimFlamDbusService,
+ path,
+ interface,
+ &error);
+ if (!proxy) {
+ LOG(ERROR) << "Error getting FlimFlam proxy: "
+ << utils::GetGErrorMessage(error);
+ return false;
+ }
+ *out_proxy = proxy;
+ return true;
+}
+
+// On success, caller owns the GHashTable at out_hash_table.
+// Returns true on success.
+bool GetProperties(DbusGlibInterface* dbus_iface,
+ const char* path,
+ const char* interface,
+ GHashTable** out_hash_table) {
+ DBusGProxy* proxy;
+ GError* error = NULL;
+
+ TEST_AND_RETURN_FALSE(GetFlimFlamProxy(dbus_iface,
+ path,
+ interface,
+ &proxy));
+
+ gboolean rc = dbus_iface->ProxyCall(proxy,
+ "GetProperties",
+ &error,
+ G_TYPE_INVALID,
+ dbus_g_type_get_map("GHashTable",
+ G_TYPE_STRING,
+ G_TYPE_VALUE),
+ out_hash_table,
+ G_TYPE_INVALID);
+ dbus_iface->ProxyUnref(proxy);
+ if (rc == FALSE) {
+ LOG(ERROR) << "dbus_g_proxy_call failed";
+ return false;
+ }
+
+ return true;
+}
+
+// Returns (via out_path) the default network path, or empty string if
+// there's no network up.
+// Returns true on success.
+bool GetDefaultServicePath(DbusGlibInterface* dbus_iface, string* out_path) {
+ GHashTable* hash_table = NULL;
+
+ TEST_AND_RETURN_FALSE(GetProperties(dbus_iface,
+ kFlimFlamDbusManagerPath,
+ kFlimFlamDbusManagerInterface,
+ &hash_table));
+
+ GValue* value = reinterpret_cast<GValue*>(g_hash_table_lookup(hash_table,
+ "Services"));
+ GArray* array = NULL;
+ bool success = false;
+ if (value &&
+ (array = reinterpret_cast<GArray*>(g_value_get_boxed(value))) &&
+ (array->len > 0)) {
+ *out_path = g_array_index(array, const char*, 0);
+ success = true;
+ }
+ g_hash_table_unref(hash_table);
+ return success;
+}
+
+NetworkConnectionType ParseConnectionType(const char* type_str) {
+ if (!strcmp(type_str, kFlimFlamNetTypeEthernet)) {
+ return kNetEthernet;
+ } else if (!strcmp(type_str, kFlimFlamNetTypeWifi)) {
+ return kNetWifi;
+ } else if (!strcmp(type_str, kFlimFlamNetTypeWimax)) {
+ return kNetWimax;
+ } else if (!strcmp(type_str, kFlimFlamNetTypeBluetooth)) {
+ return kNetBluetooth;
+ } else if (!strcmp(type_str, kFlimFlamNetTypeCellular)) {
+ return kNetCellular;
+ }
+ return kNetUnknown;
+}
+
+bool GetServicePathType(DbusGlibInterface* dbus_iface,
+ const string& path,
+ NetworkConnectionType* out_type) {
+ GHashTable* hash_table = NULL;
+
+ TEST_AND_RETURN_FALSE(GetProperties(dbus_iface,
+ path.c_str(),
+ kFlimFlamDbusServiceInterface,
+ &hash_table));
+
+ GValue* value = (GValue*)g_hash_table_lookup(hash_table, "Type");
+ const char* type_str = NULL;
+ bool success = false;
+ if (value != NULL && (type_str = g_value_get_string(value)) != NULL) {
+ *out_type = ParseConnectionType(type_str);
+ success = true;
+ }
+ g_hash_table_unref(hash_table);
+ return success;
+}
+
+} // namespace {}
+
+const char* FlimFlamProxy::StringForConnectionType(NetworkConnectionType type) {
+ static const char* const kValues[] = {kFlimFlamNetTypeEthernet,
+ kFlimFlamNetTypeWifi,
+ kFlimFlamNetTypeWimax,
+ kFlimFlamNetTypeBluetooth,
+ kFlimFlamNetTypeCellular};
+ if (type < 0 || type >= static_cast<int>(arraysize(kValues))) {
+ return "Unknown";
+ }
+ return kValues[type];
+}
+
+bool FlimFlamProxy::GetConnectionType(DbusGlibInterface* dbus_iface,
+ NetworkConnectionType* out_type) {
+ string default_service_path;
+ TEST_AND_RETURN_FALSE(GetDefaultServicePath(dbus_iface,
+ &default_service_path));
+ TEST_AND_RETURN_FALSE(GetServicePathType(dbus_iface,
+ default_service_path,
+ out_type));
+ return true;
+}
+
+const char* kFlimFlamDbusService = "org.chromium.flimflam";
+const char* kFlimFlamDbusManagerInterface = "org.chromium.flimflam.Manager";
+const char* kFlimFlamDbusManagerPath = "/";
+const char* kFlimFlamDbusServiceInterface = "org.chromium.flimflam.Service";
+
+const char* kFlimFlamNetTypeEthernet = "ethernet";
+const char* kFlimFlamNetTypeWifi = "wifi";
+const char* kFlimFlamNetTypeWimax = "wimax";
+const char* kFlimFlamNetTypeBluetooth = "bluetooth";
+const char* kFlimFlamNetTypeCellular = "cellular";
+
+} // namespace chromeos_update_engine
diff --git a/flimflam_proxy.h b/flimflam_proxy.h
new file mode 100644
index 0000000..d5e5ff7
--- /dev/null
+++ b/flimflam_proxy.h
@@ -0,0 +1,53 @@
+// 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_FLIMFLAM_PROXY_H__
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_FLIMFLAM_PROXY_H__
+
+// This class interfaces with FlimFlam to find out data about connectivity.
+
+#include <base/basictypes.h>
+
+#include "update_engine/dbus_interface.h"
+
+namespace chromeos_update_engine {
+
+extern const char* kFlimFlamDbusService;
+extern const char* kFlimFlamDbusManagerInterface;
+extern const char* kFlimFlamDbusManagerPath;
+extern const char* kFlimFlamDbusServiceInterface;
+
+extern const char* kFlimFlamNetTypeEthernet;
+extern const char* kFlimFlamNetTypeWifi;
+extern const char* kFlimFlamNetTypeWimax;
+extern const char* kFlimFlamNetTypeBluetooth;
+extern const char* kFlimFlamNetTypeCellular;
+
+enum NetworkConnectionType {
+ kNetEthernet = 0,
+ kNetWifi,
+ kNetWimax,
+ kNetBluetooth,
+ kNetCellular,
+ kNetUnknown
+};
+
+class FlimFlamProxy {
+ public:
+ static bool GetConnectionType(DbusGlibInterface* dbus_iface,
+ NetworkConnectionType* out_type);
+
+ static bool IsExpensiveConnectionType(NetworkConnectionType type) {
+ return type == kNetWimax || type == kNetBluetooth || type == kNetCellular;
+ }
+ static const char* StringForConnectionType(NetworkConnectionType type);
+
+ private:
+ // Should never be allocated
+ DISALLOW_IMPLICIT_CONSTRUCTORS(FlimFlamProxy);
+};
+
+} // namespace chromeos_update_engine
+
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_FLIMFLAM_PROXY_H__
diff --git a/flimflam_proxy_unittest.cc b/flimflam_proxy_unittest.cc
new file mode 100644
index 0000000..e1caa01
--- /dev/null
+++ b/flimflam_proxy_unittest.cc
@@ -0,0 +1,169 @@
+// 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 <base/logging.h>
+#include <gtest/gtest.h>
+
+#include "update_engine/flimflam_proxy.h"
+#include "update_engine/mock_dbus_interface.h"
+
+using ::testing::_;
+using ::testing::Return;
+using ::testing::SetArgumentPointee;
+using ::testing::StrEq;
+
+namespace chromeos_update_engine {
+
+template<typename T, void F(T)>
+class ScopedRelease {
+ public:
+ ScopedRelease(T obj) : obj_(obj) {}
+ ~ScopedRelease() {
+ F(obj_);
+ }
+
+ private:
+ T obj_;
+};
+
+class FlimFlamProxyTest : public ::testing::Test {
+ protected:
+ void TestWithServiceType(
+ const char* service_type, NetworkConnectionType expected_type);
+};
+
+void FlimFlamProxyTest::TestWithServiceType(
+ const char* service_type,
+ NetworkConnectionType expected_type) {
+ int number = 1;
+ DBusGConnection* kMockSystemBus =
+ reinterpret_cast<DBusGConnection*>(number++);
+ DBusGProxy* kMockFlimFlamManagerProxy =
+ reinterpret_cast<DBusGProxy*>(number++);
+ DBusGProxy* kMockFlimFlamServiceProxy =
+ reinterpret_cast<DBusGProxy*>(number++);
+ ASSERT_NE(kMockSystemBus, reinterpret_cast<DBusGConnection*>(NULL));
+ const char* kServicePath = "/foo/service";
+ const char kGetPropertiesMethod[] = "GetProperties";
+
+ MockDbusGlib dbus_iface;
+
+ EXPECT_CALL(dbus_iface,
+ ProxyNewForNameOwner(kMockSystemBus,
+ StrEq(kFlimFlamDbusService),
+ StrEq(kFlimFlamDbusManagerPath),
+ StrEq(kFlimFlamDbusManagerInterface),
+ _))
+ .WillOnce(Return(kMockFlimFlamManagerProxy));
+ EXPECT_CALL(dbus_iface,
+ ProxyNewForNameOwner(kMockSystemBus,
+ StrEq(kFlimFlamDbusService),
+ StrEq(kServicePath),
+ StrEq(kFlimFlamDbusServiceInterface),
+ _))
+ .WillOnce(Return(kMockFlimFlamServiceProxy));
+
+ EXPECT_CALL(dbus_iface, ProxyUnref(kMockFlimFlamManagerProxy));
+ EXPECT_CALL(dbus_iface, ProxyUnref(kMockFlimFlamServiceProxy));
+
+ EXPECT_CALL(dbus_iface, BusGet(DBUS_BUS_SYSTEM, _))
+ .Times(2)
+ .WillRepeatedly(Return(kMockSystemBus));
+
+ // Set up return value for first dbus call (to Manager object).
+ GHashTable* manager_hash_table = g_hash_table_new(g_str_hash, g_str_equal);
+ ScopedRelease<GHashTable*, g_hash_table_unref> manager_hash_table_unref(
+ manager_hash_table);
+
+ GArray* array = g_array_new(FALSE, FALSE, sizeof(const char*));
+ ASSERT_TRUE(array != NULL);
+
+ EXPECT_EQ(array, g_array_append_val(array, kServicePath));
+ GValue array_value = {0, {{0}}};
+ EXPECT_EQ(&array_value, g_value_init(&array_value, G_TYPE_ARRAY));
+ g_value_take_boxed(&array_value, array);
+ g_hash_table_insert(manager_hash_table,
+ const_cast<char*>("Services"),
+ &array_value);
+
+ // Set up return value for second dbus call (to Service object).
+
+ GHashTable* service_hash_table = g_hash_table_new(g_str_hash, g_str_equal);
+ ScopedRelease<GHashTable*, g_hash_table_unref> service_hash_table_unref(
+ service_hash_table);
+
+ GValue service_type_value = {0, {{0}}};
+ EXPECT_EQ(&service_type_value,
+ g_value_init(&service_type_value, G_TYPE_STRING));
+ g_value_set_static_string(&service_type_value, service_type);
+
+ g_hash_table_insert(service_hash_table,
+ const_cast<char*>("Type"),
+ &service_type_value);
+
+ EXPECT_CALL(dbus_iface, ProxyCall(kMockFlimFlamManagerProxy,
+ StrEq(kGetPropertiesMethod),
+ _,
+ G_TYPE_INVALID,
+ dbus_g_type_get_map("GHashTable",
+ G_TYPE_STRING,
+ G_TYPE_VALUE),
+ _,
+ G_TYPE_INVALID))
+ .WillOnce(DoAll(SetArgumentPointee<5>(manager_hash_table), Return(TRUE)));
+
+ EXPECT_CALL(dbus_iface, ProxyCall(kMockFlimFlamServiceProxy,
+ StrEq(kGetPropertiesMethod),
+ _,
+ G_TYPE_INVALID,
+ dbus_g_type_get_map("GHashTable",
+ G_TYPE_STRING,
+ G_TYPE_VALUE),
+ _,
+ G_TYPE_INVALID))
+ .WillOnce(DoAll(SetArgumentPointee<5>(service_hash_table), Return(TRUE)));
+
+ NetworkConnectionType type;
+
+ EXPECT_TRUE(FlimFlamProxy::GetConnectionType(&dbus_iface, &type));
+ EXPECT_EQ(expected_type, type);
+}
+
+TEST_F(FlimFlamProxyTest, SimpleTest) {
+ TestWithServiceType(kFlimFlamNetTypeEthernet, kNetEthernet);
+ TestWithServiceType(kFlimFlamNetTypeWifi, kNetWifi);
+ TestWithServiceType(kFlimFlamNetTypeWimax, kNetWimax);
+ TestWithServiceType(kFlimFlamNetTypeBluetooth, kNetBluetooth);
+ TestWithServiceType(kFlimFlamNetTypeCellular, kNetCellular);
+}
+
+TEST_F(FlimFlamProxyTest, UnknownTest) {
+ TestWithServiceType("foo", kNetUnknown);
+}
+
+TEST_F(FlimFlamProxyTest, ExpensiveConnectionsTest) {
+ EXPECT_FALSE(FlimFlamProxy::IsExpensiveConnectionType(kNetEthernet));
+ EXPECT_FALSE(FlimFlamProxy::IsExpensiveConnectionType(kNetWifi));
+ EXPECT_TRUE(FlimFlamProxy::IsExpensiveConnectionType(kNetWimax));
+ EXPECT_TRUE(FlimFlamProxy::IsExpensiveConnectionType(kNetBluetooth));
+ EXPECT_TRUE(FlimFlamProxy::IsExpensiveConnectionType(kNetCellular));
+}
+
+TEST_F(FlimFlamProxyTest, StringForConnectionTypeTest) {
+ EXPECT_EQ(kFlimFlamNetTypeEthernet,
+ FlimFlamProxy::StringForConnectionType(kNetEthernet));
+ EXPECT_EQ(kFlimFlamNetTypeWifi,
+ FlimFlamProxy::StringForConnectionType(kNetWifi));
+ EXPECT_EQ(kFlimFlamNetTypeWimax,
+ FlimFlamProxy::StringForConnectionType(kNetWimax));
+ EXPECT_EQ(kFlimFlamNetTypeBluetooth,
+ FlimFlamProxy::StringForConnectionType(kNetBluetooth));
+ EXPECT_EQ(kFlimFlamNetTypeCellular,
+ FlimFlamProxy::StringForConnectionType(kNetCellular));
+ EXPECT_EQ("Unknown", FlimFlamProxy::StringForConnectionType(kNetUnknown));
+ EXPECT_EQ("Unknown", FlimFlamProxy::StringForConnectionType(
+ static_cast<NetworkConnectionType>(999999)));
+}
+
+} // namespace chromeos_update_engine
diff --git a/http_fetcher_unittest.cc b/http_fetcher_unittest.cc
index 06cabac..bd16768 100644
--- a/http_fetcher_unittest.cc
+++ b/http_fetcher_unittest.cc
@@ -135,6 +135,7 @@
// Speed up test execution.
ret->set_idle_seconds(1);
ret->set_retry_seconds(1);
+ ret->SetConnectionAsExpensive(false);
return ret;
}
HttpFetcher* NewSmallFetcher() {
@@ -696,4 +697,44 @@
}
}
+namespace {
+class ExpensiveConnectionTestDelegate : public HttpFetcherDelegate {
+ public:
+ virtual void ReceivedBytes(HttpFetcher* fetcher,
+ const char* bytes, int length) {
+ ADD_FAILURE();
+ }
+ virtual void TransferComplete(HttpFetcher* fetcher, bool successful) {
+ EXPECT_FALSE(successful);
+ g_main_loop_quit(loop_);
+ }
+ GMainLoop* loop_;
+};
+
+} // namespace
+
+TYPED_TEST(HttpFetcherTest, ExpensiveConnectionTest) {
+ if (this->IsMock() || this->IsMulti())
+ return;
+ typename TestFixture::HttpServer server;
+
+ ASSERT_TRUE(server.started_);
+
+ GMainLoop* loop = g_main_loop_new(g_main_context_default(), FALSE);
+ ExpensiveConnectionTestDelegate delegate;
+ delegate.loop_ = loop;
+
+ scoped_ptr<HttpFetcher> fetcher(this->NewLargeFetcher());
+ dynamic_cast<LibcurlHttpFetcher*>(
+ fetcher.get())->SetConnectionAsExpensive(true);
+ fetcher->set_delegate(&delegate);
+
+ StartTransferArgs start_xfer_args =
+ { fetcher.get(), LocalServerUrlForPath(this->SmallUrl()) };
+
+ g_timeout_add(0, StartTransfer, &start_xfer_args);
+ g_main_loop_run(loop);
+ g_main_loop_unref(loop);
+}
+
} // namespace chromeos_update_engine
diff --git a/libcurl_http_fetcher.cc b/libcurl_http_fetcher.cc
index c846812..9cacf86 100644
--- a/libcurl_http_fetcher.cc
+++ b/libcurl_http_fetcher.cc
@@ -3,11 +3,19 @@
// found in the LICENSE file.
#include "update_engine/libcurl_http_fetcher.h"
+
#include <algorithm>
-#include "base/logging.h"
+#include <string>
+
+#include <base/logging.h>
+
+#include "update_engine/dbus_interface.h"
+#include "update_engine/flimflam_proxy.h"
+#include "update_engine/utils.h"
using std::max;
using std::make_pair;
+using std::string;
// This is a concrete implementation of HttpFetcher that uses libcurl to do the
// http work.
@@ -17,12 +25,24 @@
namespace {
const int kMaxRetriesCount = 20;
const char kCACertificatesPath[] = "/usr/share/update_engine/ca-certificates";
-}
+} // namespace {}
LibcurlHttpFetcher::~LibcurlHttpFetcher() {
CleanUp();
}
+// On error, returns false.
+bool LibcurlHttpFetcher::ConnectionIsExpensive() const {
+ if (force_connection_type_)
+ return forced_expensive_connection_;
+ NetworkConnectionType type;
+ ConcreteDbusGlib dbus_iface;
+ TEST_AND_RETURN_FALSE(FlimFlamProxy::GetConnectionType(&dbus_iface, &type));
+ LOG(INFO) << "We are connected via "
+ << FlimFlamProxy::StringForConnectionType(type);
+ return FlimFlamProxy::IsExpensiveConnectionType(type);
+}
+
void LibcurlHttpFetcher::ResumeTransfer(const std::string& url) {
LOG(INFO) << "Starting/Resuming transfer";
CHECK(!transfer_in_progress_);
@@ -54,7 +74,18 @@
CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_WRITEDATA, this), CURLE_OK);
CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_WRITEFUNCTION,
StaticLibcurlWrite), CURLE_OK);
- CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_URL, url_.c_str()), CURLE_OK);
+
+ string url_to_use(url_);
+ if (ConnectionIsExpensive()) {
+ LOG(INFO) << "Not initiating HTTP connection b/c we are on an expensive"
+ << " connection";
+ url_to_use = ""; // Sabotage the URL
+ }
+
+ CHECK_EQ(curl_easy_setopt(curl_handle_,
+ CURLOPT_URL,
+ url_to_use.c_str()),
+ CURLE_OK);
// If the connection drops under 10 bytes/sec for 3 minutes, reconnect.
CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_LOW_SPEED_LIMIT, 10),
diff --git a/libcurl_http_fetcher.h b/libcurl_http_fetcher.h
index 69e794f..2628ecc 100644
--- a/libcurl_http_fetcher.h
+++ b/libcurl_http_fetcher.h
@@ -33,6 +33,8 @@
retry_count_(0),
retry_seconds_(60),
idle_seconds_(1),
+ force_connection_type_(false),
+ forced_expensive_connection_(false),
in_write_callback_(false),
terminate_requested_(false) {}
@@ -67,6 +69,11 @@
// Sets the retry timeout. Useful for testing.
void set_retry_seconds(int seconds) { retry_seconds_ = seconds; }
+
+ void SetConnectionAsExpensive(bool is_expensive) {
+ force_connection_type_ = true;
+ forced_expensive_connection_ = is_expensive;
+ }
private:
// Asks libcurl for the http response code and stores it in the object.
@@ -122,6 +129,10 @@
// curl(m) handles, io_channels_, timeout_source_.
void CleanUp();
+ // Returns whether or not the current network connection is considered
+ // expensive.
+ bool ConnectionIsExpensive() const;
+
// Handles for the libcurl library
CURLM *curl_multi_handle_;
CURL *curl_handle_;
@@ -157,6 +168,11 @@
// Seconds to wait before asking libcurl to "perform".
int idle_seconds_;
+
+ // If true, assume the network is expensive or not, according to
+ // forced_expensive_connection_. (Useful for testing).
+ bool force_connection_type_;
+ bool forced_expensive_connection_;
// If true, we are currently performing a write callback on the delegate.
bool in_write_callback_;
diff --git a/mock_dbus_interface.h b/mock_dbus_interface.h
new file mode 100644
index 0000000..bf0d609
--- /dev/null
+++ b/mock_dbus_interface.h
@@ -0,0 +1,37 @@
+// 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_MOCK_DBUS_INTERFACE_H__
+#define CHROMEOS_PLATFORM_UPDATE_ENGINE_MOCK_DBUS_INTERFACE_H__
+
+#include <gmock/gmock.h>
+
+#include "update_engine/dbus_interface.h"
+
+namespace chromeos_update_engine {
+
+class MockDbusGlib : public DbusGlibInterface {
+ public:
+ MOCK_METHOD5(ProxyNewForNameOwner, DBusGProxy*(DBusGConnection *connection,
+ const char *name,
+ const char *path,
+ const char *interface,
+ GError **error));
+
+ MOCK_METHOD1(ProxyUnref, void(DBusGProxy* proxy));
+
+ MOCK_METHOD2(BusGet, DBusGConnection*(DBusBusType type, GError **error));
+
+ MOCK_METHOD7(ProxyCall, gboolean(DBusGProxy *proxy,
+ const char *method,
+ GError **error,
+ GType first_arg_type,
+ GType var_arg1,
+ GHashTable** var_arg2,
+ GType var_arg3));
+};
+
+} // namespace chromeos_update_engine
+
+#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_MOCK_DBUS_INTERFACE_H__