hyper_old_types/
uri.rs

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