77// except according to those terms.
88
99use std:: ascii:: AsciiExt ;
10- use std:: cmp;
1110use std:: fmt:: { self , Formatter } ;
11+ use std:: net:: { Ipv4Addr , Ipv6Addr } ;
1212use parser:: { ParseResult , ParseError } ;
13- use percent_encoding:: { from_hex , percent_decode} ;
13+ use percent_encoding:: { 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 ) ) ]
1919pub enum Host {
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
20+ /// A (DNS) domain name.
2421 Domain ( String ) ,
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 ]
22+ /// An IPv4 address.
23+ V4 ( Ipv4Addr ) ,
24+ /// An IPv6 address.
25+ V6 ( Ipv6Addr ) ,
3726}
38- #[ cfg( feature="heap_size" ) ]
39- known_heap_size ! ( 0 , Ipv6Address ) ;
40-
4127
4228impl Host {
4329 /// Parse a host: either an IPv6 address in [] square brackets, or a domain.
@@ -51,22 +37,30 @@ impl Host {
5137 Err ( ParseError :: EmptyHost )
5238 } else if input. starts_with ( "[" ) {
5339 if input. ends_with ( "]" ) {
54- Ipv6Address :: parse ( & input[ 1 ..input. len ( ) - 1 ] ) . map ( Host :: Ipv6 )
40+ if let Ok ( addr) = input[ 1 ..input. len ( ) - 1 ] . parse ( ) {
41+ Ok ( Host :: V6 ( addr) )
42+ } else {
43+ Err ( ParseError :: InvalidIpv6Address )
44+ }
5545 } else {
5646 Err ( ParseError :: InvalidIpv6Address )
5747 }
5848 } else {
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 )
49+ if let Ok ( addr) = input. parse ( ) {
50+ Ok ( Host :: V4 ( addr) )
6851 } else {
69- Ok ( Host :: Domain ( domain. to_ascii_lowercase ( ) ) )
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+ }
7064 }
7165 }
7266 }
@@ -81,203 +75,11 @@ impl Host {
8175
8276
8377impl fmt:: Display for Host {
84- fn fmt ( & self , formatter : & mut Formatter ) -> fmt:: Result {
78+ fn fmt ( & self , f : & mut Formatter ) -> fmt:: Result {
8579 match * self {
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 ;
80+ Host :: Domain ( ref domain) => domain. fmt ( f) ,
81+ Host :: V4 ( ref addr) => addr. fmt ( f) ,
82+ Host :: V6 ( ref addr) => write ! ( f, "[{}]" , addr) ,
27983 }
28084 }
281- finish_sequence ! ( 8 ) ;
282- ( longest, longest + longest_length)
28385}
0 commit comments