compile-time limit the size of post body

new-idea
Avril 4 years ago
parent b07a320234
commit 4e813abed5
Signed by: flanchan
GPG Key ID: 284488987C31F630

@ -23,3 +23,6 @@ pub const POST_EXPIRE: tokio::time::Duration = tokio::time::Duration::from_secs(
* 24 // Day
* 120
);
/// Max size of encrypted body
pub const POST_BODY_MAX_SIZE: usize = (1024 * 1024) * 20; // 20MB

@ -215,11 +215,86 @@ impl<'a, F: FormatSpec + ?Sized> TryFrom<String> for FormattedString<F>
pub mod formats
{
{
use super::*;
cfg_if! {
if #[cfg(feature="nightly")] {
/// A format valid for any string
pub type AnyFormat = !;
} else {
/// A format valid for any string
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum AnyFormat{}
}
}
impl FormatSpec for AnyFormat
{
type Error = std::convert::Infallible;
#[inline] fn validate(_: &str) -> Result<(), Self::Error>
{
Ok(())
}
}
/// A format spec that must satisfy both these format specs in order
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct BothFormat<T, U = AnyFormat>(PhantomData<(T, U)>)
where T: FormatSpec, U: FormatSpec;
#[derive(Debug)]
pub enum MultiFormatError<T,U>
{
First(T),
Second(U),
}
impl<T, U> FormatSpec for BothFormat<T,U>
where T: FormatSpec,
U: FormatSpec,
T::Error : error::Error + 'static + Send + Sync,
U::Error : error::Error + 'static + Send + Sync,
{
type Error = MultiFormatError<T::Error, U::Error>;
fn validate(s: &str) -> Result<(), Self::Error> {
T::validate(s).map_err(MultiFormatError::First)?;
U::validate(s).map_err(MultiFormatError::Second)?;
Ok(())
}
}
impl<T: 'static, U: 'static> error::Error for MultiFormatError<T,U>
where T: error::Error,
U: error::Error
{
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match &self {
Self::First(f) => Some(f),
Self::Second(n) => Some(n),
}
}
}
impl<T, U> fmt::Display for MultiFormatError<T,U>
where T: fmt::Display,
U: fmt::Display
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
match self {
Self::First(_) => write!(f, "the first condition failed"),
Self::Second(_) => write!(f, "the second condition failed"),
}
}
}
/// A hex string format specifier
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum HexFormat{}
/// A string with a constant max length
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum MaxLenFormat<const MAX: usize>{}

@ -5,20 +5,26 @@ use hard_format::formats::{
PEMFormattedString,
PEMFormattedStr,
MaxLenString,
self,
};
use tripcode::Tripcode;
id_type!(PostID; "A unique post ID");
/// Max length of `email` or `name` feild
const ID_MAX_LEN: usize = defaults::POST_ID_MAX_LEN;
/// String type that limits its bytes to the ID string max limit.
pub type IDMaxString = MaxLenString<ID_MAX_LEN>;
pub type IDMaxString = MaxLenString<{defaults::POST_ID_MAX_LEN}>;
/// The timestamp type used in posts
pub type PostTimestamp = chrono::DateTime<defaults::Timezone>;
/// A size limited PEM formatting specifier
type PostBodyFormat = formats::BothFormat<formats::MaxLenFormat<{defaults::POST_BODY_MAX_SIZE}>, formats::PEMFormat>;
/// A size limited PEM string
pub type PostBodyString = hard_format::FormattedString<PostBodyFormat>;
/// A size limited PEM string
pub type PostBodyStr = hard_format::FormattedStr<PostBodyFormat>;
/// A single completed post
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct Post
@ -31,7 +37,8 @@ pub struct Post
email: Option<IDMaxString>,
/// The client-side encrypted body string
body: PEMFormattedString,
body: PostBodyString,
/// Signature of the body (optional).
signature: Option<PEMFormattedString>,
@ -55,16 +62,16 @@ impl Post
/// Time since this post was created as a Chrono `Duration`.
pub fn time_since_creation(&self) -> chrono::Duration
{
defaults::Timezone::now() - self.created
defaults::Timezone::now() - self.created
}
/// Has this post expired?
///
/// Expired posts should be removed
pub fn expired(&self) -> bool
{
if let Ok(dur) = self.time_since_creation().to_std()
if let Ok(dur) = &self.time_since_creation().to_std()
{
dur >= *self.expires_in.as_ref().unwrap_or(&defaults::POST_EXPIRE)
dur >= self.expires_in.as_ref().unwrap_or(&defaults::POST_EXPIRE)
} else {
// Conversion failed. Expire the post
true
@ -96,10 +103,15 @@ impl Post
}
/// The body of this post
pub fn body(&self) -> &PEMFormattedStr
pub fn body(&self) -> &PostBodyStr
{
self.body.as_ref()
}
/// The PEM formatted signature of this post, if there is one.
pub fn signature(&self) -> Option<&PEMFormattedStr>
{
self.signature.as_ref().map(|x| x.as_ref())
}
}
#[cfg(test)]
@ -114,7 +126,7 @@ mod tests
name: Some("Some name".to_owned().try_into().unwrap()),
email: None,
tripcode: Some(super::Tripcode::generate("uhh hello").unwrap()),
body: unsafe { super::PEMFormattedStr::new_unchecked("test").to_owned() }, //temporary
body: unsafe { super::PostBodyStr::new_unchecked("test").to_owned() }, //temporary
signature: None,
hash: Default::default(),
@ -127,5 +139,5 @@ mod tests
let post2: super::Post = serde_json::from_slice(&post_json[..]).expect("Deserialise");
assert_eq!(post, post2);
println!("Post was: {:?}", post);
}
}
}

Loading…
Cancel
Save