Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

http: validate request parameters #167

Merged
merged 5 commits into from
May 26, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion http/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,4 @@ percent-encoding = "2.1"
url = "2"

[dev-dependencies]
tokio = "0.2"
tokio = { features = ["macros"], version = "0.2" }
2 changes: 1 addition & 1 deletion http/examples/allowed-mentions/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
.content(format!(
"<@{}> you are not allowed to ping @everyone!",
user_id.0
))
))?
.allowed_mentions()
.parse_specific_users(vec![user_id])
.build()
Expand Down
1 change: 1 addition & 0 deletions http/examples/get-message/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
client
.create_message(channel_id)
.content(format!("Ping #{}", x))
.expect("content not a valid length")
}))
.await;

Expand Down
1 change: 1 addition & 0 deletions http/examples/proxy/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
client
.create_message(channel_id)
.content(format!("Ping #{}", x))
.expect("content not a valid length")
}))
.await;

Expand Down
41 changes: 36 additions & 5 deletions http/src/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@ use self::config::ClientConfigBuilder;
use crate::{
error::{Error, ResponseError, Result, UrlError},
ratelimiting::{RatelimitHeaders, Ratelimiter},
request::{channel::message::allowed_mentions::AllowedMentions, prelude::*, Request},
request::{
channel::message::allowed_mentions::AllowedMentions,
guild::{create_guild::CreateGuildError, create_guild_channel::CreateGuildChannelError},
prelude::*,
Request,
},
};
use log::{debug, warn};
use reqwest::{
Expand All @@ -20,6 +25,7 @@ use std::{
convert::TryFrom,
fmt::{Debug, Formatter, Result as FmtResult},
ops::{Deref, DerefMut},
result::Result as StdResult,
sync::Arc,
};
use twilight_model::{
Expand Down Expand Up @@ -177,7 +183,7 @@ impl Client {
/// let guild_id = GuildId(377840580245585931);
/// let user_id = UserId(114941315417899012);
/// client.create_ban(guild_id, user_id)
/// .delete_message_days(1)
/// .delete_message_days(1)?
/// .reason("memes")
/// .await?;
///
Expand Down Expand Up @@ -265,7 +271,7 @@ impl Client {
/// let guilds = client.current_user_guilds()
/// .after(after)
/// .before(before)
/// .limit(25)
/// .limit(25)?
/// .await?;
///
/// println!("{:?}", guilds);
Expand Down Expand Up @@ -359,7 +365,21 @@ impl Client {
GetGuild::new(self, guild_id)
}

pub fn create_guild(&self, name: impl Into<String>) -> CreateGuild<'_> {
/// Create a new request to create a guild.
///
/// The minimum length of the name is 2 UTF-16 characters and the maximum is
/// 100 UTF-16 characters.
///
/// # Errors
///
/// Returns [`CreateGuildError::NameInvalid`] if the name length is too
/// short or too long.
///
/// [`CreateGuildError::NameInvalid`]: ../request/guild/enum.CreateGuildError.html#variant.NameInvalid
pub fn create_guild(
&self,
name: impl Into<String>,
) -> StdResult<CreateGuild<'_>, CreateGuildError> {
CreateGuild::new(self, name)
}

Expand All @@ -379,11 +399,22 @@ impl Client {
GetGuildChannels::new(self, guild_id)
}

/// Create a new request to create a guild channel.
///
/// The minimum length of the name is 2 UTF-16 characters and the maximum is
/// 100 UTF-16 characters.
///
/// # Errors
///
/// Returns [`CreateGuildChannelError::NameInvalid`] if the name length is too
/// short or too long.
///
/// [`CreateGuildChannelError::NameInvalid`]: ../request/guild/enum.CreateGuildChannelError.html#variant.NameInvalid
pub fn create_guild_channel(
&self,
guild_id: GuildId,
name: impl Into<String>,
) -> CreateGuildChannel<'_> {
) -> StdResult<CreateGuildChannel<'_>, CreateGuildChannelError> {
CreateGuildChannel::new(self, guild_id, name)
}

Expand Down
2 changes: 1 addition & 1 deletion http/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use std::{
};
use url::ParseError as UrlParseError;

pub type Result<T> = StdResult<T, Error>;
pub type Result<T, E = Error> = StdResult<T, E>;

#[derive(Debug)]
pub enum ResponseError {
Expand Down
2 changes: 1 addition & 1 deletion http/src/ratelimiting/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ impl Display for RatelimitError {
name,
value,
..
} => write!(f, "The header {:?} has invalid UTF-8: {:?}", name, value),
} => write!(f, "The header {:?} has invalid UTF-16: {:?}", name, value),
Self::ParsingBoolText {
name,
text,
Expand Down
66 changes: 60 additions & 6 deletions http/src/request/channel/message/create_message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,44 @@ use reqwest::{
multipart::{Form, Part},
Body,
};
use std::collections::HashMap;
use std::{
collections::HashMap,
error::Error,
fmt::{Display, Formatter, Result as FmtResult},
};
use twilight_model::{
channel::{embed::Embed, Message},
id::ChannelId,
};

#[derive(Clone, Debug)]
pub enum CreateMessageError {
ContentInvalid,
EmbedTooLarge { source: EmbedValidationError },
}

impl Display for CreateMessageError {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
match self {
Self::ContentInvalid => f.write_str("the message content is invalid"),
Self::EmbedTooLarge {
..
} => f.write_str("the embed's contents are too long"),
}
}
}

impl Error for CreateMessageError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::ContentInvalid => None,
Self::EmbedTooLarge {
source,
} => Some(source),
}
}
}

#[derive(Default, Serialize)]
pub(crate) struct CreateMessageFields {
content: Option<String>,
Expand Down Expand Up @@ -42,16 +74,38 @@ impl<'a> CreateMessage<'a> {
}
}

pub fn content(mut self, content: impl Into<String>) -> Self {
self.fields.content.replace(content.into());
/// Set the content of the message.
///
/// The maximum length is 2000 UTF-16 characters.
///
/// # Errors
///
/// Returns [`CreateMessageError::ContentInvalid`] if the content length is
/// too long.
///
/// [`CreateMessageError::ContentInvalid`]: enum.CreateMessageError.html#variant.ContentInvalid
pub fn content(self, content: impl Into<String>) -> Result<Self, CreateMessageError> {
self._content(content.into())
}

self
fn _content(mut self, content: String) -> Result<Self, CreateMessageError> {
if !validate::content_limit(&content) {
return Err(CreateMessageError::ContentInvalid);
}

self.fields.content.replace(content);

Ok(self)
}

pub fn embed(mut self, embed: Embed) -> Self {
pub fn embed(mut self, embed: Embed) -> Result<Self, CreateMessageError> {
validate::embed(&embed).map_err(|source| CreateMessageError::EmbedTooLarge {
source,
})?;

self.fields.embed.replace(embed);

self
Ok(self)
}

pub fn allowed_mentions(
Expand Down
38 changes: 36 additions & 2 deletions http/src/request/channel/message/get_channel_messages.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,30 @@
use super::GetChannelMessagesConfigured;
use crate::request::prelude::*;
use std::{
error::Error,
fmt::{Display, Formatter, Result as FmtResult},
};
use twilight_model::{
channel::Message,
id::{ChannelId, MessageId},
};

#[derive(Clone, Debug)]
pub enum GetChannelMessagesError {
/// The maximum number of messages to retrieve is either 0 or more than 100.
LimitInvalid,
}

impl Display for GetChannelMessagesError {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
match self {
Self::LimitInvalid => f.write_str("the limit is invalid"),
}
}
}

impl Error for GetChannelMessagesError {}

#[derive(Default)]
struct GetChannelMessagesFields {
limit: Option<u64>,
Expand Down Expand Up @@ -60,10 +80,24 @@ impl<'a> GetChannelMessages<'a> {
)
}

pub fn limit(mut self, limit: u64) -> Self {
/// Set the maximum number of messages to retrieve.
///
/// The minimum is 1 and the maximum is 100.
///
/// # Errors
///
/// Returns [`GetChannelMessages::LimitInvalid`] if the
/// amount is greater than 21600.
///
/// [`GetChannelMessages::LimitInvalid`]: enum.GetChannelMessages.html#variant.LimitInvalid
pub fn limit(mut self, limit: u64) -> Result<Self, GetChannelMessagesError> {
if !validate::get_channel_messages_limit(limit) {
return Err(GetChannelMessagesError::LimitInvalid);
}

self.fields.limit.replace(limit);

self
Ok(self)
}

fn start(&mut self) -> Result<()> {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,29 @@
use crate::request::prelude::*;
use std::{
error::Error,
fmt::{Display, Formatter, Result as FmtResult},
};
use twilight_model::{
channel::Message,
id::{ChannelId, MessageId},
};

#[derive(Clone, Debug)]
pub enum GetChannelMessagesConfiguredError {
/// The maximum number of messages to retrieve is either 0 or more than 100.
LimitInvalid,
}

impl Display for GetChannelMessagesConfiguredError {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
match self {
Self::LimitInvalid => f.write_str("the limit is invalid"),
}
}
}

impl Error for GetChannelMessagesConfiguredError {}

struct GetChannelMessagesConfiguredFields {
limit: Option<u64>,
}
Expand Down Expand Up @@ -43,10 +63,24 @@ impl<'a> GetChannelMessagesConfigured<'a> {
}
}

pub fn limit(mut self, limit: u64) -> Self {
/// Set the maximum number of messages to retrieve.
///
/// The minimum is 1 and the maximum is 100.
///
/// # Errors
///
/// Returns [`GetChannelMessagesConfiguredError::LimitInvalid`] if the
/// amount is greater than 21600.
///
/// [`GetChannelMessagesConfiguredError::LimitInvalid`]: enum.GetChannelMessagesConfiguredError.html#variant.LimitInvalid
pub fn limit(mut self, limit: u64) -> Result<Self, GetChannelMessagesConfiguredError> {
if !validate::get_channel_messages_limit(limit) {
return Err(GetChannelMessagesConfiguredError::LimitInvalid);
}

self.fields.limit.replace(limit);

self
Ok(self)
}

fn start(&mut self) -> Result<()> {
Expand Down
10 changes: 6 additions & 4 deletions http/src/request/channel/message/mod.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
pub mod allowed_mentions;
mod create_message;
pub mod create_message;
pub mod get_channel_messages;
pub mod get_channel_messages_configured;
pub mod update_message;

mod delete_message;
mod delete_messages;
mod get_channel_messages;
mod get_channel_messages_configured;
mod get_message;
mod update_message;

pub use self::{
create_message::CreateMessage,
Expand All @@ -16,3 +17,4 @@ pub use self::{
get_message::GetMessage,
update_message::UpdateMessage,
};
pub use super::super::validate::EmbedValidationError;
Loading