diff --git a/docs/api/session.md b/docs/api/session.md index 8a6db462eebc..e5d0c137e1ba 100644 --- a/docs/api/session.md +++ b/docs/api/session.md @@ -283,6 +283,27 @@ Writes any unwritten DOMStorage data to disk. #### `ses.setProxy(config)` * `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. * `proxyRules` String (optional) - Rules indicating which proxies to use. * `proxyBypassRules` String (optional) - Rules indicating which URLs should @@ -292,7 +313,7 @@ Returns `Promise` - Resolves when the proxy setting process is complete. 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. 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` - Resolves with the proxy information for `url`. +#### `ses.forceReloadProxyConfig()` + +Returns `Promise` - 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)` * `path` String - The download location. diff --git a/shell/browser/api/electron_api_session.cc b/shell/browser/api/electron_api_session.cc index b53f6b0a4e36..29f4d76f9cf8 100644 --- a/shell/browser/api/electron_api_session.cc +++ b/shell/browser/api/electron_api_session.cc @@ -27,6 +27,7 @@ #include "components/prefs/value_map_pref_store.h" #include "components/proxy_config/proxy_config_dictionary.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_thread.h" #include "content/public/browser/download_item_utils.h" @@ -504,27 +505,51 @@ v8::Local Session::SetProxy(gin::Arguments* args) { 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("proxyRules", &proxy_rules); options.Get("proxyBypassRules", &bypass_list); - // pacScript takes precedence over proxyRules. - if (!pac_url.empty()) { - browser_context_->in_memory_pref_store()->SetValue( - proxy_config::prefs::kProxy, - std::make_unique(ProxyConfigDictionary::CreatePacScript( - pac_url, true /* pac_mandatory */)), - WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + ProxyPrefs::ProxyMode proxy_mode = ProxyPrefs::MODE_FIXED_SERVERS; + if (!options.Get("mode", &mode)) { + // pacScript takes precedence over proxyRules. + if (!pac_url.empty()) { + proxy_mode = ProxyPrefs::MODE_PAC_SCRIPT; + } else { + proxy_mode = ProxyPrefs::MODE_FIXED_SERVERS; + } } else { - browser_context_->in_memory_pref_store()->SetValue( - proxy_config::prefs::kProxy, - std::make_unique(ProxyConfigDictionary::CreateFixedServers( - proxy_rules, bypass_list)), - WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + if (!ProxyPrefs::StringToProxyMode(mode, &proxy_mode)) { + promise.RejectWithErrorMessage( + "Invalid mode, must be one of direct, auto_detect, pac_script, " + "fixed_servers or system"); + return handle; + } } + std::unique_ptr proxy_config; + if (proxy_mode == ProxyPrefs::MODE_DIRECT) { + proxy_config = + std::make_unique(ProxyConfigDictionary::CreateDirect()); + } else if (proxy_mode == ProxyPrefs::MODE_SYSTEM) { + proxy_config = + std::make_unique(ProxyConfigDictionary::CreateSystem()); + } else if (proxy_mode == ProxyPrefs::MODE_AUTO_DETECT) { + proxy_config = std::make_unique( + ProxyConfigDictionary::CreateAutoDetect()); + } else if (proxy_mode == ProxyPrefs::MODE_PAC_SCRIPT) { + proxy_config = + std::make_unique(ProxyConfigDictionary::CreatePacScript( + pac_url, true /* pac_mandatory */)); + } else { + proxy_config = std::make_unique( + 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( FROM_HERE, base::BindOnce(gin_helper::Promise::ResolvePromise, std::move(promise))); @@ -532,6 +557,19 @@ v8::Local Session::SetProxy(gin::Arguments* args) { return handle; } +v8::Local Session::ForceReloadProxyConfig() { + v8::Isolate* isolate = JavascriptEnvironment::GetIsolate(); + gin_helper::Promise promise(isolate); + auto handle = promise.GetHandle(); + + content::BrowserContext::GetDefaultStoragePartition(browser_context_) + ->GetNetworkContext() + ->ForceReloadProxyConfig(base::BindOnce( + gin_helper::Promise::ResolvePromise, std::move(promise))); + + return handle; +} + void Session::SetDownloadPath(const base::FilePath& path) { browser_context_->prefs()->SetFilePath(prefs::kDownloadDefaultDirectory, path); @@ -1100,6 +1138,7 @@ gin::ObjectTemplateBuilder Session::GetObjectTemplateBuilder( .SetMethod("clearStorageData", &Session::ClearStorageData) .SetMethod("flushStorageData", &Session::FlushStorageData) .SetMethod("setProxy", &Session::SetProxy) + .SetMethod("forceReloadProxyConfig", &Session::ForceReloadProxyConfig) .SetMethod("setDownloadPath", &Session::SetDownloadPath) .SetMethod("enableNetworkEmulation", &Session::EnableNetworkEmulation) .SetMethod("disableNetworkEmulation", &Session::DisableNetworkEmulation) diff --git a/shell/browser/api/electron_api_session.h b/shell/browser/api/electron_api_session.h index 8dcfc970aab6..5c7aba99fd12 100644 --- a/shell/browser/api/electron_api_session.h +++ b/shell/browser/api/electron_api_session.h @@ -95,6 +95,7 @@ class Session : public gin::Wrappable, v8::Local ClearStorageData(gin::Arguments* args); void FlushStorageData(); v8::Local SetProxy(gin::Arguments* args); + v8::Local ForceReloadProxyConfig(); void SetDownloadPath(const base::FilePath& path); void EnableNetworkEmulation(const gin_helper::Dictionary& options); void DisableNetworkEmulation(); diff --git a/spec-main/api-session-spec.ts b/spec-main/api-session-spec.ts index 14bc90511d7d..a6dfa90afa87 100644 --- a/spec-main/api-session-spec.ts +++ b/spec-main/api-session-spec.ts @@ -384,10 +384,18 @@ describe('session module', () => { res.end(pac); }); 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 proxy = await customSession.resolveProxy('https://google.com'); - expect(proxy).to.equal('PROXY myproxy:8132'); + { + const config = { 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'); + } + { + 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 () => { @@ -399,6 +407,70 @@ describe('session module', () => { const proxy = await customSession.resolveProxy('http://example/'); 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()', () => {