feat(proxy): allow specifying proxy (#2485)
This commit is contained in:
parent
71dd9c2f02
commit
fb058ffe0d
|
|
@ -6,11 +6,11 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "firefox",
|
"name": "firefox",
|
||||||
"revision": "1101"
|
"revision": "1103"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "webkit",
|
"name": "webkit",
|
||||||
"revision": "1263"
|
"revision": "1269"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
15
docs/api.md
15
docs/api.md
|
|
@ -3997,6 +3997,11 @@ This methods attaches Playwright to an existing browser instance.
|
||||||
- `executablePath` <[string]> Path to a browser executable to run instead of the bundled one. If `executablePath` is a relative path, then it is resolved relative to [current working directory](https://nodejs.org/api/process.html#process_process_cwd). Note that Playwright only works with the bundled Chromium, Firefox or WebKit, use at your own risk.
|
- `executablePath` <[string]> Path to a browser executable to run instead of the bundled one. If `executablePath` is a relative path, then it is resolved relative to [current working directory](https://nodejs.org/api/process.html#process_process_cwd). Note that Playwright only works with the bundled Chromium, Firefox or WebKit, use at your own risk.
|
||||||
- `args` <[Array]<[string]>> Additional arguments to pass to the browser instance. The list of Chromium flags can be found [here](http://peter.sh/experiments/chromium-command-line-switches/).
|
- `args` <[Array]<[string]>> Additional arguments to pass to the browser instance. The list of Chromium flags can be found [here](http://peter.sh/experiments/chromium-command-line-switches/).
|
||||||
- `ignoreDefaultArgs` <[boolean]|[Array]<[string]>> If `true`, Playwright does not pass its own configurations args and only uses the ones from `args`. If an array is given, then filters out the given default arguments. Dangerous option; use with care. Defaults to `false`.
|
- `ignoreDefaultArgs` <[boolean]|[Array]<[string]>> If `true`, Playwright does not pass its own configurations args and only uses the ones from `args`. If an array is given, then filters out the given default arguments. Dangerous option; use with care. Defaults to `false`.
|
||||||
|
- `proxy` <[Object]> Network proxy settings.
|
||||||
|
- `server` <[string]> Proxy to be used for all requests. HTTP and SOCKS proxies are supported, for example `http://myproxy.com:3128` or `socks5://myproxy.com:3128`. Short form `myproxy.com:3128` is considered an HTTP proxy.
|
||||||
|
- `bypass` <[string]> Optional coma-separated domains to bypass proxy, for example `".com, chromium.org, .domain.com"`.
|
||||||
|
- `username` <[string]> Optional username to use if HTTP proxy requires authentication.
|
||||||
|
- `password` <[string]> Optional password to use if HTTP proxy requires authentication.
|
||||||
- `firefoxUserPrefs` <[Object]> Firefox user preferences. Learn more about the Firefox user preferences at [`about:config`](https://support.mozilla.org/en-US/kb/about-config-editor-firefox).
|
- `firefoxUserPrefs` <[Object]> Firefox user preferences. Learn more about the Firefox user preferences at [`about:config`](https://support.mozilla.org/en-US/kb/about-config-editor-firefox).
|
||||||
- `handleSIGINT` <[boolean]> Close the browser process on Ctrl-C. Defaults to `true`.
|
- `handleSIGINT` <[boolean]> Close the browser process on Ctrl-C. Defaults to `true`.
|
||||||
- `handleSIGTERM` <[boolean]> Close the browser process on SIGTERM. Defaults to `true`.
|
- `handleSIGTERM` <[boolean]> Close the browser process on SIGTERM. Defaults to `true`.
|
||||||
|
|
@ -4031,6 +4036,11 @@ const browser = await chromium.launch({ // Or 'firefox' or 'webkit'.
|
||||||
- `executablePath` <[string]> Path to a browser executable to run instead of the bundled one. If `executablePath` is a relative path, then it is resolved relative to [current working directory](https://nodejs.org/api/process.html#process_process_cwd). **BEWARE**: Playwright is only guaranteed to work with the bundled Chromium, Firefox or WebKit, use at your own risk.
|
- `executablePath` <[string]> Path to a browser executable to run instead of the bundled one. If `executablePath` is a relative path, then it is resolved relative to [current working directory](https://nodejs.org/api/process.html#process_process_cwd). **BEWARE**: Playwright is only guaranteed to work with the bundled Chromium, Firefox or WebKit, use at your own risk.
|
||||||
- `args` <[Array]<[string]>> Additional arguments to pass to the browser instance. The list of Chromium flags can be found [here](http://peter.sh/experiments/chromium-command-line-switches/).
|
- `args` <[Array]<[string]>> Additional arguments to pass to the browser instance. The list of Chromium flags can be found [here](http://peter.sh/experiments/chromium-command-line-switches/).
|
||||||
- `ignoreDefaultArgs` <[boolean]|[Array]<[string]>> If `true`, then do not use any of the default arguments. If an array is given, then filter out the given default arguments. Dangerous option; use with care. Defaults to `false`.
|
- `ignoreDefaultArgs` <[boolean]|[Array]<[string]>> If `true`, then do not use any of the default arguments. If an array is given, then filter out the given default arguments. Dangerous option; use with care. Defaults to `false`.
|
||||||
|
- `proxy` <[Object]> Network proxy settings.
|
||||||
|
- `server` <[string]> Proxy to be used for all requests. HTTP and SOCKS proxies are supported, for example `http://myproxy.com:3128` or `socks5://myproxy.com:3128`. Short form `myproxy.com:3128` is considered an HTTP proxy.
|
||||||
|
- `bypass` <[string]> Optional coma-separated domains to bypass proxy, for example `".com, chromium.org, .domain.com"`.
|
||||||
|
- `username` <[string]> Optional username to use if HTTP proxy requires authentication.
|
||||||
|
- `password` <[string]> Optional password to use if HTTP proxy requires authentication.
|
||||||
- `handleSIGINT` <[boolean]> Close the browser process on Ctrl-C. Defaults to `true`.
|
- `handleSIGINT` <[boolean]> Close the browser process on Ctrl-C. Defaults to `true`.
|
||||||
- `handleSIGTERM` <[boolean]> Close the browser process on SIGTERM. Defaults to `true`.
|
- `handleSIGTERM` <[boolean]> Close the browser process on SIGTERM. Defaults to `true`.
|
||||||
- `handleSIGHUP` <[boolean]> Close the browser process on SIGHUP. Defaults to `true`.
|
- `handleSIGHUP` <[boolean]> Close the browser process on SIGHUP. Defaults to `true`.
|
||||||
|
|
@ -4073,6 +4083,11 @@ Launches browser that uses persistent storage located at `userDataDir` and retur
|
||||||
- `executablePath` <[string]> Path to a browser executable to run instead of the bundled one. If `executablePath` is a relative path, then it is resolved relative to [current working directory](https://nodejs.org/api/process.html#process_process_cwd). **BEWARE**: Playwright is only guaranteed to work with the bundled Chromium, Firefox or WebKit, use at your own risk.
|
- `executablePath` <[string]> Path to a browser executable to run instead of the bundled one. If `executablePath` is a relative path, then it is resolved relative to [current working directory](https://nodejs.org/api/process.html#process_process_cwd). **BEWARE**: Playwright is only guaranteed to work with the bundled Chromium, Firefox or WebKit, use at your own risk.
|
||||||
- `args` <[Array]<[string]>> Additional arguments to pass to the browser instance. The list of Chromium flags can be found [here](http://peter.sh/experiments/chromium-command-line-switches/).
|
- `args` <[Array]<[string]>> Additional arguments to pass to the browser instance. The list of Chromium flags can be found [here](http://peter.sh/experiments/chromium-command-line-switches/).
|
||||||
- `ignoreDefaultArgs` <[boolean]|[Array]<[string]>> If `true`, then do not use any of the default arguments. If an array is given, then filter out the given default arguments. Dangerous option; use with care. Defaults to `false`.
|
- `ignoreDefaultArgs` <[boolean]|[Array]<[string]>> If `true`, then do not use any of the default arguments. If an array is given, then filter out the given default arguments. Dangerous option; use with care. Defaults to `false`.
|
||||||
|
- `proxy` <[Object]> Network proxy settings.
|
||||||
|
- `server` <[string]> Proxy to be used for all requests. HTTP and SOCKS proxies are supported, for example `http://myproxy.com:3128` or `socks5://myproxy.com:3128`. Short form `myproxy.com:3128` is considered an HTTP proxy.
|
||||||
|
- `bypass` <[string]> Optional coma-separated domains to bypass proxy, for example `".com, chromium.org, .domain.com"`.
|
||||||
|
- `username` <[string]> Optional username to use if HTTP proxy requires authentication.
|
||||||
|
- `password` <[string]> Optional password to use if HTTP proxy requires authentication.
|
||||||
- `firefoxUserPrefs` <[Object]> Firefox user preferences. Learn more about the Firefox user preferences at [`about:config`](https://support.mozilla.org/en-US/kb/about-config-editor-firefox).
|
- `firefoxUserPrefs` <[Object]> Firefox user preferences. Learn more about the Firefox user preferences at [`about:config`](https://support.mozilla.org/en-US/kb/about-config-editor-firefox).
|
||||||
- `handleSIGINT` <[boolean]> Close the browser process on Ctrl-C. Defaults to `true`.
|
- `handleSIGINT` <[boolean]> Close the browser process on Ctrl-C. Defaults to `true`.
|
||||||
- `handleSIGTERM` <[boolean]> Close the browser process on SIGTERM. Defaults to `true`.
|
- `handleSIGTERM` <[boolean]> Close the browser process on SIGTERM. Defaults to `true`.
|
||||||
|
|
|
||||||
115
package-lock.json
generated
115
package-lock.json
generated
|
|
@ -725,6 +725,12 @@
|
||||||
"integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==",
|
"integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"async": {
|
||||||
|
"version": "0.2.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz",
|
||||||
|
"integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"async-each": {
|
"async-each": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz",
|
||||||
|
|
@ -1261,6 +1267,15 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"cli": {
|
||||||
|
"version": "0.4.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/cli/-/cli-0.4.5.tgz",
|
||||||
|
"integrity": "sha1-ePlIXNFhtWbppsctcXDEJw6B22E=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"glob": ">= 3.1.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"cli-cursor": {
|
"cli-cursor": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
|
||||||
|
|
@ -1276,6 +1291,25 @@
|
||||||
"integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==",
|
"integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"cliff": {
|
||||||
|
"version": "0.1.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/cliff/-/cliff-0.1.10.tgz",
|
||||||
|
"integrity": "sha1-U74z6p9ZvshWCe4wCsQgdgPlIBM=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"colors": "~1.0.3",
|
||||||
|
"eyes": "~0.1.8",
|
||||||
|
"winston": "0.8.x"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"colors": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz",
|
||||||
|
"integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"cliui": {
|
"cliui": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
|
||||||
|
|
@ -1560,6 +1594,12 @@
|
||||||
"randomfill": "^1.0.3"
|
"randomfill": "^1.0.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"cycle": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz",
|
||||||
|
"integrity": "sha1-IegLK+hYD5i0aPN5QwZisEbDStI=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"cyclist": {
|
"cyclist": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz",
|
||||||
|
|
@ -2238,6 +2278,12 @@
|
||||||
"yauzl": "^2.10.0"
|
"yauzl": "^2.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"eyes": {
|
||||||
|
"version": "0.1.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz",
|
||||||
|
"integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"fast-deep-equal": {
|
"fast-deep-equal": {
|
||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz",
|
||||||
|
|
@ -3199,6 +3245,12 @@
|
||||||
"integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
|
"integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"isstream": {
|
||||||
|
"version": "0.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
|
||||||
|
"integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"jpeg-js": {
|
"jpeg-js": {
|
||||||
"version": "0.3.7",
|
"version": "0.3.7",
|
||||||
"resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.3.7.tgz",
|
"resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.3.7.tgz",
|
||||||
|
|
@ -4042,6 +4094,12 @@
|
||||||
"find-up": "^3.0.0"
|
"find-up": "^3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"pkginfo": {
|
||||||
|
"version": "0.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.1.tgz",
|
||||||
|
"integrity": "sha1-Wyn2qB9wcXFC4J52W76rl7T4HiE=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"pngjs": {
|
"pngjs": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz",
|
||||||
|
|
@ -4800,6 +4858,34 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"socksv5": {
|
||||||
|
"version": "0.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/socksv5/-/socksv5-0.0.6.tgz",
|
||||||
|
"integrity": "sha1-EycjX/fo3iGsQ0oKV53GnD8HEGE=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ipv6": "*"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"ipv6": {
|
||||||
|
"version": "3.1.1",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"cli": "0.4.x",
|
||||||
|
"cliff": "0.1.x",
|
||||||
|
"sprintf": "0.1.x"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"sprintf": {
|
||||||
|
"version": "0.1.3",
|
||||||
|
"bundled": true,
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"source-list-map": {
|
"source-list-map": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz",
|
||||||
|
|
@ -4867,6 +4953,12 @@
|
||||||
"figgy-pudding": "^3.5.1"
|
"figgy-pudding": "^3.5.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"stack-trace": {
|
||||||
|
"version": "0.0.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
|
||||||
|
"integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"static-extend": {
|
"static-extend": {
|
||||||
"version": "0.1.2",
|
"version": "0.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
|
||||||
|
|
@ -5725,6 +5817,29 @@
|
||||||
"integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
|
"integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"winston": {
|
||||||
|
"version": "0.8.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/winston/-/winston-0.8.3.tgz",
|
||||||
|
"integrity": "sha1-ZLar9M0Brcrv1QCTk7HY6L7BnbA=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"async": "0.2.x",
|
||||||
|
"colors": "0.6.x",
|
||||||
|
"cycle": "1.0.x",
|
||||||
|
"eyes": "0.1.x",
|
||||||
|
"isstream": "0.1.x",
|
||||||
|
"pkginfo": "0.3.x",
|
||||||
|
"stack-trace": "0.0.x"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"colors": {
|
||||||
|
"version": "0.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz",
|
||||||
|
"integrity": "sha1-JCP+ZnisDF2uiFLl0OW+CMmXq8w=",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"word-wrap": {
|
"word-wrap": {
|
||||||
"version": "1.2.3",
|
"version": "1.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,7 @@
|
||||||
"ncp": "^2.0.0",
|
"ncp": "^2.0.0",
|
||||||
"node-stream-zip": "^1.8.2",
|
"node-stream-zip": "^1.8.2",
|
||||||
"pixelmatch": "^4.0.2",
|
"pixelmatch": "^4.0.2",
|
||||||
|
"socksv5": "0.0.6",
|
||||||
"text-diff": "^1.0.1",
|
"text-diff": "^1.0.1",
|
||||||
"ts-loader": "^6.1.2",
|
"ts-loader": "^6.1.2",
|
||||||
"typescript": "^3.8.3",
|
"typescript": "^3.8.3",
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import { Download } from './download';
|
||||||
import type { BrowserServer } from './server/browserServer';
|
import type { BrowserServer } from './server/browserServer';
|
||||||
import { Events } from './events';
|
import { Events } from './events';
|
||||||
import { InnerLogger, Log } from './logger';
|
import { InnerLogger, Log } from './logger';
|
||||||
|
import { ProxySettings } from './types';
|
||||||
|
|
||||||
export type BrowserOptions = {
|
export type BrowserOptions = {
|
||||||
logger: InnerLogger,
|
logger: InnerLogger,
|
||||||
|
|
@ -29,6 +30,7 @@ export type BrowserOptions = {
|
||||||
persistent?: PersistentContextOptions, // Undefined means no persistent context.
|
persistent?: PersistentContextOptions, // Undefined means no persistent context.
|
||||||
slowMo?: number,
|
slowMo?: number,
|
||||||
ownedServer?: BrowserServer,
|
ownedServer?: BrowserServer,
|
||||||
|
proxy?: ProxySettings,
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface Browser extends EventEmitter {
|
export interface Browser extends EventEmitter {
|
||||||
|
|
|
||||||
|
|
@ -207,6 +207,26 @@ export abstract class BrowserContextBase extends ExtendedEventEmitter implements
|
||||||
await oldPage.close();
|
await oldPage.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected _authenticateProxyViaHeader() {
|
||||||
|
const proxy = this._browserBase._options.proxy || { username: undefined, password: undefined };
|
||||||
|
const { username, password } = proxy;
|
||||||
|
if (username) {
|
||||||
|
this._options.httpCredentials = { username, password: password! };
|
||||||
|
this._options.extraHTTPHeaders = this._options.extraHTTPHeaders || {};
|
||||||
|
const token = Buffer.from(`${username}:${password}`).toString('base64');
|
||||||
|
this._options.extraHTTPHeaders['Proxy-Authorization'] = `Basic ${token}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected _authenticateProxyViaCredentials() {
|
||||||
|
const proxy = this._browserBase._options.proxy;
|
||||||
|
if (!proxy)
|
||||||
|
return;
|
||||||
|
const { username, password } = proxy;
|
||||||
|
if (username && password)
|
||||||
|
this._options.httpCredentials = { username, password };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function assertBrowserContextIsNotOwned(context: BrowserContextBase) {
|
export function assertBrowserContextIsNotOwned(context: BrowserContextBase) {
|
||||||
|
|
@ -272,3 +292,17 @@ export function verifyGeolocation(geolocation: types.Geolocation): types.Geoloca
|
||||||
throw new Error(`Invalid accuracy "${accuracy}": precondition 0 <= ACCURACY failed.`);
|
throw new Error(`Invalid accuracy "${accuracy}": precondition 0 <= ACCURACY failed.`);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function verifyProxySettings(proxy: types.ProxySettings): types.ProxySettings {
|
||||||
|
let { server, bypass } = proxy;
|
||||||
|
if (!helper.isString(server))
|
||||||
|
throw new Error(`Invalid proxy.server: ` + server);
|
||||||
|
let url = new URL(server);
|
||||||
|
if (!['http:', 'https:', 'socks5:'].includes(url.protocol)) {
|
||||||
|
url = new URL('http://' + server);
|
||||||
|
server = `${url.protocol}//${url.host}`;
|
||||||
|
}
|
||||||
|
if (bypass)
|
||||||
|
bypass = bypass.split(',').map(t => t.trim()).join(',');
|
||||||
|
return { ...proxy, server, bypass };
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -286,6 +286,7 @@ export class CRBrowserContext extends BrowserContextBase {
|
||||||
this._browser = browser;
|
this._browser = browser;
|
||||||
this._browserContextId = browserContextId;
|
this._browserContextId = browserContextId;
|
||||||
this._evaluateOnNewDocumentSources = [];
|
this._evaluateOnNewDocumentSources = [];
|
||||||
|
this._authenticateProxyViaCredentials();
|
||||||
}
|
}
|
||||||
|
|
||||||
async _initialize() {
|
async _initialize() {
|
||||||
|
|
|
||||||
|
|
@ -152,6 +152,7 @@ export class FFBrowserContext extends BrowserContextBase {
|
||||||
this._browser = browser;
|
this._browser = browser;
|
||||||
this._browserContextId = browserContextId;
|
this._browserContextId = browserContextId;
|
||||||
this._evaluateOnNewDocumentSources = [];
|
this._evaluateOnNewDocumentSources = [];
|
||||||
|
this._authenticateProxyViaHeader();
|
||||||
}
|
}
|
||||||
|
|
||||||
async _initialize() {
|
async _initialize() {
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ import * as fs from 'fs';
|
||||||
import * as os from 'os';
|
import * as os from 'os';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as util from 'util';
|
import * as util from 'util';
|
||||||
import { BrowserContext, PersistentContextOptions, validatePersistentContextOptions } from '../browserContext';
|
import { BrowserContext, PersistentContextOptions, validatePersistentContextOptions, verifyProxySettings } from '../browserContext';
|
||||||
import { BrowserServer, WebSocketWrapper } from './browserServer';
|
import { BrowserServer, WebSocketWrapper } from './browserServer';
|
||||||
import * as browserPaths from '../install/browserPaths';
|
import * as browserPaths from '../install/browserPaths';
|
||||||
import { Logger, RootLogger, InnerLogger } from '../logger';
|
import { Logger, RootLogger, InnerLogger } from '../logger';
|
||||||
|
|
@ -29,11 +29,13 @@ import { launchProcess, Env, waitForLine } from './processLauncher';
|
||||||
import { Events } from '../events';
|
import { Events } from '../events';
|
||||||
import { PipeTransport } from './pipeTransport';
|
import { PipeTransport } from './pipeTransport';
|
||||||
import { Progress, runAbortableTask } from '../progress';
|
import { Progress, runAbortableTask } from '../progress';
|
||||||
|
import { ProxySettings } from '../types';
|
||||||
|
|
||||||
export type BrowserArgOptions = {
|
export type BrowserArgOptions = {
|
||||||
headless?: boolean,
|
headless?: boolean,
|
||||||
args?: string[],
|
args?: string[],
|
||||||
devtools?: boolean,
|
devtools?: boolean,
|
||||||
|
proxy?: ProxySettings,
|
||||||
};
|
};
|
||||||
|
|
||||||
export type FirefoxUserPrefsOptions = {
|
export type FirefoxUserPrefsOptions = {
|
||||||
|
|
@ -120,6 +122,7 @@ export abstract class BrowserTypeBase implements BrowserType {
|
||||||
}
|
}
|
||||||
|
|
||||||
async _innerLaunch(progress: Progress, options: LaunchOptions, logger: RootLogger, persistent: PersistentContextOptions | undefined, userDataDir?: string): Promise<BrowserBase> {
|
async _innerLaunch(progress: Progress, options: LaunchOptions, logger: RootLogger, persistent: PersistentContextOptions | undefined, userDataDir?: string): Promise<BrowserBase> {
|
||||||
|
options.proxy = options.proxy ? verifyProxySettings(options.proxy) : undefined;
|
||||||
const { browserServer, downloadsPath, transport } = await this._launchServer(progress, options, !!persistent, logger, userDataDir);
|
const { browserServer, downloadsPath, transport } = await this._launchServer(progress, options, !!persistent, logger, userDataDir);
|
||||||
if ((options as any).__testHookBeforeCreateBrowser)
|
if ((options as any).__testHookBeforeCreateBrowser)
|
||||||
await (options as any).__testHookBeforeCreateBrowser();
|
await (options as any).__testHookBeforeCreateBrowser();
|
||||||
|
|
@ -130,6 +133,7 @@ export abstract class BrowserTypeBase implements BrowserType {
|
||||||
logger,
|
logger,
|
||||||
downloadsPath,
|
downloadsPath,
|
||||||
ownedServer: browserServer,
|
ownedServer: browserServer,
|
||||||
|
proxy: options.proxy,
|
||||||
};
|
};
|
||||||
copyTestHooks(options, browserOptions);
|
copyTestHooks(options, browserOptions);
|
||||||
const browser = await this._connectToTransport(transport, browserOptions);
|
const browser = await this._connectToTransport(transport, browserOptions);
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@ export class Chromium extends BrowserTypeBase {
|
||||||
|
|
||||||
_defaultArgs(options: BrowserArgOptions, isPersistent: boolean, userDataDir: string): string[] {
|
_defaultArgs(options: BrowserArgOptions, isPersistent: boolean, userDataDir: string): string[] {
|
||||||
const { devtools, headless } = processBrowserArgOptions(options);
|
const { devtools, headless } = processBrowserArgOptions(options);
|
||||||
const { args = [] } = options;
|
const { args = [], proxy } = options;
|
||||||
const userDataDirArg = args.find(arg => arg.startsWith('--user-data-dir'));
|
const userDataDirArg = args.find(arg => arg.startsWith('--user-data-dir'));
|
||||||
if (userDataDirArg)
|
if (userDataDirArg)
|
||||||
throw new Error('Pass userDataDir parameter instead of specifying --user-data-dir argument');
|
throw new Error('Pass userDataDir parameter instead of specifying --user-data-dir argument');
|
||||||
|
|
@ -102,6 +102,20 @@ export class Chromium extends BrowserTypeBase {
|
||||||
'--mute-audio'
|
'--mute-audio'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (proxy) {
|
||||||
|
const proxyURL = new URL(proxy.server);
|
||||||
|
const isSocks = proxyURL.protocol === 'socks5:';
|
||||||
|
// https://www.chromium.org/developers/design-documents/network-settings
|
||||||
|
if (isSocks) {
|
||||||
|
// https://www.chromium.org/developers/design-documents/network-stack/socks-proxy
|
||||||
|
chromeArguments.push(`--host-resolver-rules="MAP * ~NOTFOUND , EXCLUDE ${proxyURL.hostname}"`);
|
||||||
|
}
|
||||||
|
chromeArguments.push(`--proxy-server=${proxy.server}`);
|
||||||
|
if (proxy.bypass) {
|
||||||
|
const patterns = proxy.bypass.split(',').map(t => t.trim()).map(t => t.startsWith('.') ? '*' + t : t);
|
||||||
|
chromeArguments.push(`--proxy-bypass-list=${patterns.join(';')}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
chromeArguments.push(...args);
|
chromeArguments.push(...args);
|
||||||
if (isPersistent)
|
if (isPersistent)
|
||||||
chromeArguments.push('about:blank');
|
chromeArguments.push('about:blank');
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ export class Firefox extends BrowserTypeBase {
|
||||||
|
|
||||||
_defaultArgs(options: BrowserArgOptions & FirefoxUserPrefsOptions, isPersistent: boolean, userDataDir: string): string[] {
|
_defaultArgs(options: BrowserArgOptions & FirefoxUserPrefsOptions, isPersistent: boolean, userDataDir: string): string[] {
|
||||||
const { devtools, headless } = processBrowserArgOptions(options);
|
const { devtools, headless } = processBrowserArgOptions(options);
|
||||||
const { args = [] } = options;
|
const { args = [], proxy } = options;
|
||||||
if (devtools)
|
if (devtools)
|
||||||
console.warn('devtools parameter is not supported as a launch argument in Firefox. You can launch the devtools window manually.');
|
console.warn('devtools parameter is not supported as a launch argument in Firefox. You can launch the devtools window manually.');
|
||||||
const userDataDirArg = args.find(arg => arg.startsWith('-profile') || arg.startsWith('--profile'));
|
const userDataDirArg = args.find(arg => arg.startsWith('-profile') || arg.startsWith('--profile'));
|
||||||
|
|
@ -67,6 +67,23 @@ export class Firefox extends BrowserTypeBase {
|
||||||
throw new Error('Pass userDataDir parameter instead of specifying -profile argument');
|
throw new Error('Pass userDataDir parameter instead of specifying -profile argument');
|
||||||
if (args.find(arg => arg.startsWith('-juggler')))
|
if (args.find(arg => arg.startsWith('-juggler')))
|
||||||
throw new Error('Use the port parameter instead of -juggler argument');
|
throw new Error('Use the port parameter instead of -juggler argument');
|
||||||
|
if (proxy) {
|
||||||
|
options.firefoxUserPrefs = options.firefoxUserPrefs || {};
|
||||||
|
options.firefoxUserPrefs['network.proxy.type'] = 1;
|
||||||
|
const proxyServer = new URL(proxy.server);
|
||||||
|
const isSocks = proxyServer.protocol === 'socks5:';
|
||||||
|
if (isSocks) {
|
||||||
|
options.firefoxUserPrefs['network.proxy.socks'] = proxyServer.hostname;
|
||||||
|
options.firefoxUserPrefs['network.proxy.socks_port'] = parseInt(proxyServer.port, 10);
|
||||||
|
} else {
|
||||||
|
options.firefoxUserPrefs['network.proxy.http'] = proxyServer.hostname;
|
||||||
|
options.firefoxUserPrefs['network.proxy.http_port'] = parseInt(proxyServer.port, 10);
|
||||||
|
options.firefoxUserPrefs['network.proxy.ssl'] = proxyServer.hostname;
|
||||||
|
options.firefoxUserPrefs['network.proxy.ssl_port'] = parseInt(proxyServer.port, 10);
|
||||||
|
}
|
||||||
|
if (proxy.bypass)
|
||||||
|
options.firefoxUserPrefs['network.proxy.no_proxies_on'] = proxy.bypass;
|
||||||
|
}
|
||||||
if (options.firefoxUserPrefs) {
|
if (options.firefoxUserPrefs) {
|
||||||
const lines: string[] = [];
|
const lines: string[] = [];
|
||||||
for (const [name, value] of Object.entries(options.firefoxUserPrefs))
|
for (const [name, value] of Object.entries(options.firefoxUserPrefs))
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ export class WebKit extends BrowserTypeBase {
|
||||||
|
|
||||||
_defaultArgs(options: BrowserArgOptions, isPersistent: boolean, userDataDir: string): string[] {
|
_defaultArgs(options: BrowserArgOptions, isPersistent: boolean, userDataDir: string): string[] {
|
||||||
const { devtools, headless } = processBrowserArgOptions(options);
|
const { devtools, headless } = processBrowserArgOptions(options);
|
||||||
const { args = [] } = options;
|
const { args = [], proxy } = options;
|
||||||
if (devtools)
|
if (devtools)
|
||||||
console.warn('devtools parameter as a launch argument in WebKit is not supported. Also starting Web Inspector manually will terminate the execution in WebKit.');
|
console.warn('devtools parameter as a launch argument in WebKit is not supported. Also starting Web Inspector manually will terminate the execution in WebKit.');
|
||||||
const userDataDirArg = args.find(arg => arg.startsWith('--user-data-dir='));
|
const userDataDirArg = args.find(arg => arg.startsWith('--user-data-dir='));
|
||||||
|
|
@ -66,6 +66,21 @@ export class WebKit extends BrowserTypeBase {
|
||||||
webkitArguments.push(`--user-data-dir=${userDataDir}`);
|
webkitArguments.push(`--user-data-dir=${userDataDir}`);
|
||||||
else
|
else
|
||||||
webkitArguments.push(`--no-startup-window`);
|
webkitArguments.push(`--no-startup-window`);
|
||||||
|
if (proxy) {
|
||||||
|
if (process.platform === 'darwin') {
|
||||||
|
webkitArguments.push(`--proxy=${proxy.server}`);
|
||||||
|
if (proxy.bypass)
|
||||||
|
webkitArguments.push(`--proxy-bypass-list=${proxy.bypass}`);
|
||||||
|
} else if (process.platform === 'linux') {
|
||||||
|
webkitArguments.push(`--proxy=${proxy.server}`);
|
||||||
|
if (proxy.bypass)
|
||||||
|
webkitArguments.push(...proxy.bypass.split(',').map(t => `--ignore-host=${t}`));
|
||||||
|
} else if (process.platform === 'win32') {
|
||||||
|
webkitArguments.push(`--curl-proxy=${proxy.server}`);
|
||||||
|
if (proxy.bypass)
|
||||||
|
webkitArguments.push(`--curl-noproxy=${proxy.bypass}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
webkitArguments.push(...args);
|
webkitArguments.push(...args);
|
||||||
if (isPersistent)
|
if (isPersistent)
|
||||||
webkitArguments.push('about:blank');
|
webkitArguments.push('about:blank');
|
||||||
|
|
|
||||||
|
|
@ -178,3 +178,10 @@ export type InjectedScriptPoll<T> = {
|
||||||
logs: Promise<InjectedScriptLogs>,
|
logs: Promise<InjectedScriptLogs>,
|
||||||
cancel: () => void,
|
cancel: () => void,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ProxySettings = {
|
||||||
|
server: string,
|
||||||
|
bypass?: string,
|
||||||
|
username?: string,
|
||||||
|
password?: string
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -208,6 +208,7 @@ export class WKBrowserContext extends BrowserContextBase {
|
||||||
this._browser = browser;
|
this._browser = browser;
|
||||||
this._browserContextId = browserContextId;
|
this._browserContextId = browserContextId;
|
||||||
this._evaluateOnNewDocumentSources = [];
|
this._evaluateOnNewDocumentSources = [];
|
||||||
|
this._authenticateProxyViaHeader();
|
||||||
}
|
}
|
||||||
|
|
||||||
async _initialize() {
|
async _initialize() {
|
||||||
|
|
|
||||||
118
test/proxy.spec.js
Normal file
118
test/proxy.spec.js
Normal file
|
|
@ -0,0 +1,118 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Microsoft Corporation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const socks = require('socksv5');
|
||||||
|
const utils = require('./utils');
|
||||||
|
const {FFOX, CHROMIUM, WEBKIT, MAC} = utils.testOptions(browserType);
|
||||||
|
|
||||||
|
describe('HTTP Proxy', () => {
|
||||||
|
it('should use proxy', async ({browserType, defaultBrowserOptions, server}) => {
|
||||||
|
server.setRoute('/target.html', async (req, res) => {
|
||||||
|
res.end('<html><title>Served by the proxy</title></html>');
|
||||||
|
});
|
||||||
|
const browser = await browserType.launch({
|
||||||
|
...defaultBrowserOptions,
|
||||||
|
proxy: { server: `localhost:${server.PORT}` }
|
||||||
|
});
|
||||||
|
const page = await browser.newPage();
|
||||||
|
await page.goto('http://non-existent.com/target.html');
|
||||||
|
expect(await page.title()).toBe('Served by the proxy');
|
||||||
|
await browser.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should authenticate', async ({browserType, defaultBrowserOptions, server}) => {
|
||||||
|
server.setRoute('/target.html', async (req, res) => {
|
||||||
|
const auth = req.headers['proxy-authorization'];
|
||||||
|
if (!auth) {
|
||||||
|
res.writeHead(407, 'Proxy Authentication Required', {
|
||||||
|
'Proxy-Authenticate': 'Basic realm="Access to internal site"'
|
||||||
|
});
|
||||||
|
res.end();
|
||||||
|
}
|
||||||
|
res.end(`<html><title>${auth}</title></html>`);
|
||||||
|
});
|
||||||
|
const browser = await browserType.launch({
|
||||||
|
...defaultBrowserOptions,
|
||||||
|
proxy: { server: `localhost:${server.PORT}`, username: 'user', password: 'secret' }
|
||||||
|
});
|
||||||
|
const page = await browser.newPage();
|
||||||
|
await page.goto('http://non-existent.com/target.html');
|
||||||
|
expect(await page.title()).toBe('Basic ' + Buffer.from('user:secret').toString('base64'));
|
||||||
|
await browser.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should exclude patterns', async ({browserType, defaultBrowserOptions, server}) => {
|
||||||
|
server.setRoute('/target.html', async (req, res) => {
|
||||||
|
res.end('<html><title>Served by the proxy</title></html>');
|
||||||
|
});
|
||||||
|
const browser = await browserType.launch({
|
||||||
|
...defaultBrowserOptions,
|
||||||
|
proxy: { server: `localhost:${server.PORT}`, bypass: 'non-existent1.com, .non-existent2.com, .zone' }
|
||||||
|
});
|
||||||
|
|
||||||
|
const page = await browser.newPage();
|
||||||
|
await page.goto('http://non-existent.com/target.html');
|
||||||
|
expect(await page.title()).toBe('Served by the proxy');
|
||||||
|
|
||||||
|
{
|
||||||
|
const error = await page.goto('http://non-existent1.com/target.html').catch(e => e);
|
||||||
|
expect(error.message).toBeTruthy();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const error = await page.goto('http://sub.non-existent2.com/target.html').catch(e => e);
|
||||||
|
expect(error.message).toBeTruthy();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const error = await page.goto('http://foo.zone/target.html').catch(e => e);
|
||||||
|
expect(error.message).toBeTruthy();
|
||||||
|
}
|
||||||
|
|
||||||
|
await browser.close();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('SOCKS Proxy', () => {
|
||||||
|
it('should use proxy', async ({ browserType, defaultBrowserOptions, parallelIndex }) => {
|
||||||
|
const server = socks.createServer((info, accept, deny) => {
|
||||||
|
if (socket = accept(true)) {
|
||||||
|
const body = '<html><title>Served by the SOCKS proxy</title></html>';
|
||||||
|
socket.end([
|
||||||
|
'HTTP/1.1 200 OK',
|
||||||
|
'Connection: close',
|
||||||
|
'Content-Type: text/html',
|
||||||
|
'Content-Length: ' + Buffer.byteLength(body),
|
||||||
|
'',
|
||||||
|
body
|
||||||
|
].join('\r\n'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const socksPort = 9107 + parallelIndex * 2;
|
||||||
|
server.listen(socksPort, 'localhost');
|
||||||
|
server.useAuth(socks.auth.None());
|
||||||
|
|
||||||
|
const browser = await browserType.launch({
|
||||||
|
...defaultBrowserOptions,
|
||||||
|
proxy: { server: `socks5://localhost:${socksPort}` }
|
||||||
|
});
|
||||||
|
const page = await browser.newPage();
|
||||||
|
await page.goto('http://non-existent.com');
|
||||||
|
expect(await page.title()).toBe('Served by the SOCKS proxy');
|
||||||
|
await browser.close();
|
||||||
|
server.close();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -228,6 +228,7 @@ module.exports = {
|
||||||
'./logger.spec.js',
|
'./logger.spec.js',
|
||||||
'./headful.spec.js',
|
'./headful.spec.js',
|
||||||
'./multiclient.spec.js',
|
'./multiclient.spec.js',
|
||||||
|
'./proxy.spec.js',
|
||||||
],
|
],
|
||||||
environments: [customEnvironment],
|
environments: [customEnvironment],
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,8 @@ class MDOutline {
|
||||||
property.required = defaultRequired;
|
property.required = defaultRequired;
|
||||||
if (property.comment.toLowerCase().includes('defaults to '))
|
if (property.comment.toLowerCase().includes('defaults to '))
|
||||||
property.required = false;
|
property.required = false;
|
||||||
|
if (property.comment.startsWith('Optional '))
|
||||||
|
property.required = false;
|
||||||
if (property.comment.toLowerCase().includes('if applicable.'))
|
if (property.comment.toLowerCase().includes('if applicable.'))
|
||||||
property.required = false;
|
property.required = false;
|
||||||
if (property.comment.toLowerCase().includes('if available.'))
|
if (property.comment.toLowerCase().includes('if available.'))
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue