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 use 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)]
35 pub 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
43 impl Uri {
44 /// Parse a string into a `Uri`.
new(mut s: ByteStr) -> Result<Uri, UriError>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]
path(&self) -> &str122 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]
path_start(&self) -> usize137 fn path_start(&self) -> usize {
138 self.authority_end.unwrap_or(self.scheme_end.unwrap_or(0))
139 }
140
141 #[inline]
path_end(&self) -> usize142 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]
origin_form_end(&self) -> usize153 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]
scheme(&self) -> Option<&str>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]
authority(&self) -> Option<&str>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]
host(&self) -> Option<&str>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]
port(&self) -> Option<u16>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]
query(&self) -> Option<&str>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]
is_absolute(&self) -> bool229 pub fn is_absolute(&self) -> bool {
230 self.scheme_end.is_some()
231 }
232
233 #[cfg(test)]
fragment(&self) -> Option<&str>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)]
__internal_from_utf8_unchecked(slice: Bytes) -> Result<Uri, UriError>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)]
__internal_scheme_and_authority(&self) -> Option<Uri>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)]
__internal_origin_form(&self) -> Uri263 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
parse_scheme(s: &str) -> Option<usize>293 fn parse_scheme(s: &str) -> Option<usize> {
294 s.find(':')
295 }
296
parse_authority(s: &str) -> usize297 fn 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
parse_query(s: &str) -> Option<usize>305 fn 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
parse_fragment(s: &str) -> Option<usize>319 fn parse_fragment(s: &str) -> Option<usize> {
320 s.find('#')
321 }
322
323 impl FromStr for Uri {
324 type Err = UriError;
325
from_str(s: &str) -> Result<Uri, UriError>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
333 impl PartialEq for Uri {
eq(&self, other: &Uri) -> bool334 fn eq(&self, other: &Uri) -> bool {
335 self.source.as_str() == other.source.as_str()
336 }
337 }
338
339 impl PartialEq<str> for Uri {
eq(&self, other: &str) -> bool340 fn eq(&self, other: &str) -> bool {
341 self.source.as_str() == other
342 }
343 }
344
345 // FIXME delete for 0.12
346 impl<'a> PartialEq<&'a str> for Uri {
eq(&self, other: & &'a str) -> bool347 fn eq(&self, other: & &'a str) -> bool {
348 self.source.as_str() == *other
349 }
350 }
351
352 impl<'a> PartialEq<Uri> for &'a str{
eq(&self, other: &Uri) -> bool353 fn eq(&self, other: &Uri) -> bool {
354 *self == other.source.as_str()
355 }
356 }
357
358 impl Eq for Uri {}
359
360 impl AsRef<str> for Uri {
as_ref(&self) -> &str361 fn as_ref(&self) -> &str {
362 self.source.as_str()
363 }
364 }
365
366 impl Default for Uri {
default() -> Uri367 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
378 impl fmt::Debug for Uri {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result379 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
380 fmt::Debug::fmt(self.as_ref(), f)
381 }
382 }
383
384 impl Display for Uri {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result385 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
386 f.write_str(self.as_ref())
387 }
388 }
389
390 #[cfg(feature = "compat")]
391 impl From<http::Uri> for Uri {
from(uri: http::Uri) -> Uri392 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")]
399 impl From<Uri> for http::Uri {
from(uri: Uri) -> http::Uri400 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]
asterisk_form() -> Uri409 fn 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
420 struct Range(usize, usize);
421
422 impl Range {
len(&self) -> usize423 fn len(&self) -> usize {
424 self.1 - self.0
425 }
426 }
427
428 /// An error parsing a `Uri`.
429 #[derive(Clone, Debug)]
430 pub struct UriError(ErrorKind);
431
432 #[derive(Clone, Debug)]
433 enum ErrorKind {
434 Empty,
435 Malformed,
436 MissingAuthority,
437 }
438
439 impl fmt::Display for UriError {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result440 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
441 f.pad(self.description())
442 }
443 }
444
445 impl StdError for UriError {
description(&self) -> &str446 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
455 macro_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
471 test_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
482 test_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
495 test_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
510 test_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
523 test_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
538 test_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
551 test_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
564 test_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
577 test_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
590 test_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
603 test_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
616 test_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
629 test_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
642 test_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
657 test_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
669 test_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]
test_uri_parse_error()684 fn 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]
test_uri_to_origin_form()701 fn 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 }
718