Add option to set proxy settings via PAC URL

This commit is contained in:
Michael Mintz 2022-06-28 21:33:10 -04:00
parent f6a811ccf8
commit 8e97a8b277
7 changed files with 176 additions and 40 deletions

View File

@ -102,6 +102,7 @@ if pure_python:
sb.firefox_pref = None
sb.proxy_string = None
sb.proxy_bypass_list = None
sb.proxy_pac_url = None
sb.swiftshader = False
sb.ad_block_on = False
sb.highlights = None

View File

@ -31,6 +31,8 @@ behave -D agent="User Agent String" -D demo
-D proxy=SERVER:PORT (Connect to a proxy server:port for tests.)
-D proxy=USERNAME:PASSWORD@SERVER:PORT (Use authenticated proxy server.)
-D proxy-bypass-list=STRING (";"-separated hosts to bypass, Eg "*.foo.com")
-D proxy-pac-url=URL (Connect to a proxy server using a PAC_URL.pac file.)
-D proxy-pac-url=USERNAME:PASSWORD@URL (Authenticated proxy with PAC URL.)
-D agent=STRING (Modify the web browser's User-Agent string.)
-D mobile (Use the mobile device emulator while running tests.)
-D metrics=STRING (Set mobile metrics: "CSSWidth,CSSHeight,PixelRatio".)
@ -195,6 +197,7 @@ def get_configured_sb(context):
sb.firefox_pref = None
sb.proxy_string = None
sb.proxy_bypass_list = None
sb.proxy_pac_url = None
sb.swiftshader = False
sb.ad_block_on = False
sb.highlights = None
@ -619,7 +622,7 @@ def get_configured_sb(context):
sb.firefox_pref = firefox_pref
continue
# Handle: -D proxy=SERVER:PORT / proxy=USERNAME:PASSWORD@SERVER:PORT
if low_key == "proxy":
if low_key in ["proxy", "proxy-server", "proxy-string"]:
proxy_string = userdata[key]
if proxy_string == "true":
proxy_string = sb.proxy_string # revert to default
@ -632,6 +635,13 @@ def get_configured_sb(context):
proxy_bypass_list = sb.proxy_bypass_list # revert to default
sb.proxy_bypass_list = proxy_bypass_list
continue
# Handle: -D proxy-pac-url=URL / proxy-pac-url=USERNAME:PASSWORD@URL
if low_key in ["proxy-pac-url", "proxy_pac_url", "pac-url", "pac_url"]:
proxy_pac_url = userdata[key]
if proxy_pac_url == "true":
proxy_pac_url = sb.proxy_pac_url # revert to default
sb.proxy_pac_url = proxy_pac_url
continue
# Handle: -D swiftshader
if low_key == "swiftshader":
sb.swiftshader = True

View File

@ -261,6 +261,7 @@ def _set_chrome_options(
proxy_user,
proxy_pass,
proxy_bypass_list,
proxy_pac_url,
user_agent,
recorder_ext,
disable_csp,
@ -458,6 +459,12 @@ def _set_chrome_options(
chrome_options.add_argument(
"--proxy-bypass-list=%s" % proxy_bypass_list
)
elif proxy_pac_url:
if proxy_auth:
chrome_options = _add_chrome_proxy_extension(
chrome_options, None, proxy_user, proxy_pass
)
chrome_options.add_argument("--proxy-pac-url=%s" % proxy_pac_url)
if headless:
if not proxy_auth and not browser_name == constants.Browser.OPERA:
# Headless Chrome doesn't support extensions, which are
@ -509,6 +516,7 @@ def _set_firefox_options(
locale_code,
proxy_string,
proxy_bypass_list,
proxy_pac_url,
user_agent,
disable_csp,
firefox_arg,
@ -565,6 +573,9 @@ def _set_firefox_options(
options.set_preference("network.proxy.ssl_port", int(proxy_port))
if proxy_bypass_list:
options.set_preference("no_proxies_on", proxy_bypass_list)
elif proxy_pac_url:
options.set_preference("network.proxy.type", 2)
options.set_preference("network.proxy.autoconfig_url", proxy_pac_url)
if user_agent:
options.set_preference("general.useragent.override", user_agent)
options.set_preference(
@ -716,6 +727,7 @@ def get_driver(
port=4444,
proxy_string=None,
proxy_bypass_list=None,
proxy_pac_url=None,
user_agent=None,
cap_file=None,
cap_string=None,
@ -775,6 +787,33 @@ def get_driver(
proxy_string = validate_proxy_string(proxy_string)
if proxy_string and proxy_user and proxy_pass:
proxy_auth = True
elif proxy_pac_url:
username_and_password = None
if "@" in proxy_pac_url:
# Format => username:password@PAC_URL.pac
try:
username_and_password = proxy_pac_url.split("@")[0]
proxy_pac_url = proxy_pac_url.split("@")[1]
proxy_user = username_and_password.split(":")[0]
proxy_pass = username_and_password.split(":")[1]
except Exception:
raise Exception(
"The format for using a PAC URL with authentication "
'is: "username:password@PAC_URL.pac". If using a PAC '
'URL without auth, the format is: "PAC_URL.pac".'
)
if browser_name != constants.Browser.GOOGLE_CHROME and (
browser_name != constants.Browser.EDGE
):
raise Exception(
"Chrome or Edge is required when using a PAC URL "
"that has authentication! (If using a PAC URL "
"without auth, Chrome, Edge, or Firefox may be used.)"
)
if not proxy_pac_url.lower().endswith(".pac"):
raise Exception('The proxy PAC URL must end with ".pac"!')
if proxy_pac_url and proxy_user and proxy_pass:
proxy_auth = True
if browser_name == "chrome" and user_data_dir and len(user_data_dir) < 3:
raise Exception(
"Name length of Chrome's User Data Directory must be >= 3."
@ -792,6 +831,7 @@ def get_driver(
proxy_user,
proxy_pass,
proxy_bypass_list,
proxy_pac_url,
user_agent,
cap_file,
cap_string,
@ -833,6 +873,7 @@ def get_driver(
proxy_user,
proxy_pass,
proxy_bypass_list,
proxy_pac_url,
user_agent,
recorder_ext,
disable_csp,
@ -874,6 +915,7 @@ def get_remote_driver(
proxy_user,
proxy_pass,
proxy_bypass_list,
proxy_pac_url,
user_agent,
cap_file,
cap_string,
@ -969,6 +1011,7 @@ def get_remote_driver(
proxy_user,
proxy_pass,
proxy_bypass_list,
proxy_pac_url,
user_agent,
recorder_ext,
disable_csp,
@ -1050,6 +1093,7 @@ def get_remote_driver(
locale_code,
proxy_string,
proxy_bypass_list,
proxy_pac_url,
user_agent,
disable_csp,
firefox_arg,
@ -1175,6 +1219,7 @@ def get_remote_driver(
proxy_user,
proxy_pass,
proxy_bypass_list,
proxy_pac_url,
user_agent,
recorder_ext,
disable_csp,
@ -1356,6 +1401,7 @@ def get_local_driver(
proxy_user,
proxy_pass,
proxy_bypass_list,
proxy_pac_url,
user_agent,
recorder_ext,
disable_csp,
@ -1396,6 +1442,7 @@ def get_local_driver(
locale_code,
proxy_string,
proxy_bypass_list,
proxy_pac_url,
user_agent,
disable_csp,
firefox_arg,
@ -1704,10 +1751,16 @@ def get_local_driver(
edge_options, proxy_string, proxy_user, proxy_pass
)
edge_options.add_argument("--proxy-server=%s" % proxy_string)
if proxy_bypass_list:
edge_options.add_argument(
"--proxy-bypass-list=%s" % proxy_bypass_list
)
if proxy_bypass_list:
edge_options.add_argument(
"--proxy-bypass-list=%s" % proxy_bypass_list
)
elif proxy_pac_url:
if proxy_auth:
edge_options = _add_chrome_proxy_extension(
edge_options, None, proxy_user, proxy_pass
)
edge_options.add_argument("--proxy-pac-url=%s" % proxy_pac_url)
edge_options.add_argument("--test-type")
edge_options.add_argument("--log-level=3")
edge_options.add_argument("--no-first-run")
@ -1862,6 +1915,7 @@ def get_local_driver(
proxy_user,
proxy_pass,
proxy_bypass_list,
proxy_pac_url,
user_agent,
recorder_ext,
disable_csp,
@ -1917,6 +1971,7 @@ def get_local_driver(
proxy_user,
proxy_pass,
proxy_bypass_list,
proxy_pac_url,
user_agent,
recorder_ext,
disable_csp,
@ -2036,6 +2091,7 @@ def get_local_driver(
proxy_user,
proxy_pass,
proxy_bypass_list,
proxy_pac_url,
user_agent,
recorder_ext,
disable_csp,

View File

@ -11,39 +11,65 @@ def create_proxy_zip(proxy_string, proxy_user, proxy_pass):
"""Implementation of https://stackoverflow.com/a/35293284 for
https://stackoverflow.com/questions/12848327/
(Run Selenium on a proxy server that requires authentication.)
Solution involves creating & adding a Chrome extension on the fly.
* CHROME-ONLY for now! *
Solution involves creating & adding a Chromium extension on the fly.
CHROMIUM-ONLY! *** Only Chrome and Edge browsers are supported. ***
"""
proxy_host = proxy_string.split(":")[0]
proxy_port = proxy_string.split(":")[1]
background_js = (
"""var config = {\n"""
""" mode: "fixed_servers",\n"""
""" rules: {\n"""
""" singleProxy: {\n"""
""" scheme: "http",\n"""
""" host: "%s",\n"""
""" port: parseInt("%s")\n"""
""" },\n"""
""" }\n"""
""" };\n"""
"""chrome.proxy.settings.set("""
"""{value: config, scope: "regular"}, function() {"""
"""});\n"""
"""function callbackFn(details) {\n"""
""" return {\n"""
""" authCredentials: {\n"""
""" username: "%s",\n"""
""" password: "%s"\n"""
""" }\n"""
""" };\n"""
"""}\n"""
"""chrome.webRequest.onAuthRequired.addListener(\n"""
""" callbackFn,\n"""
""" {urls: ["<all_urls>"]},\n"""
""" ['blocking']\n"""
""");""" % (proxy_host, proxy_port, proxy_user, proxy_pass)
)
background_js = None
if proxy_string:
proxy_host = proxy_string.split(":")[0]
proxy_port = proxy_string.split(":")[1]
background_js = (
"""var config = {\n"""
""" mode: "fixed_servers",\n"""
""" rules: {\n"""
""" singleProxy: {\n"""
""" scheme: "http",\n"""
""" host: "%s",\n"""
""" port: parseInt("%s")\n"""
""" },\n"""
""" }\n"""
""" };\n"""
"""chrome.proxy.settings.set("""
"""{value: config, scope: "regular"}, function() {"""
"""});\n"""
"""function callbackFn(details) {\n"""
""" return {\n"""
""" authCredentials: {\n"""
""" username: "%s",\n"""
""" password: "%s"\n"""
""" }\n"""
""" };\n"""
"""}\n"""
"""chrome.webRequest.onAuthRequired.addListener(\n"""
""" callbackFn,\n"""
""" {urls: ["<all_urls>"]},\n"""
""" ['blocking']\n"""
""");""" % (proxy_host, proxy_port, proxy_user, proxy_pass)
)
else:
background_js = (
"""var config = {\n"""
""" mode: "fixed_servers",\n"""
""" rules: {\n"""
""" }\n"""
""" };\n"""
"""chrome.proxy.settings.set("""
"""{value: config, scope: "regular"}, function() {"""
"""});\n"""
"""function callbackFn(details) {\n"""
""" return {\n"""
""" authCredentials: {\n"""
""" username: "%s",\n"""
""" password: "%s"\n"""
""" }\n"""
""" };\n"""
"""}\n"""
"""chrome.webRequest.onAuthRequired.addListener(\n"""
""" callbackFn,\n"""
""" {urls: ["<all_urls>"]},\n"""
""" ['blocking']\n"""
""");""" % (proxy_user, proxy_pass)
)
manifest_json = (
"""{\n"""
""""version": "1.0.0",\n"""
@ -79,7 +105,7 @@ def create_proxy_zip(proxy_string, proxy_user, proxy_pass):
def remove_proxy_zip_if_present():
"""Remove Chrome extension zip file used for proxy server authentication.
"""Remove Chromium extension zip file used for proxy server authentication.
Used in the implementation of https://stackoverflow.com/a/35293284
for https://stackoverflow.com/questions/12848327/
"""

View File

@ -2937,6 +2937,7 @@ class BaseCase(unittest.TestCase):
port=None,
proxy=None,
proxy_bypass_list=None,
proxy_pac_url=None,
agent=None,
switch_to=True,
cap_file=None,
@ -2980,6 +2981,7 @@ class BaseCase(unittest.TestCase):
port - if using a Selenium Grid, set the host port here
proxy - if using a proxy server, specify the "host:port" combo here
proxy_bypass_list - ";"-separated hosts to bypass (Eg. "*.foo.com")
proxy_pac_url - designates the proxy PAC URL to use (Chromium-only)
switch_to - the option to switch to the new driver (default = True)
cap_file - the file containing desired capabilities for the browser
cap_string - the string with desired capabilities for the browser
@ -3060,6 +3062,8 @@ class BaseCase(unittest.TestCase):
proxy_string = self.proxy_string
if proxy_bypass_list is None:
proxy_bypass_list = self.proxy_bypass_list
if proxy_pac_url is None:
proxy_pac_url = self.proxy_pac_url
user_agent = agent
if user_agent is None:
user_agent = self.user_agent
@ -3137,6 +3141,7 @@ class BaseCase(unittest.TestCase):
port=port,
proxy_string=proxy_string,
proxy_bypass_list=proxy_bypass_list,
proxy_pac_url=proxy_pac_url,
user_agent=user_agent,
cap_file=cap_file,
cap_string=cap_string,
@ -12060,6 +12065,7 @@ class BaseCase(unittest.TestCase):
self.port = sb_config.port
self.proxy_string = sb_config.proxy_string
self.proxy_bypass_list = sb_config.proxy_bypass_list
self.proxy_pac_url = sb_config.proxy_pac_url
self.user_agent = sb_config.user_agent
self.mobile_emulator = sb_config.mobile_emulator
self.device_metrics = sb_config.device_metrics
@ -12372,6 +12378,7 @@ class BaseCase(unittest.TestCase):
port=self.port,
proxy=self.proxy_string,
proxy_bypass_list=self.proxy_bypass_list,
proxy_pac_url=self.proxy_pac_url,
agent=self.user_agent,
switch_to=True,
cap_file=self.cap_file,

View File

@ -43,6 +43,8 @@ def pytest_addoption(parser):
--proxy=SERVER:PORT (Connect to a proxy server:port for tests.)
--proxy=USERNAME:PASSWORD@SERVER:PORT (Use authenticated proxy server.)
--proxy-bypass-list=STRING (";"-separated hosts to bypass, Eg "*.foo.com")
--proxy-pac-url=URL (Connect to a proxy server using a PAC_URL.pac file.)
--proxy-pac-url=USERNAME:PASSWORD@URL (Authenticated proxy with PAC URL.)
--agent=STRING (Modify the web browser's User-Agent string.)
--mobile (Use the mobile device emulator while running tests.)
--metrics=STRING (Set mobile metrics: "CSSWidth,CSSHeight,PixelRatio".)
@ -410,13 +412,15 @@ def pytest_addoption(parser):
)
parser.addoption(
"--proxy",
"--proxy-server",
"--proxy-string",
action="store",
dest="proxy_string",
default=None,
help="""Designates the proxy server:port to use.
Format: servername:port. OR
username:password@servername:port OR
A dict key from proxy_list.PROXY_LIST
username:password@servername:port OR
A dict key from proxy_list.PROXY_LIST
Default: None.""",
)
parser.addoption(
@ -437,6 +441,19 @@ def pytest_addoption(parser):
--proxy-bypass-list="127.0.0.1:8080"
Default: None.""",
)
parser.addoption(
"--proxy-pac-url",
"--proxy_pac_url",
"--pac-url",
"--pac_url",
action="store",
dest="proxy_pac_url",
default=None,
help="""Designates the proxy PAC URL to use.
Format: A URL string OR
A username:password@URL string
Default: None.""",
)
parser.addoption(
"--agent",
"--user-agent",
@ -1176,6 +1193,7 @@ def pytest_configure(config):
sb_config.protocol = "https"
sb_config.proxy_string = config.getoption("proxy_string")
sb_config.proxy_bypass_list = config.getoption("proxy_bypass_list")
sb_config.proxy_pac_url = config.getoption("proxy_pac_url")
sb_config.cap_file = config.getoption("cap_file")
sb_config.cap_string = config.getoption("cap_string")
sb_config.settings_file = config.getoption("settings_file")

View File

@ -26,6 +26,8 @@ class SeleniumBrowser(Plugin):
--proxy=SERVER:PORT (Connect to a proxy server:port for tests.)
--proxy=USERNAME:PASSWORD@SERVER:PORT (Use authenticated proxy server.)
--proxy-bypass-list=STRING (";"-separated hosts to bypass, Eg "*.foo.com")
--proxy-pac-url=URL (Connect to a proxy server using a PAC_URL.pac file.)
--proxy-pac-url=USERNAME:PASSWORD@URL (Authenticated proxy with PAC URL.)
--agent=STRING (Modify the web browser's User-Agent string.)
--mobile (Use the mobile device emulator while running tests.)
--metrics=STRING (Set mobile metrics: "CSSWidth,CSSHeight,PixelRatio".)
@ -160,6 +162,8 @@ class SeleniumBrowser(Plugin):
)
parser.add_option(
"--proxy",
"--proxy-server",
"--proxy-string",
action="store",
dest="proxy_string",
default=None,
@ -187,6 +191,19 @@ class SeleniumBrowser(Plugin):
--proxy-bypass-list="127.0.0.1:8080"
Default: None.""",
)
parser.add_option(
"--proxy-pac-url",
"--proxy_pac_url",
"--pac-url",
"--pac_url",
action="store",
dest="proxy_pac_url",
default=None,
help="""Designates the proxy PAC URL to use.
Format: A URL string OR
A username:password@URL string
Default: None.""",
)
parser.add_option(
"--agent",
"--user-agent",
@ -743,6 +760,7 @@ class SeleniumBrowser(Plugin):
test.test.firefox_pref = self.options.firefox_pref
test.test.proxy_string = self.options.proxy_string
test.test.proxy_bypass_list = self.options.proxy_bypass_list
test.test.proxy_pac_url = self.options.proxy_pac_url
test.test.user_agent = self.options.user_agent
test.test.mobile_emulator = self.options.mobile_emulator
test.test.device_metrics = self.options.device_metrics