Skip to content

Commit c633569

Browse files
committed
Revert "Add IPv4 variant to Host and remove custom Ipv6 address type"
This reverts commit ecc7bb7.
1 parent 7161ea8 commit c633569

File tree

4 files changed

+232
-45
lines changed

4 files changed

+232
-45
lines changed

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[package]
22

33
name = "url"
4-
version = "0.3.0"
4+
version = "0.2.37"
55
authors = [ "Simon Sapin <[email protected]>" ]
66

77
description = "URL library for Rust, based on the WHATWG URL Standard"
@@ -17,7 +17,7 @@ serde_serialization = ["serde"]
1717
heap_size = ["heapsize", "heapsize_plugin"]
1818

1919
[dependencies.heapsize]
20-
version = "0.1.3"
20+
version = "0.1.1"
2121
optional = true
2222

2323
[dependencies.heapsize_plugin]

src/host.rs

Lines changed: 228 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,37 @@
77
// except according to those terms.
88

99
use std::ascii::AsciiExt;
10+
use std::cmp;
1011
use std::fmt::{self, Formatter};
11-
use std::net::{Ipv4Addr, Ipv6Addr};
1212
use parser::{ParseResult, ParseError};
13-
use percent_encoding::{percent_decode};
13+
use percent_encoding::{from_hex, percent_decode};
1414

1515

1616
/// The host name of an URL.
1717
#[derive(PartialEq, Eq, Clone, Debug, Hash, PartialOrd, Ord)]
1818
#[cfg_attr(feature="heap_size", derive(HeapSizeOf))]
1919
pub enum Host {
20-
/// A (DNS) domain name.
20+
/// A (DNS) domain name or an IPv4 address.
21+
///
22+
/// FIXME: IPv4 probably should be a separate variant.
23+
/// See https://www.w3.org/Bugs/Public/show_bug.cgi?id=26431
2124
Domain(String),
22-
/// An IPv4 address.
23-
V4(Ipv4Addr),
24-
/// An IPv6 address.
25-
V6(Ipv6Addr),
25+
26+
/// An IPv6 address, represented inside `[...]` square brackets
27+
/// so that `:` colon characters in the address are not ambiguous
28+
/// with the port number delimiter.
29+
Ipv6(Ipv6Address),
30+
}
31+
32+
33+
/// A 128 bit IPv6 address
34+
#[derive(Clone, Eq, PartialEq, Copy, Debug, Hash, PartialOrd, Ord)]
35+
pub struct Ipv6Address {
36+
pub pieces: [u16; 8]
2637
}
38+
#[cfg(feature="heap_size")]
39+
known_heap_size!(0, Ipv6Address);
40+
2741

2842
impl Host {
2943
/// Parse a host: either an IPv6 address in [] square brackets, or a domain.
@@ -37,30 +51,22 @@ impl Host {
3751
Err(ParseError::EmptyHost)
3852
} else if input.starts_with("[") {
3953
if input.ends_with("]") {
40-
if let Ok(addr) = input[1..input.len() - 1].parse() {
41-
Ok(Host::V6(addr))
42-
} else {
43-
Err(ParseError::InvalidIpv6Address)
44-
}
54+
Ipv6Address::parse(&input[1..input.len() - 1]).map(Host::Ipv6)
4555
} else {
4656
Err(ParseError::InvalidIpv6Address)
4757
}
4858
} else {
49-
if let Ok(addr) = input.parse() {
50-
Ok(Host::V4(addr))
59+
let decoded = percent_decode(input.as_bytes());
60+
let domain = String::from_utf8_lossy(&decoded);
61+
// TODO: Remove this check and use IDNA "domain to ASCII"
62+
if !domain.is_ascii() {
63+
Err(ParseError::NonAsciiDomainsNotSupportedYet)
64+
} else if domain.find(&[
65+
'\0', '\t', '\n', '\r', ' ', '#', '%', '/', ':', '?', '@', '[', '\\', ']'
66+
][..]).is_some() {
67+
Err(ParseError::InvalidDomainCharacter)
5168
} else {
52-
let decoded = percent_decode(input.as_bytes());
53-
let domain = String::from_utf8_lossy(&decoded);
54-
// TODO: Remove this check and use IDNA "domain to ASCII"
55-
if !domain.is_ascii() {
56-
Err(ParseError::NonAsciiDomainsNotSupportedYet)
57-
} else if domain.find(&[
58-
'\0', '\t', '\n', '\r', ' ', '#', '%', '/', ':', '?', '@', '[', '\\', ']'
59-
][..]).is_some() {
60-
Err(ParseError::InvalidDomainCharacter)
61-
} else {
62-
Ok(Host::Domain(domain.to_ascii_lowercase()))
63-
}
69+
Ok(Host::Domain(domain.to_ascii_lowercase()))
6470
}
6571
}
6672
}
@@ -75,11 +81,203 @@ impl Host {
7581

7682

7783
impl fmt::Display for Host {
78-
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
84+
fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
7985
match *self {
80-
Host::Domain(ref domain) => domain.fmt(f),
81-
Host::V4(ref addr) => addr.fmt(f),
82-
Host::V6(ref addr) => write!(f, "[{}]", addr),
86+
Host::Domain(ref domain) => domain.fmt(formatter),
87+
Host::Ipv6(ref address) => {
88+
try!(formatter.write_str("["));
89+
try!(address.fmt(formatter));
90+
formatter.write_str("]")
91+
}
92+
}
93+
}
94+
}
95+
96+
97+
impl Ipv6Address {
98+
/// Parse an IPv6 address, without the [] square brackets.
99+
pub fn parse(input: &str) -> ParseResult<Ipv6Address> {
100+
let input = input.as_bytes();
101+
let len = input.len();
102+
let mut is_ip_v4 = false;
103+
let mut pieces = [0, 0, 0, 0, 0, 0, 0, 0];
104+
let mut piece_pointer = 0;
105+
let mut compress_pointer = None;
106+
let mut i = 0;
107+
108+
if len < 2 {
109+
return Err(ParseError::InvalidIpv6Address)
110+
}
111+
112+
if input[0] == b':' {
113+
if input[1] != b':' {
114+
return Err(ParseError::InvalidIpv6Address)
115+
}
116+
i = 2;
117+
piece_pointer = 1;
118+
compress_pointer = Some(1);
119+
}
120+
121+
while i < len {
122+
if piece_pointer == 8 {
123+
return Err(ParseError::InvalidIpv6Address)
124+
}
125+
if input[i] == b':' {
126+
if compress_pointer.is_some() {
127+
return Err(ParseError::InvalidIpv6Address)
128+
}
129+
i += 1;
130+
piece_pointer += 1;
131+
compress_pointer = Some(piece_pointer);
132+
continue
133+
}
134+
let start = i;
135+
let end = cmp::min(len, start + 4);
136+
let mut value = 0u16;
137+
while i < end {
138+
match from_hex(input[i]) {
139+
Some(digit) => {
140+
value = value * 0x10 + digit as u16;
141+
i += 1;
142+
},
143+
None => break
144+
}
145+
}
146+
if i < len {
147+
match input[i] {
148+
b'.' => {
149+
if i == start {
150+
return Err(ParseError::InvalidIpv6Address)
151+
}
152+
i = start;
153+
is_ip_v4 = true;
154+
},
155+
b':' => {
156+
i += 1;
157+
if i == len {
158+
return Err(ParseError::InvalidIpv6Address)
159+
}
160+
},
161+
_ => return Err(ParseError::InvalidIpv6Address)
162+
}
163+
}
164+
if is_ip_v4 {
165+
break
166+
}
167+
pieces[piece_pointer] = value;
168+
piece_pointer += 1;
169+
}
170+
171+
if is_ip_v4 {
172+
if piece_pointer > 6 {
173+
return Err(ParseError::InvalidIpv6Address)
174+
}
175+
let mut dots_seen = 0;
176+
while i < len {
177+
// FIXME: https://github.com/whatwg/url/commit/1c22aa119c354e0020117e02571cec53f7c01064
178+
let mut value = 0u16;
179+
while i < len {
180+
let digit = match input[i] {
181+
c @ b'0' ... b'9' => c - b'0',
182+
_ => break
183+
};
184+
value = value * 10 + digit as u16;
185+
if value == 0 || value > 255 {
186+
return Err(ParseError::InvalidIpv6Address)
187+
}
188+
}
189+
if dots_seen < 3 && !(i < len && input[i] == b'.') {
190+
return Err(ParseError::InvalidIpv6Address)
191+
}
192+
pieces[piece_pointer] = pieces[piece_pointer] * 0x100 + value;
193+
if dots_seen == 0 || dots_seen == 2 {
194+
piece_pointer += 1;
195+
}
196+
i += 1;
197+
if dots_seen == 3 && i < len {
198+
return Err(ParseError::InvalidIpv6Address)
199+
}
200+
dots_seen += 1;
201+
}
202+
}
203+
204+
match compress_pointer {
205+
Some(compress_pointer) => {
206+
let mut swaps = piece_pointer - compress_pointer;
207+
piece_pointer = 7;
208+
while swaps > 0 {
209+
pieces[piece_pointer] = pieces[compress_pointer + swaps - 1];
210+
pieces[compress_pointer + swaps - 1] = 0;
211+
swaps -= 1;
212+
piece_pointer -= 1;
213+
}
214+
}
215+
_ => if piece_pointer != 8 {
216+
return Err(ParseError::InvalidIpv6Address)
217+
}
218+
}
219+
Ok(Ipv6Address { pieces: pieces })
220+
}
221+
222+
/// Serialize the IPv6 address to a string.
223+
pub fn serialize(&self) -> String {
224+
self.to_string()
225+
}
226+
}
227+
228+
229+
impl fmt::Display for Ipv6Address {
230+
fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
231+
let (compress_start, compress_end) = longest_zero_sequence(&self.pieces);
232+
let mut i = 0;
233+
while i < 8 {
234+
if i == compress_start {
235+
try!(formatter.write_str(":"));
236+
if i == 0 {
237+
try!(formatter.write_str(":"));
238+
}
239+
if compress_end < 8 {
240+
i = compress_end;
241+
} else {
242+
break;
243+
}
244+
}
245+
try!(write!(formatter, "{:x}", self.pieces[i as usize]));
246+
if i < 7 {
247+
try!(formatter.write_str(":"));
248+
}
249+
i += 1;
250+
}
251+
Ok(())
252+
}
253+
}
254+
255+
256+
fn longest_zero_sequence(pieces: &[u16; 8]) -> (isize, isize) {
257+
let mut longest = -1;
258+
let mut longest_length = -1;
259+
let mut start = -1;
260+
macro_rules! finish_sequence(
261+
($end: expr) => {
262+
if start >= 0 {
263+
let length = $end - start;
264+
if length > longest_length {
265+
longest = start;
266+
longest_length = length;
267+
}
268+
}
269+
};
270+
);
271+
for i in 0..8 {
272+
if pieces[i as usize] == 0 {
273+
if start < 0 {
274+
start = i;
275+
}
276+
} else {
277+
finish_sequence!(i);
278+
start = -1;
83279
}
84280
}
281+
finish_sequence!(8);
282+
(longest, longest + longest_length)
85283
}

src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ use std::cmp::Ordering;
143143
#[cfg(feature="serde_serialization")]
144144
use std::str::FromStr;
145145

146-
pub use host::Host;
146+
pub use host::{Host, Ipv6Address};
147147
pub use parser::{ErrorHandler, ParseResult, ParseError};
148148

149149
use percent_encoding::{percent_encode, lossy_utf8_percent_decode, DEFAULT_ENCODE_SET};
@@ -1140,3 +1140,4 @@ fn file_url_path_to_pathbuf_windows(path: &[String]) -> Result<PathBuf, ()> {
11401140
"to_file_path() failed to produce an absolute Path");
11411141
Ok(path)
11421142
}
1143+

src/tests.rs

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88

99

1010
use std::char;
11-
use std::net::{Ipv4Addr, Ipv6Addr};
1211
use super::{UrlParser, Url, SchemeData, RelativeSchemeData, Host};
1312

1413

@@ -348,14 +347,3 @@ fn relative_scheme_data_equality() {
348347
let b: Url = url("http://foo.com/");
349348
check_eq(&a, &b);
350349
}
351-
352-
#[test]
353-
fn host() {
354-
let a = Host::parse("www.mozilla.org").unwrap();
355-
let b = Host::parse("1.35.33.49").unwrap();
356-
let c = Host::parse("[2001:0db8:85a3:08d3:1319:8a2e:0370:7344]").unwrap();
357-
assert_eq!(a, Host::Domain("www.mozilla.org".to_owned()));
358-
assert_eq!(b, Host::V4(Ipv4Addr::new(1, 35, 33, 49)));
359-
assert_eq!(c, Host::V6(Ipv6Addr::new(0x2001, 0x0db8, 0x85a3, 0x08d3,
360-
0x1319, 0x8a2e, 0x0370, 0x7344)));
361-
}

0 commit comments

Comments
 (0)