Loading crates/s3s/src/http/aws_chunked_stream.rs +13 −21 Original line number Diff line number Diff line Loading @@ -75,27 +75,19 @@ struct ChunkMeta<'a> { /// nom parser fn parse_chunk_meta(mut input: &[u8]) -> nom::IResult<&[u8], ChunkMeta<'_>> { use nom::{ bytes::complete::{tag, take, take_till1}, combinator::{all_consuming, map_res}, number::complete::hex_u32, sequence::tuple, }; use crate::utils::parser::consume; let mut parser = all_consuming(tuple(( take_till1(|c| c == b';'), tag(b";chunk-signature="), take(64_usize), tag(b"\r\n"), ))); let (size_str, signature) = { let (remain, (size_str, _, signature, _)) = parser(input)?; input = remain; (size_str, signature) }; use nom::bytes::complete::{tag, take, take_till1}; use nom::combinator::{all_consuming, map_res}; use nom::number::complete::hex_u32; use nom::sequence::delimited; let s = &mut input; let size = consume(s, take_till1(|c| c == b';'))?; let (_, size) = map_res(hex_u32, TryInto::try_into)(size)?; let (_, size) = map_res(hex_u32, TryInto::try_into)(size_str)?; let signature = consume(s, all_consuming(delimited(tag(b";chunk-signature="), take(64_usize), tag(b"\r\n"))))?; Ok((input, ChunkMeta { size, signature })) } Loading crates/s3s/src/http/multipart.rs +11 −11 Original line number Diff line number Diff line Loading @@ -427,26 +427,26 @@ struct ContentDisposition<'a> { /// parse content disposition value fn parse_content_disposition(input: &[u8]) -> nom::IResult<&[u8], ContentDisposition<'_>> { use nom::{ bytes::complete::{tag, take, take_till1}, combinator::{all_consuming, map_res, opt}, sequence::{delimited, preceded, tuple}, }; use nom::bytes::complete::{tag, take, take_till1}; use nom::combinator::{all_consuming, map_res, opt}; use nom::sequence::{delimited, preceded, tuple}; // TODO: escape? let name_parser = delimited(tag(b"name=\""), map_res(take_till1(|c| c == b'"'), std::str::from_utf8), take(1_usize)); let parse_name = delimited(tag(b"name=\""), map_res(take_till1(|c| c == b'"'), std::str::from_utf8), take(1_usize)); let filename_parser = delimited( let parse_filename = delimited( tag(b"filename=\""), map_res(take_till1(|c| c == b'"'), std::str::from_utf8), take(1_usize), ); let mut parser = all_consuming(tuple(( preceded(tag(b"form-data; "), name_parser), opt(preceded(tag(b"; "), filename_parser)), let mut parse = all_consuming(tuple(( preceded(tag(b"form-data; "), parse_name), opt(preceded(tag(b"; "), parse_filename)), ))); let (remaining, (name, filename)) = parser(input)?; let (remaining, (name, filename)) = parse(input)?; Ok((remaining, ContentDisposition { name, filename })) } Loading crates/s3s/src/lib.rs +1 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ clippy::single_match_else, clippy::wildcard_imports, clippy::let_underscore_untyped, clippy::inline_always, )] #[macro_use] Loading crates/s3s/src/sig_v2/authorization_v2.rs +26 −15 Original line number Diff line number Diff line Loading @@ -3,12 +3,6 @@ //! <https://docs.aws.amazon.com/AmazonS3/latest/userguide/RESTAuthentication.html#ConstructingTheAuthenticationHeader> //! use crate::utils::parser; use nom::bytes::complete::{tag, take, take_till}; use nom::combinator::{all_consuming, rest}; use nom::sequence::terminated; pub struct AuthorizationV2<'a> { pub access_key: &'a str, pub signature: &'a str, Loading @@ -24,18 +18,35 @@ pub struct ParseAuthorizationV2Error { impl<'a> AuthorizationV2<'a> { pub fn parse(input: &'a str) -> Result<Self, ParseAuthorizationV2Error> { let error = |_| ParseAuthorizationV2Error { _priv: () }; let colon_tail0 = terminated(take_till(|c| c == ':'), take(1_usize)); match parser::parse_authorization(input) { Ok(("", ans)) => Ok(ans), Ok(_) | Err(_) => Err(ParseAuthorizationV2Error { _priv: () }), } } } parser::parse(input, |p| { p.nom(tag("AWS "))?; mod parser { use super::AuthorizationV2; let access_key = p.nom(colon_tail0)?; let signature = p.nom(all_consuming(rest))?; use crate::utils::parser::consume; use nom::bytes::complete::{tag, take, take_till}; use nom::combinator::rest; use nom::sequence::terminated; use nom::IResult; pub fn parse_authorization(mut input: &str) -> IResult<&str, AuthorizationV2<'_>> { let s = &mut input; consume(s, tag("AWS "))?; let access_key = consume(s, until_colon0)?; let signature = consume(s, rest)?; Ok((input, AuthorizationV2 { access_key, signature })) } Ok(Self { access_key, signature }) }) .map_err(error) fn until_colon0(input: &str) -> IResult<&str, &str> { terminated(take_till(|c| c == ':'), take(1_usize))(input) } } Loading crates/s3s/src/sig_v4/amz_date.rs +40 −48 Original line number Diff line number Diff line //! x-amz-date use std::fmt::Write as _; use std::str::FromStr; use arrayvec::ArrayString; Loading @@ -25,59 +24,14 @@ pub struct AmzDate { /// [`AmzDate`] #[derive(Debug, thiserror::Error)] #[error("ParseAmzDateError")] pub struct ParseAmzDateError { /// private place holder _priv: (), } pub struct ParseAmzDateError(()); impl AmzDate { /// Parses `AmzDate` from header /// # Errors /// Returns an error if the header is invalid pub fn parse(header: &str) -> Result<Self, ParseAmzDateError> { /// nom parser fn nom_parse(input: &str) -> nom::IResult<&str, [&str; 6]> { use nom::{ bytes::complete::{tag, take}, combinator::all_consuming, sequence::tuple, }; let mut parser = all_consuming(tuple(( take(4_usize), take(2_usize), take(2_usize), tag("T"), take(2_usize), take(2_usize), take(2_usize), tag("Z"), ))); let (_, (year_str, month_str, day_str, _, hour_str, minute_str, second_str, _)) = parser(input)?; Ok((input, [year_str, month_str, day_str, hour_str, minute_str, second_str])) } /// parse number fn to_num<T: FromStr>(input: &str) -> Result<T, ParseAmzDateError> { match input.parse::<T>() { Ok(x) => Ok(x), Err(_) => Err(ParseAmzDateError { _priv: () }), } } match nom_parse(header) { Err(_) => Err(ParseAmzDateError { _priv: () }), Ok((_, [year_str, month_str, day_str, hour_str, minute_str, second_str])) => Ok(Self { year: to_num(year_str)?, month: to_num(month_str)?, day: to_num(day_str)?, hour: to_num(hour_str)?, minute: to_num(minute_str)?, second: to_num(second_str)?, }), } self::parser::parse(header).map_err(|_| ParseAmzDateError(())) } /// `{YYYY}{MM}{DD}T{HH}{MM}{SS}Z` Loading Loading @@ -107,3 +61,41 @@ impl AmzDate { Some(t.assume_utc()) } } mod parser { use super::*; use crate::utils::parser::{digit2, digit4, Error}; macro_rules! ensure { ($cond:expr) => { if !$cond { return Err(Error); } }; } pub fn parse(input: &str) -> Result<AmzDate, Error> { let x = input.as_bytes(); ensure!(x.len() == 16); let year = digit4([x[0], x[1], x[2], x[3]])?; let month = digit2([x[4], x[5]])?; let day = digit2([x[6], x[7]])?; ensure!(x[8] == b'T'); let hour = digit2([x[9], x[10]])?; let minute = digit2([x[11], x[12]])?; let second = digit2([x[13], x[14]])?; ensure!(x[15] == b'Z'); Ok(AmzDate { year, month, day, hour, minute, second, }) } } Loading
crates/s3s/src/http/aws_chunked_stream.rs +13 −21 Original line number Diff line number Diff line Loading @@ -75,27 +75,19 @@ struct ChunkMeta<'a> { /// nom parser fn parse_chunk_meta(mut input: &[u8]) -> nom::IResult<&[u8], ChunkMeta<'_>> { use nom::{ bytes::complete::{tag, take, take_till1}, combinator::{all_consuming, map_res}, number::complete::hex_u32, sequence::tuple, }; use crate::utils::parser::consume; let mut parser = all_consuming(tuple(( take_till1(|c| c == b';'), tag(b";chunk-signature="), take(64_usize), tag(b"\r\n"), ))); let (size_str, signature) = { let (remain, (size_str, _, signature, _)) = parser(input)?; input = remain; (size_str, signature) }; use nom::bytes::complete::{tag, take, take_till1}; use nom::combinator::{all_consuming, map_res}; use nom::number::complete::hex_u32; use nom::sequence::delimited; let s = &mut input; let size = consume(s, take_till1(|c| c == b';'))?; let (_, size) = map_res(hex_u32, TryInto::try_into)(size)?; let (_, size) = map_res(hex_u32, TryInto::try_into)(size_str)?; let signature = consume(s, all_consuming(delimited(tag(b";chunk-signature="), take(64_usize), tag(b"\r\n"))))?; Ok((input, ChunkMeta { size, signature })) } Loading
crates/s3s/src/http/multipart.rs +11 −11 Original line number Diff line number Diff line Loading @@ -427,26 +427,26 @@ struct ContentDisposition<'a> { /// parse content disposition value fn parse_content_disposition(input: &[u8]) -> nom::IResult<&[u8], ContentDisposition<'_>> { use nom::{ bytes::complete::{tag, take, take_till1}, combinator::{all_consuming, map_res, opt}, sequence::{delimited, preceded, tuple}, }; use nom::bytes::complete::{tag, take, take_till1}; use nom::combinator::{all_consuming, map_res, opt}; use nom::sequence::{delimited, preceded, tuple}; // TODO: escape? let name_parser = delimited(tag(b"name=\""), map_res(take_till1(|c| c == b'"'), std::str::from_utf8), take(1_usize)); let parse_name = delimited(tag(b"name=\""), map_res(take_till1(|c| c == b'"'), std::str::from_utf8), take(1_usize)); let filename_parser = delimited( let parse_filename = delimited( tag(b"filename=\""), map_res(take_till1(|c| c == b'"'), std::str::from_utf8), take(1_usize), ); let mut parser = all_consuming(tuple(( preceded(tag(b"form-data; "), name_parser), opt(preceded(tag(b"; "), filename_parser)), let mut parse = all_consuming(tuple(( preceded(tag(b"form-data; "), parse_name), opt(preceded(tag(b"; "), parse_filename)), ))); let (remaining, (name, filename)) = parser(input)?; let (remaining, (name, filename)) = parse(input)?; Ok((remaining, ContentDisposition { name, filename })) } Loading
crates/s3s/src/lib.rs +1 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ clippy::single_match_else, clippy::wildcard_imports, clippy::let_underscore_untyped, clippy::inline_always, )] #[macro_use] Loading
crates/s3s/src/sig_v2/authorization_v2.rs +26 −15 Original line number Diff line number Diff line Loading @@ -3,12 +3,6 @@ //! <https://docs.aws.amazon.com/AmazonS3/latest/userguide/RESTAuthentication.html#ConstructingTheAuthenticationHeader> //! use crate::utils::parser; use nom::bytes::complete::{tag, take, take_till}; use nom::combinator::{all_consuming, rest}; use nom::sequence::terminated; pub struct AuthorizationV2<'a> { pub access_key: &'a str, pub signature: &'a str, Loading @@ -24,18 +18,35 @@ pub struct ParseAuthorizationV2Error { impl<'a> AuthorizationV2<'a> { pub fn parse(input: &'a str) -> Result<Self, ParseAuthorizationV2Error> { let error = |_| ParseAuthorizationV2Error { _priv: () }; let colon_tail0 = terminated(take_till(|c| c == ':'), take(1_usize)); match parser::parse_authorization(input) { Ok(("", ans)) => Ok(ans), Ok(_) | Err(_) => Err(ParseAuthorizationV2Error { _priv: () }), } } } parser::parse(input, |p| { p.nom(tag("AWS "))?; mod parser { use super::AuthorizationV2; let access_key = p.nom(colon_tail0)?; let signature = p.nom(all_consuming(rest))?; use crate::utils::parser::consume; use nom::bytes::complete::{tag, take, take_till}; use nom::combinator::rest; use nom::sequence::terminated; use nom::IResult; pub fn parse_authorization(mut input: &str) -> IResult<&str, AuthorizationV2<'_>> { let s = &mut input; consume(s, tag("AWS "))?; let access_key = consume(s, until_colon0)?; let signature = consume(s, rest)?; Ok((input, AuthorizationV2 { access_key, signature })) } Ok(Self { access_key, signature }) }) .map_err(error) fn until_colon0(input: &str) -> IResult<&str, &str> { terminated(take_till(|c| c == ':'), take(1_usize))(input) } } Loading
crates/s3s/src/sig_v4/amz_date.rs +40 −48 Original line number Diff line number Diff line //! x-amz-date use std::fmt::Write as _; use std::str::FromStr; use arrayvec::ArrayString; Loading @@ -25,59 +24,14 @@ pub struct AmzDate { /// [`AmzDate`] #[derive(Debug, thiserror::Error)] #[error("ParseAmzDateError")] pub struct ParseAmzDateError { /// private place holder _priv: (), } pub struct ParseAmzDateError(()); impl AmzDate { /// Parses `AmzDate` from header /// # Errors /// Returns an error if the header is invalid pub fn parse(header: &str) -> Result<Self, ParseAmzDateError> { /// nom parser fn nom_parse(input: &str) -> nom::IResult<&str, [&str; 6]> { use nom::{ bytes::complete::{tag, take}, combinator::all_consuming, sequence::tuple, }; let mut parser = all_consuming(tuple(( take(4_usize), take(2_usize), take(2_usize), tag("T"), take(2_usize), take(2_usize), take(2_usize), tag("Z"), ))); let (_, (year_str, month_str, day_str, _, hour_str, minute_str, second_str, _)) = parser(input)?; Ok((input, [year_str, month_str, day_str, hour_str, minute_str, second_str])) } /// parse number fn to_num<T: FromStr>(input: &str) -> Result<T, ParseAmzDateError> { match input.parse::<T>() { Ok(x) => Ok(x), Err(_) => Err(ParseAmzDateError { _priv: () }), } } match nom_parse(header) { Err(_) => Err(ParseAmzDateError { _priv: () }), Ok((_, [year_str, month_str, day_str, hour_str, minute_str, second_str])) => Ok(Self { year: to_num(year_str)?, month: to_num(month_str)?, day: to_num(day_str)?, hour: to_num(hour_str)?, minute: to_num(minute_str)?, second: to_num(second_str)?, }), } self::parser::parse(header).map_err(|_| ParseAmzDateError(())) } /// `{YYYY}{MM}{DD}T{HH}{MM}{SS}Z` Loading Loading @@ -107,3 +61,41 @@ impl AmzDate { Some(t.assume_utc()) } } mod parser { use super::*; use crate::utils::parser::{digit2, digit4, Error}; macro_rules! ensure { ($cond:expr) => { if !$cond { return Err(Error); } }; } pub fn parse(input: &str) -> Result<AmzDate, Error> { let x = input.as_bytes(); ensure!(x.len() == 16); let year = digit4([x[0], x[1], x[2], x[3]])?; let month = digit2([x[4], x[5]])?; let day = digit2([x[6], x[7]])?; ensure!(x[8] == b'T'); let hour = digit2([x[9], x[10]])?; let minute = digit2([x[11], x[12]])?; let second = digit2([x[13], x[14]])?; ensure!(x[15] == b'Z'); Ok(AmzDate { year, month, day, hour, minute, second, }) } }