diff options
author | battre@chromium.org <battre@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-02-14 12:14:23 +0000 |
---|---|---|
committer | battre@chromium.org <battre@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-02-14 12:14:23 +0000 |
commit | ce50d075f11db8d9a87260627d47f2f493104c73 (patch) | |
tree | 78e4fc5769382c32e403bef06348230972204b79 | |
parent | fc28ed6cc072d544a0f3787cb0e5aa62c09f119d (diff) | |
download | chromium_src-ce50d075f11db8d9a87260627d47f2f493104c73.zip chromium_src-ce50d075f11db8d9a87260627d47f2f493104c73.tar.gz chromium_src-ce50d075f11db8d9a87260627d47f2f493104c73.tar.bz2 |
Support bypassList in Proxy Settings API.
BUG=72549,67778
TEST=
Review URL: http://codereview.chromium.org/6469030
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@74811 0039d316-1c4b-4281-b951-d872f2087c98
9 files changed, 283 insertions, 27 deletions
diff --git a/chrome/browser/extensions/extension_proxy_api.cc b/chrome/browser/extensions/extension_proxy_api.cc index f816528..6256b39 100644 --- a/chrome/browser/extensions/extension_proxy_api.cc +++ b/chrome/browser/extensions/extension_proxy_api.cc @@ -5,6 +5,7 @@ #include "chrome/browser/extensions/extension_proxy_api.h" #include "base/string_util.h" +#include "base/string_tokenizer.h" #include "base/values.h" #include "chrome/browser/prefs/proxy_config_dictionary.h" #include "chrome/browser/profiles/profile.h" @@ -49,6 +50,7 @@ const char kProxyCfgPacScriptUrl[] = "url"; const char kProxyCfgRules[] = "rules"; const char kProxyCfgRuleHost[] = "host"; const char kProxyCfgRulePort[] = "port"; +const char kProxyCfgBypassList[] = "bypassList"; const char kProxyCfgScheme[] = "scheme"; COMPILE_ASSERT(SCHEME_MAX == SCHEME_SOCKS, SCHEME_MAX_must_equal_SCHEME_SOCKS); @@ -58,6 +60,35 @@ COMPILE_ASSERT(arraysize(scheme_name) == SCHEME_MAX + 1, scheme_name_array_is_wrong_size); COMPILE_ASSERT(SCHEME_ALL == 0, singleProxy_must_be_first_option); +bool TokenizeToStringList( + const std::string& in, const std::string& delims, ListValue** out) { + scoped_ptr<ListValue> result(new ListValue); + StringTokenizer entries(in, delims); + while (entries.GetNext()) { + result->Append(Value::CreateStringValue(entries.token())); + } + *out = result.release(); + return true; +} + +bool JoinStringList( + ListValue* list, const std::string& joiner, std::string* out) { + std::string result; + for (size_t i = 0; i < list->GetSize(); ++i) { + if (!result.empty()) + result.append(joiner); + // TODO(battre): handle UTF-8 (http://crbug.com/72692) + string16 entry; + if (!list->GetString(i, &entry)) + return false; + if (!IsStringASCII(entry)) + return false; + result.append(UTF16ToASCII(entry)); + } + *out = result; + return true; +} + // Converts a proxy server description |dict| as passed by the API caller // (e.g. for the http proxy in the rules element) and converts it to a // ProxyServer. Returns true if successful. @@ -65,16 +96,22 @@ bool GetProxyServer(const DictionaryValue* dict, net::ProxyServer::Scheme default_scheme, net::ProxyServer* proxy_server) { std::string scheme_string; // optional. - dict->GetString(kProxyCfgScheme, &scheme_string); + // We can safely assume that this is ASCII due to the allowed enumeration + // values specified in extension_api.json. + dict->GetStringASCII(kProxyCfgScheme, &scheme_string); net::ProxyServer::Scheme scheme = net::ProxyServer::GetSchemeFromURI(scheme_string); if (scheme == net::ProxyServer::SCHEME_INVALID) scheme = default_scheme; - std::string host; - if (!dict->GetString(kProxyCfgRuleHost, &host)) + // TODO(battre): handle UTF-8 in hostnames (http://crbug.com/72692) + string16 host16; + if (!dict->GetString(kProxyCfgRuleHost, &host16)) return false; + if (!IsStringASCII(host16)) + return false; + std::string host = UTF16ToASCII(host16); int port; // optional. if (!dict->GetInteger(kProxyCfgRulePort, &port)) @@ -146,6 +183,25 @@ bool GetProxyRules(DictionaryValue* proxy_rules, std::string* out) { return true; } +// Creates a string of the "bypassList" entries of a ProxyRules object (see API +// documentation) by joining the elements with commas. +// Returns true if successful (i.e. string could be delivered or no "bypassList" +// exists in the |proxy_rules|). +bool GetBypassList(DictionaryValue* proxy_rules, std::string* out) { + if (!proxy_rules) + return false; + + ListValue* bypass_list; + if (!proxy_rules->HasKey(kProxyCfgBypassList)) { + *out = ""; + return true; + } + if (!proxy_rules->GetList(kProxyCfgBypassList, &bypass_list)) + return false; + + return JoinStringList(bypass_list, ",", out); +} + } // namespace void ProxySettingsFunction::ApplyPreference(const char* pref_path, @@ -180,7 +236,9 @@ bool UseCustomProxySettingsFunction::RunImpl() { } std::string proxy_mode; - proxy_config->GetString(kProxyCfgMode, &proxy_mode); + // We can safely assume that this is ASCII due to the allowed enumeration + // values specified in extension_api.json. + proxy_config->GetStringASCII(kProxyCfgMode, &proxy_mode); ProxyPrefs::ProxyMode mode_enum; if (!ProxyPrefs::StringToProxyMode(proxy_mode, &mode_enum)) { LOG(ERROR) << "Invalid mode for proxy settings: " << proxy_mode << ". " @@ -190,12 +248,18 @@ bool UseCustomProxySettingsFunction::RunImpl() { DictionaryValue* pac_dict = NULL; proxy_config->GetDictionary(kProxyCfgPacScript, &pac_dict); - std::string pac_url; - if (pac_dict && !pac_dict->GetString(kProxyCfgPacScriptUrl, &pac_url)) { + // TODO(battre): Handle UTF-8 URLs (http://crbug.com/72692) + string16 pac_url16; + if (pac_dict && !pac_dict->GetString(kProxyCfgPacScriptUrl, &pac_url16)) { LOG(ERROR) << "'pacScript' requires a 'url' field. " << "Setting custom proxy settings failed."; return false; } + if (!IsStringASCII(pac_url16)) { + LOG(ERROR) << "Only ASCII URLs are supported, yet"; + return false; + } + std::string pac_url = UTF16ToASCII(pac_url16); DictionaryValue* proxy_rules = NULL; proxy_config->GetDictionary(kProxyCfgRules, &proxy_rules); @@ -205,9 +269,12 @@ bool UseCustomProxySettingsFunction::RunImpl() { << "Setting custom proxy settings failed."; return false; } - - // not supported, yet. std::string bypass_list; + if (proxy_rules && !GetBypassList(proxy_rules, &bypass_list)) { + LOG(ERROR) << "Invalid 'bypassList' specified. " + << "Setting custom proxy settings failed."; + return false; + } DictionaryValue* result_proxy_config = NULL; switch (mode_enum) { @@ -315,18 +382,34 @@ bool GetCurrentProxySettingsFunction::ConvertToApiFormat( break; } case ProxyPrefs::MODE_FIXED_SERVERS: { - // TODO(battre): Handle bypass list. + scoped_ptr<DictionaryValue> rules_dict(new DictionaryValue); + std::string proxy_servers; if (!dict.GetProxyServer(&proxy_servers)) { - LOG(ERROR) << "Missing proxy servers"; + LOG(ERROR) << "Missing proxy servers in configuration"; return false; } - DictionaryValue* rules_dict = new DictionaryValue; - if (!ParseRules(proxy_servers, rules_dict)) { + if (!ParseRules(proxy_servers, rules_dict.get())) { LOG(ERROR) << "Could not parse proxy rules"; return false; } - api_proxy_config->Set(kProxyCfgRules, rules_dict); + + bool hasBypassList = dict.HasBypassList(); + if (hasBypassList) { + std::string bypass_list_string; + if (!dict.GetBypassList(&bypass_list_string)) { + LOG(ERROR) << "Invalid bypassList in configuration"; + return false; + } + ListValue* bypass_list = NULL; + if (TokenizeToStringList(bypass_list_string, ",;", &bypass_list)) { + rules_dict->Set(kProxyCfgBypassList, bypass_list); + } else { + LOG(ERROR) << "Error parsing bypassList " << bypass_list_string; + return false; + } + } + api_proxy_config->Set(kProxyCfgRules, rules_dict.release()); break; } case ProxyPrefs::kModeCount: diff --git a/chrome/browser/extensions/extension_proxy_apitest.cc b/chrome/browser/extensions/extension_proxy_apitest.cc index b07e2cb..71ddde5 100644 --- a/chrome/browser/extensions/extension_proxy_apitest.cc +++ b/chrome/browser/extensions/extension_proxy_apitest.cc @@ -13,8 +13,9 @@ namespace { -const char NO_SERVER[] = ""; -const char NO_PAC[] = ""; +const char kNoServer[] = ""; +const char kNoBypass[] = ""; +const char kNoPac[] = ""; } // namespace @@ -22,6 +23,7 @@ class ProxySettingsApiTest : public ExtensionApiTest { protected: void ValidateSettings(int expected_mode, const std::string& expected_server, + const std::string& bypass, const std::string& expected_pac_url, PrefService* pref_service) { const PrefService::Preference* pref = @@ -36,6 +38,13 @@ class ProxySettingsApiTest : public ExtensionApiTest { EXPECT_EQ(expected_mode, mode); std::string value; + if (!bypass.empty()) { + ASSERT_TRUE(dict.GetBypassList(&value)); + EXPECT_EQ(bypass, value); + } else { + EXPECT_FALSE(dict.GetBypassList(&value)); + } + if (!expected_pac_url.empty()) { ASSERT_TRUE(dict.GetPacUrl(&value)); EXPECT_EQ(expected_pac_url, value); @@ -59,13 +68,6 @@ class ProxySettingsApiTest : public ExtensionApiTest { } }; -namespace { - -const char kNoServer[] = ""; -const char kNoPac[] = ""; - -} // namespace - // Tests direct connection settings. IN_PROC_BROWSER_TEST_F(ProxySettingsApiTest, ProxyDirectSettings) { CommandLine::ForCurrentProcess()->AppendSwitch( @@ -76,7 +78,8 @@ IN_PROC_BROWSER_TEST_F(ProxySettingsApiTest, ProxyDirectSettings) { ASSERT_TRUE(extension); PrefService* pref_service = browser()->profile()->GetPrefs(); - ValidateSettings(ProxyPrefs::MODE_DIRECT, kNoServer, kNoPac, pref_service); + ValidateSettings(ProxyPrefs::MODE_DIRECT, kNoServer, kNoBypass, kNoPac, + pref_service); } // Tests auto-detect settings. @@ -89,7 +92,7 @@ IN_PROC_BROWSER_TEST_F(ProxySettingsApiTest, ProxyAutoSettings) { ASSERT_TRUE(extension); PrefService* pref_service = browser()->profile()->GetPrefs(); - ValidateSettings(ProxyPrefs::MODE_AUTO_DETECT, kNoServer, kNoPac, + ValidateSettings(ProxyPrefs::MODE_AUTO_DETECT, kNoServer, kNoBypass, kNoPac, pref_service); } @@ -103,7 +106,7 @@ IN_PROC_BROWSER_TEST_F(ProxySettingsApiTest, ProxyPacScript) { ASSERT_TRUE(extension); PrefService* pref_service = browser()->profile()->GetPrefs(); - ValidateSettings(ProxyPrefs::MODE_PAC_SCRIPT, kNoServer, + ValidateSettings(ProxyPrefs::MODE_PAC_SCRIPT, kNoServer, kNoBypass, "http://wpad/windows.pac", pref_service); } @@ -119,6 +122,7 @@ IN_PROC_BROWSER_TEST_F(ProxySettingsApiTest, ProxyFixedSingle) { PrefService* pref_service = browser()->profile()->GetPrefs(); ValidateSettings(ProxyPrefs::MODE_FIXED_SERVERS, "127.0.0.1:100", + kNoBypass, kNoPac, pref_service); } @@ -133,7 +137,8 @@ IN_PROC_BROWSER_TEST_F(ProxySettingsApiTest, ProxySystem) { ASSERT_TRUE(extension); PrefService* pref_service = browser()->profile()->GetPrefs(); - ValidateSettings(ProxyPrefs::MODE_SYSTEM, kNoServer, kNoPac, pref_service); + ValidateSettings(ProxyPrefs::MODE_SYSTEM, kNoServer, kNoBypass, kNoPac, + pref_service); } // Tests setting separate proxies for each scheme. @@ -151,6 +156,7 @@ IN_PROC_BROWSER_TEST_F(ProxySettingsApiTest, ProxyFixedIndividual) { "https=2.2.2.2:80;" // http:// is pruned. "ftp=3.3.3.3:9000;" // http:// is pruned. "socks=socks4://4.4.4.4:9090", + kNoBypass, kNoPac, pref_service); @@ -161,6 +167,7 @@ IN_PROC_BROWSER_TEST_F(ProxySettingsApiTest, ProxyFixedIndividual) { "https=2.2.2.2:80;" "ftp=3.3.3.3:9000;" "socks=socks4://4.4.4.4:9090", + kNoBypass, kNoPac, pref_service); } @@ -185,6 +192,7 @@ IN_PROC_BROWSER_TEST_F(ProxySettingsApiTest, "https=socks5://2.2.2.2:1080;" // socks5 equals socks. "ftp=3.3.3.3:9000;" "socks=socks4://4.4.4.4:9090", + kNoBypass, kNoPac, pref_service); } @@ -205,6 +213,7 @@ IN_PROC_BROWSER_TEST_F(ProxySettingsApiTest, "https=socks5://2.2.2.2:1080;" "ftp=3.3.3.3:9000;" "socks=socks4://4.4.4.4:9090", + kNoBypass, kNoPac, pref_service); @@ -215,6 +224,7 @@ IN_PROC_BROWSER_TEST_F(ProxySettingsApiTest, "https=socks5://6.6.6.6:1080;" "ftp=7.7.7.7:9000;" "socks=socks4://8.8.8.8:9090", + kNoBypass, kNoPac, pref_service); } @@ -231,3 +241,28 @@ IN_PROC_BROWSER_TEST_F(ProxySettingsApiTest, ProxyFixedIndividualRemove) { PrefService* pref_service = browser()->profile()->GetPrefs(); ExpectNoSettings(pref_service); } + +IN_PROC_BROWSER_TEST_F(ProxySettingsApiTest, + ProxyBypass) { + CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kEnableExperimentalExtensionApis); + + ASSERT_TRUE(RunExtensionTest("proxy/bypass")) << message_; + const Extension* extension = GetSingleLoadedExtension(); + ASSERT_TRUE(extension); + + PrefService* pref_service = browser()->profile()->GetPrefs(); + ValidateSettings(ProxyPrefs::MODE_FIXED_SERVERS, + "http=1.1.1.1:80", + "localhost,::1,foo.bar,<local>", + kNoPac, + pref_service); + + // Now check the incognito preferences. + pref_service = browser()->profile()->GetOffTheRecordProfile()->GetPrefs(); + ValidateSettings(ProxyPrefs::MODE_FIXED_SERVERS, + "http=1.1.1.1:80", + "localhost,::1,foo.bar,<local>", + kNoPac, + pref_service); +} diff --git a/chrome/browser/prefs/proxy_config_dictionary.cc b/chrome/browser/prefs/proxy_config_dictionary.cc index f383a85..0c063cd 100644 --- a/chrome/browser/prefs/proxy_config_dictionary.cc +++ b/chrome/browser/prefs/proxy_config_dictionary.cc @@ -49,6 +49,10 @@ bool ProxyConfigDictionary::GetBypassList(std::string* out) const { return dict_->GetString(kProxyBypassList, out); } +bool ProxyConfigDictionary::HasBypassList() const { + return dict_->HasKey(kProxyBypassList); +} + // static DictionaryValue* ProxyConfigDictionary::CreateDirect() { return CreateDictionary(ProxyPrefs::MODE_DIRECT, "", "", ""); diff --git a/chrome/browser/prefs/proxy_config_dictionary.h b/chrome/browser/prefs/proxy_config_dictionary.h index b54d1e6..7af2e27 100644 --- a/chrome/browser/prefs/proxy_config_dictionary.h +++ b/chrome/browser/prefs/proxy_config_dictionary.h @@ -33,6 +33,7 @@ class ProxyConfigDictionary { bool GetPacUrl(std::string* out) const; bool GetProxyServer(std::string* out) const; bool GetBypassList(std::string* out) const; + bool HasBypassList() const; static DictionaryValue* CreateDirect(); static DictionaryValue* CreateAutoDetect(); diff --git a/chrome/common/extensions/api/extension_api.json b/chrome/common/extensions/api/extension_api.json index 7b49a23..e19bad7 100644 --- a/chrome/common/extensions/api/extension_api.json +++ b/chrome/common/extensions/api/extension_api.json @@ -3757,7 +3757,8 @@ "proxyForHttp": {"$ref": "ProxyServer", "optional": true, "description": "The proxy server to be used for HTTP requests."}, "proxyForHttps": {"$ref": "ProxyServer", "optional": true, "description": "The proxy server to be used for HTTPS requests."}, "proxyForFtp": {"$ref": "ProxyServer", "optional": true, "description": "The proxy server to be used for FTP requests."}, - "socksProxy": {"$ref": "ProxyServer", "optional": true, "description": "The proxy server to be used for SOCKS requests."} + "socksProxy": {"$ref": "ProxyServer", "optional": true, "description": "The proxy server to be used for SOCKS requests."}, + "bypassList": {"type": "array", "items": {"type": "string"}, "optional": true, "description": "List of servers to connect to without a proxy server."} } }, { diff --git a/chrome/common/extensions/docs/experimental.proxy.html b/chrome/common/extensions/docs/experimental.proxy.html index e0f0187..1b1a16c 100644 --- a/chrome/common/extensions/docs/experimental.proxy.html +++ b/chrome/common/extensions/docs/experimental.proxy.html @@ -1654,6 +1654,85 @@ </dd> </div> + </div><div> + <div> + <dt> + <var>bypassList</var> + <em> + + <!-- TYPE --> + <div style="display:inline"> + ( + <span class="optional">optional</span> + <span class="enum" style="display: none; ">enumerated</span> + <span id="typeTemplate"> + <span style="display: none; "> + <a> Type</a> + </span> + <span> + <span> + array of <span><span> + <span style="display: none; "> + <a> Type</a> + </span> + <span> + <span style="display: none; "> + array of <span><span></span></span> + </span> + <span>string</span> + <span style="display: none; "></span> + </span> + </span></span> + </span> + <span style="display: none; ">paramType</span> + <span style="display: none; "></span> + </span> + </span> + ) + </div> + + </em> + </dt> + <dd class="todo" style="display: none; "> + Undocumented. + </dd> + <dd>List of servers to connect to without a proxy server.</dd> + <dd style="display: none; "> + This parameter was added in version + <b><span></span></b>. + You must omit this parameter in earlier versions, + and you may omit it in any version. If you require this + parameter, the manifest key + <a href="manifest.html#minimum_chrome_version">minimum_chrome_version</a> + can ensure that your extension won't be run in an earlier browser version. + </dd> + + <!-- OBJECT PROPERTIES --> + <dd style="display: none; "> + <dl> + <div> + <div> + </div> + </div> + </dl> + </dd> + + <!-- OBJECT METHODS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- OBJECT EVENT FIELDS --> + <dd style="display: none; "> + <div></div> + </dd> + + <!-- FUNCTION PARAMETERS --> + <dd style="display: none; "> + <div></div> + </dd> + + </div> </div> </dl> </dd> diff --git a/chrome/test/data/extensions/api_test/proxy/bypass/manifest.json b/chrome/test/data/extensions/api_test/proxy/bypass/manifest.json new file mode 100644 index 0000000..ada15d1 --- /dev/null +++ b/chrome/test/data/extensions/api_test/proxy/bypass/manifest.json @@ -0,0 +1,7 @@ +{ + "name": "chrome.proxy", + "version": "0.1", + "description": "end-to-end browser test for chrome.proxy API", + "background_page": "test.html", + "permissions": ["experimental", "proxy"] +} diff --git a/chrome/test/data/extensions/api_test/proxy/bypass/test.html b/chrome/test/data/extensions/api_test/proxy/bypass/test.html new file mode 100644 index 0000000..46f4d74 --- /dev/null +++ b/chrome/test/data/extensions/api_test/proxy/bypass/test.html @@ -0,0 +1 @@ +<script src="test.js"></script> diff --git a/chrome/test/data/extensions/api_test/proxy/bypass/test.js b/chrome/test/data/extensions/api_test/proxy/bypass/test.js new file mode 100644 index 0000000..36c5872 --- /dev/null +++ b/chrome/test/data/extensions/api_test/proxy/bypass/test.js @@ -0,0 +1,45 @@ +// 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. + +// proxy api test +// browser_tests.exe --gtest_filter=ExtensionApiTest.ProxyBypass + +function expect(expected, message) { + return chrome.test.callbackPass(function(value) { + chrome.test.assertEq(expected, value, message); + }); +} + +chrome.test.runTests([ + function setIndividualProxies() { + var httpProxy = { + host: "1.1.1.1" + }; + var httpProxyExpected = { + scheme: "http", + host: "1.1.1.1", + port: 80 + }; + + var rules = { + proxyForHttp: httpProxy, + bypassList: ["localhost", "::1", "foo.bar", "<local>"] + }; + var rulesExpected = { + proxyForHttp: httpProxyExpected, + bypassList: ["localhost", "::1", "foo.bar", "<local>"] + }; + + var config = { rules: rules, mode: "fixed_servers" }; + var configExpected = { rules: rulesExpected, mode: "fixed_servers" }; + + chrome.experimental.proxy.useCustomProxySettings(config); + chrome.experimental.proxy.getCurrentProxySettings( + false, + expect(configExpected, "invalid proxy settings")); + chrome.experimental.proxy.getCurrentProxySettings( + true, + expect(configExpected, "invalid proxy settings")); + } +]); |