feat: Added support for all proxy modes (#24937)

* feat: Added support for all proxy modes

This commit extended setProxy to support all proxy modes including
direct, auto_detect, pac_script, fixed_servers and system.

* feat: New api for reload proxy configurations
This commit is contained in:
LuoJinghua 2020-10-27 14:50:06 +08:00 committed by GitHub
parent 33ac7dbd48
commit 201fc11b4b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 155 additions and 18 deletions

View file

@ -283,6 +283,27 @@ Writes any unwritten DOMStorage data to disk.
#### `ses.setProxy(config)` #### `ses.setProxy(config)`
* `config` Object * `config` Object
* `mode` String (optional) - The proxy mode. Should be one of `direct`,
`auto_detect`, `pac_script`, `fixed_servers` or `system`. If it's
unspecified, it will be automatically determined based on other specified
options.
* `direct`
In direct mode all connections are created directly, without any proxy involved.
* `auto_detect`
In auto_detect mode the proxy configuration is determined by a PAC script that can
be downloaded at http://wpad/wpad.dat.
* `pac_script`
In pac_script mode the proxy configuration is determined by a PAC script that is
retrieved from the URL specified in the `pacScript`. This is the default mode
if `pacScript` is specified.
* `fixed_servers`
In fixed_servers mode the proxy configuration is specified in `proxyRules`.
This is the default mode if `proxyRules` is specified.
* `system`
In system mode the proxy configuration is taken from the operating system.
Note that the system mode is different from setting no proxy configuration.
In the latter case, Electron falls back to the system settings
only if no command-line options influence the proxy configuration.
* `pacScript` String (optional) - The URL associated with the PAC file. * `pacScript` String (optional) - The URL associated with the PAC file.
* `proxyRules` String (optional) - Rules indicating which proxies to use. * `proxyRules` String (optional) - Rules indicating which proxies to use.
* `proxyBypassRules` String (optional) - Rules indicating which URLs should * `proxyBypassRules` String (optional) - Rules indicating which URLs should
@ -292,7 +313,7 @@ Returns `Promise<void>` - Resolves when the proxy setting process is complete.
Sets the proxy settings. Sets the proxy settings.
When `pacScript` and `proxyRules` are provided together, the `proxyRules` When `mode` is unspecified, `pacScript` and `proxyRules` are provided together, the `proxyRules`
option is ignored and `pacScript` configuration is applied. option is ignored and `pacScript` configuration is applied.
You may need `ses.closeAllConnections` to close currently in flight connections to prevent You may need `ses.closeAllConnections` to close currently in flight connections to prevent
@ -366,6 +387,10 @@ The `proxyBypassRules` is a comma separated list of rules described below:
Returns `Promise<String>` - Resolves with the proxy information for `url`. Returns `Promise<String>` - Resolves with the proxy information for `url`.
#### `ses.forceReloadProxyConfig()`
Returns `Promise<void>` - Resolves when the all internal states of proxy service is reset and the latest proxy configuration is reapplied if it's already available. The pac script will be fetched from `pacScript` again if the proxy mode is `pac_script`.
#### `ses.setDownloadPath(path)` #### `ses.setDownloadPath(path)`
* `path` String - The download location. * `path` String - The download location.

View file

@ -27,6 +27,7 @@
#include "components/prefs/value_map_pref_store.h" #include "components/prefs/value_map_pref_store.h"
#include "components/proxy_config/proxy_config_dictionary.h" #include "components/proxy_config/proxy_config_dictionary.h"
#include "components/proxy_config/proxy_config_pref_names.h" #include "components/proxy_config/proxy_config_pref_names.h"
#include "components/proxy_config/proxy_prefs.h"
#include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h" #include "content/public/browser/browser_thread.h"
#include "content/public/browser/download_item_utils.h" #include "content/public/browser/download_item_utils.h"
@ -504,27 +505,51 @@ v8::Local<v8::Promise> Session::SetProxy(gin::Arguments* args) {
return handle; return handle;
} }
std::string proxy_rules, bypass_list, pac_url; std::string mode, proxy_rules, bypass_list, pac_url;
options.Get("pacScript", &pac_url); options.Get("pacScript", &pac_url);
options.Get("proxyRules", &proxy_rules); options.Get("proxyRules", &proxy_rules);
options.Get("proxyBypassRules", &bypass_list); options.Get("proxyBypassRules", &bypass_list);
// pacScript takes precedence over proxyRules. ProxyPrefs::ProxyMode proxy_mode = ProxyPrefs::MODE_FIXED_SERVERS;
if (!pac_url.empty()) { if (!options.Get("mode", &mode)) {
browser_context_->in_memory_pref_store()->SetValue( // pacScript takes precedence over proxyRules.
proxy_config::prefs::kProxy, if (!pac_url.empty()) {
std::make_unique<base::Value>(ProxyConfigDictionary::CreatePacScript( proxy_mode = ProxyPrefs::MODE_PAC_SCRIPT;
pac_url, true /* pac_mandatory */)), } else {
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); proxy_mode = ProxyPrefs::MODE_FIXED_SERVERS;
}
} else { } else {
browser_context_->in_memory_pref_store()->SetValue( if (!ProxyPrefs::StringToProxyMode(mode, &proxy_mode)) {
proxy_config::prefs::kProxy, promise.RejectWithErrorMessage(
std::make_unique<base::Value>(ProxyConfigDictionary::CreateFixedServers( "Invalid mode, must be one of direct, auto_detect, pac_script, "
proxy_rules, bypass_list)), "fixed_servers or system");
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); return handle;
}
} }
std::unique_ptr<base::Value> proxy_config;
if (proxy_mode == ProxyPrefs::MODE_DIRECT) {
proxy_config =
std::make_unique<base::Value>(ProxyConfigDictionary::CreateDirect());
} else if (proxy_mode == ProxyPrefs::MODE_SYSTEM) {
proxy_config =
std::make_unique<base::Value>(ProxyConfigDictionary::CreateSystem());
} else if (proxy_mode == ProxyPrefs::MODE_AUTO_DETECT) {
proxy_config = std::make_unique<base::Value>(
ProxyConfigDictionary::CreateAutoDetect());
} else if (proxy_mode == ProxyPrefs::MODE_PAC_SCRIPT) {
proxy_config =
std::make_unique<base::Value>(ProxyConfigDictionary::CreatePacScript(
pac_url, true /* pac_mandatory */));
} else {
proxy_config = std::make_unique<base::Value>(
ProxyConfigDictionary::CreateFixedServers(proxy_rules, bypass_list));
}
browser_context_->in_memory_pref_store()->SetValue(
proxy_config::prefs::kProxy, std::move(proxy_config),
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
base::ThreadTaskRunnerHandle::Get()->PostTask( base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(gin_helper::Promise<void>::ResolvePromise, FROM_HERE, base::BindOnce(gin_helper::Promise<void>::ResolvePromise,
std::move(promise))); std::move(promise)));
@ -532,6 +557,19 @@ v8::Local<v8::Promise> Session::SetProxy(gin::Arguments* args) {
return handle; return handle;
} }
v8::Local<v8::Promise> Session::ForceReloadProxyConfig() {
v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
gin_helper::Promise<void> promise(isolate);
auto handle = promise.GetHandle();
content::BrowserContext::GetDefaultStoragePartition(browser_context_)
->GetNetworkContext()
->ForceReloadProxyConfig(base::BindOnce(
gin_helper::Promise<void>::ResolvePromise, std::move(promise)));
return handle;
}
void Session::SetDownloadPath(const base::FilePath& path) { void Session::SetDownloadPath(const base::FilePath& path) {
browser_context_->prefs()->SetFilePath(prefs::kDownloadDefaultDirectory, browser_context_->prefs()->SetFilePath(prefs::kDownloadDefaultDirectory,
path); path);
@ -1100,6 +1138,7 @@ gin::ObjectTemplateBuilder Session::GetObjectTemplateBuilder(
.SetMethod("clearStorageData", &Session::ClearStorageData) .SetMethod("clearStorageData", &Session::ClearStorageData)
.SetMethod("flushStorageData", &Session::FlushStorageData) .SetMethod("flushStorageData", &Session::FlushStorageData)
.SetMethod("setProxy", &Session::SetProxy) .SetMethod("setProxy", &Session::SetProxy)
.SetMethod("forceReloadProxyConfig", &Session::ForceReloadProxyConfig)
.SetMethod("setDownloadPath", &Session::SetDownloadPath) .SetMethod("setDownloadPath", &Session::SetDownloadPath)
.SetMethod("enableNetworkEmulation", &Session::EnableNetworkEmulation) .SetMethod("enableNetworkEmulation", &Session::EnableNetworkEmulation)
.SetMethod("disableNetworkEmulation", &Session::DisableNetworkEmulation) .SetMethod("disableNetworkEmulation", &Session::DisableNetworkEmulation)

View file

@ -95,6 +95,7 @@ class Session : public gin::Wrappable<Session>,
v8::Local<v8::Promise> ClearStorageData(gin::Arguments* args); v8::Local<v8::Promise> ClearStorageData(gin::Arguments* args);
void FlushStorageData(); void FlushStorageData();
v8::Local<v8::Promise> SetProxy(gin::Arguments* args); v8::Local<v8::Promise> SetProxy(gin::Arguments* args);
v8::Local<v8::Promise> ForceReloadProxyConfig();
void SetDownloadPath(const base::FilePath& path); void SetDownloadPath(const base::FilePath& path);
void EnableNetworkEmulation(const gin_helper::Dictionary& options); void EnableNetworkEmulation(const gin_helper::Dictionary& options);
void DisableNetworkEmulation(); void DisableNetworkEmulation();

View file

@ -384,10 +384,18 @@ describe('session module', () => {
res.end(pac); res.end(pac);
}); });
await new Promise(resolve => server.listen(0, '127.0.0.1', resolve)); await new Promise(resolve => server.listen(0, '127.0.0.1', resolve));
const config = { pacScript: `http://127.0.0.1:${(server.address() as AddressInfo).port}` }; {
await customSession.setProxy(config); const config = { pacScript: `http://127.0.0.1:${(server.address() as AddressInfo).port}` };
const proxy = await customSession.resolveProxy('https://google.com'); await customSession.setProxy(config);
expect(proxy).to.equal('PROXY myproxy:8132'); const proxy = await customSession.resolveProxy('https://google.com');
expect(proxy).to.equal('PROXY myproxy:8132');
}
{
const config = { mode: 'pac_script' as any, pacScript: `http://127.0.0.1:${(server.address() as AddressInfo).port}` };
await customSession.setProxy(config);
const proxy = await customSession.resolveProxy('https://google.com');
expect(proxy).to.equal('PROXY myproxy:8132');
}
}); });
it('allows bypassing proxy settings', async () => { it('allows bypassing proxy settings', async () => {
@ -399,6 +407,70 @@ describe('session module', () => {
const proxy = await customSession.resolveProxy('http://example/'); const proxy = await customSession.resolveProxy('http://example/');
expect(proxy).to.equal('DIRECT'); expect(proxy).to.equal('DIRECT');
}); });
it('allows configuring proxy settings with mode `direct`', async () => {
const config = { mode: 'direct' as any, proxyRules: 'http=myproxy:80' };
await customSession.setProxy(config);
const proxy = await customSession.resolveProxy('http://example.com/');
expect(proxy).to.equal('DIRECT');
});
it('allows configuring proxy settings with mode `auto_detect`', async () => {
const config = { mode: 'auto_detect' as any };
await customSession.setProxy(config);
});
it('allows configuring proxy settings with mode `pac_script`', async () => {
const config = { mode: 'pac_script' as any };
await customSession.setProxy(config);
const proxy = await customSession.resolveProxy('http://example.com/');
expect(proxy).to.equal('DIRECT');
});
it('allows configuring proxy settings with mode `fixed_servers`', async () => {
const config = { mode: 'fixed_servers' as any, proxyRules: 'http=myproxy:80' };
await customSession.setProxy(config);
const proxy = await customSession.resolveProxy('http://example.com/');
expect(proxy).to.equal('PROXY myproxy:80');
});
it('allows configuring proxy settings with mode `system`', async () => {
const config = { mode: 'system' as any };
await customSession.setProxy(config);
});
it('disallows configuring proxy settings with mode `invalid`', async () => {
const config = { mode: 'invalid' as any };
await expect(customSession.setProxy(config)).to.eventually.be.rejectedWith(/Invalid mode/);
});
it('reload proxy configuration', async () => {
let proxyPort = 8132;
server = http.createServer((req, res) => {
const pac = `
function FindProxyForURL(url, host) {
return "PROXY myproxy:${proxyPort}";
}
`;
res.writeHead(200, {
'Content-Type': 'application/x-ns-proxy-autoconfig'
});
res.end(pac);
});
await new Promise(resolve => server.listen(0, '127.0.0.1', resolve));
const config = { mode: 'pac_script' as any, pacScript: `http://127.0.0.1:${(server.address() as AddressInfo).port}` };
await customSession.setProxy(config);
{
const proxy = await customSession.resolveProxy('https://google.com');
expect(proxy).to.equal(`PROXY myproxy:${proxyPort}`);
}
{
proxyPort = 8133;
await customSession.forceReloadProxyConfig();
const proxy = await customSession.resolveProxy('https://google.com');
expect(proxy).to.equal(`PROXY myproxy:${proxyPort}`);
}
});
}); });
describe('ses.getBlobData()', () => { describe('ses.getBlobData()', () => {