Skip to content

Commit 8e97a8b

Browse files
committed
Add option to set proxy settings via PAC URL
1 parent f6a811c commit 8e97a8b

File tree

7 files changed

+176
-40
lines changed

7 files changed

+176
-40
lines changed

examples/raw_parameter_script.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@
102102
sb.firefox_pref = None
103103
sb.proxy_string = None
104104
sb.proxy_bypass_list = None
105+
sb.proxy_pac_url = None
105106
sb.swiftshader = False
106107
sb.ad_block_on = False
107108
sb.highlights = None

seleniumbase/behave/behave_sb.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
-D proxy=SERVER:PORT (Connect to a proxy server:port for tests.)
3232
-D proxy=USERNAME:PASSWORD@SERVER:PORT (Use authenticated proxy server.)
3333
-D proxy-bypass-list=STRING (";"-separated hosts to bypass, Eg "*.foo.com")
34+
-D proxy-pac-url=URL (Connect to a proxy server using a PAC_URL.pac file.)
35+
-D proxy-pac-url=USERNAME:PASSWORD@URL (Authenticated proxy with PAC URL.)
3436
-D agent=STRING (Modify the web browser's User-Agent string.)
3537
-D mobile (Use the mobile device emulator while running tests.)
3638
-D metrics=STRING (Set mobile metrics: "CSSWidth,CSSHeight,PixelRatio".)
@@ -195,6 +197,7 @@ def get_configured_sb(context):
195197
sb.firefox_pref = None
196198
sb.proxy_string = None
197199
sb.proxy_bypass_list = None
200+
sb.proxy_pac_url = None
198201
sb.swiftshader = False
199202
sb.ad_block_on = False
200203
sb.highlights = None
@@ -619,7 +622,7 @@ def get_configured_sb(context):
619622
sb.firefox_pref = firefox_pref
620623
continue
621624
# Handle: -D proxy=SERVER:PORT / proxy=USERNAME:PASSWORD@SERVER:PORT
622-
if low_key == "proxy":
625+
if low_key in ["proxy", "proxy-server", "proxy-string"]:
623626
proxy_string = userdata[key]
624627
if proxy_string == "true":
625628
proxy_string = sb.proxy_string # revert to default
@@ -632,6 +635,13 @@ def get_configured_sb(context):
632635
proxy_bypass_list = sb.proxy_bypass_list # revert to default
633636
sb.proxy_bypass_list = proxy_bypass_list
634637
continue
638+
# Handle: -D proxy-pac-url=URL / proxy-pac-url=USERNAME:PASSWORD@URL
639+
if low_key in ["proxy-pac-url", "proxy_pac_url", "pac-url", "pac_url"]:
640+
proxy_pac_url = userdata[key]
641+
if proxy_pac_url == "true":
642+
proxy_pac_url = sb.proxy_pac_url # revert to default
643+
sb.proxy_pac_url = proxy_pac_url
644+
continue
635645
# Handle: -D swiftshader
636646
if low_key == "swiftshader":
637647
sb.swiftshader = True

seleniumbase/core/browser_launcher.py

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,7 @@ def _set_chrome_options(
261261
proxy_user,
262262
proxy_pass,
263263
proxy_bypass_list,
264+
proxy_pac_url,
264265
user_agent,
265266
recorder_ext,
266267
disable_csp,
@@ -458,6 +459,12 @@ def _set_chrome_options(
458459
chrome_options.add_argument(
459460
"--proxy-bypass-list=%s" % proxy_bypass_list
460461
)
462+
elif proxy_pac_url:
463+
if proxy_auth:
464+
chrome_options = _add_chrome_proxy_extension(
465+
chrome_options, None, proxy_user, proxy_pass
466+
)
467+
chrome_options.add_argument("--proxy-pac-url=%s" % proxy_pac_url)
461468
if headless:
462469
if not proxy_auth and not browser_name == constants.Browser.OPERA:
463470
# Headless Chrome doesn't support extensions, which are
@@ -509,6 +516,7 @@ def _set_firefox_options(
509516
locale_code,
510517
proxy_string,
511518
proxy_bypass_list,
519+
proxy_pac_url,
512520
user_agent,
513521
disable_csp,
514522
firefox_arg,
@@ -565,6 +573,9 @@ def _set_firefox_options(
565573
options.set_preference("network.proxy.ssl_port", int(proxy_port))
566574
if proxy_bypass_list:
567575
options.set_preference("no_proxies_on", proxy_bypass_list)
576+
elif proxy_pac_url:
577+
options.set_preference("network.proxy.type", 2)
578+
options.set_preference("network.proxy.autoconfig_url", proxy_pac_url)
568579
if user_agent:
569580
options.set_preference("general.useragent.override", user_agent)
570581
options.set_preference(
@@ -716,6 +727,7 @@ def get_driver(
716727
port=4444,
717728
proxy_string=None,
718729
proxy_bypass_list=None,
730+
proxy_pac_url=None,
719731
user_agent=None,
720732
cap_file=None,
721733
cap_string=None,
@@ -775,6 +787,33 @@ def get_driver(
775787
proxy_string = validate_proxy_string(proxy_string)
776788
if proxy_string and proxy_user and proxy_pass:
777789
proxy_auth = True
790+
elif proxy_pac_url:
791+
username_and_password = None
792+
if "@" in proxy_pac_url:
793+
# Format => username:password@PAC_URL.pac
794+
try:
795+
username_and_password = proxy_pac_url.split("@")[0]
796+
proxy_pac_url = proxy_pac_url.split("@")[1]
797+
proxy_user = username_and_password.split(":")[0]
798+
proxy_pass = username_and_password.split(":")[1]
799+
except Exception:
800+
raise Exception(
801+
"The format for using a PAC URL with authentication "
802+
'is: "username:password@PAC_URL.pac". If using a PAC '
803+
'URL without auth, the format is: "PAC_URL.pac".'
804+
)
805+
if browser_name != constants.Browser.GOOGLE_CHROME and (
806+
browser_name != constants.Browser.EDGE
807+
):
808+
raise Exception(
809+
"Chrome or Edge is required when using a PAC URL "
810+
"that has authentication! (If using a PAC URL "
811+
"without auth, Chrome, Edge, or Firefox may be used.)"
812+
)
813+
if not proxy_pac_url.lower().endswith(".pac"):
814+
raise Exception('The proxy PAC URL must end with ".pac"!')
815+
if proxy_pac_url and proxy_user and proxy_pass:
816+
proxy_auth = True
778817
if browser_name == "chrome" and user_data_dir and len(user_data_dir) < 3:
779818
raise Exception(
780819
"Name length of Chrome's User Data Directory must be >= 3."
@@ -792,6 +831,7 @@ def get_driver(
792831
proxy_user,
793832
proxy_pass,
794833
proxy_bypass_list,
834+
proxy_pac_url,
795835
user_agent,
796836
cap_file,
797837
cap_string,
@@ -833,6 +873,7 @@ def get_driver(
833873
proxy_user,
834874
proxy_pass,
835875
proxy_bypass_list,
876+
proxy_pac_url,
836877
user_agent,
837878
recorder_ext,
838879
disable_csp,
@@ -874,6 +915,7 @@ def get_remote_driver(
874915
proxy_user,
875916
proxy_pass,
876917
proxy_bypass_list,
918+
proxy_pac_url,
877919
user_agent,
878920
cap_file,
879921
cap_string,
@@ -969,6 +1011,7 @@ def get_remote_driver(
9691011
proxy_user,
9701012
proxy_pass,
9711013
proxy_bypass_list,
1014+
proxy_pac_url,
9721015
user_agent,
9731016
recorder_ext,
9741017
disable_csp,
@@ -1050,6 +1093,7 @@ def get_remote_driver(
10501093
locale_code,
10511094
proxy_string,
10521095
proxy_bypass_list,
1096+
proxy_pac_url,
10531097
user_agent,
10541098
disable_csp,
10551099
firefox_arg,
@@ -1175,6 +1219,7 @@ def get_remote_driver(
11751219
proxy_user,
11761220
proxy_pass,
11771221
proxy_bypass_list,
1222+
proxy_pac_url,
11781223
user_agent,
11791224
recorder_ext,
11801225
disable_csp,
@@ -1356,6 +1401,7 @@ def get_local_driver(
13561401
proxy_user,
13571402
proxy_pass,
13581403
proxy_bypass_list,
1404+
proxy_pac_url,
13591405
user_agent,
13601406
recorder_ext,
13611407
disable_csp,
@@ -1396,6 +1442,7 @@ def get_local_driver(
13961442
locale_code,
13971443
proxy_string,
13981444
proxy_bypass_list,
1445+
proxy_pac_url,
13991446
user_agent,
14001447
disable_csp,
14011448
firefox_arg,
@@ -1704,10 +1751,16 @@ def get_local_driver(
17041751
edge_options, proxy_string, proxy_user, proxy_pass
17051752
)
17061753
edge_options.add_argument("--proxy-server=%s" % proxy_string)
1707-
if proxy_bypass_list:
1708-
edge_options.add_argument(
1709-
"--proxy-bypass-list=%s" % proxy_bypass_list
1710-
)
1754+
if proxy_bypass_list:
1755+
edge_options.add_argument(
1756+
"--proxy-bypass-list=%s" % proxy_bypass_list
1757+
)
1758+
elif proxy_pac_url:
1759+
if proxy_auth:
1760+
edge_options = _add_chrome_proxy_extension(
1761+
edge_options, None, proxy_user, proxy_pass
1762+
)
1763+
edge_options.add_argument("--proxy-pac-url=%s" % proxy_pac_url)
17111764
edge_options.add_argument("--test-type")
17121765
edge_options.add_argument("--log-level=3")
17131766
edge_options.add_argument("--no-first-run")
@@ -1862,6 +1915,7 @@ def get_local_driver(
18621915
proxy_user,
18631916
proxy_pass,
18641917
proxy_bypass_list,
1918+
proxy_pac_url,
18651919
user_agent,
18661920
recorder_ext,
18671921
disable_csp,
@@ -1917,6 +1971,7 @@ def get_local_driver(
19171971
proxy_user,
19181972
proxy_pass,
19191973
proxy_bypass_list,
1974+
proxy_pac_url,
19201975
user_agent,
19211976
recorder_ext,
19221977
disable_csp,
@@ -2036,6 +2091,7 @@ def get_local_driver(
20362091
proxy_user,
20372092
proxy_pass,
20382093
proxy_bypass_list,
2094+
proxy_pac_url,
20392095
user_agent,
20402096
recorder_ext,
20412097
disable_csp,

seleniumbase/core/proxy_helper.py

Lines changed: 59 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -11,39 +11,65 @@ def create_proxy_zip(proxy_string, proxy_user, proxy_pass):
1111
"""Implementation of https://stackoverflow.com/a/35293284 for
1212
https://stackoverflow.com/questions/12848327/
1313
(Run Selenium on a proxy server that requires authentication.)
14-
Solution involves creating & adding a Chrome extension on the fly.
15-
* CHROME-ONLY for now! *
14+
Solution involves creating & adding a Chromium extension on the fly.
15+
CHROMIUM-ONLY! *** Only Chrome and Edge browsers are supported. ***
1616
"""
17-
proxy_host = proxy_string.split(":")[0]
18-
proxy_port = proxy_string.split(":")[1]
19-
background_js = (
20-
"""var config = {\n"""
21-
""" mode: "fixed_servers",\n"""
22-
""" rules: {\n"""
23-
""" singleProxy: {\n"""
24-
""" scheme: "http",\n"""
25-
""" host: "%s",\n"""
26-
""" port: parseInt("%s")\n"""
27-
""" },\n"""
28-
""" }\n"""
29-
""" };\n"""
30-
"""chrome.proxy.settings.set("""
31-
"""{value: config, scope: "regular"}, function() {"""
32-
"""});\n"""
33-
"""function callbackFn(details) {\n"""
34-
""" return {\n"""
35-
""" authCredentials: {\n"""
36-
""" username: "%s",\n"""
37-
""" password: "%s"\n"""
38-
""" }\n"""
39-
""" };\n"""
40-
"""}\n"""
41-
"""chrome.webRequest.onAuthRequired.addListener(\n"""
42-
""" callbackFn,\n"""
43-
""" {urls: ["<all_urls>"]},\n"""
44-
""" ['blocking']\n"""
45-
""");""" % (proxy_host, proxy_port, proxy_user, proxy_pass)
46-
)
17+
background_js = None
18+
if proxy_string:
19+
proxy_host = proxy_string.split(":")[0]
20+
proxy_port = proxy_string.split(":")[1]
21+
background_js = (
22+
"""var config = {\n"""
23+
""" mode: "fixed_servers",\n"""
24+
""" rules: {\n"""
25+
""" singleProxy: {\n"""
26+
""" scheme: "http",\n"""
27+
""" host: "%s",\n"""
28+
""" port: parseInt("%s")\n"""
29+
""" },\n"""
30+
""" }\n"""
31+
""" };\n"""
32+
"""chrome.proxy.settings.set("""
33+
"""{value: config, scope: "regular"}, function() {"""
34+
"""});\n"""
35+
"""function callbackFn(details) {\n"""
36+
""" return {\n"""
37+
""" authCredentials: {\n"""
38+
""" username: "%s",\n"""
39+
""" password: "%s"\n"""
40+
""" }\n"""
41+
""" };\n"""
42+
"""}\n"""
43+
"""chrome.webRequest.onAuthRequired.addListener(\n"""
44+
""" callbackFn,\n"""
45+
""" {urls: ["<all_urls>"]},\n"""
46+
""" ['blocking']\n"""
47+
""");""" % (proxy_host, proxy_port, proxy_user, proxy_pass)
48+
)
49+
else:
50+
background_js = (
51+
"""var config = {\n"""
52+
""" mode: "fixed_servers",\n"""
53+
""" rules: {\n"""
54+
""" }\n"""
55+
""" };\n"""
56+
"""chrome.proxy.settings.set("""
57+
"""{value: config, scope: "regular"}, function() {"""
58+
"""});\n"""
59+
"""function callbackFn(details) {\n"""
60+
""" return {\n"""
61+
""" authCredentials: {\n"""
62+
""" username: "%s",\n"""
63+
""" password: "%s"\n"""
64+
""" }\n"""
65+
""" };\n"""
66+
"""}\n"""
67+
"""chrome.webRequest.onAuthRequired.addListener(\n"""
68+
""" callbackFn,\n"""
69+
""" {urls: ["<all_urls>"]},\n"""
70+
""" ['blocking']\n"""
71+
""");""" % (proxy_user, proxy_pass)
72+
)
4773
manifest_json = (
4874
"""{\n"""
4975
""""version": "1.0.0",\n"""
@@ -79,7 +105,7 @@ def create_proxy_zip(proxy_string, proxy_user, proxy_pass):
79105

80106

81107
def remove_proxy_zip_if_present():
82-
"""Remove Chrome extension zip file used for proxy server authentication.
108+
"""Remove Chromium extension zip file used for proxy server authentication.
83109
Used in the implementation of https://stackoverflow.com/a/35293284
84110
for https://stackoverflow.com/questions/12848327/
85111
"""

seleniumbase/fixtures/base_case.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2937,6 +2937,7 @@ def get_new_driver(
29372937
port=None,
29382938
proxy=None,
29392939
proxy_bypass_list=None,
2940+
proxy_pac_url=None,
29402941
agent=None,
29412942
switch_to=True,
29422943
cap_file=None,
@@ -2980,6 +2981,7 @@ def get_new_driver(
29802981
port - if using a Selenium Grid, set the host port here
29812982
proxy - if using a proxy server, specify the "host:port" combo here
29822983
proxy_bypass_list - ";"-separated hosts to bypass (Eg. "*.foo.com")
2984+
proxy_pac_url - designates the proxy PAC URL to use (Chromium-only)
29832985
switch_to - the option to switch to the new driver (default = True)
29842986
cap_file - the file containing desired capabilities for the browser
29852987
cap_string - the string with desired capabilities for the browser
@@ -3060,6 +3062,8 @@ def get_new_driver(
30603062
proxy_string = self.proxy_string
30613063
if proxy_bypass_list is None:
30623064
proxy_bypass_list = self.proxy_bypass_list
3065+
if proxy_pac_url is None:
3066+
proxy_pac_url = self.proxy_pac_url
30633067
user_agent = agent
30643068
if user_agent is None:
30653069
user_agent = self.user_agent
@@ -3137,6 +3141,7 @@ def get_new_driver(
31373141
port=port,
31383142
proxy_string=proxy_string,
31393143
proxy_bypass_list=proxy_bypass_list,
3144+
proxy_pac_url=proxy_pac_url,
31403145
user_agent=user_agent,
31413146
cap_file=cap_file,
31423147
cap_string=cap_string,
@@ -12060,6 +12065,7 @@ def setUp(self, masterqa_mode=False):
1206012065
self.port = sb_config.port
1206112066
self.proxy_string = sb_config.proxy_string
1206212067
self.proxy_bypass_list = sb_config.proxy_bypass_list
12068+
self.proxy_pac_url = sb_config.proxy_pac_url
1206312069
self.user_agent = sb_config.user_agent
1206412070
self.mobile_emulator = sb_config.mobile_emulator
1206512071
self.device_metrics = sb_config.device_metrics
@@ -12372,6 +12378,7 @@ def setUp(self, masterqa_mode=False):
1237212378
port=self.port,
1237312379
proxy=self.proxy_string,
1237412380
proxy_bypass_list=self.proxy_bypass_list,
12381+
proxy_pac_url=self.proxy_pac_url,
1237512382
agent=self.user_agent,
1237612383
switch_to=True,
1237712384
cap_file=self.cap_file,

0 commit comments

Comments
 (0)