1
0
mirror of https://github.com/actix/actix-extras.git synced 2024-11-28 09:42:40 +01:00
actix-extras/src/param.rs

217 lines
6.2 KiB
Rust
Raw Normal View History

2018-04-14 01:02:01 +02:00
use http::StatusCode;
use smallvec::SmallVec;
use std;
2018-04-14 01:02:01 +02:00
use std::borrow::Cow;
use std::ops::Index;
use std::path::PathBuf;
2017-12-28 04:09:36 +01:00
use std::slice::Iter;
2018-04-14 01:02:01 +02:00
use std::str::FromStr;
2018-04-14 01:02:01 +02:00
use error::{InternalError, ResponseError, UriSegmentError};
2018-04-14 01:02:01 +02:00
/// A trait to abstract the idea of creating a new instance of a type from a
/// path parameter.
pub trait FromParam: Sized {
/// The associated error which can be returned from parsing.
type Err: ResponseError;
/// Parses a string `s` to return a value of this type.
fn from_param(s: &str) -> Result<Self, Self::Err>;
}
/// Route match information
///
/// If resource path contains variable patterns, `Params` stores this variables.
#[derive(Debug)]
2017-12-28 03:41:09 +01:00
pub struct Params<'a>(SmallVec<[(Cow<'a, str>, Cow<'a, str>); 3]>);
2017-12-28 04:19:28 +01:00
impl<'a> Params<'a> {
pub(crate) fn new() -> Params<'a> {
Params(SmallVec::new())
}
2017-12-15 04:34:31 +01:00
pub(crate) fn clear(&mut self) {
self.0.clear();
}
2017-12-28 03:41:09 +01:00
pub(crate) fn add<N, V>(&mut self, name: N, value: V)
2018-04-14 01:02:01 +02:00
where
N: Into<Cow<'a, str>>,
V: Into<Cow<'a, str>>,
2017-12-28 03:41:09 +01:00
{
self.0.push((name.into(), value.into()));
}
/// Check if there are any matched patterns
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
2018-03-27 00:58:30 +02:00
/// Check number of extracted parameters
pub fn len(&self) -> usize {
self.0.len()
}
/// Get matched parameter by name without type conversion
2017-12-28 03:41:09 +01:00
pub fn get(&'a self, key: &str) -> Option<&'a str> {
for item in self.0.iter() {
if key == item.0 {
2018-04-14 01:02:01 +02:00
return Some(item.1.as_ref());
}
}
None
}
/// Get matched `FromParam` compatible parameter by name.
///
2018-04-14 01:02:01 +02:00
/// If keyed parameter is not available empty string is used as default
/// value.
///
/// ```rust
/// # extern crate actix_web;
/// # use actix_web::*;
/// fn index(req: HttpRequest) -> Result<String> {
/// let ivalue: isize = req.match_info().query("val")?;
/// Ok(format!("isuze value: {:?}", ivalue))
/// }
/// # fn main() {}
/// ```
2018-04-14 01:02:01 +02:00
pub fn query<T: FromParam>(&'a self, key: &str) -> Result<T, <T as FromParam>::Err> {
if let Some(s) = self.get(key) {
T::from_param(s)
} else {
T::from_param("")
}
}
2017-12-28 04:09:36 +01:00
2018-01-15 22:47:25 +01:00
/// Return iterator to items in parameter container
2017-12-28 04:09:36 +01:00
pub fn iter(&self) -> Iter<(Cow<'a, str>, Cow<'a, str>)> {
self.0.iter()
}
}
2017-12-28 03:41:09 +01:00
impl<'a, 'b, 'c: 'a> Index<&'b str> for &'c Params<'a> {
type Output = str;
fn index(&self, name: &'b str) -> &str {
2018-04-29 07:55:47 +02:00
self.get(name).expect("Value for parameter is not available")
}
}
impl<'a, 'c: 'a> Index<usize> for &'c Params<'a> {
type Output = str;
fn index(&self, idx: usize) -> &str {
self.0[idx].1.as_ref()
}
}
/// Creates a `PathBuf` from a path parameter. The returned `PathBuf` is
/// percent-decoded. If a segment is equal to "..", the previous segment (if
/// any) is skipped.
///
/// For security purposes, if a segment meets any of the following conditions,
/// an `Err` is returned indicating the condition met:
///
/// * Decoded segment starts with any of: `.` (except `..`), `*`
/// * Decoded segment ends with any of: `:`, `>`, `<`
/// * Decoded segment contains any of: `/`
/// * On Windows, decoded segment contains any of: '\'
/// * Percent-encoding results in invalid UTF8.
///
2018-04-14 01:02:01 +02:00
/// As a result of these conditions, a `PathBuf` parsed from request path
/// parameter is safe to interpolate within, or use as a suffix of, a path
/// without additional checks.
impl FromParam for PathBuf {
type Err = UriSegmentError;
fn from_param(val: &str) -> Result<PathBuf, UriSegmentError> {
let mut buf = PathBuf::new();
for segment in val.split('/') {
if segment == ".." {
buf.pop();
} else if segment.starts_with('.') {
2018-04-14 01:02:01 +02:00
return Err(UriSegmentError::BadStart('.'));
} else if segment.starts_with('*') {
2018-04-14 01:02:01 +02:00
return Err(UriSegmentError::BadStart('*'));
} else if segment.ends_with(':') {
2018-04-14 01:02:01 +02:00
return Err(UriSegmentError::BadEnd(':'));
} else if segment.ends_with('>') {
2018-04-14 01:02:01 +02:00
return Err(UriSegmentError::BadEnd('>'));
} else if segment.ends_with('<') {
2018-04-14 01:02:01 +02:00
return Err(UriSegmentError::BadEnd('<'));
} else if segment.is_empty() {
2018-04-14 01:02:01 +02:00
continue;
} else if cfg!(windows) && segment.contains('\\') {
2018-04-14 01:02:01 +02:00
return Err(UriSegmentError::BadChar('\\'));
} else {
buf.push(segment)
}
}
Ok(buf)
}
}
macro_rules! FROM_STR {
($type:ty) => {
impl FromParam for $type {
2018-01-21 06:11:46 +01:00
type Err = InternalError<<$type as FromStr>::Err>;
fn from_param(val: &str) -> Result<Self, Self::Err> {
<$type as FromStr>::from_str(val)
.map_err(|e| InternalError::new(e, StatusCode::BAD_REQUEST))
}
}
2018-04-14 01:02:01 +02:00
};
}
FROM_STR!(u8);
FROM_STR!(u16);
FROM_STR!(u32);
FROM_STR!(u64);
FROM_STR!(usize);
FROM_STR!(i8);
FROM_STR!(i16);
FROM_STR!(i32);
FROM_STR!(i64);
FROM_STR!(isize);
FROM_STR!(f32);
FROM_STR!(f64);
FROM_STR!(String);
FROM_STR!(std::net::IpAddr);
FROM_STR!(std::net::Ipv4Addr);
FROM_STR!(std::net::Ipv6Addr);
FROM_STR!(std::net::SocketAddr);
FROM_STR!(std::net::SocketAddrV4);
FROM_STR!(std::net::SocketAddrV6);
#[cfg(test)]
mod tests {
use super::*;
use std::iter::FromIterator;
#[test]
fn test_path_buf() {
2018-04-14 01:02:01 +02:00
assert_eq!(
PathBuf::from_param("/test/.tt"),
Err(UriSegmentError::BadStart('.'))
);
assert_eq!(
PathBuf::from_param("/test/*tt"),
Err(UriSegmentError::BadStart('*'))
);
2018-04-29 07:55:47 +02:00
assert_eq!(PathBuf::from_param("/test/tt:"), Err(UriSegmentError::BadEnd(':')));
assert_eq!(PathBuf::from_param("/test/tt<"), Err(UriSegmentError::BadEnd('<')));
assert_eq!(PathBuf::from_param("/test/tt>"), Err(UriSegmentError::BadEnd('>')));
2018-04-14 01:02:01 +02:00
assert_eq!(
PathBuf::from_param("/seg1/seg2/"),
Ok(PathBuf::from_iter(vec!["seg1", "seg2"]))
);
assert_eq!(
PathBuf::from_param("/seg1/../seg2/"),
Ok(PathBuf::from_iter(vec!["seg2"]))
);
}
}