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
parse_scheme(s: &str) -> Option<usize>242 fn parse_scheme(s: &str) -> Option<usize> {
243 s.find(':')
244 }
245
parse_authority(s: &str) -> usize246 fn parse_authority(s: &str) -> usize {
247 let i = s.find("://").map(|p| p + 3).unwrap_or(0);
248 s[i..]
249 .find(|ch| ch == '/' || ch == '?' || ch == '#')
250 .map(|end| end + i)
251 .unwrap_or(s.len())
252 }
253
parse_query(s: &str) -> Option<usize>254 fn parse_query(s: &str) -> Option<usize> {
255 s.find('?').and_then(|i| {
256 if let Some(frag) = s.find('#') {
257 if frag < i {
258 None
259 } else {
260 Some(i)
261 }
262 } else {
263 Some(i)
264 }
265 })
266 }
267
parse_fragment(s: &str) -> Option<usize>268 fn parse_fragment(s: &str) -> Option<usize> {
269 s.find('#')
270 }
271
272 impl FromStr for Uri {
273 type Err = UriError;
274
from_str(s: &str) -> Result<Uri, UriError>275 fn from_str(s: &str) -> Result<Uri, UriError> {
276 //TODO: refactor such that the to_owned() is only required at the end
277 //of successful parsing, so an Err doesn't needlessly clone the string.
278 Uri::new(ByteStr::from(s))
279 }
280 }
281
282 impl PartialEq for Uri {
eq(&self, other: &Uri) -> bool283 fn eq(&self, other: &Uri) -> bool {
284 self.source.as_str() == other.source.as_str()
285 }
286 }
287
288 impl PartialEq<str> for Uri {
eq(&self, other: &str) -> bool289 fn eq(&self, other: &str) -> bool {
290 self.source.as_str() == other
291 }
292 }
293
294 // FIXME delete for 0.12
295 impl<'a> PartialEq<&'a str> for Uri {
eq(&self, other: & &'a str) -> bool296 fn eq(&self, other: & &'a str) -> bool {
297 self.source.as_str() == *other
298 }
299 }
300
301 impl<'a> PartialEq<Uri> for &'a str{
eq(&self, other: &Uri) -> bool302 fn eq(&self, other: &Uri) -> bool {
303 *self == other.source.as_str()
304 }
305 }
306
307 impl Eq for Uri {}
308
309 impl AsRef<str> for Uri {
as_ref(&self) -> &str310 fn as_ref(&self) -> &str {
311 self.source.as_str()
312 }
313 }
314
315 impl Default for Uri {
default() -> Uri316 fn default() -> Uri {
317 Uri {
318 source: ByteStr::from_static("/"),
319 scheme_end: None,
320 authority_end: None,
321 query_start: None,
322 fragment_start: None,
323 }
324 }
325 }
326
327 impl fmt::Debug for Uri {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result328 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
329 fmt::Debug::fmt(self.as_ref(), f)
330 }
331 }
332
333 impl Display for Uri {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result334 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
335 f.write_str(self.as_ref())
336 }
337 }
338
339 #[cfg(feature = "compat")]
340 impl From<http::Uri> for Uri {
from(uri: http::Uri) -> Uri341 fn from(uri: http::Uri) -> Uri {
342 uri.to_string().parse()
343 .expect("attempted to convert invalid uri")
344 }
345 }
346
347 #[cfg(feature = "compat")]
348 impl From<Uri> for http::Uri {
from(uri: Uri) -> http::Uri349 fn from(uri: Uri) -> http::Uri {
350 let bytes = uri.source.into_bytes();
351 http::Uri::from_shared(bytes)
352 .expect("attempted to convert invalid uri")
353 }
354 }
355
from_utf8_unchecked(slice: Bytes) -> Result<Uri, UriError>356 pub unsafe fn from_utf8_unchecked(slice: Bytes) -> Result<Uri, UriError> {
357 Uri::new(ByteStr::from_utf8_unchecked(slice))
358 }
359
scheme_and_authority(uri: &Uri) -> Option<Uri>360 pub fn scheme_and_authority(uri: &Uri) -> Option<Uri> {
361 if uri.scheme_end.is_some() {
362 Some(Uri {
363 source: uri.source.slice_to(uri.authority_end.expect("scheme without authority")),
364 scheme_end: uri.scheme_end,
365 authority_end: uri.authority_end,
366 query_start: None,
367 fragment_start: None,
368 })
369 } else {
370 None
371 }
372 }
373
374 #[inline]
asterisk_form() -> Uri375 fn asterisk_form() -> Uri {
376 Uri {
377 source: ByteStr::from_static("*"),
378 scheme_end: None,
379 authority_end: None,
380 query_start: None,
381 fragment_start: None,
382 }
383 }
384
origin_form(uri: &Uri) -> Uri385 pub fn origin_form(uri: &Uri) -> Uri {
386 let range = Range(uri.path_start(), uri.origin_form_end());
387
388 let clone = if range.len() == 0 {
389 ByteStr::from_static("/")
390 } else if uri.source.as_bytes()[range.0] == b'*' {
391 return asterisk_form();
392 } else if uri.source.as_bytes()[range.0] != b'/' {
393 let mut new = BytesMut::with_capacity(range.1 - range.0 + 1);
394 new.put_u8(b'/');
395 new.put_slice(&uri.source.as_bytes()[range.0..range.1]);
396 // safety: the bytes are '/' + previous utf8 str
397 unsafe { ByteStr::from_utf8_unchecked(new.freeze()) }
398 } else if range.0 == 0 && range.1 == uri.source.len() {
399 uri.source.clone()
400 } else {
401 uri.source.slice(range.0, range.1)
402 };
403
404 Uri {
405 source: clone,
406 scheme_end: None,
407 authority_end: None,
408 query_start: uri.query_start,
409 fragment_start: None,
410 }
411 }
412
413 struct Range(usize, usize);
414
415 impl Range {
len(&self) -> usize416 fn len(&self) -> usize {
417 self.1 - self.0
418 }
419 }
420
421 /// An error parsing a `Uri`.
422 #[derive(Clone, Debug)]
423 pub struct UriError(ErrorKind);
424
425 #[derive(Clone, Debug)]
426 enum ErrorKind {
427 Empty,
428 Malformed,
429 MissingAuthority,
430 }
431
432 impl fmt::Display for UriError {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result433 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
434 f.pad(self.description())
435 }
436 }
437
438 impl StdError for UriError {
description(&self) -> &str439 fn description(&self) -> &str {
440 match self.0 {
441 ErrorKind::Empty => "empty Uri string",
442 ErrorKind::Malformed => "invalid character in Uri authority",
443 ErrorKind::MissingAuthority => "absolute Uri missing authority segment",
444 }
445 }
446 }
447
448 macro_rules! test_parse {
449 (
450 $test_name:ident,
451 $str:expr,
452 $($method:ident = $value:expr,)*
453 ) => (
454 #[test]
455 fn $test_name() {
456 let uri = Uri::from_str($str).unwrap();
457 $(
458 assert_eq!(uri.$method(), $value, stringify!($method));
459 )+
460 }
461 );
462 }
463
464 test_parse! {
465 test_uri_parse_origin_form,
466 "/some/path/here?and=then&hello#and-bye",
467
468 scheme = None,
469 authority = None,
470 path = "/some/path/here",
471 query = Some("and=then&hello"),
472 fragment = Some("and-bye"),
473 }
474
475 test_parse! {
476 test_uri_parse_absolute_form,
477 "http://127.0.0.1:61761/chunks",
478
479 scheme = Some("http"),
480 authority = Some("127.0.0.1:61761"),
481 host = Some("127.0.0.1"),
482 path = "/chunks",
483 query = None,
484 fragment = None,
485 port = Some(61761),
486 }
487
488 test_parse! {
489 test_uri_parse_absolute_form_without_path,
490 "https://127.0.0.1:61761",
491
492 scheme = Some("https"),
493 authority = Some("127.0.0.1:61761"),
494 host = Some("127.0.0.1"),
495 path = "/",
496 query = None,
497 fragment = None,
498 port = Some(61761),
499
500 to_string = "https://127.0.0.1:61761/",
501 }
502
503 test_parse! {
504 test_uri_parse_asterisk_form,
505 "*",
506
507 scheme = None,
508 authority = None,
509 path = "*",
510 query = None,
511 fragment = None,
512
513 to_string = "*",
514 }
515
516 test_parse! {
517 test_uri_parse_authority_no_port,
518 "localhost",
519
520 scheme = None,
521 authority = Some("localhost"),
522 host = Some("localhost"),
523 path = "",
524 query = None,
525 fragment = None,
526 port = None,
527
528 to_string = "localhost",
529 }
530
531 test_parse! {
532 test_uri_parse_authority_form,
533 "localhost:3000",
534
535 scheme = None,
536 authority = Some("localhost:3000"),
537 host = Some("localhost"),
538 path = "",
539 query = None,
540 fragment = None,
541 port = Some(3000),
542 }
543
544 test_parse! {
545 test_uri_parse_absolute_with_default_port_http,
546 "http://127.0.0.1:80/foo",
547
548 scheme = Some("http"),
549 authority = Some("127.0.0.1:80"),
550 host = Some("127.0.0.1"),
551 path = "/foo",
552 query = None,
553 fragment = None,
554 port = Some(80),
555 }
556
557 test_parse! {
558 test_uri_parse_absolute_with_default_port_https,
559 "https://127.0.0.1:443",
560
561 scheme = Some("https"),
562 authority = Some("127.0.0.1:443"),
563 host = Some("127.0.0.1"),
564 path = "/",
565 query = None,
566 fragment = None,
567 port = Some(443),
568 }
569
570 test_parse! {
571 test_uri_parse_absolute_with_ipv6,
572 "https://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:8008",
573
574 scheme = Some("https"),
575 authority = Some("[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:8008"),
576 host = Some("2001:0db8:85a3:0000:0000:8a2e:0370:7334"),
577 path = "/",
578 query = None,
579 fragment = None,
580 port = Some(8008),
581 }
582
583 test_parse! {
584 test_uri_parse_absolute_with_ipv6_and_no_port,
585 "https://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]",
586
587 scheme = Some("https"),
588 authority = Some("[2001:0db8:85a3:0000:0000:8a2e:0370:7334]"),
589 host = Some("2001:0db8:85a3:0000:0000:8a2e:0370:7334"),
590 path = "/",
591 query = None,
592 fragment = None,
593 port = None,
594 }
595
596 test_parse! {
597 test_uri_parse_absolute_with_userinfo,
598 "https://seanmonstar:password@hyper.rs",
599
600 scheme = Some("https"),
601 authority = Some("seanmonstar:password@hyper.rs"),
602 host = Some("hyper.rs"),
603 path = "/",
604 query = None,
605 fragment = None,
606 port = None,
607 }
608
609 test_parse! {
610 test_uri_parse_fragment_questionmark,
611 "http://127.0.0.1/#?",
612
613 scheme = Some("http"),
614 authority = Some("127.0.0.1"),
615 host = Some("127.0.0.1"),
616 path = "/",
617 query = None,
618 fragment = Some("?"),
619 port = None,
620 }
621
622 test_parse! {
623 test_uri_parse_path_with_terminating_questionmark,
624 "http://127.0.0.1/path?",
625
626 scheme = Some("http"),
627 authority = Some("127.0.0.1"),
628 host = Some("127.0.0.1"),
629 path = "/path",
630 query = Some(""),
631 fragment = None,
632 port = None,
633 }
634
635 test_parse! {
636 test_uri_parse_absolute_form_with_empty_path_and_nonempty_query,
637 "http://127.0.0.1?foo=bar",
638
639 scheme = Some("http"),
640 authority = Some("127.0.0.1"),
641 host = Some("127.0.0.1"),
642 path = "/",
643 query = Some("foo=bar"),
644 fragment = None,
645 port = None,
646
647 to_string = "http://127.0.0.1/?foo=bar",
648 }
649
650 test_parse! {
651 test_uri_parse_absolute_form_with_empty_path_and_fragment_with_slash,
652 "http://127.0.0.1#foo/bar",
653 scheme = Some("http"),
654 authority = Some("127.0.0.1"),
655 host = Some("127.0.0.1"),
656 path = "/",
657 query = None,
658 fragment = Some("foo/bar"),
659 port = None,
660 }
661
662 test_parse! {
663 test_uri_parse_absolute_form_with_empty_path_and_fragment_with_questionmark,
664 "http://127.0.0.1#foo?bar",
665 scheme = Some("http"),
666 authority = Some("127.0.0.1"),
667 host = Some("127.0.0.1"),
668 path = "/",
669 query = None,
670 fragment = Some("foo?bar"),
671 port = None,
672
673 to_string = "http://127.0.0.1/#foo?bar",
674 }
675
676 #[test]
test_uri_parse_error()677 fn test_uri_parse_error() {
678 fn err(s: &str) {
679 Uri::from_str(s).unwrap_err();
680 }
681
682 err("http://");
683 err("htt:p//host");
684 err("hyper.rs/");
685 err("hyper.rs?key=val");
686 err("?key=val");
687 err("localhost/");
688 err("localhost?key=val");
689 err("http://::1]");
690 err("http://[::1");
691 }
692
693 #[test]
test_uri_to_origin_form()694 fn test_uri_to_origin_form() {
695 let cases = vec![
696 ("/", "/"),
697 ("/foo?bar", "/foo?bar"),
698 ("/foo?bar#nope", "/foo?bar"),
699 ("http://hyper.rs", "/"),
700 ("http://hyper.rs/", "/"),
701 ("http://hyper.rs/path", "/path"),
702 ("http://hyper.rs?query", "/?query"),
703 ("*", "*"),
704 ];
705
706 for case in cases {
707 let uri = Uri::from_str(case.0).unwrap();
708 assert_eq!(origin_form(&uri), case.1); //, "{:?}", case);
709 }
710 }
711