зеркало из https://github.com/electron/electron.git
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:
Родитель
136762b45f
Коммит
26131b23b8
|
@ -1468,6 +1468,24 @@ details.
|
|||
|
||||
**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
|
||||
|
||||
### `app.accessibilitySupportEnabled` _macOS_ _Windows_
|
||||
|
|
|
@ -589,105 +589,15 @@ 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
|
||||
bypass the proxy settings.
|
||||
* `config` [ProxyConfig](structures/proxy-config.md)
|
||||
|
||||
Returns `Promise<void>` - Resolves when the proxy setting process is complete.
|
||||
|
||||
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
|
||||
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])`
|
||||
|
||||
* `host` string - Hostname to resolve.
|
||||
|
|
|
@ -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-response-upload-data.md",
|
||||
"docs/api/structures/protocol-response.md",
|
||||
"docs/api/structures/proxy-config.md",
|
||||
"docs/api/structures/rectangle.md",
|
||||
"docs/api/structures/referrer.md",
|
||||
"docs/api/structures/render-process-gone-details.md",
|
||||
|
|
|
@ -26,6 +26,9 @@
|
|||
#include "chrome/browser/icon_manager.h"
|
||||
#include "chrome/common/chrome_features.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/gpu_data_manager_impl.h" // nogncheck
|
||||
#include "content/public/browser/browser_accessibility_state.h"
|
||||
|
@ -1472,6 +1475,98 @@ void App::EnableSandbox(gin_helper::ErrorThrower thrower) {
|
|||
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) {
|
||||
ElectronBrowserClient::Get()->SetUserAgent(user_agent);
|
||||
}
|
||||
|
@ -1776,7 +1871,9 @@ gin::ObjectTemplateBuilder App::GetObjectTemplateBuilder(v8::Isolate* isolate) {
|
|||
.SetProperty("userAgentFallback", &App::GetUserAgentFallback,
|
||||
&App::SetUserAgentFallback)
|
||||
.SetMethod("configureHostResolver", &ConfigureHostResolver)
|
||||
.SetMethod("enableSandbox", &App::EnableSandbox);
|
||||
.SetMethod("enableSandbox", &App::EnableSandbox)
|
||||
.SetMethod("setProxy", &App::SetProxy)
|
||||
.SetMethod("resolveProxy", &App::ResolveProxy);
|
||||
}
|
||||
|
||||
const char* App::GetTypeName() {
|
||||
|
|
|
@ -222,6 +222,8 @@ class App : public ElectronBrowserClient::Delegate,
|
|||
void EnableSandbox(gin_helper::ErrorThrower thrower);
|
||||
void SetUserAgentFallback(const std::string& user_agent);
|
||||
std::string GetUserAgentFallback();
|
||||
v8::Local<v8::Promise> SetProxy(gin::Arguments* args);
|
||||
v8::Local<v8::Promise> ResolveProxy(gin::Arguments* args);
|
||||
|
||||
#if BUILDFLAG(IS_MAC)
|
||||
void SetActivationPolicy(gin_helper::ErrorThrower thrower,
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include "net/proxy_resolution/proxy_config_with_annotation.h"
|
||||
#include "services/device/public/cpp/geolocation/geolocation_manager.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/thread_restrictions.h"
|
||||
|
||||
|
@ -100,9 +101,9 @@ void BrowserProcessImpl::PostEarlyInitialization() {
|
|||
OSCrypt::RegisterLocalPrefs(pref_registry.get());
|
||||
#endif
|
||||
|
||||
auto pref_store = base::MakeRefCounted<ValueMapPrefStore>();
|
||||
ApplyProxyModeFromCommandLine(pref_store.get());
|
||||
prefs_factory.set_command_line_prefs(std::move(pref_store));
|
||||
in_memory_pref_store_ = base::MakeRefCounted<ValueMapPrefStore>();
|
||||
ApplyProxyModeFromCommandLine(in_memory_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
|
||||
// is the only key that needs it
|
||||
|
@ -316,6 +317,14 @@ const std::string& BrowserProcessImpl::GetSystemLocale() const {
|
|||
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)
|
||||
void BrowserProcessImpl::SetLinuxStorageBackend(
|
||||
os_crypt::SelectedLinuxBackend selected_backend) {
|
||||
|
|
|
@ -31,6 +31,10 @@ namespace printing {
|
|||
class PrintJobManager;
|
||||
}
|
||||
|
||||
namespace electron {
|
||||
class ResolveProxyHelper;
|
||||
}
|
||||
|
||||
// Empty definition for std::unique_ptr, rather than a forward declaration
|
||||
class BackgroundModeManager {};
|
||||
|
||||
|
@ -53,9 +57,9 @@ class BrowserProcessImpl : public BrowserProcess {
|
|||
void PreMainMessageLoopRun();
|
||||
void PostDestroyThreads() {}
|
||||
void PostMainMessageLoopRun();
|
||||
|
||||
void SetSystemLocale(const std::string& locale);
|
||||
const std::string& GetSystemLocale() const;
|
||||
electron::ResolveProxyHelper* GetResolveProxyHelper();
|
||||
|
||||
#if BUILDFLAG(IS_LINUX)
|
||||
void SetLinuxStorageBackend(os_crypt::SelectedLinuxBackend selected_backend);
|
||||
|
@ -123,6 +127,10 @@ class BrowserProcessImpl : public BrowserProcess {
|
|||
printing::PrintJobManager* print_job_manager() override;
|
||||
StartupData* startup_data() override;
|
||||
|
||||
ValueMapPrefStore* in_memory_pref_store() const {
|
||||
return in_memory_pref_store_.get();
|
||||
}
|
||||
|
||||
private:
|
||||
void CreateNetworkQualityObserver();
|
||||
void CreateOSCryptAsync();
|
||||
|
@ -139,6 +147,8 @@ class BrowserProcessImpl : public BrowserProcess {
|
|||
#endif
|
||||
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::RTTAndThroughputEstimatesObserver>
|
||||
|
|
|
@ -535,7 +535,8 @@ ElectronBrowserContext::GetReduceAcceptLanguageControllerDelegate() {
|
|||
|
||||
ResolveProxyHelper* ElectronBrowserContext::GetResolveProxyHelper() {
|
||||
if (!resolve_proxy_helper_) {
|
||||
resolve_proxy_helper_ = base::MakeRefCounted<ResolveProxyHelper>(this);
|
||||
resolve_proxy_helper_ = base::MakeRefCounted<ResolveProxyHelper>(
|
||||
GetDefaultStoragePartition()->GetNetworkContext());
|
||||
}
|
||||
return resolve_proxy_helper_.get();
|
||||
}
|
||||
|
|
|
@ -8,19 +8,17 @@
|
|||
|
||||
#include "base/functional/bind.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "content/public/browser/storage_partition.h"
|
||||
#include "mojo/public/cpp/bindings/pending_remote.h"
|
||||
#include "net/base/network_anonymization_key.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;
|
||||
|
||||
namespace electron {
|
||||
|
||||
ResolveProxyHelper::ResolveProxyHelper(ElectronBrowserContext* browser_context)
|
||||
: browser_context_(browser_context) {}
|
||||
ResolveProxyHelper::ResolveProxyHelper(
|
||||
network::mojom::NetworkContext* network_context)
|
||||
: network_context_(network_context) {}
|
||||
|
||||
ResolveProxyHelper::~ResolveProxyHelper() {
|
||||
DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
||||
|
@ -54,9 +52,7 @@ void ResolveProxyHelper::StartPendingRequest() {
|
|||
receiver_.set_disconnect_handler(
|
||||
base::BindOnce(&ResolveProxyHelper::OnProxyLookupComplete,
|
||||
base::Unretained(this), net::ERR_ABORTED, std::nullopt));
|
||||
browser_context_->GetDefaultStoragePartition()
|
||||
->GetNetworkContext()
|
||||
->LookUpProxyForURL(pending_requests_.front().url,
|
||||
network_context_->LookUpProxyForURL(pending_requests_.front().url,
|
||||
net::NetworkAnonymizationKey(),
|
||||
std::move(proxy_lookup_client));
|
||||
}
|
||||
|
|
|
@ -12,20 +12,19 @@
|
|||
#include "base/memory/raw_ptr.h"
|
||||
#include "base/memory/ref_counted.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 "url/gurl.h"
|
||||
|
||||
namespace electron {
|
||||
|
||||
class ElectronBrowserContext;
|
||||
|
||||
class ResolveProxyHelper
|
||||
: public base::RefCountedThreadSafe<ResolveProxyHelper>,
|
||||
network::mojom::ProxyLookupClient {
|
||||
public:
|
||||
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);
|
||||
|
||||
|
@ -71,7 +70,7 @@ class ResolveProxyHelper
|
|||
mojo::Receiver<network::mojom::ProxyLookupClient> receiver_{this};
|
||||
|
||||
// Weak Ref
|
||||
raw_ptr<ElectronBrowserContext> browser_context_;
|
||||
raw_ptr<network::mojom::NetworkContext> network_context_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace electron
|
||||
|
|
|
@ -6,9 +6,10 @@ import * as net from 'node:net';
|
|||
import * as fs from 'fs-extra';
|
||||
import * as path from 'node:path';
|
||||
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 { ifdescribe, ifit, listen, waitUntil } from './lib/spec-helpers';
|
||||
import { collectStreamBody, getResponse } from './lib/net-helpers';
|
||||
import { once } from 'node:events';
|
||||
import split = require('split')
|
||||
import * as semver from 'semver';
|
||||
|
@ -1895,6 +1896,154 @@ describe('app module', () => {
|
|||
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', () => {
|
||||
|
|
|
@ -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()));
|
||||
const { port } = server.address() as net.AddressInfo;
|
||||
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 }) };
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче