// Copyright (c) 2010 The Chromium 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 "net/proxy/proxy_config.h"
#include "base/logging.h"
#include "base/string_tokenizer.h"
#include "base/string_util.h"
#include "base/values.h"
#include "net/proxy/proxy_info.h"
namespace net {
namespace {
// If |proxy| is valid, sets it in |dict| under the key |name|.
void AddProxyToValue(const char* name,
const ProxyServer& proxy,
DictionaryValue* dict) {
if (proxy.is_valid())
dict->SetString(name, proxy.ToURI());
}
} // namespace
ProxyConfig::ProxyRules::ProxyRules()
: reverse_bypass(false),
type(TYPE_NO_RULES) {
}
ProxyConfig::ProxyRules::~ProxyRules() {
}
void ProxyConfig::ProxyRules::Apply(const GURL& url, ProxyInfo* result) {
if (empty()) {
result->UseDirect();
return;
}
bool bypass_proxy = bypass_rules.Matches(url);
if (reverse_bypass)
bypass_proxy = !bypass_proxy;
if (bypass_proxy) {
result->UseDirect();
return;
}
switch (type) {
case ProxyRules::TYPE_SINGLE_PROXY: {
result->UseProxyServer(single_proxy);
return;
}
case ProxyRules::TYPE_PROXY_PER_SCHEME: {
const ProxyServer* entry = MapUrlSchemeToProxy(url.scheme());
if (entry) {
result->UseProxyServer(*entry);
} else {
// We failed to find a matching proxy server for the current URL
// scheme. Default to direct.
result->UseDirect();
}
return;
}
default: {
result->UseDirect();
NOTREACHED();
return;
}
}
}
void ProxyConfig::ProxyRules::ParseFromString(const std::string& proxy_rules) {
// Reset.
type = TYPE_NO_RULES;
single_proxy = ProxyServer();
proxy_for_http = ProxyServer();
proxy_for_https = ProxyServer();
proxy_for_ftp = ProxyServer();
fallback_proxy = ProxyServer();
StringTokenizer proxy_server_list(proxy_rules, ";");
while (proxy_server_list.GetNext()) {
StringTokenizer proxy_server_for_scheme(
proxy_server_list.token_begin(), proxy_server_list.token_end(), "=");
while (proxy_server_for_scheme.GetNext()) {
std::string url_scheme = proxy_server_for_scheme.token();
// If we fail to get the proxy server here, it means that
// this is a regular proxy server configuration, i.e. proxies
// are not configured per protocol.
if (!proxy_server_for_scheme.GetNext()) {
if (type == TYPE_PROXY_PER_SCHEME)
continue; // Unexpected.
single_proxy = ProxyServer::FromURI(url_scheme,
ProxyServer::SCHEME_HTTP);
type = TYPE_SINGLE_PROXY;
return;
}
// Trim whitespace off the url scheme.
TrimWhitespaceASCII(url_scheme, TRIM_ALL, &url_scheme);
// Add it to the per-scheme mappings (if supported scheme).
type = TYPE_PROXY_PER_SCHEME;
ProxyServer* entry = MapUrlSchemeToProxyNoFallback(url_scheme);
ProxyServer::Scheme default_scheme = ProxyServer::SCHEME_HTTP;
// socks=XXX is inconsistent with the other formats, since "socks"
// is not a URL scheme. Rather this means "for everything else, send
// it to the SOCKS proxy server XXX".
if (url_scheme == "socks") {
DCHECK(!entry);
entry = &fallback_proxy;
default_scheme = ProxyServer::SCHEME_SOCKS4;
}
if (entry) {
*entry = ProxyServer::FromURI(proxy_server_for_scheme.token(),
default_scheme);
}
}
}
}
const ProxyServer* ProxyConfig::ProxyRules::MapUrlSchemeToProxy(
const std::string& url_scheme) const {
const ProxyServer* proxy_server =
const_cast<ProxyRules*>(this)->MapUrlSchemeToProxyNoFallback(url_scheme);
if (proxy_server && proxy_server->is_valid())
return proxy_server;
if (fallback_proxy.is_valid())
return &fallback_proxy;
return NULL; // No mapping for this scheme. Use direct.
}
bool ProxyConfig::ProxyRules::Equals(const ProxyRules& other) const {
return type == other.type &&
single_proxy == other.single_proxy &&
proxy_for_http == other.proxy_for_http &&
proxy_for_https == other.proxy_for_https &&
proxy_for_ftp == other.proxy_for_ftp &&
fallback_proxy == other.fallback_proxy &&
bypass_rules.Equals(other.bypass_rules) &&
reverse_bypass == other.reverse_bypass;
}
ProxyServer* ProxyConfig::ProxyRules::MapUrlSchemeToProxyNoFallback(
const std::string& scheme) {
DCHECK_EQ(TYPE_PROXY_PER_SCHEME, type);
if (scheme == "http")
return &proxy_for_http;
if (scheme == "https")
return &proxy_for_https;
if (scheme == "ftp")
return &proxy_for_ftp;
return NULL; // No mapping for this scheme.
}
ProxyConfig::ProxyConfig() : auto_detect_(false), id_(INVALID_ID) {
}
ProxyConfig::ProxyConfig(const ProxyConfig& config)
: auto_detect_(config.auto_detect_),
pac_url_(config.pac_url_),
proxy_rules_(config.proxy_rules_),
id_(config.id_) {
}
ProxyConfig::~ProxyConfig() {
}
ProxyConfig& ProxyConfig::operator=(const ProxyConfig& config) {
auto_detect_ = config.auto_detect_;
pac_url_ = config.pac_url_;
proxy_rules_ = config.proxy_rules_;
id_ = config.id_;
return *this;
}
bool ProxyConfig::Equals(const ProxyConfig& other) const {
// The two configs can have different IDs. We are just interested in if they
// have the same settings.
return auto_detect_ == other.auto_detect_ &&
pac_url_ == other.pac_url_ &&
proxy_rules_.Equals(other.proxy_rules());
}
bool ProxyConfig::HasAutomaticSettings() const {
return auto_detect_ || has_pac_url();
}
void ProxyConfig::ClearAutomaticSettings() {
auto_detect_ = false;
pac_url_ = GURL();
}
Value* ProxyConfig::ToValue() const {
DictionaryValue* dict = new DictionaryValue();
// Output the automatic settings.
if (auto_detect_)
dict->SetBoolean("auto_detect", auto_detect_);
if (has_pac_url())
dict->SetString("pac_url", pac_url_.possibly_invalid_spec());
// Output the manual settings.
if (proxy_rules_.type != ProxyRules::TYPE_NO_RULES) {
switch (proxy_rules_.type) {
case ProxyRules::TYPE_SINGLE_PROXY:
AddProxyToValue("single_proxy", proxy_rules_.single_proxy, dict);
break;
case ProxyRules::TYPE_PROXY_PER_SCHEME: {
DictionaryValue* dict2 = new DictionaryValue();
AddProxyToValue("http", proxy_rules_.proxy_for_http, dict2);
AddProxyToValue("https", proxy_rules_.proxy_for_https, dict2);
AddProxyToValue("ftp", proxy_rules_.proxy_for_ftp, dict2);
AddProxyToValue("fallback", proxy_rules_.fallback_proxy, dict2);
dict->Set("proxy_per_scheme", dict2);
break;
}
default:
NOTREACHED();
}
// Output the bypass rules.
const ProxyBypassRules& bypass = proxy_rules_.bypass_rules;
if (!bypass.rules().empty()) {
if (proxy_rules_.reverse_bypass)
dict->SetBoolean("reverse_bypass", true);
ListValue* list = new ListValue();
for (ProxyBypassRules::RuleList::const_iterator it =
bypass.rules().begin();
it != bypass.rules().end(); ++it) {
list->Append(Value::CreateStringValue((*it)->ToString()));
}
dict->Set("bypass_list", list);
}
}
return dict;
}
} // namespace net