7
7
// except according to those terms.
8
8
9
9
use std:: ascii:: AsciiExt ;
10
- use std:: cmp;
11
10
use std:: fmt:: { self , Formatter } ;
11
+ use std:: net:: { Ipv4Addr , Ipv6Addr } ;
12
12
use parser:: { ParseResult , ParseError } ;
13
- use percent_encoding:: { from_hex , percent_decode} ;
13
+ use percent_encoding:: { percent_decode} ;
14
14
15
15
16
16
/// The host name of an URL.
17
17
#[ derive( PartialEq , Eq , Clone , Debug , Hash , PartialOrd , Ord ) ]
18
18
#[ cfg_attr( feature="heap_size" , derive( HeapSizeOf ) ) ]
19
19
pub 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.
24
21
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 ) ,
37
26
}
38
- #[ cfg( feature="heap_size" ) ]
39
- known_heap_size ! ( 0 , Ipv6Address ) ;
40
-
41
27
42
28
impl Host {
43
29
/// Parse a host: either an IPv6 address in [] square brackets, or a domain.
@@ -51,22 +37,30 @@ impl Host {
51
37
Err ( ParseError :: EmptyHost )
52
38
} else if input. starts_with ( "[" ) {
53
39
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
+ }
55
45
} else {
56
46
Err ( ParseError :: InvalidIpv6Address )
57
47
}
58
48
} 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) )
68
51
} 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
+ }
70
64
}
71
65
}
72
66
}
@@ -81,203 +75,11 @@ impl Host {
81
75
82
76
83
77
impl fmt:: Display for Host {
84
- fn fmt ( & self , formatter : & mut Formatter ) -> fmt:: Result {
78
+ fn fmt ( & self , f : & mut Formatter ) -> fmt:: Result {
85
79
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) ,
279
83
}
280
84
}
281
- finish_sequence ! ( 8 ) ;
282
- ( longest, longest + longest_length)
283
85
}
0 commit comments