Loading crates/s3s/src/ops/mod.rs +2 −8 Original line number Diff line number Diff line Loading @@ -40,22 +40,16 @@ fn serialize_error(x: S3Error) -> S3Result<Response> { } fn extract_s3_path(req: &mut Request) -> S3Result<S3Path> { if let Some(path) = req.extensions_mut().remove::<S3Path>() { return Ok(path); } let path = urlencoding::decode(req.uri().path()).map_err(|_| S3ErrorCode::InvalidURI)?; let ans = S3Path::parse(&path).map_err(|err| match err { let ans = crate::path::parse_path_style(&path).map_err(|err| match err { ParseS3PathError::InvalidPath => S3ErrorCode::InvalidURI, ParseS3PathError::InvalidBucketName => S3ErrorCode::InvalidBucketName, ParseS3PathError::KeyTooLong => S3ErrorCode::KeyTooLongError, })?; Ok(ans) Ok(ans.into()) } fn extract_qs(req: &mut Request) -> S3Result<Option<OrderedQs>> { if let Some(qs) = req.extensions_mut().remove::<OrderedQs>() { return Ok(Some(qs)); } let Some(query) = req.uri().query() else { return Ok(None) }; match OrderedQs::parse(query) { Ok(ans) => Ok(Some(ans)), Loading crates/s3s/src/path.rs +48 −56 Original line number Diff line number Diff line Loading @@ -47,50 +47,6 @@ pub enum ParseS3PathError { KeyTooLong, } impl S3Path { /// Parses a path-style request /// # Errors /// Returns an `Err` if the s3 path is invalid pub fn parse(path: &str) -> Result<Self, ParseS3PathError> { S3PathRef::parse(path).map(Into::into) } } impl<'a> S3PathRef<'a> { pub fn parse(path: &'a str) -> Result<Self, ParseS3PathError> { let path = if let Some(("", x)) = path.split_once('/') { x } else { return Err(ParseS3PathError::InvalidPath); }; if path.is_empty() { return Ok(Self::Root); } let (bucket, key) = match path.split_once('/') { None => (path, None), Some((x, "")) => (x, None), Some((bucket, key)) => (bucket, Some(key)), }; if !check_bucket_name(bucket) { return Err(ParseS3PathError::InvalidBucketName); } let key = match key { None => return Ok(Self::Bucket { bucket }), Some(k) => k, }; if !check_key(key) { return Err(ParseS3PathError::KeyTooLong); } Ok(Self::Object { bucket, key }) } } impl From<S3PathRef<'_>> for S3Path { fn from(val: S3PathRef<'_>) -> Self { match val { Loading Loading @@ -145,34 +101,70 @@ pub const fn check_key(key: &str) -> bool { key.len() <= 1024 } /// Parses a path-style request /// # Errors /// Returns an `Err` if the s3 path is invalid pub fn parse_path_style(uri_path: &str) -> Result<S3PathRef, ParseS3PathError> { let path = if let Some(("", x)) = uri_path.split_once('/') { x } else { return Err(ParseS3PathError::InvalidPath); }; if path.is_empty() { return Ok(S3PathRef::Root); } let (bucket, key) = match path.split_once('/') { None => (path, None), Some((x, "")) => (x, None), Some((bucket, key)) => (bucket, Some(key)), }; if !check_bucket_name(bucket) { return Err(ParseS3PathError::InvalidBucketName); } let key = match key { None => return Ok(S3PathRef::Bucket { bucket }), Some(k) => k, }; if !check_key(key) { return Err(ParseS3PathError::KeyTooLong); } Ok(S3PathRef::Object { bucket, key }) } #[cfg(test)] mod tests { use super::*; #[test] fn parse_s3_path() { assert_eq!(S3Path::parse("/"), Ok(S3Path::Root)); fn path_style() { assert_eq!(parse_path_style("/"), Ok(S3PathRef::Root)); assert_eq!(S3Path::parse("/bucket"), Ok(S3Path::Bucket { bucket: "bucket".into() })); assert_eq!(parse_path_style("/bucket"), Ok(S3PathRef::Bucket { bucket: "bucket" })); assert_eq!(S3Path::parse("/bucket/"), Ok(S3Path::Bucket { bucket: "bucket".into() })); assert_eq!(parse_path_style("/bucket/"), Ok(S3PathRef::Bucket { bucket: "bucket" })); assert_eq!( S3Path::parse("/bucket/dir/object"), Ok(S3Path::Object { bucket: "bucket".into(), key: "dir/object".into(), parse_path_style("/bucket/dir/object"), Ok(S3PathRef::Object { bucket: "bucket", key: "dir/object", }) ); assert_eq!(S3Path::parse("asd").unwrap_err(), ParseS3PathError::InvalidPath); assert_eq!(parse_path_style("asd").unwrap_err(), ParseS3PathError::InvalidPath); assert_eq!(S3Path::parse("a/").unwrap_err(), ParseS3PathError::InvalidPath); assert_eq!(parse_path_style("a/").unwrap_err(), ParseS3PathError::InvalidPath); assert_eq!(S3Path::parse("/*").unwrap_err(), ParseS3PathError::InvalidBucketName); assert_eq!(parse_path_style("/*").unwrap_err(), ParseS3PathError::InvalidBucketName); let too_long_path = format!("/{}/{}", "asd", "b".repeat(2048).as_str()); assert_eq!(S3Path::parse(&too_long_path).unwrap_err(), ParseS3PathError::KeyTooLong); assert_eq!(parse_path_style(&too_long_path).unwrap_err(), ParseS3PathError::KeyTooLong); } } Loading
crates/s3s/src/ops/mod.rs +2 −8 Original line number Diff line number Diff line Loading @@ -40,22 +40,16 @@ fn serialize_error(x: S3Error) -> S3Result<Response> { } fn extract_s3_path(req: &mut Request) -> S3Result<S3Path> { if let Some(path) = req.extensions_mut().remove::<S3Path>() { return Ok(path); } let path = urlencoding::decode(req.uri().path()).map_err(|_| S3ErrorCode::InvalidURI)?; let ans = S3Path::parse(&path).map_err(|err| match err { let ans = crate::path::parse_path_style(&path).map_err(|err| match err { ParseS3PathError::InvalidPath => S3ErrorCode::InvalidURI, ParseS3PathError::InvalidBucketName => S3ErrorCode::InvalidBucketName, ParseS3PathError::KeyTooLong => S3ErrorCode::KeyTooLongError, })?; Ok(ans) Ok(ans.into()) } fn extract_qs(req: &mut Request) -> S3Result<Option<OrderedQs>> { if let Some(qs) = req.extensions_mut().remove::<OrderedQs>() { return Ok(Some(qs)); } let Some(query) = req.uri().query() else { return Ok(None) }; match OrderedQs::parse(query) { Ok(ans) => Ok(Some(ans)), Loading
crates/s3s/src/path.rs +48 −56 Original line number Diff line number Diff line Loading @@ -47,50 +47,6 @@ pub enum ParseS3PathError { KeyTooLong, } impl S3Path { /// Parses a path-style request /// # Errors /// Returns an `Err` if the s3 path is invalid pub fn parse(path: &str) -> Result<Self, ParseS3PathError> { S3PathRef::parse(path).map(Into::into) } } impl<'a> S3PathRef<'a> { pub fn parse(path: &'a str) -> Result<Self, ParseS3PathError> { let path = if let Some(("", x)) = path.split_once('/') { x } else { return Err(ParseS3PathError::InvalidPath); }; if path.is_empty() { return Ok(Self::Root); } let (bucket, key) = match path.split_once('/') { None => (path, None), Some((x, "")) => (x, None), Some((bucket, key)) => (bucket, Some(key)), }; if !check_bucket_name(bucket) { return Err(ParseS3PathError::InvalidBucketName); } let key = match key { None => return Ok(Self::Bucket { bucket }), Some(k) => k, }; if !check_key(key) { return Err(ParseS3PathError::KeyTooLong); } Ok(Self::Object { bucket, key }) } } impl From<S3PathRef<'_>> for S3Path { fn from(val: S3PathRef<'_>) -> Self { match val { Loading Loading @@ -145,34 +101,70 @@ pub const fn check_key(key: &str) -> bool { key.len() <= 1024 } /// Parses a path-style request /// # Errors /// Returns an `Err` if the s3 path is invalid pub fn parse_path_style(uri_path: &str) -> Result<S3PathRef, ParseS3PathError> { let path = if let Some(("", x)) = uri_path.split_once('/') { x } else { return Err(ParseS3PathError::InvalidPath); }; if path.is_empty() { return Ok(S3PathRef::Root); } let (bucket, key) = match path.split_once('/') { None => (path, None), Some((x, "")) => (x, None), Some((bucket, key)) => (bucket, Some(key)), }; if !check_bucket_name(bucket) { return Err(ParseS3PathError::InvalidBucketName); } let key = match key { None => return Ok(S3PathRef::Bucket { bucket }), Some(k) => k, }; if !check_key(key) { return Err(ParseS3PathError::KeyTooLong); } Ok(S3PathRef::Object { bucket, key }) } #[cfg(test)] mod tests { use super::*; #[test] fn parse_s3_path() { assert_eq!(S3Path::parse("/"), Ok(S3Path::Root)); fn path_style() { assert_eq!(parse_path_style("/"), Ok(S3PathRef::Root)); assert_eq!(S3Path::parse("/bucket"), Ok(S3Path::Bucket { bucket: "bucket".into() })); assert_eq!(parse_path_style("/bucket"), Ok(S3PathRef::Bucket { bucket: "bucket" })); assert_eq!(S3Path::parse("/bucket/"), Ok(S3Path::Bucket { bucket: "bucket".into() })); assert_eq!(parse_path_style("/bucket/"), Ok(S3PathRef::Bucket { bucket: "bucket" })); assert_eq!( S3Path::parse("/bucket/dir/object"), Ok(S3Path::Object { bucket: "bucket".into(), key: "dir/object".into(), parse_path_style("/bucket/dir/object"), Ok(S3PathRef::Object { bucket: "bucket", key: "dir/object", }) ); assert_eq!(S3Path::parse("asd").unwrap_err(), ParseS3PathError::InvalidPath); assert_eq!(parse_path_style("asd").unwrap_err(), ParseS3PathError::InvalidPath); assert_eq!(S3Path::parse("a/").unwrap_err(), ParseS3PathError::InvalidPath); assert_eq!(parse_path_style("a/").unwrap_err(), ParseS3PathError::InvalidPath); assert_eq!(S3Path::parse("/*").unwrap_err(), ParseS3PathError::InvalidBucketName); assert_eq!(parse_path_style("/*").unwrap_err(), ParseS3PathError::InvalidBucketName); let too_long_path = format!("/{}/{}", "asd", "b".repeat(2048).as_str()); assert_eq!(S3Path::parse(&too_long_path).unwrap_err(), ParseS3PathError::KeyTooLong); assert_eq!(parse_path_style(&too_long_path).unwrap_err(), ParseS3PathError::KeyTooLong); } }