| Index: chrome_browser_proxy_resolver.cc
|
| diff --git a/chrome_browser_proxy_resolver.cc b/chrome_browser_proxy_resolver.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..a481f4fbb0d1d9ef68109ab0333320d62d8db126
|
| --- /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
|
|
|