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#[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 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 Ok(asterisk_form())
51 } else if s.as_bytes() == b"/" {
52 Ok(Uri::default())
54 } else if s.as_bytes()[0] == b'/' {
55 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 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 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 return Err(UriError(ErrorKind::Malformed));
82 }
83 }
84
85 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 Err(UriError(ErrorKind::Malformed))
107 } else {
108 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 #[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 "/" } 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 #[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 #[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 #[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 #[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 #[inline]
213 pub fn query(&self) -> Option<&str> {
214 self.query_start.map(|start| {
215 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 #[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 &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 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 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
345impl<'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#[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); }
717}