137
src/uri.rs
137
src/uri.rs
@@ -32,8 +32,8 @@ pub struct Uri {
|
|||||||
source: ByteStr,
|
source: ByteStr,
|
||||||
scheme_end: Option<usize>,
|
scheme_end: Option<usize>,
|
||||||
authority_end: Option<usize>,
|
authority_end: Option<usize>,
|
||||||
query: Option<usize>,
|
query_start: Option<usize>,
|
||||||
fragment: Option<usize>,
|
fragment_start: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Uri {
|
impl Uri {
|
||||||
@@ -47,8 +47,8 @@ impl Uri {
|
|||||||
source: ByteStr::from_static("*"),
|
source: ByteStr::from_static("*"),
|
||||||
scheme_end: None,
|
scheme_end: None,
|
||||||
authority_end: None,
|
authority_end: None,
|
||||||
query: None,
|
query_start: None,
|
||||||
fragment: None,
|
fragment_start: None,
|
||||||
})
|
})
|
||||||
} else if s.as_bytes() == b"/" {
|
} else if s.as_bytes() == b"/" {
|
||||||
// shortcut for '/'
|
// shortcut for '/'
|
||||||
@@ -61,13 +61,13 @@ impl Uri {
|
|||||||
source: s,
|
source: s,
|
||||||
scheme_end: None,
|
scheme_end: None,
|
||||||
authority_end: None,
|
authority_end: None,
|
||||||
query: query,
|
query_start: query,
|
||||||
fragment: fragment,
|
fragment_start: fragment,
|
||||||
})
|
})
|
||||||
} else if s.contains("://") {
|
} else if s.contains("://") {
|
||||||
// absolute-form
|
// absolute-form
|
||||||
let scheme = parse_scheme(&s);
|
let scheme = parse_scheme(&s);
|
||||||
let auth = parse_authority(&s);
|
let auth = Some(parse_authority(&s));
|
||||||
let scheme_end = scheme.expect("just checked for ':' above");
|
let scheme_end = scheme.expect("just checked for ':' above");
|
||||||
let auth_end = auth.expect("just checked for ://");
|
let auth_end = auth.expect("just checked for ://");
|
||||||
if scheme_end + 3 == auth_end {
|
if scheme_end + 3 == auth_end {
|
||||||
@@ -80,8 +80,8 @@ impl Uri {
|
|||||||
source: s,
|
source: s,
|
||||||
scheme_end: scheme,
|
scheme_end: scheme,
|
||||||
authority_end: auth,
|
authority_end: auth,
|
||||||
query: query,
|
query_start: query,
|
||||||
fragment: fragment,
|
fragment_start: fragment,
|
||||||
})
|
})
|
||||||
} else if (s.contains("/") || s.contains("?")) && !s.contains("://") {
|
} else if (s.contains("/") || s.contains("?")) && !s.contains("://") {
|
||||||
// last possibility is authority-form, above are illegal characters
|
// last possibility is authority-form, above are illegal characters
|
||||||
@@ -93,8 +93,8 @@ impl Uri {
|
|||||||
source: s,
|
source: s,
|
||||||
scheme_end: None,
|
scheme_end: None,
|
||||||
authority_end: Some(len),
|
authority_end: Some(len),
|
||||||
query: None,
|
query_start: None,
|
||||||
fragment: None,
|
fragment_start: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -102,10 +102,13 @@ impl Uri {
|
|||||||
/// Get the path of this `Uri`.
|
/// Get the path of this `Uri`.
|
||||||
pub fn path(&self) -> &str {
|
pub fn path(&self) -> &str {
|
||||||
let index = self.authority_end.unwrap_or(self.scheme_end.unwrap_or(0));
|
let index = self.authority_end.unwrap_or(self.scheme_end.unwrap_or(0));
|
||||||
let query_len = self.query.unwrap_or(0);
|
let end = if let Some(query) = self.query_start {
|
||||||
let fragment_len = self.fragment.unwrap_or(0);
|
query
|
||||||
let end = self.source.len() - if query_len > 0 { query_len + 1 } else { 0 } -
|
} else if let Some(fragment) = self.fragment_start {
|
||||||
if fragment_len > 0 { fragment_len + 1 } else { 0 };
|
fragment
|
||||||
|
} else {
|
||||||
|
self.source.len()
|
||||||
|
};
|
||||||
if index >= end {
|
if index >= end {
|
||||||
if self.scheme().is_some() {
|
if self.scheme().is_some() {
|
||||||
"/" // absolute-form MUST have path
|
"/" // absolute-form MUST have path
|
||||||
@@ -156,23 +159,23 @@ impl Uri {
|
|||||||
|
|
||||||
/// Get the query string of this `Uri`, starting after the `?`.
|
/// Get the query string of this `Uri`, starting after the `?`.
|
||||||
pub fn query(&self) -> Option<&str> {
|
pub fn query(&self) -> Option<&str> {
|
||||||
let fragment_len = self.fragment.unwrap_or(0);
|
self.query_start.map(|start| {
|
||||||
let fragment_len = if fragment_len > 0 { fragment_len + 1 } else { 0 };
|
// +1 to remove '?'
|
||||||
if let Some(len) = self.query {
|
let start = start + 1;
|
||||||
Some(&self.source[self.source.len() - len - fragment_len..
|
if let Some(end) = self.fragment_start {
|
||||||
self.source.len() - fragment_len])
|
&self.source[start..end]
|
||||||
} else {
|
} else {
|
||||||
None
|
&self.source[start..]
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
fn fragment(&self) -> Option<&str> {
|
fn fragment(&self) -> Option<&str> {
|
||||||
if let Some(len) = self.fragment {
|
self.fragment_start.map(|start| {
|
||||||
Some(&self.source[self.source.len() - len..self.source.len()])
|
// +1 to remove the '#'
|
||||||
} else {
|
&self.source[start + 1..]
|
||||||
None
|
})
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,35 +183,31 @@ fn parse_scheme(s: &str) -> Option<usize> {
|
|||||||
s.find(':')
|
s.find(':')
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_authority(s: &str) -> Option<usize> {
|
fn parse_authority(s: &str) -> usize {
|
||||||
let i = s.find("://").and_then(|p| Some(p + 3)).unwrap_or(0);
|
let i = s.find("://").map(|p| p + 3).unwrap_or(0);
|
||||||
|
s[i..].find('/')
|
||||||
Some(&s[i..].split("/")
|
.or_else(|| s[i..].find('?'))
|
||||||
.next()
|
.or_else(|| s[i..].find('#'))
|
||||||
.unwrap_or(s)
|
.map(|end| end + i)
|
||||||
.len() + i)
|
.unwrap_or(s.len())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_query(s: &str) -> Option<usize> {
|
fn parse_query(s: &str) -> Option<usize> {
|
||||||
match s.find('?') {
|
s.find('?').and_then(|i| {
|
||||||
Some(i) => {
|
if let Some(frag) = s.find('#') {
|
||||||
let frag_pos = s.find('#').unwrap_or(s.len());
|
if frag < i {
|
||||||
|
|
||||||
if frag_pos < i + 1 {
|
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(frag_pos - i - 1)
|
Some(i)
|
||||||
}
|
}
|
||||||
},
|
} else {
|
||||||
None => None,
|
Some(i)
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_fragment(s: &str) -> Option<usize> {
|
fn parse_fragment(s: &str) -> Option<usize> {
|
||||||
match s.find('#') {
|
s.find('#')
|
||||||
Some(i) => Some(s.len() - i - 1),
|
|
||||||
None => None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for Uri {
|
impl FromStr for Uri {
|
||||||
@@ -241,8 +240,8 @@ impl Default for Uri {
|
|||||||
source: ByteStr::from_static("/"),
|
source: ByteStr::from_static("/"),
|
||||||
scheme_end: None,
|
scheme_end: None,
|
||||||
authority_end: None,
|
authority_end: None,
|
||||||
query: None,
|
query_start: None,
|
||||||
fragment: None,
|
fragment_start: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -269,8 +268,8 @@ pub fn scheme_and_authority(uri: &Uri) -> Option<Uri> {
|
|||||||
source: uri.source.slice_to(uri.authority_end.expect("scheme without authority")),
|
source: uri.source.slice_to(uri.authority_end.expect("scheme without authority")),
|
||||||
scheme_end: uri.scheme_end,
|
scheme_end: uri.scheme_end,
|
||||||
authority_end: uri.authority_end,
|
authority_end: uri.authority_end,
|
||||||
query: None,
|
query_start: None,
|
||||||
fragment: None,
|
fragment_start: None,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@@ -279,8 +278,8 @@ pub fn scheme_and_authority(uri: &Uri) -> Option<Uri> {
|
|||||||
|
|
||||||
pub fn origin_form(uri: &Uri) -> Uri {
|
pub fn origin_form(uri: &Uri) -> Uri {
|
||||||
let start = uri.authority_end.unwrap_or(uri.scheme_end.unwrap_or(0));
|
let start = uri.authority_end.unwrap_or(uri.scheme_end.unwrap_or(0));
|
||||||
let end = if let Some(f) = uri.fragment {
|
let end = if let Some(f) = uri.fragment_start {
|
||||||
uri.source.len() - f - 1
|
f
|
||||||
} else {
|
} else {
|
||||||
uri.source.len()
|
uri.source.len()
|
||||||
};
|
};
|
||||||
@@ -288,8 +287,8 @@ pub fn origin_form(uri: &Uri) -> Uri {
|
|||||||
source: uri.source.slice(start, end),
|
source: uri.source.slice(start, end),
|
||||||
scheme_end: None,
|
scheme_end: None,
|
||||||
authority_end: None,
|
authority_end: None,
|
||||||
query: uri.query,
|
query_start: uri.query_start,
|
||||||
fragment: None,
|
fragment_start: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -329,6 +328,7 @@ macro_rules! test_parse {
|
|||||||
#[test]
|
#[test]
|
||||||
fn $test_name() {
|
fn $test_name() {
|
||||||
let uri = Uri::from_str($str).unwrap();
|
let uri = Uri::from_str($str).unwrap();
|
||||||
|
println!("{:?} = {:#?}", $str, uri);
|
||||||
$(
|
$(
|
||||||
assert_eq!(uri.$method(), $value);
|
assert_eq!(uri.$method(), $value);
|
||||||
)+
|
)+
|
||||||
@@ -442,6 +442,30 @@ test_parse! {
|
|||||||
port = None,
|
port = None,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test_parse! {
|
||||||
|
test_uri_parse_path_with_terminating_questionmark,
|
||||||
|
"http://127.0.0.1/path?",
|
||||||
|
|
||||||
|
scheme = Some("http"),
|
||||||
|
authority = Some("127.0.0.1"),
|
||||||
|
path = "/path",
|
||||||
|
query = Some(""),
|
||||||
|
fragment = None,
|
||||||
|
port = None,
|
||||||
|
}
|
||||||
|
|
||||||
|
test_parse! {
|
||||||
|
test_uri_parse_absolute_form_with_empty_path_and_nonempty_query,
|
||||||
|
"http://127.0.0.1?foo=bar",
|
||||||
|
|
||||||
|
scheme = Some("http"),
|
||||||
|
authority = Some("127.0.0.1"),
|
||||||
|
path = "/",
|
||||||
|
query = Some("foo=bar"),
|
||||||
|
fragment = None,
|
||||||
|
port = None,
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_uri_parse_error() {
|
fn test_uri_parse_error() {
|
||||||
fn err(s: &str) {
|
fn err(s: &str) {
|
||||||
@@ -452,6 +476,7 @@ fn test_uri_parse_error() {
|
|||||||
err("htt:p//host");
|
err("htt:p//host");
|
||||||
err("hyper.rs/");
|
err("hyper.rs/");
|
||||||
err("hyper.rs?key=val");
|
err("hyper.rs?key=val");
|
||||||
|
err("?key=val");
|
||||||
err("localhost/");
|
err("localhost/");
|
||||||
err("localhost?key=val");
|
err("localhost?key=val");
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user