1
0
mirror of https://github.com/fafhrd91/actix-net synced 2025-01-18 23:21:50 +01:00

revert re-quoting change

This commit is contained in:
Nikolay Kim 2019-03-09 13:56:09 -08:00
parent 0ff300c40f
commit 34995a8ccf

View File

@ -1,13 +1,26 @@
use http::Uri;
use std::rc::Rc; use std::rc::Rc;
use crate::ResourcePath; use crate::ResourcePath;
// https://tools.ietf.org/html/rfc3986#section-2.2 #[allow(dead_code)]
const RESERVED_PLUS_EXTRA: &[u8] = b":/?#[]@!$&'()*,+?;=%^ <>\"\\`{}|"; const GEN_DELIMS: &[u8] = b":/?#[]@";
#[allow(dead_code)]
// https://tools.ietf.org/html/rfc3986#section-2.3 const SUB_DELIMS_WITHOUT_QS: &[u8] = b"!$'()*,";
const UNRESERVED: &[u8] = b"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890-._~"; #[allow(dead_code)]
const SUB_DELIMS: &[u8] = b"!$'()*,+?=;";
#[allow(dead_code)]
const RESERVED: &[u8] = b":/?#[]@!$'()*,+?=;";
#[allow(dead_code)]
const UNRESERVED: &[u8] = b"abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ
1234567890
-._~";
const ALLOWED: &[u8] = b"abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ
1234567890
-._~
!$'()*,";
const QS: &[u8] = b"+&=;b";
#[inline] #[inline]
fn bit_at(array: &[u8], ch: u8) -> bool { fn bit_at(array: &[u8], ch: u8) -> bool {
@ -20,26 +33,23 @@ fn set_bit(array: &mut [u8], ch: u8) {
} }
thread_local! { thread_local! {
static UNRESERVED_QUOTER: Quoter = { Quoter::new(UNRESERVED) }; static DEFAULT_QUOTER: Quoter = { Quoter::new(b"@:", b"/+") };
pub(crate) static RESERVED_QUOTER: Quoter = { Quoter::new(RESERVED_PLUS_EXTRA) };
} }
#[derive(Default, Clone, Debug)] #[derive(Default, Clone, Debug)]
pub struct Url { pub struct Url {
uri: Uri, uri: http::Uri,
path: Option<Rc<String>>, path: Option<Rc<String>>,
} }
impl Url { impl Url {
pub fn new(uri: Uri) -> Url { pub fn new(uri: http::Uri) -> Url {
let path = UNRESERVED_QUOTER let path = DEFAULT_QUOTER.with(|q| q.requote(uri.path().as_bytes()));
.with(|q| q.requote(uri.path().as_bytes()))
.map(Rc::new);
Url { uri, path } Url { uri, path }
} }
pub fn uri(&self) -> &Uri { pub fn uri(&self) -> &http::Uri {
&self.uri &self.uri
} }
@ -53,9 +63,7 @@ impl Url {
pub fn update(&mut self, uri: &http::Uri) { pub fn update(&mut self, uri: &http::Uri) {
self.uri = uri.clone(); self.uri = uri.clone();
self.path = UNRESERVED_QUOTER self.path = DEFAULT_QUOTER.with(|q| q.requote(uri.path().as_bytes()));
.with(|q| q.requote(uri.path().as_bytes()))
.map(Rc::new);
} }
} }
@ -67,23 +75,40 @@ impl ResourcePath for Url {
pub(crate) struct Quoter { pub(crate) struct Quoter {
safe_table: [u8; 16], safe_table: [u8; 16],
protected_table: [u8; 16],
} }
impl Quoter { impl Quoter {
pub fn new(safe: &[u8]) -> Quoter { pub fn new(safe: &[u8], protected: &[u8]) -> Quoter {
let mut q = Quoter { let mut q = Quoter {
safe_table: [0; 16], safe_table: [0; 16],
protected_table: [0; 16],
}; };
// prepare safe table // prepare safe table
for i in 0..128 {
if ALLOWED.contains(&i) {
set_bit(&mut q.safe_table, i);
}
if QS.contains(&i) {
set_bit(&mut q.safe_table, i);
}
}
for ch in safe { for ch in safe {
set_bit(&mut q.safe_table, *ch) set_bit(&mut q.safe_table, *ch)
} }
// prepare protected table
for ch in protected {
set_bit(&mut q.safe_table, *ch);
set_bit(&mut q.protected_table, *ch);
}
q q
} }
pub fn requote(&self, val: &[u8]) -> Option<String> { pub fn requote(&self, val: &[u8]) -> Option<Rc<String>> {
let mut has_pct = 0; let mut has_pct = 0;
let mut pct = [b'%', 0, 0]; let mut pct = [b'%', 0, 0];
let mut idx = 0; let mut idx = 0;
@ -102,17 +127,19 @@ impl Quoter {
if let Some(ch) = restore_ch(pct[1], pct[2]) { if let Some(ch) = restore_ch(pct[1], pct[2]) {
if ch < 128 { if ch < 128 {
if bit_at(&self.protected_table, ch) {
buf.extend_from_slice(&pct);
idx += 1;
continue;
}
if bit_at(&self.safe_table, ch) { if bit_at(&self.safe_table, ch) {
buf.push(ch); buf.push(ch);
idx += 1; idx += 1;
continue; continue;
} }
buf.extend_from_slice(&pct);
} else {
// Not ASCII, decode it
buf.push(ch);
} }
buf.push(ch);
} else { } else {
buf.extend_from_slice(&pct[..]); buf.extend_from_slice(&pct[..]);
} }
@ -133,7 +160,7 @@ impl Quoter {
if let Some(data) = cloned { if let Some(data) = cloned {
// Unsafe: we get data from http::Uri, which does utf-8 checks already // Unsafe: we get data from http::Uri, which does utf-8 checks already
// this code only decodes valid pct encoded values // this code only decodes valid pct encoded values
Some(unsafe { String::from_utf8_unchecked(data) }) Some(Rc::new(unsafe { String::from_utf8_unchecked(data) }))
} else { } else {
None None
} }
@ -160,36 +187,28 @@ fn restore_ch(d1: u8, d2: u8) -> Option<u8> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use http::{HttpTryFrom, Uri};
use super::*; use super::*;
use crate::{Path, ResourceDef};
#[test] #[test]
fn decode_path() { fn test_parse_url() {
assert_eq!( let re = ResourceDef::new("/user/{id}/test");
UNRESERVED_QUOTER.with(|q| q.requote(b"https://localhost:80/foo")),
None
);
assert_eq!( let url = Uri::try_from("/user/2345/test").unwrap();
UNRESERVED_QUOTER let mut path = Path::new(Url::new(url));
.with(|q| q.requote(b"https://localhost:80/foo%25")) assert!(re.match_path(&mut path));
.unwrap(), assert_eq!(path.get("id").unwrap(), "2345");
"https://localhost:80/foo%25"
);
assert_eq!( let url = Uri::try_from("/user/qwe%25/test").unwrap();
UNRESERVED_QUOTER let mut path = Path::new(Url::new(url));
.with(|q| q.requote(b"http://cache-service/http%3A%2F%2Flocalhost%3A80%2Ffoo")) assert!(re.match_path(&mut path));
.unwrap(), assert_eq!(path.get("id").unwrap(), "qwe%");
"http://cache-service/http%3A%2F%2Flocalhost%3A80%2Ffoo".to_string()
);
assert_eq!( let url = Uri::try_from("/user/qwe%25rty/test").unwrap();
UNRESERVED_QUOTER let mut path = Path::new(Url::new(url));
.with(|q| q.requote( assert!(re.match_path(&mut path));
b"http://cache/http%3A%2F%2Flocal%3A80%2Ffile%2F%252Fvar%252Flog%0A" assert_eq!(path.get("id").unwrap(), "qwe%rty");
))
.unwrap(),
"http://cache/http%3A%2F%2Flocal%3A80%2Ffile%2F%252Fvar%252Flog%0A".to_string()
);
} }
} }