1 use std::error::Error as StdError;
2 use std::fmt::{Display, self};
3 use std::str::{self, FromStr};
4 
5 #[cfg(feature = "compat")]
6 use http;
7 
8 use ::common::ByteStr;
9 
10 /// The Request-URI of a Request's StartLine.
11 ///
12 /// From Section 5.3, Request Target:
13 /// > Once an inbound connection is obtained, the client sends an HTTP
14 /// > request message (Section 3) with a request-target derived from the
15 /// > target URI.  There are four distinct formats for the request-target,
16 /// > depending on both the method being requested and whether the request
17 /// > is to a proxy.
18 /// >
19 /// > ```notrust
20 /// > request-target = origin-form
21 /// >                / absolute-form
22 /// >                / authority-form
23 /// >                / asterisk-form
24 /// > ```
25 ///
26 /// # Uri explanations
27 /// ```notrust
28 /// abc://username:password@example.com:123/path/data?key=value&key2=value2#fragid1
29 /// |-|   |-------------------------------||--------| |-------------------| |-----|
30 ///  |                  |                       |               |              |
31 /// scheme          authority                 path            query         fragment
32 /// ```
33 #[derive(Clone, Hash)]
34 pub struct Uri {
35     source: ByteStr,
36     scheme_end: Option<usize>,
37     authority_end: Option<usize>,
38     query_start: Option<usize>,
39     fragment_start: Option<usize>,
40 }
41 
42 impl Uri {
43     /// Parse a string into a `Uri`.
new(mut s: ByteStr) -> Result<Uri, UriError>44     fn new(mut s: ByteStr) -> Result<Uri, UriError> {
45         if s.len() == 0 {
46             Err(UriError(ErrorKind::Empty))
47         } else if s.as_bytes() == b"*" {
48             // asterisk-form
49             Ok(asterisk_form())
50         } else if s.as_bytes() == b"/" {
51             // shortcut for '/'
52             Ok(Uri::default())
53         } else if s.as_bytes()[0] == b'/' {
54             // origin-form
55             let query = parse_query(&s);
56             let fragment = parse_fragment(&s);
57             Ok(Uri {
58                 source: s,
59                 scheme_end: None,
60                 authority_end: None,
61                 query_start: query,
62                 fragment_start: fragment,
63             })
64         } else if s.contains("://") {
65             // absolute-form
66             let scheme = parse_scheme(&s);
67             let auth = Some(parse_authority(&s));
68             let scheme_end = scheme.expect("just checked for ':' above");
69             let auth_end = auth.expect("just checked for ://");
70             if scheme_end + 3 == auth_end {
71                 // authority was empty
72                 return Err(UriError(ErrorKind::MissingAuthority));
73             }
74             {
75                 let authority = &s.as_bytes()[scheme_end + 3..auth_end];
76                 let has_start_bracket = authority.contains(&b'[');
77                 let has_end_bracket = authority.contains(&b']');
78                 if has_start_bracket ^ has_end_bracket {
79                     // has only 1 of [ and ]
80                     return Err(UriError(ErrorKind::Malformed));
81                 }
82             }
83 
84             // absolute-form must always have a path
85             // if there isn't a '/', for consistency, add one.
86             let slash = auth_end;
87             if s.len() == slash {
88                 s.insert(slash, '/');
89             } else if s.as_bytes()[slash] != b'/' {
90                 s.insert(slash, '/');
91             }
92 
93             let query = parse_query(&s);
94             let fragment = parse_fragment(&s);
95 
96             Ok(Uri {
97                 source: s,
98                 scheme_end: scheme,
99                 authority_end: auth,
100                 query_start: query,
101                 fragment_start: fragment,
102             })
103         } else if s.contains("/") || s.contains("?") {
104             // last possibility is authority-form, above are illegal characters
105             Err(UriError(ErrorKind::Malformed))
106         } else {
107             // authority-form
108             let len = s.len();
109             Ok(Uri {
110                 source: s,
111                 scheme_end: None,
112                 authority_end: Some(len),
113                 query_start: None,
114                 fragment_start: None,
115             })
116         }
117     }
118 
119     /// Get the path of this `Uri`.
120     #[inline]
path(&self) -> &str121     pub fn path(&self) -> &str {
122         let index = self.path_start();
123         let end = self.path_end();
124         if index >= end {
125             if self.scheme().is_some() {
126                 "/" // absolute-form MUST have path
127             } else {
128                 ""
129             }
130         } else {
131             &self.source[index..end]
132         }
133     }
134 
135     #[inline]
path_start(&self) -> usize136     fn path_start(&self) -> usize {
137         self.authority_end.unwrap_or(self.scheme_end.unwrap_or(0))
138     }
139 
140     #[inline]
path_end(&self) -> usize141     fn path_end(&self) -> usize {
142         if let Some(query) = self.query_start {
143             query
144         } else if let Some(fragment) = self.fragment_start {
145             fragment
146         } else {
147             self.source.len()
148         }
149     }
150 
151     /// Get the scheme of this `Uri`.
152     #[inline]
scheme(&self) -> Option<&str>153     pub fn scheme(&self) -> Option<&str> {
154         if let Some(end) = self.scheme_end {
155             Some(&self.source[..end])
156         } else {
157             None
158         }
159     }
160 
161     /// Get the authority of this `Uri`.
162     #[inline]
authority(&self) -> Option<&str>163     pub fn authority(&self) -> Option<&str> {
164         if let Some(end) = self.authority_end {
165             let index = self.scheme_end.map(|i| i + 3).unwrap_or(0);
166 
167             Some(&self.source[index..end])
168         } else {
169             None
170         }
171     }
172 
173     /// Get the host of this `Uri`.
174     #[inline]
host(&self) -> Option<&str>175     pub fn host(&self) -> Option<&str> {
176         self.authority().map(|auth| {
177             let host_port = auth.rsplit('@')
178                 .next()
179                 .expect("split always has at least 1 item");
180             if host_port.as_bytes()[0] == b'[' {
181                 let i = host_port.find(']')
182                     .expect("parsing should validate matching brackets");
183                 &host_port[1..i]
184             } else {
185                 host_port.split(':')
186                     .next()
187                     .expect("split always has at least 1 item")
188             }
189         })
190     }
191 
192     /// Get the port of this `Uri`.
193     #[inline]
port(&self) -> Option<u16>194     pub fn port(&self) -> Option<u16> {
195         match self.authority() {
196             Some(auth) => auth.rfind(':').and_then(|i| auth[i+1..].parse().ok()),
197             None => None,
198        }
199     }
200 
201     /// Get the query string of this `Uri`, starting after the `?`.
202     #[inline]
query(&self) -> Option<&str>203     pub fn query(&self) -> Option<&str> {
204         self.query_start.map(|start| {
205             // +1 to remove '?'
206             let start = start + 1;
207             if let Some(end) = self.fragment_start {
208                 &self.source[start..end]
209             } else {
210                 &self.source[start..]
211             }
212         })
213     }
214 
215     /// Returns whether this URI is in `absolute-form`.
216     ///
217     /// An example of absolute form is `https://hyper.rs`.
218     #[inline]
is_absolute(&self) -> bool219     pub fn is_absolute(&self) -> bool {
220         self.scheme_end.is_some()
221     }
222 
223     #[cfg(test)]
fragment(&self) -> Option<&str>224     fn fragment(&self) -> Option<&str> {
225         self.fragment_start.map(|start| {
226             // +1 to remove the '#'
227            &self.source[start + 1..]
228         })
229     }
230 }
231 
parse_scheme(s: &str) -> Option<usize>232 fn parse_scheme(s: &str) -> Option<usize> {
233     s.find(':')
234 }
235 
parse_authority(s: &str) -> usize236 fn parse_authority(s: &str) -> usize {
237     let i = s.find("://").map(|p| p + 3).unwrap_or(0);
238     s[i..]
239         .find(|ch| ch == '/' || ch == '?' || ch == '#')
240         .map(|end| end + i)
241         .unwrap_or(s.len())
242 }
243 
parse_query(s: &str) -> Option<usize>244 fn parse_query(s: &str) -> Option<usize> {
245     s.find('?').and_then(|i| {
246         if let Some(frag) = s.find('#') {
247             if frag < i {
248                 None
249             } else {
250                 Some(i)
251             }
252         } else {
253             Some(i)
254         }
255     })
256 }
257 
parse_fragment(s: &str) -> Option<usize>258 fn parse_fragment(s: &str) -> Option<usize> {
259     s.find('#')
260 }
261 
262 impl FromStr for Uri {
263     type Err = UriError;
264 
from_str(s: &str) -> Result<Uri, UriError>265     fn from_str(s: &str) -> Result<Uri, UriError> {
266         //TODO: refactor such that the to_owned() is only required at the end
267         //of successful parsing, so an Err doesn't needlessly clone the string.
268         Uri::new(ByteStr::from(s))
269     }
270 }
271 
272 impl PartialEq for Uri {
eq(&self, other: &Uri) -> bool273     fn eq(&self, other: &Uri) -> bool {
274         self.source.as_str() == other.source.as_str()
275     }
276 }
277 
278 impl PartialEq<str> for Uri {
eq(&self, other: &str) -> bool279     fn eq(&self, other: &str) -> bool {
280         self.source.as_str() == other
281     }
282 }
283 
284 // FIXME delete for 0.12
285 impl<'a> PartialEq<&'a str> for Uri {
eq(&self, other: & &'a str) -> bool286     fn eq(&self, other: & &'a str) -> bool {
287         self.source.as_str() == *other
288     }
289 }
290 
291 impl<'a> PartialEq<Uri> for &'a str{
eq(&self, other: &Uri) -> bool292     fn eq(&self, other: &Uri) -> bool {
293         *self == other.source.as_str()
294     }
295 }
296 
297 impl Eq for Uri {}
298 
299 impl AsRef<str> for Uri {
as_ref(&self) -> &str300     fn as_ref(&self) -> &str {
301         self.source.as_str()
302     }
303 }
304 
305 impl Default for Uri {
default() -> Uri306     fn default() -> Uri {
307         Uri {
308             source: ByteStr::from_static("/"),
309             scheme_end: None,
310             authority_end: None,
311             query_start: None,
312             fragment_start: None,
313         }
314     }
315 }
316 
317 impl fmt::Debug for Uri {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result318     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
319         fmt::Debug::fmt(self.as_ref(), f)
320     }
321 }
322 
323 impl Display for Uri {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result324     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
325         f.write_str(self.as_ref())
326     }
327 }
328 
329 #[cfg(feature = "compat")]
330 impl From<http::Uri> for Uri {
from(uri: http::Uri) -> Uri331     fn from(uri: http::Uri) -> Uri {
332         uri.to_string().parse()
333             .expect("attempted to convert invalid uri")
334     }
335 }
336 
337 #[cfg(feature = "compat")]
338 impl From<Uri> for http::Uri {
from(uri: Uri) -> http::Uri339     fn from(uri: Uri) -> http::Uri {
340         let bytes = uri.source.into_bytes();
341         http::Uri::from_shared(bytes)
342             .expect("attempted to convert invalid uri")
343     }
344 }
345 
346 #[inline]
asterisk_form() -> Uri347 fn asterisk_form() -> Uri {
348     Uri {
349         source: ByteStr::from_static("*"),
350         scheme_end: None,
351         authority_end: None,
352         query_start: None,
353         fragment_start: None,
354     }
355 }
356 
357 /// An error parsing a `Uri`.
358 #[derive(Clone, Debug)]
359 pub struct UriError(ErrorKind);
360 
361 #[derive(Clone, Debug)]
362 enum ErrorKind {
363     Empty,
364     Malformed,
365     MissingAuthority,
366 }
367 
368 impl fmt::Display for UriError {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result369     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
370         f.pad(self.description())
371     }
372 }
373 
374 impl StdError for UriError {
description(&self) -> &str375     fn description(&self) -> &str {
376         match self.0 {
377             ErrorKind::Empty => "empty Uri string",
378             ErrorKind::Malformed => "invalid character in Uri authority",
379             ErrorKind::MissingAuthority => "absolute Uri missing authority segment",
380         }
381     }
382 }
383 
384 macro_rules! test_parse {
385     (
386         $test_name:ident,
387         $str:expr,
388         $($method:ident = $value:expr,)*
389     ) => (
390         #[test]
391         fn $test_name() {
392             let uri = Uri::from_str($str).unwrap();
393             $(
394             assert_eq!(uri.$method(), $value, stringify!($method));
395             )+
396         }
397     );
398 }
399 
400 test_parse! {
401     test_uri_parse_origin_form,
402     "/some/path/here?and=then&hello#and-bye",
403 
404     scheme = None,
405     authority = None,
406     path = "/some/path/here",
407     query = Some("and=then&hello"),
408     fragment = Some("and-bye"),
409 }
410 
411 test_parse! {
412     test_uri_parse_absolute_form,
413     "http://127.0.0.1:61761/chunks",
414 
415     scheme = Some("http"),
416     authority = Some("127.0.0.1:61761"),
417     host = Some("127.0.0.1"),
418     path = "/chunks",
419     query = None,
420     fragment = None,
421     port = Some(61761),
422 }
423 
424 test_parse! {
425     test_uri_parse_absolute_form_without_path,
426     "https://127.0.0.1:61761",
427 
428     scheme = Some("https"),
429     authority = Some("127.0.0.1:61761"),
430     host = Some("127.0.0.1"),
431     path = "/",
432     query = None,
433     fragment = None,
434     port = Some(61761),
435 
436     to_string = "https://127.0.0.1:61761/",
437 }
438 
439 test_parse! {
440     test_uri_parse_asterisk_form,
441     "*",
442 
443     scheme = None,
444     authority = None,
445     path = "*",
446     query = None,
447     fragment = None,
448 
449     to_string = "*",
450 }
451 
452 test_parse! {
453     test_uri_parse_authority_no_port,
454     "localhost",
455 
456     scheme = None,
457     authority = Some("localhost"),
458     host = Some("localhost"),
459     path = "",
460     query = None,
461     fragment = None,
462     port = None,
463 
464     to_string = "localhost",
465 }
466 
467 test_parse! {
468     test_uri_parse_authority_form,
469     "localhost:3000",
470 
471     scheme = None,
472     authority = Some("localhost:3000"),
473     host = Some("localhost"),
474     path = "",
475     query = None,
476     fragment = None,
477     port = Some(3000),
478 }
479 
480 test_parse! {
481     test_uri_parse_absolute_with_default_port_http,
482     "http://127.0.0.1:80/foo",
483 
484     scheme = Some("http"),
485     authority = Some("127.0.0.1:80"),
486     host = Some("127.0.0.1"),
487     path = "/foo",
488     query = None,
489     fragment = None,
490     port = Some(80),
491 }
492 
493 test_parse! {
494     test_uri_parse_absolute_with_default_port_https,
495     "https://127.0.0.1:443",
496 
497     scheme = Some("https"),
498     authority = Some("127.0.0.1:443"),
499     host = Some("127.0.0.1"),
500     path = "/",
501     query = None,
502     fragment = None,
503     port = Some(443),
504 }
505 
506 test_parse! {
507     test_uri_parse_absolute_with_ipv6,
508     "https://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:8008",
509 
510     scheme = Some("https"),
511     authority = Some("[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:8008"),
512     host = Some("2001:0db8:85a3:0000:0000:8a2e:0370:7334"),
513     path = "/",
514     query = None,
515     fragment = None,
516     port = Some(8008),
517 }
518 
519 test_parse! {
520     test_uri_parse_absolute_with_ipv6_and_no_port,
521     "https://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]",
522 
523     scheme = Some("https"),
524     authority = Some("[2001:0db8:85a3:0000:0000:8a2e:0370:7334]"),
525     host = Some("2001:0db8:85a3:0000:0000:8a2e:0370:7334"),
526     path = "/",
527     query = None,
528     fragment = None,
529     port = None,
530 }
531 
532 test_parse! {
533     test_uri_parse_absolute_with_userinfo,
534     "https://seanmonstar:password@hyper.rs",
535 
536     scheme = Some("https"),
537     authority = Some("seanmonstar:password@hyper.rs"),
538     host = Some("hyper.rs"),
539     path = "/",
540     query = None,
541     fragment = None,
542     port = None,
543 }
544 
545 test_parse! {
546     test_uri_parse_fragment_questionmark,
547     "http://127.0.0.1/#?",
548 
549     scheme = Some("http"),
550     authority = Some("127.0.0.1"),
551     host = Some("127.0.0.1"),
552     path = "/",
553     query = None,
554     fragment = Some("?"),
555     port = None,
556 }
557 
558 test_parse! {
559     test_uri_parse_path_with_terminating_questionmark,
560     "http://127.0.0.1/path?",
561 
562     scheme = Some("http"),
563     authority = Some("127.0.0.1"),
564     host = Some("127.0.0.1"),
565     path = "/path",
566     query = Some(""),
567     fragment = None,
568     port = None,
569 }
570 
571 test_parse! {
572     test_uri_parse_absolute_form_with_empty_path_and_nonempty_query,
573     "http://127.0.0.1?foo=bar",
574 
575     scheme = Some("http"),
576     authority = Some("127.0.0.1"),
577     host = Some("127.0.0.1"),
578     path = "/",
579     query = Some("foo=bar"),
580     fragment = None,
581     port = None,
582 
583     to_string = "http://127.0.0.1/?foo=bar",
584 }
585 
586 test_parse! {
587     test_uri_parse_absolute_form_with_empty_path_and_fragment_with_slash,
588     "http://127.0.0.1#foo/bar",
589     scheme = Some("http"),
590     authority = Some("127.0.0.1"),
591     host = Some("127.0.0.1"),
592     path = "/",
593     query = None,
594     fragment = Some("foo/bar"),
595     port = None,
596 }
597 
598 test_parse! {
599     test_uri_parse_absolute_form_with_empty_path_and_fragment_with_questionmark,
600     "http://127.0.0.1#foo?bar",
601     scheme = Some("http"),
602     authority = Some("127.0.0.1"),
603     host = Some("127.0.0.1"),
604     path = "/",
605     query = None,
606     fragment = Some("foo?bar"),
607     port = None,
608 
609     to_string = "http://127.0.0.1/#foo?bar",
610 }
611 
612 #[test]
test_uri_parse_error()613 fn test_uri_parse_error() {
614     fn err(s: &str) {
615         Uri::from_str(s).unwrap_err();
616     }
617 
618     err("http://");
619     err("htt:p//host");
620     err("hyper.rs/");
621     err("hyper.rs?key=val");
622     err("?key=val");
623     err("localhost/");
624     err("localhost?key=val");
625     err("http://::1]");
626     err("http://[::1");
627 }
628