feat: add support for configuring system network context proxies (#41335)
* feat: add support for configuring system network context proxies * chore: add specs * chore: fix lint * fix: address review feedback
This commit is contained in:
parent
136762b45f
commit
26131b23b8
13 changed files with 391 additions and 113 deletions
|
@ -1468,6 +1468,24 @@ details.
|
||||||
|
|
||||||
**Note:** Enable `Secure Keyboard Entry` only when it is needed and disable it when it is no longer needed.
|
**Note:** Enable `Secure Keyboard Entry` only when it is needed and disable it when it is no longer needed.
|
||||||
|
|
||||||
|
### `app.setProxy(config)`
|
||||||
|
|
||||||
|
* `config` [ProxyConfig](structures/proxy-config.md)
|
||||||
|
|
||||||
|
Returns `Promise<void>` - Resolves when the proxy setting process is complete.
|
||||||
|
|
||||||
|
Sets the proxy settings for networks requests made without an associated [Session](session.md).
|
||||||
|
Currently this will affect requests made with [Net](net.md) in the [utility process](../glossary.md#utility-process)
|
||||||
|
and internal requests made by the runtime (ex: geolocation queries).
|
||||||
|
|
||||||
|
This method can only be called after app is ready.
|
||||||
|
|
||||||
|
#### `app.resolveProxy(url)`
|
||||||
|
|
||||||
|
* `url` URL
|
||||||
|
|
||||||
|
Returns `Promise<string>` - Resolves with the proxy information for `url` that will be used when attempting to make requests using [Net](net.md) in the [utility process](../glossary.md#utility-process).
|
||||||
|
|
||||||
## Properties
|
## Properties
|
||||||
|
|
||||||
### `app.accessibilitySupportEnabled` _macOS_ _Windows_
|
### `app.accessibilitySupportEnabled` _macOS_ _Windows_
|
||||||
|
|
|
@ -589,105 +589,15 @@ Writes any unwritten DOMStorage data to disk.
|
||||||
|
|
||||||
#### `ses.setProxy(config)`
|
#### `ses.setProxy(config)`
|
||||||
|
|
||||||
* `config` Object
|
* `config` [ProxyConfig](structures/proxy-config.md)
|
||||||
* `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
|
|
||||||
bypass the proxy settings.
|
|
||||||
|
|
||||||
Returns `Promise<void>` - Resolves when the proxy setting process is complete.
|
Returns `Promise<void>` - Resolves when the proxy setting process is complete.
|
||||||
|
|
||||||
Sets the proxy settings.
|
Sets the proxy settings.
|
||||||
|
|
||||||
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
|
You may need `ses.closeAllConnections` to close currently in flight connections to prevent
|
||||||
pooled sockets using previous proxy from being reused by future requests.
|
pooled sockets using previous proxy from being reused by future requests.
|
||||||
|
|
||||||
The `proxyRules` has to follow the rules below:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
proxyRules = schemeProxies[";"<schemeProxies>]
|
|
||||||
schemeProxies = [<urlScheme>"="]<proxyURIList>
|
|
||||||
urlScheme = "http" | "https" | "ftp" | "socks"
|
|
||||||
proxyURIList = <proxyURL>[","<proxyURIList>]
|
|
||||||
proxyURL = [<proxyScheme>"://"]<proxyHost>[":"<proxyPort>]
|
|
||||||
```
|
|
||||||
|
|
||||||
For example:
|
|
||||||
|
|
||||||
* `http=foopy:80;ftp=foopy2` - Use HTTP proxy `foopy:80` for `http://` URLs, and
|
|
||||||
HTTP proxy `foopy2:80` for `ftp://` URLs.
|
|
||||||
* `foopy:80` - Use HTTP proxy `foopy:80` for all URLs.
|
|
||||||
* `foopy:80,bar,direct://` - Use HTTP proxy `foopy:80` for all URLs, failing
|
|
||||||
over to `bar` if `foopy:80` is unavailable, and after that using no proxy.
|
|
||||||
* `socks4://foopy` - Use SOCKS v4 proxy `foopy:1080` for all URLs.
|
|
||||||
* `http=foopy,socks5://bar.com` - Use HTTP proxy `foopy` for http URLs, and fail
|
|
||||||
over to the SOCKS5 proxy `bar.com` if `foopy` is unavailable.
|
|
||||||
* `http=foopy,direct://` - Use HTTP proxy `foopy` for http URLs, and use no
|
|
||||||
proxy if `foopy` is unavailable.
|
|
||||||
* `http=foopy;socks=foopy2` - Use HTTP proxy `foopy` for http URLs, and use
|
|
||||||
`socks4://foopy2` for all other URLs.
|
|
||||||
|
|
||||||
The `proxyBypassRules` is a comma separated list of rules described below:
|
|
||||||
|
|
||||||
* `[ URL_SCHEME "://" ] HOSTNAME_PATTERN [ ":" <port> ]`
|
|
||||||
|
|
||||||
Match all hostnames that match the pattern HOSTNAME_PATTERN.
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
"foobar.com", "\*foobar.com", "\*.foobar.com", "\*foobar.com:99",
|
|
||||||
"https://x.\*.y.com:99"
|
|
||||||
|
|
||||||
* `"." HOSTNAME_SUFFIX_PATTERN [ ":" PORT ]`
|
|
||||||
|
|
||||||
Match a particular domain suffix.
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
".google.com", ".com", "http://.google.com"
|
|
||||||
|
|
||||||
* `[ SCHEME "://" ] IP_LITERAL [ ":" PORT ]`
|
|
||||||
|
|
||||||
Match URLs which are IP address literals.
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
"127.0.1", "\[0:0::1]", "\[::1]", "http://\[::1]:99"
|
|
||||||
|
|
||||||
* `IP_LITERAL "/" PREFIX_LENGTH_IN_BITS`
|
|
||||||
|
|
||||||
Match any URL that is to an IP literal that falls between the
|
|
||||||
given range. IP range is specified using CIDR notation.
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
"192.168.1.1/16", "fefe:13::abc/33".
|
|
||||||
|
|
||||||
* `<local>`
|
|
||||||
|
|
||||||
Match local addresses. The meaning of `<local>` is whether the
|
|
||||||
host matches one of: "127.0.0.1", "::1", "localhost".
|
|
||||||
|
|
||||||
#### `ses.resolveHost(host, [options])`
|
#### `ses.resolveHost(host, [options])`
|
||||||
|
|
||||||
* `host` string - Hostname to resolve.
|
* `host` string - Hostname to resolve.
|
||||||
|
|
86
docs/api/structures/proxy-config.md
Normal file
86
docs/api/structures/proxy-config.md
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
# ProxyConfig Object
|
||||||
|
|
||||||
|
* `mode` string (optional) - The proxy mode. Should be one of `direct`,
|
||||||
|
`auto_detect`, `pac_script`, `fixed_servers` or `system`.
|
||||||
|
Defaults to `pac_script` proxy mode if `pacScript` option is specified
|
||||||
|
otherwise defaults to `fixed_servers`.
|
||||||
|
* `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
|
||||||
|
bypass the proxy settings.
|
||||||
|
|
||||||
|
When `mode` is unspecified, `pacScript` and `proxyRules` are provided together, the `proxyRules`
|
||||||
|
option is ignored and `pacScript` configuration is applied.
|
||||||
|
|
||||||
|
The `proxyRules` has to follow the rules below:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
proxyRules = schemeProxies[";"<schemeProxies>]
|
||||||
|
schemeProxies = [<urlScheme>"="]<proxyURIList>
|
||||||
|
urlScheme = "http" | "https" | "ftp" | "socks"
|
||||||
|
proxyURIList = <proxyURL>[","<proxyURIList>]
|
||||||
|
proxyURL = [<proxyScheme>"://"]<proxyHost>[":"<proxyPort>]
|
||||||
|
```
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
* `http=foopy:80;ftp=foopy2` - Use HTTP proxy `foopy:80` for `http://` URLs, and
|
||||||
|
HTTP proxy `foopy2:80` for `ftp://` URLs.
|
||||||
|
* `foopy:80` - Use HTTP proxy `foopy:80` for all URLs.
|
||||||
|
* `foopy:80,bar,direct://` - Use HTTP proxy `foopy:80` for all URLs, failing
|
||||||
|
over to `bar` if `foopy:80` is unavailable, and after that using no proxy.
|
||||||
|
* `socks4://foopy` - Use SOCKS v4 proxy `foopy:1080` for all URLs.
|
||||||
|
* `http=foopy,socks5://bar.com` - Use HTTP proxy `foopy` for http URLs, and fail
|
||||||
|
over to the SOCKS5 proxy `bar.com` if `foopy` is unavailable.
|
||||||
|
* `http=foopy,direct://` - Use HTTP proxy `foopy` for http URLs, and use no
|
||||||
|
proxy if `foopy` is unavailable.
|
||||||
|
* `http=foopy;socks=foopy2` - Use HTTP proxy `foopy` for http URLs, and use
|
||||||
|
`socks4://foopy2` for all other URLs.
|
||||||
|
|
||||||
|
The `proxyBypassRules` is a comma separated list of rules described below:
|
||||||
|
|
||||||
|
* `[ URL_SCHEME "://" ] HOSTNAME_PATTERN [ ":" <port> ]`
|
||||||
|
|
||||||
|
Match all hostnames that match the pattern HOSTNAME_PATTERN.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
"foobar.com", "\*foobar.com", "\*.foobar.com", "\*foobar.com:99",
|
||||||
|
"https://x.\*.y.com:99"
|
||||||
|
|
||||||
|
* `"." HOSTNAME_SUFFIX_PATTERN [ ":" PORT ]`
|
||||||
|
|
||||||
|
Match a particular domain suffix.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
".google.com", ".com", "http://.google.com"
|
||||||
|
|
||||||
|
* `[ SCHEME "://" ] IP_LITERAL [ ":" PORT ]`
|
||||||
|
|
||||||
|
Match URLs which are IP address literals.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
"127.0.1", "\[0:0::1]", "\[::1]", "http://\[::1]:99"
|
||||||
|
|
||||||
|
* `IP_LITERAL "/" PREFIX_LENGTH_IN_BITS`
|
||||||
|
|
||||||
|
Match any URL that is to an IP literal that falls between the
|
||||||
|
given range. IP range is specified using CIDR notation.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
"192.168.1.1/16", "fefe:13::abc/33".
|
||||||
|
|
||||||
|
* `<local>`
|
||||||
|
|
||||||
|
Match local addresses. The meaning of `<local>` is whether the
|
||||||
|
host matches one of: "127.0.0.1", "::1", "localhost".
|
|
@ -118,6 +118,7 @@ auto_filenames = {
|
||||||
"docs/api/structures/protocol-request.md",
|
"docs/api/structures/protocol-request.md",
|
||||||
"docs/api/structures/protocol-response-upload-data.md",
|
"docs/api/structures/protocol-response-upload-data.md",
|
||||||
"docs/api/structures/protocol-response.md",
|
"docs/api/structures/protocol-response.md",
|
||||||
|
"docs/api/structures/proxy-config.md",
|
||||||
"docs/api/structures/rectangle.md",
|
"docs/api/structures/rectangle.md",
|
||||||
"docs/api/structures/referrer.md",
|
"docs/api/structures/referrer.md",
|
||||||
"docs/api/structures/render-process-gone-details.md",
|
"docs/api/structures/render-process-gone-details.md",
|
||||||
|
|
|
@ -26,6 +26,9 @@
|
||||||
#include "chrome/browser/icon_manager.h"
|
#include "chrome/browser/icon_manager.h"
|
||||||
#include "chrome/common/chrome_features.h"
|
#include "chrome/common/chrome_features.h"
|
||||||
#include "chrome/common/chrome_paths.h"
|
#include "chrome/common/chrome_paths.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/browser/gpu/compositor_util.h" // nogncheck
|
#include "content/browser/gpu/compositor_util.h" // nogncheck
|
||||||
#include "content/browser/gpu/gpu_data_manager_impl.h" // nogncheck
|
#include "content/browser/gpu/gpu_data_manager_impl.h" // nogncheck
|
||||||
#include "content/public/browser/browser_accessibility_state.h"
|
#include "content/public/browser/browser_accessibility_state.h"
|
||||||
|
@ -1472,6 +1475,98 @@ void App::EnableSandbox(gin_helper::ErrorThrower thrower) {
|
||||||
command_line->AppendSwitch(switches::kEnableSandbox);
|
command_line->AppendSwitch(switches::kEnableSandbox);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
v8::Local<v8::Promise> App::SetProxy(gin::Arguments* args) {
|
||||||
|
v8::Isolate* isolate = args->isolate();
|
||||||
|
gin_helper::Promise<void> promise(isolate);
|
||||||
|
v8::Local<v8::Promise> handle = promise.GetHandle();
|
||||||
|
|
||||||
|
gin_helper::Dictionary options;
|
||||||
|
args->GetNext(&options);
|
||||||
|
|
||||||
|
if (!Browser::Get()->is_ready()) {
|
||||||
|
promise.RejectWithErrorMessage(
|
||||||
|
"app.setProxy() can only be called after app is ready.");
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!g_browser_process->local_state()) {
|
||||||
|
promise.RejectWithErrorMessage(
|
||||||
|
"app.setProxy() failed due to internal error.");
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string mode, proxy_rules, bypass_list, pac_url;
|
||||||
|
|
||||||
|
options.Get("pacScript", &pac_url);
|
||||||
|
options.Get("proxyRules", &proxy_rules);
|
||||||
|
options.Get("proxyBypassRules", &bypass_list);
|
||||||
|
|
||||||
|
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 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
base::Value::Dict proxy_config;
|
||||||
|
switch (proxy_mode) {
|
||||||
|
case ProxyPrefs::MODE_DIRECT:
|
||||||
|
proxy_config = ProxyConfigDictionary::CreateDirect();
|
||||||
|
break;
|
||||||
|
case ProxyPrefs::MODE_SYSTEM:
|
||||||
|
proxy_config = ProxyConfigDictionary::CreateSystem();
|
||||||
|
break;
|
||||||
|
case ProxyPrefs::MODE_AUTO_DETECT:
|
||||||
|
proxy_config = ProxyConfigDictionary::CreateAutoDetect();
|
||||||
|
break;
|
||||||
|
case ProxyPrefs::MODE_PAC_SCRIPT:
|
||||||
|
proxy_config = ProxyConfigDictionary::CreatePacScript(pac_url, true);
|
||||||
|
break;
|
||||||
|
case ProxyPrefs::MODE_FIXED_SERVERS:
|
||||||
|
proxy_config =
|
||||||
|
ProxyConfigDictionary::CreateFixedServers(proxy_rules, bypass_list);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
NOTIMPLEMENTED();
|
||||||
|
}
|
||||||
|
|
||||||
|
static_cast<BrowserProcessImpl*>(g_browser_process)
|
||||||
|
->in_memory_pref_store()
|
||||||
|
->SetValue(proxy_config::prefs::kProxy,
|
||||||
|
base::Value{std::move(proxy_config)},
|
||||||
|
WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
|
||||||
|
|
||||||
|
g_browser_process->system_network_context_manager()
|
||||||
|
->GetContext()
|
||||||
|
->ForceReloadProxyConfig(base::BindOnce(
|
||||||
|
gin_helper::Promise<void>::ResolvePromise, std::move(promise)));
|
||||||
|
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
v8::Local<v8::Promise> App::ResolveProxy(gin::Arguments* args) {
|
||||||
|
v8::Isolate* isolate = args->isolate();
|
||||||
|
gin_helper::Promise<std::string> promise(isolate);
|
||||||
|
v8::Local<v8::Promise> handle = promise.GetHandle();
|
||||||
|
|
||||||
|
GURL url;
|
||||||
|
args->GetNext(&url);
|
||||||
|
|
||||||
|
static_cast<BrowserProcessImpl*>(g_browser_process)
|
||||||
|
->GetResolveProxyHelper()
|
||||||
|
->ResolveProxy(
|
||||||
|
url, base::BindOnce(gin_helper::Promise<std::string>::ResolvePromise,
|
||||||
|
std::move(promise)));
|
||||||
|
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
void App::SetUserAgentFallback(const std::string& user_agent) {
|
void App::SetUserAgentFallback(const std::string& user_agent) {
|
||||||
ElectronBrowserClient::Get()->SetUserAgent(user_agent);
|
ElectronBrowserClient::Get()->SetUserAgent(user_agent);
|
||||||
}
|
}
|
||||||
|
@ -1776,7 +1871,9 @@ gin::ObjectTemplateBuilder App::GetObjectTemplateBuilder(v8::Isolate* isolate) {
|
||||||
.SetProperty("userAgentFallback", &App::GetUserAgentFallback,
|
.SetProperty("userAgentFallback", &App::GetUserAgentFallback,
|
||||||
&App::SetUserAgentFallback)
|
&App::SetUserAgentFallback)
|
||||||
.SetMethod("configureHostResolver", &ConfigureHostResolver)
|
.SetMethod("configureHostResolver", &ConfigureHostResolver)
|
||||||
.SetMethod("enableSandbox", &App::EnableSandbox);
|
.SetMethod("enableSandbox", &App::EnableSandbox)
|
||||||
|
.SetMethod("setProxy", &App::SetProxy)
|
||||||
|
.SetMethod("resolveProxy", &App::ResolveProxy);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* App::GetTypeName() {
|
const char* App::GetTypeName() {
|
||||||
|
|
|
@ -222,6 +222,8 @@ class App : public ElectronBrowserClient::Delegate,
|
||||||
void EnableSandbox(gin_helper::ErrorThrower thrower);
|
void EnableSandbox(gin_helper::ErrorThrower thrower);
|
||||||
void SetUserAgentFallback(const std::string& user_agent);
|
void SetUserAgentFallback(const std::string& user_agent);
|
||||||
std::string GetUserAgentFallback();
|
std::string GetUserAgentFallback();
|
||||||
|
v8::Local<v8::Promise> SetProxy(gin::Arguments* args);
|
||||||
|
v8::Local<v8::Promise> ResolveProxy(gin::Arguments* args);
|
||||||
|
|
||||||
#if BUILDFLAG(IS_MAC)
|
#if BUILDFLAG(IS_MAC)
|
||||||
void SetActivationPolicy(gin_helper::ErrorThrower thrower,
|
void SetActivationPolicy(gin_helper::ErrorThrower thrower,
|
||||||
|
|
|
@ -37,6 +37,7 @@
|
||||||
#include "net/proxy_resolution/proxy_config_with_annotation.h"
|
#include "net/proxy_resolution/proxy_config_with_annotation.h"
|
||||||
#include "services/device/public/cpp/geolocation/geolocation_manager.h"
|
#include "services/device/public/cpp/geolocation/geolocation_manager.h"
|
||||||
#include "services/network/public/cpp/network_switches.h"
|
#include "services/network/public/cpp/network_switches.h"
|
||||||
|
#include "shell/browser/net/resolve_proxy_helper.h"
|
||||||
#include "shell/common/electron_paths.h"
|
#include "shell/common/electron_paths.h"
|
||||||
#include "shell/common/thread_restrictions.h"
|
#include "shell/common/thread_restrictions.h"
|
||||||
|
|
||||||
|
@ -100,9 +101,9 @@ void BrowserProcessImpl::PostEarlyInitialization() {
|
||||||
OSCrypt::RegisterLocalPrefs(pref_registry.get());
|
OSCrypt::RegisterLocalPrefs(pref_registry.get());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
auto pref_store = base::MakeRefCounted<ValueMapPrefStore>();
|
in_memory_pref_store_ = base::MakeRefCounted<ValueMapPrefStore>();
|
||||||
ApplyProxyModeFromCommandLine(pref_store.get());
|
ApplyProxyModeFromCommandLine(in_memory_pref_store());
|
||||||
prefs_factory.set_command_line_prefs(std::move(pref_store));
|
prefs_factory.set_command_line_prefs(in_memory_pref_store());
|
||||||
|
|
||||||
// Only use a persistent prefs store when cookie encryption is enabled as that
|
// Only use a persistent prefs store when cookie encryption is enabled as that
|
||||||
// is the only key that needs it
|
// is the only key that needs it
|
||||||
|
@ -316,6 +317,14 @@ const std::string& BrowserProcessImpl::GetSystemLocale() const {
|
||||||
return system_locale_;
|
return system_locale_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
electron::ResolveProxyHelper* BrowserProcessImpl::GetResolveProxyHelper() {
|
||||||
|
if (!resolve_proxy_helper_) {
|
||||||
|
resolve_proxy_helper_ = base::MakeRefCounted<electron::ResolveProxyHelper>(
|
||||||
|
system_network_context_manager()->GetContext());
|
||||||
|
}
|
||||||
|
return resolve_proxy_helper_.get();
|
||||||
|
}
|
||||||
|
|
||||||
#if BUILDFLAG(IS_LINUX)
|
#if BUILDFLAG(IS_LINUX)
|
||||||
void BrowserProcessImpl::SetLinuxStorageBackend(
|
void BrowserProcessImpl::SetLinuxStorageBackend(
|
||||||
os_crypt::SelectedLinuxBackend selected_backend) {
|
os_crypt::SelectedLinuxBackend selected_backend) {
|
||||||
|
|
|
@ -31,6 +31,10 @@ namespace printing {
|
||||||
class PrintJobManager;
|
class PrintJobManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace electron {
|
||||||
|
class ResolveProxyHelper;
|
||||||
|
}
|
||||||
|
|
||||||
// Empty definition for std::unique_ptr, rather than a forward declaration
|
// Empty definition for std::unique_ptr, rather than a forward declaration
|
||||||
class BackgroundModeManager {};
|
class BackgroundModeManager {};
|
||||||
|
|
||||||
|
@ -53,9 +57,9 @@ class BrowserProcessImpl : public BrowserProcess {
|
||||||
void PreMainMessageLoopRun();
|
void PreMainMessageLoopRun();
|
||||||
void PostDestroyThreads() {}
|
void PostDestroyThreads() {}
|
||||||
void PostMainMessageLoopRun();
|
void PostMainMessageLoopRun();
|
||||||
|
|
||||||
void SetSystemLocale(const std::string& locale);
|
void SetSystemLocale(const std::string& locale);
|
||||||
const std::string& GetSystemLocale() const;
|
const std::string& GetSystemLocale() const;
|
||||||
|
electron::ResolveProxyHelper* GetResolveProxyHelper();
|
||||||
|
|
||||||
#if BUILDFLAG(IS_LINUX)
|
#if BUILDFLAG(IS_LINUX)
|
||||||
void SetLinuxStorageBackend(os_crypt::SelectedLinuxBackend selected_backend);
|
void SetLinuxStorageBackend(os_crypt::SelectedLinuxBackend selected_backend);
|
||||||
|
@ -123,6 +127,10 @@ class BrowserProcessImpl : public BrowserProcess {
|
||||||
printing::PrintJobManager* print_job_manager() override;
|
printing::PrintJobManager* print_job_manager() override;
|
||||||
StartupData* startup_data() override;
|
StartupData* startup_data() override;
|
||||||
|
|
||||||
|
ValueMapPrefStore* in_memory_pref_store() const {
|
||||||
|
return in_memory_pref_store_.get();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void CreateNetworkQualityObserver();
|
void CreateNetworkQualityObserver();
|
||||||
void CreateOSCryptAsync();
|
void CreateOSCryptAsync();
|
||||||
|
@ -139,6 +147,8 @@ class BrowserProcessImpl : public BrowserProcess {
|
||||||
#endif
|
#endif
|
||||||
embedder_support::OriginTrialsSettingsStorage origin_trials_settings_storage_;
|
embedder_support::OriginTrialsSettingsStorage origin_trials_settings_storage_;
|
||||||
|
|
||||||
|
scoped_refptr<ValueMapPrefStore> in_memory_pref_store_;
|
||||||
|
scoped_refptr<electron::ResolveProxyHelper> resolve_proxy_helper_;
|
||||||
std::unique_ptr<network::NetworkQualityTracker> network_quality_tracker_;
|
std::unique_ptr<network::NetworkQualityTracker> network_quality_tracker_;
|
||||||
std::unique_ptr<
|
std::unique_ptr<
|
||||||
network::NetworkQualityTracker::RTTAndThroughputEstimatesObserver>
|
network::NetworkQualityTracker::RTTAndThroughputEstimatesObserver>
|
||||||
|
|
|
@ -535,7 +535,8 @@ ElectronBrowserContext::GetReduceAcceptLanguageControllerDelegate() {
|
||||||
|
|
||||||
ResolveProxyHelper* ElectronBrowserContext::GetResolveProxyHelper() {
|
ResolveProxyHelper* ElectronBrowserContext::GetResolveProxyHelper() {
|
||||||
if (!resolve_proxy_helper_) {
|
if (!resolve_proxy_helper_) {
|
||||||
resolve_proxy_helper_ = base::MakeRefCounted<ResolveProxyHelper>(this);
|
resolve_proxy_helper_ = base::MakeRefCounted<ResolveProxyHelper>(
|
||||||
|
GetDefaultStoragePartition()->GetNetworkContext());
|
||||||
}
|
}
|
||||||
return resolve_proxy_helper_.get();
|
return resolve_proxy_helper_.get();
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,19 +8,17 @@
|
||||||
|
|
||||||
#include "base/functional/bind.h"
|
#include "base/functional/bind.h"
|
||||||
#include "content/public/browser/browser_thread.h"
|
#include "content/public/browser/browser_thread.h"
|
||||||
#include "content/public/browser/storage_partition.h"
|
|
||||||
#include "mojo/public/cpp/bindings/pending_remote.h"
|
#include "mojo/public/cpp/bindings/pending_remote.h"
|
||||||
#include "net/base/network_anonymization_key.h"
|
#include "net/base/network_anonymization_key.h"
|
||||||
#include "net/proxy_resolution/proxy_info.h"
|
#include "net/proxy_resolution/proxy_info.h"
|
||||||
#include "services/network/public/mojom/network_context.mojom.h"
|
|
||||||
#include "shell/browser/electron_browser_context.h"
|
|
||||||
|
|
||||||
using content::BrowserThread;
|
using content::BrowserThread;
|
||||||
|
|
||||||
namespace electron {
|
namespace electron {
|
||||||
|
|
||||||
ResolveProxyHelper::ResolveProxyHelper(ElectronBrowserContext* browser_context)
|
ResolveProxyHelper::ResolveProxyHelper(
|
||||||
: browser_context_(browser_context) {}
|
network::mojom::NetworkContext* network_context)
|
||||||
|
: network_context_(network_context) {}
|
||||||
|
|
||||||
ResolveProxyHelper::~ResolveProxyHelper() {
|
ResolveProxyHelper::~ResolveProxyHelper() {
|
||||||
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
||||||
|
@ -54,11 +52,9 @@ void ResolveProxyHelper::StartPendingRequest() {
|
||||||
receiver_.set_disconnect_handler(
|
receiver_.set_disconnect_handler(
|
||||||
base::BindOnce(&ResolveProxyHelper::OnProxyLookupComplete,
|
base::BindOnce(&ResolveProxyHelper::OnProxyLookupComplete,
|
||||||
base::Unretained(this), net::ERR_ABORTED, std::nullopt));
|
base::Unretained(this), net::ERR_ABORTED, std::nullopt));
|
||||||
browser_context_->GetDefaultStoragePartition()
|
network_context_->LookUpProxyForURL(pending_requests_.front().url,
|
||||||
->GetNetworkContext()
|
net::NetworkAnonymizationKey(),
|
||||||
->LookUpProxyForURL(pending_requests_.front().url,
|
std::move(proxy_lookup_client));
|
||||||
net::NetworkAnonymizationKey(),
|
|
||||||
std::move(proxy_lookup_client));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ResolveProxyHelper::OnProxyLookupComplete(
|
void ResolveProxyHelper::OnProxyLookupComplete(
|
||||||
|
|
|
@ -12,20 +12,19 @@
|
||||||
#include "base/memory/raw_ptr.h"
|
#include "base/memory/raw_ptr.h"
|
||||||
#include "base/memory/ref_counted.h"
|
#include "base/memory/ref_counted.h"
|
||||||
#include "mojo/public/cpp/bindings/receiver.h"
|
#include "mojo/public/cpp/bindings/receiver.h"
|
||||||
|
#include "services/network/public/mojom/network_context.mojom.h"
|
||||||
#include "services/network/public/mojom/proxy_lookup_client.mojom.h"
|
#include "services/network/public/mojom/proxy_lookup_client.mojom.h"
|
||||||
#include "url/gurl.h"
|
#include "url/gurl.h"
|
||||||
|
|
||||||
namespace electron {
|
namespace electron {
|
||||||
|
|
||||||
class ElectronBrowserContext;
|
|
||||||
|
|
||||||
class ResolveProxyHelper
|
class ResolveProxyHelper
|
||||||
: public base::RefCountedThreadSafe<ResolveProxyHelper>,
|
: public base::RefCountedThreadSafe<ResolveProxyHelper>,
|
||||||
network::mojom::ProxyLookupClient {
|
network::mojom::ProxyLookupClient {
|
||||||
public:
|
public:
|
||||||
using ResolveProxyCallback = base::OnceCallback<void(std::string)>;
|
using ResolveProxyCallback = base::OnceCallback<void(std::string)>;
|
||||||
|
|
||||||
explicit ResolveProxyHelper(ElectronBrowserContext* browser_context);
|
explicit ResolveProxyHelper(network::mojom::NetworkContext* network_context);
|
||||||
|
|
||||||
void ResolveProxy(const GURL& url, ResolveProxyCallback callback);
|
void ResolveProxy(const GURL& url, ResolveProxyCallback callback);
|
||||||
|
|
||||||
|
@ -71,7 +70,7 @@ class ResolveProxyHelper
|
||||||
mojo::Receiver<network::mojom::ProxyLookupClient> receiver_{this};
|
mojo::Receiver<network::mojom::ProxyLookupClient> receiver_{this};
|
||||||
|
|
||||||
// Weak Ref
|
// Weak Ref
|
||||||
raw_ptr<ElectronBrowserContext> browser_context_;
|
raw_ptr<network::mojom::NetworkContext> network_context_ = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace electron
|
} // namespace electron
|
||||||
|
|
|
@ -6,9 +6,10 @@ import * as net from 'node:net';
|
||||||
import * as fs from 'fs-extra';
|
import * as fs from 'fs-extra';
|
||||||
import * as path from 'node:path';
|
import * as path from 'node:path';
|
||||||
import { promisify } from 'node:util';
|
import { promisify } from 'node:util';
|
||||||
import { app, BrowserWindow, Menu, session, net as electronNet, WebContents } from 'electron/main';
|
import { app, BrowserWindow, Menu, session, net as electronNet, WebContents, utilityProcess } from 'electron/main';
|
||||||
import { closeWindow, closeAllWindows } from './lib/window-helpers';
|
import { closeWindow, closeAllWindows } from './lib/window-helpers';
|
||||||
import { ifdescribe, ifit, listen, waitUntil } from './lib/spec-helpers';
|
import { ifdescribe, ifit, listen, waitUntil } from './lib/spec-helpers';
|
||||||
|
import { collectStreamBody, getResponse } from './lib/net-helpers';
|
||||||
import { once } from 'node:events';
|
import { once } from 'node:events';
|
||||||
import split = require('split')
|
import split = require('split')
|
||||||
import * as semver from 'semver';
|
import * as semver from 'semver';
|
||||||
|
@ -1895,6 +1896,154 @@ describe('app module', () => {
|
||||||
app.showAboutPanel();
|
app.showAboutPanel();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('app.setProxy(options)', () => {
|
||||||
|
let server: http.Server;
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
if (server) {
|
||||||
|
server.close();
|
||||||
|
}
|
||||||
|
await app.setProxy({ mode: 'direct' as const });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('allows configuring proxy settings', async () => {
|
||||||
|
const config = { proxyRules: 'http=myproxy:80' };
|
||||||
|
await app.setProxy(config);
|
||||||
|
const proxy = await app.resolveProxy('http://example.com/');
|
||||||
|
expect(proxy).to.equal('PROXY myproxy:80');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('allows removing the implicit bypass rules for localhost', async () => {
|
||||||
|
const config = {
|
||||||
|
proxyRules: 'http=myproxy:80',
|
||||||
|
proxyBypassRules: '<-loopback>'
|
||||||
|
};
|
||||||
|
|
||||||
|
await app.setProxy(config);
|
||||||
|
const proxy = await app.resolveProxy('http://localhost');
|
||||||
|
expect(proxy).to.equal('PROXY myproxy:80');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('allows configuring proxy settings with pacScript', async () => {
|
||||||
|
server = http.createServer((req, res) => {
|
||||||
|
const pac = `
|
||||||
|
function FindProxyForURL(url, host) {
|
||||||
|
return "PROXY myproxy:8132";
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
res.writeHead(200, {
|
||||||
|
'Content-Type': 'application/x-ns-proxy-autoconfig'
|
||||||
|
});
|
||||||
|
res.end(pac);
|
||||||
|
});
|
||||||
|
const { url } = await listen(server);
|
||||||
|
{
|
||||||
|
const config = { pacScript: url };
|
||||||
|
await app.setProxy(config);
|
||||||
|
const proxy = await app.resolveProxy('https://google.com');
|
||||||
|
expect(proxy).to.equal('PROXY myproxy:8132');
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const config = { mode: 'pac_script' as any, pacScript: url };
|
||||||
|
await app.setProxy(config);
|
||||||
|
const proxy = await app.resolveProxy('https://google.com');
|
||||||
|
expect(proxy).to.equal('PROXY myproxy:8132');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('allows bypassing proxy settings', async () => {
|
||||||
|
const config = {
|
||||||
|
proxyRules: 'http=myproxy:80',
|
||||||
|
proxyBypassRules: '<local>'
|
||||||
|
};
|
||||||
|
await app.setProxy(config);
|
||||||
|
const proxy = await app.resolveProxy('http://example/');
|
||||||
|
expect(proxy).to.equal('DIRECT');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('allows configuring proxy settings with mode `direct`', async () => {
|
||||||
|
const config = { mode: 'direct' as const, proxyRules: 'http=myproxy:80' };
|
||||||
|
await app.setProxy(config);
|
||||||
|
const proxy = await app.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 const };
|
||||||
|
await app.setProxy(config);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('allows configuring proxy settings with mode `pac_script`', async () => {
|
||||||
|
const config = { mode: 'pac_script' as const };
|
||||||
|
await app.setProxy(config);
|
||||||
|
const proxy = await app.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 const, proxyRules: 'http=myproxy:80' };
|
||||||
|
await app.setProxy(config);
|
||||||
|
const proxy = await app.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 const };
|
||||||
|
await app.setProxy(config);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('disallows configuring proxy settings with mode `invalid`', async () => {
|
||||||
|
const config = { mode: 'invalid' as any };
|
||||||
|
await expect(app.setProxy(config)).to.eventually.be.rejectedWith(/Invalid mode/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('impacts proxy for requests made from utility process', async () => {
|
||||||
|
const utilityFixturePath = path.resolve(__dirname, 'fixtures', 'api', 'utility-process', 'api-net-spec.js');
|
||||||
|
const fn = async () => {
|
||||||
|
const urlRequest = electronNet.request('http://example.com/');
|
||||||
|
const response = await getResponse(urlRequest);
|
||||||
|
expect(response.statusCode).to.equal(200);
|
||||||
|
const message = await collectStreamBody(response);
|
||||||
|
expect(message).to.equal('ok from proxy\n');
|
||||||
|
};
|
||||||
|
server = http.createServer((req, res) => {
|
||||||
|
res.writeHead(200);
|
||||||
|
res.end('ok from proxy\n');
|
||||||
|
});
|
||||||
|
const { port, hostname } = await listen(server);
|
||||||
|
const config = { mode: 'fixed_servers' as const, proxyRules: `http=${hostname}:${port}` };
|
||||||
|
await app.setProxy(config);
|
||||||
|
const proxy = await app.resolveProxy('http://example.com/');
|
||||||
|
expect(proxy).to.equal(`PROXY ${hostname}:${port}`);
|
||||||
|
const child = utilityProcess.fork(utilityFixturePath, [], {
|
||||||
|
execArgv: ['--expose-gc']
|
||||||
|
});
|
||||||
|
child.postMessage({ fn: `(${fn})()` });
|
||||||
|
const [data] = await once(child, 'message');
|
||||||
|
expect(data.ok).to.be.true(data.message);
|
||||||
|
// Cleanup.
|
||||||
|
const [code] = await once(child, 'exit');
|
||||||
|
expect(code).to.equal(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not impact proxy for requests made from main process', async () => {
|
||||||
|
server = http.createServer((req, res) => {
|
||||||
|
res.writeHead(200);
|
||||||
|
res.end('ok from server\n');
|
||||||
|
});
|
||||||
|
const { url } = await listen(server);
|
||||||
|
const config = { mode: 'fixed_servers' as const, proxyRules: 'http=myproxy:80' };
|
||||||
|
await app.setProxy(config);
|
||||||
|
const proxy = await app.resolveProxy('http://example.com/');
|
||||||
|
expect(proxy).to.equal('PROXY myproxy:80');
|
||||||
|
const urlRequest = electronNet.request(url);
|
||||||
|
const response = await getResponse(urlRequest);
|
||||||
|
expect(response.statusCode).to.equal(200);
|
||||||
|
const message = await collectStreamBody(response);
|
||||||
|
expect(message).to.equal('ok from server\n');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('default behavior', () => {
|
describe('default behavior', () => {
|
||||||
|
|
|
@ -200,5 +200,5 @@ export async function listen (server: http.Server | https.Server | http2.Http2Se
|
||||||
await new Promise<void>(resolve => server.listen(0, hostname, () => resolve()));
|
await new Promise<void>(resolve => server.listen(0, hostname, () => resolve()));
|
||||||
const { port } = server.address() as net.AddressInfo;
|
const { port } = server.address() as net.AddressInfo;
|
||||||
const protocol = (server instanceof http.Server) ? 'http' : 'https';
|
const protocol = (server instanceof http.Server) ? 'http' : 'https';
|
||||||
return { port, url: url.format({ protocol, hostname, port }) };
|
return { port, hostname, url: url.format({ protocol, hostname, port }) };
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue