PAC support
proxylib.pac
PAC (Proxy Auto-Config) support.
- :data:
functions-- the PAC builtins (Netscape set plus Microsoft*Ex). - :class:
PacScript-- a script running in a JavaScript engine (proxylib[pac]for quickjs,proxylib[dukpy]for dukpy), with a time limit per evaluation. - :class:
PacProxyMap-- the proxy map a script (or a Python function) answers. - :func:
load/ :func:loads-- a map from a file or URL, or from text.
MAX_PAC_BYTES = 1024 * 1024
module-attribute
__all__ = ('functions', 'PacScript', 'PacProxyMap', 'load', 'loads', 'MAX_PAC_BYTES')
module-attribute
functions = _MappingProxyType({'dnsResolve': dnsResolve, 'dnsResolveEx': dnsResolveEx, 'myIpAddress': myIpAddress, 'myIpAddressEx': myIpAddressEx, 'dnsDomainLevels': dnsDomainLevels, 'convert_addr': convert_addr, 'shExpMatch': shExpMatch, 'weekdayRange': weekdayRange, 'dateRange': dateRange, 'timeRange': timeRange, 'isPlainHostName': isPlainHostName, 'dnsDomainIs': dnsDomainIs, 'localHostOrDomainIs': localHostOrDomainIs, 'isResolvable': isResolvable, 'isResolvableEx': isResolvableEx, 'isInNet': isInNet, 'isInNetEx': isInNetEx, 'sortIpAddressList': sortIpAddressList, 'getClientVersion': getClientVersion})
module-attribute
Every PAC builtin by its PAC name; read-only.
PacProxyMap(script, *, browser_compatibility=False)
A proxy map that asks FindProxyForURL(url, host) for every URL.
script is a :class:PacScript or a Python callable with the same
signature, which takes the place of subclassing. host is the URL's
host, lower-cased. The result string is read with
:meth:Proxy.parse_pac_result; a PAC always answers, so this map never
raises KeyError: whatever a Python function raises becomes
:class:~proxylib.PACError, as a script error does.
browser_compatibility=True passes an https URL without its path and
query, as Chrome and Firefox do so a PAC server cannot see them.
Source code in src/proxylib/pac/map.py
33 34 35 36 37 | |
__slots__ = ('script', 'browser_compatibility')
class-attribute
instance-attribute
browser_compatibility = browser_compatibility
instance-attribute
script = script
instance-attribute
__getitem__(url)
Source code in src/proxylib/pac/map.py
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | |
__repr__()
Source code in src/proxylib/pac/map.py
39 40 | |
close()
End the script's helper process (a Python function has none).
Source code in src/proxylib/pac/map.py
42 43 44 45 46 | |
PacScript(source, *, engine=None, functions=None, timeout=5.0, on_missing_engine='raise')
A PAC script, ready to answer FindProxyForURL.
source is the script text. engine names the JS engine (default:
PROXYLIB_JS_ENGINE, else quickjs, else dukpy; see
:mod:proxylib.pac.engines). functions adds to or overrides the PAC
builtins the script can call; the script runs in a helper process, so
they must be picklable module-level functions it can import.
timeout bounds each call in seconds (the builtins' own DNS lookups are
bounded at 2 s each): a call that overruns raises :class:PACError, its
helper process is ended, and for the next 30 seconds every call raises
:class:PACError at once; after that the script is started afresh. A
script that overruns while loading is not loaded.
With no engine installed, on_missing_engine="raise" (the default)
raises :class:MissingExtraError; "direct" warns and the script
answers "DIRECT" to everything.
A script error, a script that does not define FindProxyForURL and an
overrun all raise :class:PACError. Safe to share between threads; calls
are serialised. close() releases the engine (garbage collection does
too).
Source code in src/proxylib/pac/script.py
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 | |
engine_name = engine_cls.name
instance-attribute
source = source
instance-attribute
timeout = timeout
instance-attribute
__del__()
Source code in src/proxylib/pac/script.py
198 199 | |
__repr__()
Source code in src/proxylib/pac/script.py
206 207 208 | |
close()
Source code in src/proxylib/pac/script.py
201 202 203 204 | |
find_proxy_for_url(url, host)
Run FindProxyForURL(url, host). A result that is not a string
is "DIRECT", as browsers treat it.
Source code in src/proxylib/pac/script.py
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 | |
from_file(path, *, encoding='utf-8-sig', **options)
classmethod
Read a script from a file. options are those of PacScript.
Source code in src/proxylib/pac/script.py
240 241 242 243 244 245 | |
from_url(url, *, timeout=10.0, max_bytes=MAX_PAC_BYTES, cache_ttl=300.0, **options)
classmethod
Download a script, never through a proxy. timeout bounds the
download; a body over max_bytes raises :class:PACError. A
file:// URL is read directly and never cached; downloaded text is
kept for cache_ttl seconds (0/None: no cache). options
are those of PacScript, whose timeout is spelled
script_timeout here.
Source code in src/proxylib/pac/script.py
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 | |
load(location, *, timeout=10.0, cache_ttl=300.0, script_timeout=5.0, engine=None, on_missing_engine='raise')
A :class:PacProxyMap for the PAC file at location: a URL
(http, https, file) or a path. Never script text -- use
:func:loads. A file:path without // is a path.
timeout, cache_ttl: the download (see :meth:PacScript.from_url).
script_timeout, engine, on_missing_engine: the script (see
:class:PacScript).
Source code in src/proxylib/pac/__init__.py
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | |
loads(text, *, script_timeout=5.0, engine=None, on_missing_engine='raise')
A :class:PacProxyMap for PAC script text.
Source code in src/proxylib/pac/__init__.py
53 54 55 56 57 58 59 60 61 62 63 | |
proxylib.pac.script
PacScript: one PAC script running in its own JavaScript engine.
MAX_PAC_BYTES = 1024 * 1024
module-attribute
__all__ = ('PacScript', 'MAX_PAC_BYTES')
module-attribute
PacScript(source, *, engine=None, functions=None, timeout=5.0, on_missing_engine='raise')
A PAC script, ready to answer FindProxyForURL.
source is the script text. engine names the JS engine (default:
PROXYLIB_JS_ENGINE, else quickjs, else dukpy; see
:mod:proxylib.pac.engines). functions adds to or overrides the PAC
builtins the script can call; the script runs in a helper process, so
they must be picklable module-level functions it can import.
timeout bounds each call in seconds (the builtins' own DNS lookups are
bounded at 2 s each): a call that overruns raises :class:PACError, its
helper process is ended, and for the next 30 seconds every call raises
:class:PACError at once; after that the script is started afresh. A
script that overruns while loading is not loaded.
With no engine installed, on_missing_engine="raise" (the default)
raises :class:MissingExtraError; "direct" warns and the script
answers "DIRECT" to everything.
A script error, a script that does not define FindProxyForURL and an
overrun all raise :class:PACError. Safe to share between threads; calls
are serialised. close() releases the engine (garbage collection does
too).
Source code in src/proxylib/pac/script.py
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 | |
engine_name = engine_cls.name
instance-attribute
source = source
instance-attribute
timeout = timeout
instance-attribute
__del__()
Source code in src/proxylib/pac/script.py
198 199 | |
__repr__()
Source code in src/proxylib/pac/script.py
206 207 208 | |
close()
Source code in src/proxylib/pac/script.py
201 202 203 204 | |
find_proxy_for_url(url, host)
Run FindProxyForURL(url, host). A result that is not a string
is "DIRECT", as browsers treat it.
Source code in src/proxylib/pac/script.py
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 | |
from_file(path, *, encoding='utf-8-sig', **options)
classmethod
Read a script from a file. options are those of PacScript.
Source code in src/proxylib/pac/script.py
240 241 242 243 244 245 | |
from_url(url, *, timeout=10.0, max_bytes=MAX_PAC_BYTES, cache_ttl=300.0, **options)
classmethod
Download a script, never through a proxy. timeout bounds the
download; a body over max_bytes raises :class:PACError. A
file:// URL is read directly and never cached; downloaded text is
kept for cache_ttl seconds (0/None: no cache). options
are those of PacScript, whose timeout is spelled
script_timeout here.
Source code in src/proxylib/pac/script.py
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 | |
proxylib.pac.map
PacProxyMap: a proxy map answered by a PAC script or a Python function.
FindProxyForURL = Callable[[str, str], str]
module-attribute
__all__ = ('PacProxyMap',)
module-attribute
PacProxyMap(script, *, browser_compatibility=False)
A proxy map that asks FindProxyForURL(url, host) for every URL.
script is a :class:PacScript or a Python callable with the same
signature, which takes the place of subclassing. host is the URL's
host, lower-cased. The result string is read with
:meth:Proxy.parse_pac_result; a PAC always answers, so this map never
raises KeyError: whatever a Python function raises becomes
:class:~proxylib.PACError, as a script error does.
browser_compatibility=True passes an https URL without its path and
query, as Chrome and Firefox do so a PAC server cannot see them.
Source code in src/proxylib/pac/map.py
33 34 35 36 37 | |
__slots__ = ('script', 'browser_compatibility')
class-attribute
instance-attribute
browser_compatibility = browser_compatibility
instance-attribute
script = script
instance-attribute
__getitem__(url)
Source code in src/proxylib/pac/map.py
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | |
__repr__()
Source code in src/proxylib/pac/map.py
39 40 | |
close()
End the script's helper process (a Python function has none).
Source code in src/proxylib/pac/map.py
42 43 44 45 46 | |
proxylib.pac.wpad
DNS + HTTP WPAD (Web Proxy Auto-Discovery), which
ProxyConfig.to_proxymap() runs when the OS asks for auto-detection.
DHCP option 252 discovery is intentionally not implemented: it needs raw access to the OS's DHCP lease data, which has no portable stdlib-only path.
__all__ = ('discover',)
module-attribute
discover(fqdn=None, *, timeout=3.0, cache_ttl=300.0, on_missing_engine='raise')
Try each http://wpad.<domain>/wpad.dat from most to least specific.
Returns a :class:PacProxyMap for the first PAC that loads, or None.
A domain whose PAC fails to download or to run is skipped; with no JS
engine installed, :class:MissingExtraError is raised unless
on_missing_engine="direct". Results, failures included, are cached
per fqdn for cache_ttl seconds (0/None: probe afresh).
Source code in src/proxylib/pac/wpad.py
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | |
proxylib.pac.engines
PAC JavaScript engines.
Two are supported: quickjs (proxylib[pac]; closer to the spec, has
time and memory limits) and dukpy (proxylib[dukpy]; a wheel exists
where quickjs has none, such as Windows ARM64). Neither is imported until a
script needs it.
Which one runs: the engine= argument, else the PROXYLIB_JS_ENGINE
environment variable (a comma-separated priority list such as
"dukpy,quickjs"), else quickjs, then dukpy. An unknown name is a
ValueError.
__all__ = ('JSEngine', 'names', 'get_engine')
module-attribute
JSEngine
Bases: Protocol
One JavaScript execution context. Not thread-safe: PacScript calls
it from a single thread.
call(name, *args)
Call the JS global function name with args.
name is resolved in the global scope on every call, so a script
that redefines an exported function overrides it.
Source code in src/proxylib/pac/engines/_base.py
27 28 29 30 31 32 33 | |
eval(code)
Evaluate a script (the PAC source, once).
Source code in src/proxylib/pac/engines/_base.py
23 24 25 | |
export_function(name, func)
Make func callable from JS as the global function name.
Source code in src/proxylib/pac/engines/_base.py
19 20 21 | |
set_memory_limit(limit)
Cap the context's memory at limit bytes where the engine
supports it; an engine without one ignores the call. (Time is bounded
from outside: the script runs in a helper process.)
Source code in src/proxylib/pac/engines/_base.py
35 36 37 38 39 | |
get_engine(engine=None)
The engine class to use, or None when none of the wanted engines
is installed.
Source code in src/proxylib/pac/engines/__init__.py
58 59 60 61 62 63 64 | |
names()
The engines installed here, in default priority order.
Source code in src/proxylib/pac/engines/__init__.py
36 37 38 | |