Skip to content

Commit 5bb6c1d

Browse files
ComputerDruidseanmonstar
authored andcommitted
Allow % in IPv6 addresses to support zone identifiers. (#343)
https://tools.ietf.org/html/rfc6874 explains that %25 can be used to indicate a "zone identifier", useful for IPv6 link-local addresses.
1 parent 78058fd commit 5bb6c1d

File tree

1 file changed

+26
-2
lines changed

1 file changed

+26
-2
lines changed

src/uri/authority.rs

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,15 +103,20 @@ impl Authority {
103103
}
104104
b':' => {
105105
colon_cnt += 1;
106-
},
106+
}
107107
b'[' => {
108108
start_bracket = true;
109+
if has_percent {
110+
// Something other than the userinfo has a `%`, so reject it.
111+
return Err(ErrorKind::InvalidAuthority.into());
112+
}
109113
}
110114
b']' => {
111115
end_bracket = true;
112116

113117
// Those were part of an IPv6 hostname, so forget them...
114118
colon_cnt = 0;
119+
has_percent = false;
115120
}
116121
b'@' => {
117122
at_sign_pos = Some(i);
@@ -127,8 +132,11 @@ impl Authority {
127132
// the userinfo can have a percent-encoded username and password,
128133
// so record that a `%` was found. If this turns out to be
129134
// part of the userinfo, this flag will be cleared.
135+
// Also per https://tools.ietf.org/html/rfc6874, percent-encoding can
136+
// be used to indicate a zone identifier.
130137
// If the flag hasn't been cleared at the end, that means this
131-
// was part of the hostname, and will fail with an error.
138+
// was part of the hostname (and not part of an IPv6 address), and
139+
// will fail with an error.
132140
has_percent = true;
133141
}
134142
0 => {
@@ -612,4 +620,20 @@ mod tests {
612620
let err = Authority::parse_non_empty(b"a%2f:b%2f@example%2f.com").unwrap_err();
613621
assert_eq!(err.0, ErrorKind::InvalidAuthority);
614622
}
623+
624+
#[test]
625+
fn allows_percent_in_ipv6_address() {
626+
let authority_str = "[fe80::1:2:3:4%25eth0]";
627+
let result: Authority = authority_str.parse().unwrap();
628+
assert_eq!(result, authority_str);
629+
}
630+
631+
#[test]
632+
fn rejects_percent_outside_ipv6_address() {
633+
let err = Authority::parse_non_empty(b"1234%20[fe80::1:2:3:4]").unwrap_err();
634+
assert_eq!(err.0, ErrorKind::InvalidAuthority);
635+
636+
let err = Authority::parse_non_empty(b"[fe80::1:2:3:4]%20").unwrap_err();
637+
assert_eq!(err.0, ErrorKind::InvalidAuthority);
638+
}
615639
}

0 commit comments

Comments
 (0)