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