#[non_exhaustive]pub struct BypassRules {
pub patterns: Vec<HostPattern>,
pub exclude_simple_hostnames: bool,
pub reversed_exceptions: bool,
pub rejected: Vec<RejectedValue>,
pub require_explicit_port: bool,
pub implicit: ImplicitBypass,
pub ipv4_mapped_as_ipv4: bool,
pub strip_trailing_dot: bool,
}Expand description
Destinations that must not go through a proxy
(parse::no_proxy / parse::proxy_override).
Fields (Non-exhaustive)§
This struct is marked as non-exhaustive
Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.patterns: Vec<HostPattern>Parsed list entries.
exclude_simple_hostnames: boolDot-less host bypass, as a switch (macOS ExcludeSimpleHostnames). It takes any
destination without a dot, a dot-less IPv6 literal included, where the list
spelling HostPattern::Local takes no IP literal; and under
reversed_exceptions the two mean opposite things (see
excludes_simple_hostnames).
reversed_exceptions: boolKDE/config-kde inclusion list; the implicit set, where there is one, still
applies; see matches.
rejected: Vec<RejectedValue>Redacted unparseable originals; affects verdict under
reversed_exceptions. Not a complete ledger of the
entries that match nothing: see HostPattern::Wildcard.
require_explicit_port: boolWhether a destination’s port counts only when the destination wrote one, so that a
ported entry such as example.com:80 does not meet a portless http://example.com/.
Set for GNOME ignore-hosts and nowhere else. GLib resolves the destination with
G_URI_FLAGS_NONE (gsimpleproxyresolver.c:341) and fills a scheme’s default port
only under G_URI_FLAGS_SCHEME_NORMALIZE (guri.c:1006), so the port it compares
against is 0 unless the URL carried one. Windows fills it: a ProxyOverride entry
of host:80 was measured bypassing a portless http://host/.
matches does not read this: it is told a port and answers about
that port. matches_url is where it is read, and is the reason
to prefer that method whenever the destination is a Url: it passes
Url::port rather than
port_or_known_default when this is set. Reading the field by hand is supported, not
recommended.
Url drops a port equal to the scheme’s default while parsing, so http://host:80/
and http://host/ become the same value and port returns None for both. A rule
on that port (host:80 for http, host:443 for https) therefore never matches
here, while GLib still matches the spelling with an explicit port. The error is in
the safe direction: the destination uses the proxy instead of connecting directly.
URL parsing removes the distinction before this crate receives the URL.
implicit: ImplicitBypassThe destinations bypassed with no entry naming them. ImplicitBypass::Broad
unless the source says otherwise; each store sets its own resolver’s set.
ipv4_mapped_as_ipv4: boolWhether an IPv4-mapped IPv6 destination ([::ffff:10.0.0.1]) meets the entries as
the IPv4 address it maps. Set by default and for
parse::no_proxy, which is Chromium’s and Go’s reading.
Cleared for every store’s list. Android’s selector compares the host string, and
WinINet and CFNetwork are unmeasured for an explicit entry and take the reading that
cannot send a proxied destination direct. GNOME ignore-hosts and KDE NoProxyFor
are read off their resolvers, which compare the address in the family it was
written in: GLib’s g_inet_address_mask_matches
and Qt’s QHostAddress::isInSubnet, which KIO calls, both answer no across
families, so 10.0.0.0/8 does not bypass [::ffff:10.0.0.1] there.
implicit does not read this; which spellings an implicit set
covers is part of the set.
strip_trailing_dot: boolWhether a destination’s trailing dot is shed before it meets the entries, so that
example.com and *.example.com also match example.com. and www.example.com..
Set by default and for parse::no_proxy, which read the
dot as the DNS root it denotes.
Set for macOS and iOS too: CFNetwork was measured shedding the dot on the
destination and on the entry (cfnetwork_sheds_a_trailing_dot in
tests/mac_exceptions_list.rs).
Cleared for every other store, because each of those resolvers compares the name as
written (and a store entry that itself ends in a dot is refused into
rejected there for the same reason): WinINet sent localhost.
to the proxy under a ProxyOverride naming localhost, GLib’s ignore_host and
KIO’s revmatch compare text, and Android’s ProxySelector compares strings. The
implicit set and the simple-hostname switch do not read this; each set says which
spellings it covers, and a trailing dot is a dot.
Implementations§
Source§impl BypassRules
impl BypassRules
Sourcepub const fn new() -> Self
pub const fn new() -> Self
An empty rule set with the ImplicitBypass::Broad implicit set, so loopback
destinations are bypassed.
Sourcepub fn bypass_loopback(&self) -> bool
pub fn bypass_loopback(&self) -> bool
Whether the list leaves the implicit bypass set (implicit) in
force, which it does unless it carries HostPattern::SubtractImplicit
(<-loopback>). Under ImplicitBypass::Empty there is no set to leave in force,
and the answer is about the list alone.
A rough answer, and the reason it is not a field: <-loopback> subtracts the
implicit set only from the entries written before it, so a list that carries the
token can still bypass a loopback destination named after it. Ask
matches about a destination; ask this about the list.
assert!(parse::no_proxy("localhost").bypass_loopback());
assert!(!parse::proxy_override("<-loopback>").bypass_loopback());
// The token is in the list, so this is `false`, but `localhost` is written
// after it and wins on the destination it names.
let readded = parse::proxy_override("<-loopback>;localhost");
assert!(!readded.bypass_loopback());
assert!(readded.matches_authority("localhost"));
assert!(!readded.matches_authority("127.0.0.1"));Sourcepub fn excludes_simple_hostnames(&self) -> bool
pub fn excludes_simple_hostnames(&self) -> bool
Whether either representation of the dot-less host rule is present: the
exclude_simple_hostnames switch or a
HostPattern::Local entry.
The switch also bypasses dot-less IPv6 literals, which a Local entry does not.
Under reversed_exceptions, the switch bypasses
dot-less hosts before the list is consulted, as the implicit set does. A Local
entry is part of the inclusion list, so it keeps dot-less hosts using the proxy. A
true result therefore does not guarantee that matches bypasses
a dot-less host; use matches to check a destination. Converting between these
representations silently reverses that verdict.
Sourcepub fn is_empty(&self) -> bool
pub fn is_empty(&self) -> bool
No entries and neither switch: patterns empty,
exclude_simple_hostnames and
reversed_exceptions off. <-loopback> is an entry
in patterns, so a list holding only that is not empty; it
carries an instruction, and reporting it empty would invite a caller to drop it and
put the implicit bypass back. Omits rejected (only matters under
reversed_exceptions, which already answers false).
Omits implicit too: an empty list is “no entries”, not “bypasses
nothing”. Under ImplicitBypass::WinInet it still sends WinINet’s six destinations
direct, and only under ImplicitBypass::Empty does it send nothing direct.
assert!(BypassRules::new().is_empty());
assert!(parse::no_proxy("").is_empty());
// `<-loopback>` is an instruction, not an absence of one.
assert!(!parse::proxy_override("<-loopback>").is_empty());Sourcepub fn matches(&self, host: &Host, port: Option<u16>) -> bool
pub fn matches(&self, host: &Host, port: Option<u16>) -> bool
Whether host bypasses the proxy.
An empty host returns false in either mode before any other checks. The crate’s
routing functions handle hostless URLs as Direct before consulting the bypass list,
so this applies to caller-constructed Host values. A Host::Domain is treated as
text without re-parsing it as an address: Domain("0177.0.0.1") misses the loopback
switch that matches_authority matches for the same
text. Url::host and url::Host::parse normalize numeric spellings to Host::Ipv4
and cannot produce this domain value. Re-parsing here would add work to every call
for a value that requires explicit construction with Host::Domain(_).
No name resolution is performed. A host that /etc/hosts or DNS maps to 127.0.0.1
is matched as text, so it misses the implicit set and uses the proxy unless an entry
matches it. Chromium matches the same way:ProxyHostMatchingRules::Matches takes
a GURL and its rules check url.host(). Resolving names in this per-request
predicate would add lookup latency and failures to each request.
Ported :port rules never match port = None (Go). Under
reversed_exceptions, patterns are inclusion-only
while rejected is empty.
Entries are evaluated back to front, and the first matching rule determines the
result: “Later rules override earlier rules … when mixing positive and negative
rules, evaluation order makes a difference” (Chromium,
net/base/scheme_host_port_matcher.cc; the expectation “comes from WinInet (which
is where <-loopback> comes from)”, proxy_host_matching_rules_unittest.cc). Order
matters only with HostPattern::SubtractImplicit, which removes a bypass:
127.0.0.1;<-loopback> proxies 127.0.0.1, while <-loopback>;127.0.0.1 sends it
direct. If no entry matches, the implicit set (implicit)
determines the result. It is not inverted by reversed_exceptions, because an
inclusion list that does not name loopback does not request proxying for loopback.
exclude_simple_hostnames is a switch and not an
entry, so it is not inverted either, unlike a HostPattern::Local entry, which
is; see excludes_simple_hostnames for why that
changes the answer here. An IPv4-mapped IPv6 destination (::ffff:a.b.c.d) is
compared with the entries as the IPv4 address it maps while
ipv4_mapped_as_ipv4 is set, against CIDR and exact
patterns alike.
let rules = parse::no_proxy("localhost, .example.com, 10.0.0.0/8");
assert!(rules.matches_authority("www.example.com"));
assert!(rules.matches_authority("10.1.2.3:443"));
assert!(!rules.matches_authority("example.org"));
// Loopback bypasses even though the list above names only `localhost`; link-local
// does not, in `no_proxy`.
assert!(rules.matches_authority("127.0.0.2"));
assert!(!rules.matches_authority("169.254.1.1"));
// A Windows list carries WinINet's implicit set, which stops at `127.0.0.1` and
// takes in link-local.
assert!(parse::proxy_override("").matches_authority("169.254.1.1"));
assert!(parse::proxy_override("").matches_authority("127.0.0.1"));
assert!(!parse::proxy_override("").matches_authority("127.0.0.2"));Sourcepub fn matches_url(&self, url: &Url) -> bool
pub fn matches_url(&self, url: &Url) -> bool
Whether a destination URL bypasses the proxy.
Prefer this over matches whenever the destination is a Url: it is
the only entry point that reads require_explicit_port,
and getting that wrong by hand is silent: a GNOME ignore-hosts of example.com:80
asked about with port_or_known_default reports a bypass GNOME does not have. This
crate’s own resolve goes through here.
A URL with no host to compare (data:, mailto:, or one whose host was emptied)
is reported as “does not bypass”, the same reading as an unparseable authority in
matches_authority. It is not a statement that such a URL
needs a proxy; a caller that routes hostless URLs direct should say so before asking.
let rules = parse::no_proxy(".example.com");
assert!(rules.matches_url(&Url::parse("https://www.example.com/x").unwrap()));
assert!(!rules.matches_url(&Url::parse("https://example.org/").unwrap()));
assert!(!rules.matches_url(&Url::parse("data:,hello").unwrap()));Convenience wrapper around BypassRules::matches taking a host[:port]
string such as example.com:8080 or [::1]:443.
Unparseable input is reported as “does not bypass”: an authority this cannot split is one it cannot prove is exempt, so it goes through the proxy, which can still refuse it. (Other stacks disagree on this edge; this crate does not claim their spelling.)
Supplies no default port: an authority written without one is asked with none, so
a rule spelled example.com:80 does not match example.com.
matches_url is the entry point that fills one in.