1
0
Fork 0
mirror of https://github.com/actix/actix-web.git synced 2024-05-19 16:58:14 +00:00
actix-web/actix-router/src/url.rs

138 lines
3.7 KiB
Rust
Raw Normal View History

2023-07-17 01:38:12 +00:00
use crate::{Quoter, ResourcePath};
2021-08-06 21:42:31 +00:00
thread_local! {
static DEFAULT_QUOTER: Quoter = Quoter::new(b"", b"%/+");
2021-08-06 21:42:31 +00:00
}
#[derive(Debug, Clone, Default)]
2021-08-06 21:42:31 +00:00
pub struct Url {
uri: http::Uri,
path: Option<String>,
}
impl Url {
#[inline]
2021-08-06 21:42:31 +00:00
pub fn new(uri: http::Uri) -> Url {
let path = DEFAULT_QUOTER.with(|q| q.requote_str_lossy(uri.path()));
2021-08-06 21:42:31 +00:00
Url { uri, path }
}
#[inline]
2022-01-04 12:58:40 +00:00
pub fn new_with_quoter(uri: http::Uri, quoter: &Quoter) -> Url {
2021-08-06 21:42:31 +00:00
Url {
path: quoter.requote_str_lossy(uri.path()),
2021-08-06 21:42:31 +00:00
uri,
}
}
2022-01-04 12:58:40 +00:00
/// Returns URI.
#[inline]
2021-08-06 21:42:31 +00:00
pub fn uri(&self) -> &http::Uri {
&self.uri
}
2022-01-04 12:58:40 +00:00
/// Returns path.
#[inline]
2021-08-06 21:42:31 +00:00
pub fn path(&self) -> &str {
match self.path {
Some(ref path) => path,
_ => self.uri.path(),
2021-08-06 21:42:31 +00:00
}
}
#[inline]
pub fn update(&mut self, uri: &http::Uri) {
self.uri = uri.clone();
self.path = DEFAULT_QUOTER.with(|q| q.requote_str_lossy(uri.path()));
2021-08-06 21:42:31 +00:00
}
#[inline]
pub fn update_with_quoter(&mut self, uri: &http::Uri, quoter: &Quoter) {
self.uri = uri.clone();
self.path = quoter.requote_str_lossy(uri.path());
2021-08-06 21:42:31 +00:00
}
}
impl ResourcePath for Url {
#[inline]
fn path(&self) -> &str {
self.path()
}
}
#[cfg(test)]
mod tests {
use http::Uri;
use super::*;
use crate::{Path, ResourceDef};
const PROTECTED: &[u8] = b"%/+";
fn match_url(pattern: &'static str, url: impl AsRef<str>) -> Path<Url> {
let re = ResourceDef::new(pattern);
let uri = Uri::try_from(url.as_ref()).unwrap();
let mut path = Path::new(Url::new(uri));
assert!(re.capture_match_info(&mut path));
path
}
fn percent_encode(data: &[u8]) -> String {
data.iter().map(|c| format!("%{:02X}", c)).collect()
}
#[test]
fn parse_url() {
2021-08-06 21:42:31 +00:00
let re = "/user/{id}/test";
let path = match_url(re, "/user/2345/test");
assert_eq!(path.get("id").unwrap(), "2345");
2022-01-04 03:48:12 +00:00
}
#[test]
fn protected_chars() {
let re = "/user/{id}/test";
let encoded = percent_encode(PROTECTED);
let path = match_url(re, format!("/user/{}/test", encoded));
// characters in captured segment remain unencoded
assert_eq!(path.get("id").unwrap(), &encoded);
2021-08-06 21:42:31 +00:00
// "%25" should never be decoded into '%' to guarantee the output is a valid
// percent-encoded format
let path = match_url(re, "/user/qwe%25/test");
assert_eq!(path.get("id").unwrap(), "qwe%25");
let path = match_url(re, "/user/qwe%25rty/test");
assert_eq!(path.get("id").unwrap(), "qwe%25rty");
}
#[test]
fn non_protected_ascii() {
let non_protected_ascii = ('\u{0}'..='\u{7F}')
2021-08-06 21:42:31 +00:00
.filter(|&c| c.is_ascii() && !PROTECTED.contains(&(c as u8)))
.collect::<String>();
let encoded = percent_encode(non_protected_ascii.as_bytes());
2021-08-06 21:42:31 +00:00
let path = match_url("/user/{id}/test", format!("/user/{}/test", encoded));
assert_eq!(path.get("id").unwrap(), &non_protected_ascii);
2021-08-06 21:42:31 +00:00
}
#[test]
2022-01-19 20:26:33 +00:00
fn valid_utf8_multi_byte() {
2021-08-06 21:42:31 +00:00
let test = ('\u{FF00}'..='\u{FFFF}').collect::<String>();
let encoded = percent_encode(test.as_bytes());
let path = match_url("/a/{id}/b", format!("/a/{}/b", &encoded));
assert_eq!(path.get("id").unwrap(), &test);
}
#[test]
fn invalid_utf8() {
2021-08-06 21:42:31 +00:00
let invalid_utf8 = percent_encode((0x80..=0xff).collect::<Vec<_>>().as_slice());
let uri = Uri::try_from(format!("/{}", invalid_utf8)).unwrap();
let path = Path::new(Url::new(uri));
// We should always get a valid utf8 string
2022-01-19 20:26:33 +00:00
assert!(String::from_utf8(path.as_str().as_bytes().to_owned()).is_ok());
2021-08-06 21:42:31 +00:00
}
}