Run cargo fmt

pull/1/head
Trinity Pointard 6 years ago
parent e16eb40fd9
commit f3ec5bd6dc

@ -1,5 +1,4 @@
use csrf::{AesGcmCsrfProtection, CsrfProtection,
CSRF_COOKIE_NAME, CSRF_FORM_FIELD};
use csrf::{AesGcmCsrfProtection, CsrfProtection, CSRF_COOKIE_NAME, CSRF_FORM_FIELD};
use data_encoding::{BASE64, BASE64URL_NOPAD};
use rand::prelude::thread_rng;
use rand::Rng;
@ -19,7 +18,8 @@ use csrf_token::CsrfToken;
use path::Path;
use utils::parse_args;
const CSRF_FORM_FIELD_MULTIPART: &[u8] = "Content-Disposition: form-data; name=\"csrf-token\"".as_bytes();
const CSRF_FORM_FIELD_MULTIPART: &[u8] =
"Content-Disposition: form-data; name=\"csrf-token\"".as_bytes();
/// Builder for [CsrfFairing](struct.CsrfFairing.html)
///
@ -321,10 +321,12 @@ impl Fairing for CsrfFairing {
let _ = request.guard::<CsrfToken>(); //force regeneration of csrf cookies
let token = if request.content_type()
.map(|c| c.media_type())
.filter(|m| m.top()=="multipart" && m.sub()=="form-data" )
.is_some() {
let token = if request
.content_type()
.map(|c| c.media_type())
.filter(|m| m.top() == "multipart" && m.sub() == "form-data")
.is_some()
{
data.peek().split(|&c| c==0x0A || c==0x0D)//0x0A=='\n', 0x0D=='\r'
.filter(|l| l.len() > 0)
.skip_while(|&l| l != CSRF_FORM_FIELD_MULTIPART && l != &CSRF_FORM_FIELD_MULTIPART[..CSRF_FORM_FIELD_MULTIPART.len()-2])
@ -333,7 +335,13 @@ impl Fairing for CsrfFairing {
.next().unwrap_or(None)
} else {
parse_args(from_utf8(data.peek()).unwrap_or(""))
.filter_map(|(key, token)| if key == CSRF_FORM_FIELD {Some(token.as_bytes())} else {None})
.filter_map(|(key, token)| {
if key == CSRF_FORM_FIELD {
Some(token.as_bytes())
} else {
None
}
})
.next()
}.and_then(|token| BASE64URL_NOPAD.decode(&token).ok())
.and_then(|token| csrf_engine.parse_token(&token).ok());
@ -379,7 +387,8 @@ impl Fairing for CsrfFairing {
if self
.auto_insert_disable_prefix
.iter()
.any(|prefix| uri.starts_with(prefix)) {
.any(|prefix| uri.starts_with(prefix))
{
return;
} //if request is on an ignored prefix, ignore it

@ -1,8 +1,7 @@
use std::io::{Read, Error};
use csrf_proxy::ParseState::*;
use std::cmp;
use std::collections::VecDeque;
use csrf_proxy::ParseState::*;
use std::io::{Error, Read};
#[derive(Debug)]
struct Buffer {
@ -30,20 +29,21 @@ impl Buffer {
let buf_len = buf.len() - read;
let to_copy = cmp::min(part_len, buf_len);
buf[read..read + to_copy]
.copy_from_slice(&self.buf[0][self.pos[0]..self.pos[0]+to_copy]);
.copy_from_slice(&self.buf[0][self.pos[0]..self.pos[0] + to_copy]);
read += to_copy;
if part_len == to_copy {
self.buf.pop_front();
self.pos.pop_front();
} else {
self.pos[0]+=to_copy;
self.pos[0] += to_copy;
}
}
read
}
fn len(&self) -> usize {
self.buf.iter().fold(0, |size, buf| size+buf.len()) - self.pos.iter().fold(0, |size, pos| size + pos)
self.buf.iter().fold(0, |size, buf| size + buf.len())
- self.pos.iter().fold(0, |size, pos| size + pos)
}
fn is_empty(&self) -> bool {
@ -57,16 +57,15 @@ impl Default for Buffer {
}
}
#[derive(Debug)]
enum ParseState {
Init, //default state
PartialFormMatch, //when parsing "<form"
SearchFormElem, //like default state, but inside a form
PartialFormElemMatch, //when parsing "<input", "<textarea" or other form elems, and "</form"
SearchMethod(usize), //when inside the first <input>, search for begining of a param
PartialNameMatch(usize), //when parsing "name="_method""
CloseInputTag, //only if insert after, search for '>' of a "<input name=\"_method\">"
Init, //default state
PartialFormMatch, //when parsing "<form"
SearchFormElem, //like default state, but inside a form
PartialFormElemMatch, //when parsing "<input", "<textarea" or other form elems, and "</form"
SearchMethod(usize), //when inside the first <input>, search for begining of a param
PartialNameMatch(usize), //when parsing "name="_method""
CloseInputTag, //only if insert after, search for '>' of a "<input name=\"_method\">"
}
pub struct CsrfProxy<'a> {
@ -74,7 +73,7 @@ pub struct CsrfProxy<'a> {
token: Vec<u8>, //a full input tag loaded with a valid token
buf: Buffer,
unparsed: Vec<u8>,
state: ParseState, //state of the parser
state: ParseState, //state of the parser
eof: bool,
}
@ -100,46 +99,46 @@ impl<'a> CsrfProxy<'a> {
impl<'a> Read for CsrfProxy<'a> {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
while self.buf.len() < buf.len() && !(self.eof && self.unparsed.is_empty()) {
let len = if !self.eof {
let unparsed_len = self.unparsed.len();
self.unparsed.resize(4096,0);
unparsed_len +
match self.underlying.read(&mut self.unparsed[unparsed_len..]) {
Ok(0) => {
self.eof = true;
0
},
Ok(len) => len,
Err(e) => return Err(e),
self.unparsed.resize(4096, 0);
unparsed_len + match self.underlying.read(&mut self.unparsed[unparsed_len..]) {
Ok(0) => {
self.eof = true;
0
}
Ok(len) => len,
Err(e) => return Err(e),
}
} else {
self.unparsed.len()
};
self.unparsed.resize(len, 0);//we growed unparsed buffer to 4k before, so shrink it to it's needed size
self.unparsed.resize(len, 0); //we growed unparsed buffer to 4k before, so shrink it to it's needed size
let (consumed, insert_token) = {
let mut buf = &self.unparsed[..len];//work only on the initialized part
let mut buf = &self.unparsed[..len]; //work only on the initialized part
let mut consumed = 0;
let mut leave = false;
let mut insert_token = false;
while !leave {
self.state = match self.state {
Init => {
if let Some(tag_pos) = buf.iter().position(|&c| c as char=='<') {
if let Some(tag_pos) = buf.iter().position(|&c| c as char == '<') {
buf = &buf[tag_pos..];
consumed+=tag_pos;
consumed += tag_pos;
PartialFormMatch
} else {
leave = true;
consumed += buf.len();
Init
}
},
}
PartialFormMatch => {
if let Some(lower_begin) = buf.get(1..5).map(|slice| slice.to_ascii_lowercase()) {
if let Some(lower_begin) =
buf.get(1..5).map(|slice| slice.to_ascii_lowercase())
{
buf = &buf[5..];
consumed += 5;
if lower_begin == "form".as_bytes() {
@ -148,30 +147,34 @@ impl<'a> Read for CsrfProxy<'a> {
Init
}
} else {
leave = true;
PartialFormMatch
leave = true;
PartialFormMatch
}
},
}
SearchFormElem => {
if let Some(tag_pos) = buf.iter().position(|&c| c as char=='<') {
if let Some(tag_pos) = buf.iter().position(|&c| c as char == '<') {
buf = &buf[tag_pos..];
consumed+=tag_pos;
consumed += tag_pos;
PartialFormElemMatch
} else {
leave = true;
consumed += buf.len();
SearchFormElem
}},
}
}
PartialFormElemMatch => {
if let Some(lower_begin) = buf.get(1..9).map(|slice| slice.to_ascii_lowercase()) {
if let Some(lower_begin) =
buf.get(1..9).map(|slice| slice.to_ascii_lowercase())
{
if lower_begin.starts_with("/form".as_bytes())
|| lower_begin.starts_with("textarea".as_bytes())
|| lower_begin.starts_with("button".as_bytes())
|| lower_begin.starts_with("select".as_bytes()) {
|| lower_begin.starts_with("select".as_bytes())
{
insert_token = true;
leave = true;
Init
} else if lower_begin.starts_with("input".as_bytes()){
} else if lower_begin.starts_with("input".as_bytes()) {
SearchMethod(6)
} else {
buf = &buf[9..];
@ -179,15 +182,19 @@ impl<'a> Read for CsrfProxy<'a> {
SearchFormElem
}
} else {
leave = true;
SearchFormElem
leave = true;
SearchFormElem
}
},
}
SearchMethod(pos) => {
if let Some(meth_pos) = buf[pos..].iter().position(|&c| c as char == ' ' || c as char == '>') {
if let Some(meth_pos) = buf[pos..]
.iter()
.position(|&c| c as char == ' ' || c as char == '>')
{
if buf[meth_pos + pos] as char == ' ' {
PartialNameMatch(meth_pos + pos + 1)
} else { //reached '>'
} else {
//reached '>'
insert_token = true;
leave = true;
Init
@ -196,38 +203,42 @@ impl<'a> Read for CsrfProxy<'a> {
leave = true;
SearchMethod(buf.len())
}
},
}
PartialNameMatch(pos) => {
if let Some(lower_begin) = buf.get(pos..pos+14).map(|slice| slice.to_ascii_lowercase()) {
if let Some(lower_begin) = buf
.get(pos..pos + 14)
.map(|slice| slice.to_ascii_lowercase())
{
if lower_begin.starts_with("name=\"_method\"".as_bytes())
|| lower_begin.starts_with("name='_method'".as_bytes()) {
buf = &buf[pos+14..];
consumed += pos+14;
|| lower_begin.starts_with("name='_method'".as_bytes())
{
buf = &buf[pos + 14..];
consumed += pos + 14;
CloseInputTag
} else if lower_begin.starts_with("name=_method".as_bytes()) {
buf = &buf[pos+12..];
consumed += pos+12;
buf = &buf[pos + 12..];
consumed += pos + 12;
CloseInputTag
} else {
SearchMethod(pos)
}
} else {
leave = true;
PartialNameMatch(pos)
leave = true;
PartialNameMatch(pos)
}
},
}
CloseInputTag => {
leave = true;
if let Some(tag_pos) = buf.iter().position(|&c| c as char=='>') {
buf = &buf[tag_pos+1..];
consumed+=tag_pos+1;
if let Some(tag_pos) = buf.iter().position(|&c| c as char == '>') {
buf = &buf[tag_pos + 1..];
consumed += tag_pos + 1;
insert_token = true;
Init
} else {
consumed += buf.len();
CloseInputTag
}
},
}
}
}
(consumed, insert_token)
@ -243,9 +254,9 @@ impl<'a> Read for CsrfProxy<'a> {
}
#[cfg(test)]
mod tests{
mod tests {
use csrf_proxy::{Buffer, CsrfProxy};
use std::io::{Cursor,Read};
use std::io::{Cursor, Read};
#[test]
fn test_buffer_size() {
@ -270,26 +281,26 @@ mod tests{
let mut buffer = Buffer::new();
let mut buf = [0; 8];
buffer.push_back(vec![0,1,2,3,4,5,6,7,8,9]);
buffer.push_back(vec![10,11,12,13,14,15,16,17,18,19]);
buffer.push_back(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
buffer.push_back(vec![10, 11, 12, 13, 14, 15, 16, 17, 18, 19]);
let size = buffer.read(&mut buf);
assert_eq!(size, 8);
assert_eq!(buf,[0,1,2,3,4,5,6,7]);
assert_eq!(buf, [0, 1, 2, 3, 4, 5, 6, 7]);
buffer.push_back(vec![20,21,22,23,24,25,26,27,28,29]);
buffer.push_back(vec![20, 21, 22, 23, 24, 25, 26, 27, 28, 29]);
let size = buffer.read(&mut buf);
assert_eq!(size, 8);
assert_eq!(buf,[8,9,10,11,12,13,14,15]);
assert_eq!(buf, [8, 9, 10, 11, 12, 13, 14, 15]);
let size = buffer.read(&mut buf);
assert_eq!(size, 8);
assert_eq!(buf,[16,17,18,19,20,21,22,23]);
assert_eq!(buf, [16, 17, 18, 19, 20, 21, 22, 23]);
let size = buffer.read(&mut buf);
assert_eq!(size, 6);
assert_eq!(buf[..6], [24,25,26,27,28,29]);
assert_eq!(buf[..6], [24, 25, 26, 27, 28, 29]);
let size = buffer.read(&mut buf);
assert_eq!(size, 0);
@ -305,12 +316,13 @@ mod tests{
<body>
Body of this simple doc
</body>
</html>".as_bytes();
</html>"
.as_bytes();
let mut proxy = CsrfProxy::from(Box::new(Cursor::new(data)), "abcd".as_bytes());
let mut pr_data = Vec::new();
let read = proxy.read_to_end(&mut pr_data);
assert_eq!(read.unwrap(), data.len());
assert_eq!(&pr_data,&data)
assert_eq!(&pr_data, &data)
}
#[test]
@ -327,7 +339,8 @@ mod tests{
</p>
</form>
</body>
</html>".as_bytes();
</html>"
.as_bytes();
let expected = "<!DOCTYPE html>
<html>
<head>
@ -340,12 +353,16 @@ mod tests{
</p>
<input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/></form>
</body>
</html>".as_bytes();
</html>"
.as_bytes();
let mut proxy = CsrfProxy::from(Box::new(Cursor::new(data)), "abcd".as_bytes());
let mut pr_data = Vec::new();
let read = proxy.read_to_end(&mut pr_data);
assert_eq!(read.unwrap(), data.len() + "<input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/>".len());
assert_eq!(&pr_data,&expected)
assert_eq!(
read.unwrap(),
data.len() + "<input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/>".len()
);
assert_eq!(&pr_data, &expected)
}
#[test]
@ -360,7 +377,8 @@ mod tests{
<input name=\"name\"/>
</form>
</body>
</html>".as_bytes();
</html>"
.as_bytes();
let expected = "<!DOCTYPE html>
<html>
<head>
@ -371,12 +389,16 @@ mod tests{
<input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/><input name=\"name\"/>
</form>
</body>
</html>".as_bytes();
</html>"
.as_bytes();
let mut proxy = CsrfProxy::from(Box::new(Cursor::new(data)), "abcd".as_bytes());
let mut pr_data = Vec::new();
let read = proxy.read_to_end(&mut pr_data);
assert_eq!(read.unwrap(), data.len() + "<input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/>".len());
assert_eq!(&pr_data,&expected)
assert_eq!(
read.unwrap(),
data.len() + "<input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/>".len()
);
assert_eq!(&pr_data, &expected)
}
#[test]
@ -391,7 +413,8 @@ mod tests{
<input name=\"_method\"/>
</form>
</body>
</html>".as_bytes();
</html>"
.as_bytes();
let expected = "<!DOCTYPE html>
<html>
<head>
@ -402,15 +425,19 @@ mod tests{
<input name=\"_method\"/><input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/>
</form>
</body>
</html>".as_bytes();
</html>"
.as_bytes();
let mut proxy = CsrfProxy::from(Box::new(Cursor::new(data)), "abcd".as_bytes());
let mut pr_data = Vec::new();
let read = proxy.read_to_end(&mut pr_data);
assert_eq!(read.unwrap(), data.len() + "<input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/>".len());
assert_eq!(&pr_data,&expected)
assert_eq!(
read.unwrap(),
data.len() + "<input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/>".len()
);
assert_eq!(&pr_data, &expected)
}
struct ErrorReader{}
struct ErrorReader {}
impl Read for ErrorReader {
fn read(&mut self, _buf: &mut [u8]) -> Result<usize, ::std::io::Error> {
@ -420,15 +447,18 @@ mod tests{
#[test]
fn test_relay_error() {
let buf = &mut[0; 1];
let err = ErrorReader{};
let buf = &mut [0; 1];
let err = ErrorReader {};
let mut proxy_err = CsrfProxy::from(Box::new(err), &[0]);
let read = proxy_err.read(buf).unwrap_err();
assert_eq!(read.kind(), ::std::io::Error::new(::std::io::ErrorKind::Other, "").kind());
assert_eq!(
read.kind(),
::std::io::Error::new(::std::io::ErrorKind::Other, "").kind()
);
}
struct SlowReader<'a>{
content: &'a[u8],
struct SlowReader<'a> {
content: &'a [u8],
}
impl<'a> Read for SlowReader<'a> {
@ -444,7 +474,8 @@ mod tests{
}
#[test]
fn test_difficult_cut() {//this basically re-test the parser, using short reads so it encounter rare code paths
fn test_difficult_cut() {
//this basically re-test the parser, using short reads so it encounter rare code paths
let data = "<!DOCTYPE html>
<html>
<head>
@ -457,7 +488,8 @@ mod tests{
</p>
</form>
</body>
</html>".as_bytes();
</html>"
.as_bytes();
let expected = "<!DOCTYPE html>
<html>
<head>
@ -470,12 +502,16 @@ mod tests{
</p>
<input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/></form>
</body>
</html>".as_bytes();
let mut proxy = CsrfProxy::from(Box::new(SlowReader{content: data}), "abcd".as_bytes());
</html>"
.as_bytes();
let mut proxy = CsrfProxy::from(Box::new(SlowReader { content: data }), "abcd".as_bytes());
let mut pr_data = Vec::new();
let read = proxy.read_to_end(&mut pr_data);
assert_eq!(read.unwrap(), data.len() + "<input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/>".len());
assert_eq!(&pr_data,&expected);
assert_eq!(
read.unwrap(),
data.len() + "<input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/>".len()
);
assert_eq!(&pr_data, &expected);
let data = "<!DOCTYPE html>
<html>
@ -487,7 +523,8 @@ mod tests{
<input name=\"name\"/>
</form>
</body>
</html>".as_bytes();
</html>"
.as_bytes();
let expected = "<!DOCTYPE html>
<html>
<head>
@ -498,12 +535,16 @@ mod tests{
<input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/><input name=\"name\"/>
</form>
</body>
</html>".as_bytes();
let mut proxy = CsrfProxy::from(Box::new(SlowReader{content: data}), "abcd".as_bytes());
</html>"
.as_bytes();
let mut proxy = CsrfProxy::from(Box::new(SlowReader { content: data }), "abcd".as_bytes());
let mut pr_data = Vec::new();
let read = proxy.read_to_end(&mut pr_data);
assert_eq!(read.unwrap(), data.len() + "<input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/>".len());
assert_eq!(&pr_data,&expected);
assert_eq!(
read.unwrap(),
data.len() + "<input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/>".len()
);
assert_eq!(&pr_data, &expected);
let data = "<!DOCTYPE html>
<html>
@ -515,7 +556,8 @@ mod tests{
<input name=\"_method\"/>
</form>
</body>
</html>".as_bytes();
</html>"
.as_bytes();
let expected = "<!DOCTYPE html>
<html>
<head>
@ -526,11 +568,15 @@ mod tests{
<input name=\"_method\"/><input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/>
</form>
</body>
</html>".as_bytes();
let mut proxy = CsrfProxy::from(Box::new(SlowReader{content: data}), "abcd".as_bytes());
</html>"
.as_bytes();
let mut proxy = CsrfProxy::from(Box::new(SlowReader { content: data }), "abcd".as_bytes());
let mut pr_data = Vec::new();
let read = proxy.read_to_end(&mut pr_data);
assert_eq!(read.unwrap(), data.len() + "<input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/>".len());
assert_eq!(&pr_data,&expected)
assert_eq!(
read.unwrap(),
data.len() + "<input type=\"hidden\" name=\"csrf-token\" value=\"abcd\"/>".len()
);
assert_eq!(&pr_data, &expected)
}
}

@ -1,9 +1,9 @@
use csrf::{AesGcmCsrfProtection, CsrfProtection, CSRF_COOKIE_NAME};
use data_encoding::{BASE64, BASE64URL_NOPAD};
use rocket::{Request, State};
use rocket::http::{Cookie, Status};
use rocket::outcome::Outcome;
use rocket::request::{self, FromRequest};
use rocket::{Request, State};
use serde::{Serialize, Serializer};
use time::Duration;

@ -49,11 +49,11 @@ extern crate rocket;
extern crate serde;
extern crate time;
mod csrf_proxy;
mod csrf_fairing;
mod csrf_proxy;
mod csrf_token;
mod path;
mod utils;
pub use self::csrf_fairing::{CsrfFairingBuilder, CsrfFairing};
pub use self::csrf_fairing::{CsrfFairing, CsrfFairingBuilder};
pub use self::csrf_token::CsrfToken;

@ -30,32 +30,34 @@ impl Path {
}
})
.collect();
if path[0..path.len()-1].iter().any(|a|
if let PathPart::MultiDynamic(_) = a {true} else {false}
) {
if path[0..path.len() - 1].iter().any(|a| {
if let PathPart::MultiDynamic(_) = a {
true
} else {
false
}
}) {
panic!("PathPart::MultiDynamic can only be found at end of path"); //TODO return error instead of panic
}
let param = query.map(|query| {
let param = query.map(|query| {
parse_args(query)
.map(|(k, v)| {(
k.to_owned(),
if v.starts_with('<') && v.ends_with("..>") {
panic!("PathPart::MultiDynamic is invalid in query part");
} else if v.starts_with('<') && v.ends_with('>') {
//do the same kind of parsing as above, but on query params
PathPart::Dynamic(v[1..v.len() - 1].to_owned())
} else {
PathPart::Static(v.to_owned())
},
)
})
.collect()
.map(|(k, v)| {
(
k.to_owned(),
if v.starts_with('<') && v.ends_with("..>") {
panic!("PathPart::MultiDynamic is invalid in query part");
} else if v.starts_with('<') && v.ends_with('>') {
//do the same kind of parsing as above, but on query params
PathPart::Dynamic(v[1..v.len() - 1].to_owned())
} else {
PathPart::Static(v.to_owned())
},
)
})
.collect()
});
Path {
path,
param,
}
Path { path, param }
}
pub fn extract<'a>(&self, uri: &'a str) -> Option<HashMap<&str, String>> {
@ -73,32 +75,32 @@ impl Path {
loop {
match reference.next() {
Some(reference) => {
match reference {
PathPart::Static(reference) => {
//static, but not the same, fail to parse
if let Some(val) = path.next() {
if val!=reference{
return None
}
} else {
return None
}
},
PathPart::Dynamic(key) => {
//dynamic, store to hashmap
if let Some(val) = path.next() {
res.insert(key, val.to_owned());
} else {
return None
match reference {
PathPart::Static(reference) => {
//static, but not the same, fail to parse
if let Some(val) = path.next() {
if val != reference {
return None;
}
} else {
return None;
}
PathPart::MultiDynamic(key) => {
let val = path.collect::<Vec<_>>().join("/");
res.insert(key, val);
break;
}
PathPart::Dynamic(key) => {
//dynamic, store to hashmap
if let Some(val) = path.next() {
res.insert(key, val.to_owned());
} else {
return None;
}
};
}
}
PathPart::MultiDynamic(key) => {
let val = path.collect::<Vec<_>>().join("/");
res.insert(key, val);
break;
}
};
}
None => if path.next().is_some() {
//not the same lenght, fail to parse
return None;
@ -120,7 +122,9 @@ impl Path {
//dynamic, store to hashmap
res.insert(key, hm.get::<&str>(&(k as &str))?.to_string());
}
PathPart::MultiDynamic(_) => panic!("PathPart::MultiDynamic is invalid in query part"),
PathPart::MultiDynamic(_) => {
panic!("PathPart::MultiDynamic is invalid in query part")
}
}
}
} else {
@ -143,7 +147,9 @@ impl Path {
res.push('/');
match seg {
PathPart::Static(val) => res.push_str(val),
PathPart::Dynamic(val) | PathPart::MultiDynamic(val) => res.push_str(param.get::<str>(val)?),
PathPart::Dynamic(val) | PathPart::MultiDynamic(val) => {
res.push_str(param.get::<str>(val)?)
}
}
}
if let Some(ref keymap) = self.param {
@ -155,7 +161,9 @@ impl Path {
match v {
PathPart::Static(val) => res.push_str(val),
PathPart::Dynamic(val) => res.push_str(param.get::<str>(val)?),
PathPart::MultiDynamic(_) => panic!("PathPart::MultiDynamic is invalid in query part"),
PathPart::MultiDynamic(_) => {
panic!("PathPart::MultiDynamic is invalid in query part")
}
}
res.push('&');
}
@ -172,7 +180,7 @@ enum PathPart {
}
#[cfg(test)]
mod tests{
mod tests {
use path::Path;
use std::collections::HashMap;
#[test]
@ -189,41 +197,66 @@ mod tests{
assert_eq!(no_query.map(&HashMap::new()).unwrap(), "/path/no_query");
let mut hashmap = HashMap::new();
hashmap.insert("key","value".to_owned());
hashmap.insert("key", "value".to_owned());
assert_eq!(no_query.map(&hashmap).unwrap(), "/path/no_query");
}
#[test]
fn test_static_path_with_query() {
let query = Path::from("/path/query?param=value&param2=value2");
assert!(query.extract("/path/query").is_none());
assert!(query.extract("/path/other?param=value&param2=value2").is_none());
assert!(query.extract("/path/query/longer?param=value&param2=value2").is_none());
assert!(
query
.extract("/path/other?param=value&param2=value2")
.is_none()
);
assert!(
query
.extract("/path/query/longer?param=value&param2=value2")
.is_none()
);
assert!(query.extract("/path?param=value&param2=value2").is_none());
let hashmap = query.extract("/path/query?param=value&param2=value2").unwrap();
let hashmap = query
.extract("/path/query?param=value&param2=value2")
.unwrap();
assert_eq!(hashmap.len(), 0);
let hashmap = query.extract("/path/query?param2=value2&param=value").unwrap();
let hashmap = query
.extract("/path/query?param2=value2&param=value")
.unwrap();
assert_eq!(hashmap.len(), 0);
let uri = query.map(&HashMap::new()).unwrap();
assert!(uri=="/path/query?param=value&param2=value2" || uri=="/path/query?param2=value2&param=value");
assert!(
uri == "/path/query?param=value&param2=value2"
|| uri == "/path/query?param2=value2&param=value"
);
let mut hashmap = HashMap::new();
hashmap.insert("key","value".to_owned());
hashmap.insert("key", "value".to_owned());
let uri = query.map(&hashmap).unwrap();
assert!(uri=="/path/query?param=value&param2=value2" || uri=="/path/query?param2=value2&param=value");
assert!(
uri == "/path/query?param=value&param2=value2"
|| uri == "/path/query?param2=value2&param=value"
);
}
#[test]
fn test_dynamic_path_without_query() {
let no_query = Path::from("/path/<with>/<dynamic>/values");
assert!(no_query.extract("/path/with/dynamic/values/longer").is_none());
assert!(
no_query
.extract("/path/with/dynamic/values/longer")
.is_none()
);
assert!(no_query.extract("/path/with/dynamic").is_none());
assert!(no_query.extract("/path/with/dynamic/non_value").is_none());
assert!(no_query.extract("/path/with/dynamic/values?and=query").is_none());
assert!(
no_query
.extract("/path/with/dynamic/values?and=query")
.is_none()
);
let hashmap = no_query.extract("/path/containing/moving/values").unwrap();
assert_eq!(hashmap.len(), 2);
@ -233,25 +266,39 @@ mod tests{
assert!(no_query.map(&HashMap::new()).is_none());
let mut hashmap = HashMap::new();
hashmap.insert("with","with".to_owned());
hashmap.insert("dynamic","non_static".to_owned());
assert_eq!(no_query.map(&hashmap).unwrap(), "/path/with/non_static/values");
hashmap.insert("random","value".to_owned());
assert_eq!(no_query.map(&hashmap).unwrap(), "/path/with/non_static/values");
hashmap.insert("with", "with".to_owned());
hashmap.insert("dynamic", "non_static".to_owned());
assert_eq!(
no_query.map(&hashmap).unwrap(),
"/path/with/non_static/values"
);
hashmap.insert("random", "value".to_owned());
assert_eq!(
no_query.map(&hashmap).unwrap(),
"/path/with/non_static/values"
);
}
#[test]
fn test_dynamic_path_with_query() {
let query = Path::from("/path/<with>/<dynamic>/values?key=<value>&static=static");
assert!(query.extract("/path/with/dynamic/values?key=something&static=error").is_none());
assert!(
query
.extract("/path/with/dynamic/values?key=something&static=error")
.is_none()
);
let hashmap = query.extract("/path/containing/moving/values?key=val&static=static").unwrap();
let hashmap = query
.extract("/path/containing/moving/values?key=val&static=static")
.unwrap();
assert_eq!(hashmap.len(), 3);
assert_eq!(hashmap.get("with").unwrap(), "containing");
assert_eq!(hashmap.get("dynamic").unwrap(), "moving");
assert_eq!(hashmap.get("value").unwrap(), "val");
let hashmap = query.extract("/path/containing/moving/values?static=static&key=val").unwrap();
let hashmap = query
.extract("/path/containing/moving/values?static=static&key=val")
.unwrap();
assert_eq!(hashmap.len(), 3);
assert_eq!(hashmap.get("with").unwrap(), "containing");
assert_eq!(hashmap.get("dynamic").unwrap(), "moving");
@ -260,12 +307,22 @@ mod tests{
assert!(query.map(&HashMap::new()).is_none());
let mut hashmap = HashMap::new();
hashmap.insert("with","with".to_owned());
hashmap.insert("dynamic","non_static".to_owned());
hashmap.insert("with", "with".to_owned());
hashmap.insert("dynamic", "non_static".to_owned());
hashmap.insert("value", "something".to_owned());
assert!(query.map(&hashmap).unwrap()=="/path/with/non_static/values?key=something&static=static" || query.map(&hashmap).unwrap()=="/path/with/non_static/values?static=static&key=something");
hashmap.insert("random","value".to_owned());
assert!(query.map(&hashmap).unwrap()=="/path/with/non_static/values?key=something&static=static" || query.map(&hashmap).unwrap()=="/path/with/non_static/values?static=static&key=something");
assert!(
query.map(&hashmap).unwrap()
== "/path/with/non_static/values?key=something&static=static"
|| query.map(&hashmap).unwrap()
== "/path/with/non_static/values?static=static&key=something"
);
hashmap.insert("random", "value".to_owned());
assert!(
query.map(&hashmap).unwrap()
== "/path/with/non_static/values?key=something&static=static"
|| query.map(&hashmap).unwrap()
== "/path/with/non_static/values?static=static&key=something"
);
}
#[test]
@ -283,17 +340,22 @@ mod tests{
#[test]
fn test_multidynamic() {
let query = Path::from("/path/<multidyn..>?static=static");
let hashmap = query.extract("/path?static=static").unwrap();
assert_eq!(hashmap.len(), 1);
assert_eq!(hashmap.get("multidyn").unwrap(), "");
let hashmap = query.extract("/path/longer/than/before?static=static").unwrap();
let hashmap = query
.extract("/path/longer/than/before?static=static")
.unwrap();
assert_eq!(hashmap.len(), 1);
assert_eq!(hashmap.get("multidyn").unwrap(), "longer/than/before");
let mut hashmap = HashMap::new();
hashmap.insert("multidyn","something".to_owned());
assert_eq!(query.map(&hashmap).unwrap(), "/path/something?static=static");
hashmap.insert("multidyn", "something".to_owned());
assert_eq!(
query.map(&hashmap).unwrap(),
"/path/something?static=static"
);
}
}

@ -15,18 +15,24 @@ fn parse_keyvalue(kv: &str) -> Option<(&str, &str)> {
#[cfg(test)]
mod tests {
use utils::{parse_keyvalue, parse_args};
use utils::{parse_args, parse_keyvalue};
#[test]
fn test_parse_keyvalue() {
assert_eq!(parse_keyvalue("a_key=a_value").unwrap(),("a_key", "a_value"));
assert_eq!(
parse_keyvalue("a_key=a_value").unwrap(),
("a_key", "a_value")
);
assert_eq!(parse_keyvalue("=a_value").unwrap(),("", "a_value"));
assert_eq!(parse_keyvalue("=a_value").unwrap(), ("", "a_value"));
assert_eq!(parse_keyvalue("a_key=").unwrap(),("a_key", ""));
assert_eq!(parse_keyvalue("a_key=").unwrap(), ("a_key", ""));
assert_eq!(parse_keyvalue("a_key=a=value").unwrap(),("a_key", "a=value"));
assert_eq!(
parse_keyvalue("a_key=a=value").unwrap(),
("a_key", "a=value")
);
assert_eq!(parse_keyvalue("=").unwrap(),("", ""));
assert_eq!(parse_keyvalue("=").unwrap(), ("", ""));
assert!(parse_keyvalue("a_key_a_value").is_none());

Loading…
Cancel
Save