aboutsummaryrefslogtreecommitdiff
path: root/src/http/ratelimiting.rs
diff options
context:
space:
mode:
authorZeyla Hellyer <[email protected]>2018-08-05 11:07:21 -0700
committerZeyla Hellyer <[email protected]>2018-08-05 11:07:21 -0700
commitbbbf63868a8ef3c0f21c1896f7afb96f4d8fbcc1 (patch)
treea04c4f18f56d5c5d34a7c2e31de52c1cb87966de /src/http/ratelimiting.rs
parentMake http::wind perform requests (diff)
downloadserenity-bbbf63868a8ef3c0f21c1896f7afb96f4d8fbcc1.tar.xz
serenity-bbbf63868a8ef3c0f21c1896f7afb96f4d8fbcc1.zip
[http] Abstract out routing methods and paths
Diffstat (limited to 'src/http/ratelimiting.rs')
-rw-r--r--src/http/ratelimiting.rs283
1 files changed, 18 insertions, 265 deletions
diff --git a/src/http/ratelimiting.rs b/src/http/ratelimiting.rs
index 1c31c9f..309ba35 100644
--- a/src/http/ratelimiting.rs
+++ b/src/http/ratelimiting.rs
@@ -40,6 +40,8 @@
//! [Taken from]: https://discordapp.com/developers/docs/topics/rate-limits#rate-limits
#![allow(zero_ptr)]
+pub use super::routing::Route;
+
use chrono::{DateTime, Utc};
use hyper::client::Response;
use hyper::header::Headers;
@@ -54,7 +56,7 @@ use std::{
thread,
i64
};
-use super::{HttpError, LightMethod, Request};
+use super::{HttpError, Request};
/// Refer to [`offset`].
///
@@ -102,271 +104,22 @@ lazy_static! {
};
}
-/// A representation of all routes registered within the library. These are safe
-/// and memory-efficient representations of each path that functions exist for
-/// in the [`http`] module.
-///
-/// [`http`]: ../index.html
-#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
-pub enum Route {
- /// Route for the `/channels/:channel_id` path.
- ///
- /// The data is the relevant [`ChannelId`].
- ///
- /// [`ChannelId`]: ../../model/id/struct.ChannelId.html
- ChannelsId(u64),
- /// Route for the `/channels/:channel_id/invites` path.
- ///
- /// The data is the relevant [`ChannelId`].
- ///
- /// [`ChannelId`]: ../../model/id/struct.ChannelId.html
- ChannelsIdInvites(u64),
- /// Route for the `/channels/:channel_id/messages` path.
- ///
- /// The data is the relevant [`ChannelId`].
- ///
- /// [`ChannelId`]: ../../model/id/struct.ChannelId.html
- ChannelsIdMessages(u64),
- /// Route for the `/channels/:channel_id/messages/bulk-delete` path.
- ///
- /// The data is the relevant [`ChannelId`].
- ///
- /// [`ChannelId`]: ../../model/id/struct.ChannelId.html
- ChannelsIdMessagesBulkDelete(u64),
- /// Route for the `/channels/:channel_id/messages/:message_id` path.
- ///
- /// The data is the relevant [`ChannelId`].
- ///
- /// [`ChannelId`]: ../../model/id/struct.ChannelId.html
- // This route is a unique case. The ratelimit for message _deletions_ is
- // different than the overall route ratelimit.
- //
- // Refer to the docs on [Rate Limits] in the yellow warning section.
- //
- // Additionally, this needs to be a `LightMethod` from the parent module
- // and _not_ a `hyper` `Method` due to `hyper`'s not deriving `Copy`.
- //
- // [Rate Limits]: https://discordapp.com/developers/docs/topics/rate-limits
- ChannelsIdMessagesId(LightMethod, u64),
- /// Route for the `/channels/:channel_id/messages/:message_id/ack` path.
- ///
- /// The data is the relevant [`ChannelId`].
- ///
- /// [`ChannelId`]: ../../model/id/struct.ChannelId.html
- ChannelsIdMessagesIdAck(u64),
- /// Route for the `/channels/:channel_id/messages/:message_id/reactions`
- /// path.
- ///
- /// The data is the relevant [`ChannelId`].
- ///
- /// [`ChannelId`]: ../../model/id/struct.ChannelId.html
- ChannelsIdMessagesIdReactions(u64),
- /// Route for the
- /// `/channels/:channel_id/messages/:message_id/reactions/:reaction/@me`
- /// path.
- ///
- /// The data is the relevant [`ChannelId`].
- ///
- /// [`ChannelId`]: ../../model/id/struct.ChannelId.html
- ChannelsIdMessagesIdReactionsUserIdType(u64),
- /// Route for the `/channels/:channel_id/permissions/:target_id` path.
- ///
- /// The data is the relevant [`ChannelId`].
- ///
- /// [`ChannelId`]: ../../model/id/struct.ChannelId.html
- ChannelsIdPermissionsOverwriteId(u64),
- /// Route for the `/channels/:channel_id/pins` path.
- ///
- /// The data is the relevant [`ChannelId`].
- ///
- /// [`ChannelId`]: ../../model/id/struct.ChannelId.html
- ChannelsIdPins(u64),
- /// Route for the `/channels/:channel_id/pins/:message_id` path.
- ///
- /// The data is the relevant [`ChannelId`].
- ///
- /// [`ChannelId`]: ../../model/id/struct.ChannelId.html
- ChannelsIdPinsMessageId(u64),
- /// Route for the `/channels/:channel_id/typing` path.
- ///
- /// The data is the relevant [`ChannelId`].
- ///
- /// [`ChannelId`]: ../../model/id/struct.ChannelId.html
- ChannelsIdTyping(u64),
- /// Route for the `/channels/:channel_id/webhooks` path.
- ///
- /// The data is the relevant [`ChannelId`].
- ///
- /// [`ChannelId`]: ../../model/id/struct.ChannelId.html
- ChannelsIdWebhooks(u64),
- /// Route for the `/gateway` path.
- Gateway,
- /// Route for the `/gateway/bot` path.
- GatewayBot,
- /// Route for the `/guilds` path.
- Guilds,
- /// Route for the `/guilds/:guild_id` path.
- ///
- /// The data is the relevant [`GuildId`].
- ///
- /// [`GuildId`]: struct.GuildId.html
- GuildsId(u64),
- /// Route for the `/guilds/:guild_id/bans` path.
- ///
- /// The data is the relevant [`GuildId`].
- ///
- /// [`GuildId`]: struct.GuildId.html
- GuildsIdBans(u64),
- /// Route for the `/guilds/:guild_id/audit-logs` path.
- /// The data is the relevant [`GuildId`].
- ///
- /// [`GuildId`]: struct.GuildId.html
- GuildsIdAuditLogs(u64),
- /// Route for the `/guilds/:guild_id/bans/:user_id` path.
- ///
- /// The data is the relevant [`GuildId`].
- ///
- /// [`GuildId`]: struct.GuildId.html
- GuildsIdBansUserId(u64),
- /// Route for the `/guilds/:guild_id/channels/:channel_id` path.
- ///
- /// The data is the relevant [`GuildId`].
- ///
- /// [`GuildId`]: struct.GuildId.html
- GuildsIdChannels(u64),
- /// Route for the `/guilds/:guild_id/embed` path.
- ///
- /// The data is the relevant [`GuildId`].
- ///
- /// [`GuildId`]: struct.GuildId.html
- GuildsIdEmbed(u64),
- /// Route for the `/guilds/:guild_id/emojis` path.
- ///
- /// The data is the relevant [`GuildId`].
- ///
- /// [`GuildId`]: struct.GuildId.html
- GuildsIdEmojis(u64),
- /// Route for the `/guilds/:guild_id/emojis/:emoji_id` path.
- ///
- /// The data is the relevant [`GuildId`].
- ///
- /// [`GuildId`]: struct.GuildId.html
- GuildsIdEmojisId(u64),
- /// Route for the `/guilds/:guild_id/integrations` path.
- ///
- /// The data is the relevant [`GuildId`].
- ///
- /// [`GuildId`]: struct.GuildId.html
- GuildsIdIntegrations(u64),
- /// Route for the `/guilds/:guild_id/integrations/:integration_id` path.
- ///
- /// The data is the relevant [`GuildId`].
- ///
- /// [`GuildId`]: struct.GuildId.html
- GuildsIdIntegrationsId(u64),
- /// Route for the `/guilds/:guild_id/integrations/:integration_id/sync`
- /// path.
- ///
- /// The data is the relevant [`GuildId`].
- ///
- /// [`GuildId`]: struct.GuildId.html
- GuildsIdIntegrationsIdSync(u64),
- /// Route for the `/guilds/:guild_id/invites` path.
- ///
- /// The data is the relevant [`GuildId`].
- ///
- /// [`GuildId`]: struct.GuildId.html
- GuildsIdInvites(u64),
- /// Route for the `/guilds/:guild_id/members` path.
- ///
- /// The data is the relevant [`GuildId`].
- ///
- /// [`GuildId`]: struct.GuildId.html
- GuildsIdMembers(u64),
- /// Route for the `/guilds/:guild_id/members/:user_id` path.
- ///
- /// The data is the relevant [`GuildId`].
- ///
- /// [`GuildId`]: struct.GuildId.html
- GuildsIdMembersId(u64),
- /// Route for the `/guilds/:guild_id/members/:user_id/roles/:role_id` path.
- ///
- /// The data is the relevant [`GuildId`].
- ///
- /// [`GuildId`]: struct.GuildId.html
- GuildsIdMembersIdRolesId(u64),
- /// Route for the `/guilds/:guild_id/members/@me/nick` path.
- ///
- /// The data is the relevant [`GuildId`].
- ///
- /// [`GuildId`]: struct.GuildId.html
- GuildsIdMembersMeNick(u64),
- /// Route for the `/guilds/:guild_id/prune` path.
- ///
- /// The data is the relevant [`GuildId`].
- ///
- /// [`GuildId`]: struct.GuildId.html
- GuildsIdPrune(u64),
- /// Route for the `/guilds/:guild_id/regions` path.
- ///
- /// The data is the relevant [`GuildId`].
- ///
- /// [`GuildId`]: struct.GuildId.html
- GuildsIdRegions(u64),
- /// Route for the `/guilds/:guild_id/roles` path.
- ///
- /// The data is the relevant [`GuildId`].
- ///
- /// [`GuildId`]: struct.GuildId.html
- GuildsIdRoles(u64),
- /// Route for the `/guilds/:guild_id/roles/:role_id` path.
- ///
- /// The data is the relevant [`GuildId`].
- ///
- /// [`GuildId`]: struct.GuildId.html
- GuildsIdRolesId(u64),
- /// Route for the `/guilds/:guild_id/vanity-url` path.
- ///
- /// The data is the relevant [`GuildId`].
- ///
- /// [`GuildId`]: struct.GuildId.html
- GuildsIdVanityUrl(u64),
- /// Route for the `/guilds/:guild_id/webhooks` path.
- ///
- /// The data is the relevant [`GuildId`].
- ///
- /// [`GuildId`]: struct.GuildId.html
- GuildsIdWebhooks(u64),
- /// Route for the `/invites/:code` path.
- InvitesCode,
- /// Route for the `/users/:user_id` path.
- UsersId,
- /// Route for the `/users/@me` path.
- UsersMe,
- /// Route for the `/users/@me/channels` path.
- UsersMeChannels,
- /// Route for the `/users/@me/guilds` path.
- UsersMeGuilds,
- /// Route for the `/users/@me/guilds/:guild_id` path.
- UsersMeGuildsId,
- /// Route for the `/voice/regions` path.
- VoiceRegions,
- /// Route for the `/webhooks/:webhook_id` path.
- WebhooksId(u64),
- /// Route where no ratelimit headers are in place (i.e. user account-only
- /// routes).
- ///
- /// This is a special case, in that if the route is `None` then pre- and
- /// post-hooks are not executed.
- None,
-}
-
pub(super) fn perform(req: Request) -> Result<Response> {
loop {
// This will block if another thread already has the global
// unlocked already (due to receiving an x-ratelimit-global).
let _ = GLOBAL.lock();
+ // Destructure the tuple instead of retrieving the third value to
+ // take advantage of the type system. If `RouteInfo::deconstruct`
+ // returns a different number of tuple elements in the future, directly
+ // accessing a certain index (e.g. `req.route.deconstruct().1`) would
+ // mean this code would not indicate it might need to be updated for the
+ // new tuple element amount.
+ //
+ // This isn't normally important, but might be for ratelimiting.
+ let (_, route, _) = req.route.deconstruct();
+
// Perform pre-checking here:
//
// - get the route's relevant rate
@@ -377,7 +130,7 @@ pub(super) fn perform(req: Request) -> Result<Response> {
// - then, perform the request
let bucket = Arc::clone(ROUTES
.lock()
- .entry(req.route)
+ .entry(route)
.or_insert_with(|| {
Arc::new(Mutex::new(RateLimit {
limit: i64::MAX,
@@ -387,7 +140,7 @@ pub(super) fn perform(req: Request) -> Result<Response> {
}));
let mut lock = bucket.lock();
- lock.pre_hook(&req.route);
+ lock.pre_hook(&route);
let response = super::retry(&req)?;
@@ -415,7 +168,7 @@ pub(super) fn perform(req: Request) -> Result<Response> {
// It _may_ be possible for the limit to be raised at any time,
// so check if it did from the value of the 'x-ratelimit-limit'
// header. If the limit was 5 and is now 7, add 2 to the 'remaining'
- if req.route == Route::None {
+ if route == Route::None {
return Ok(response);
} else {
let redo = if response.headers.get_raw("x-ratelimit-global").is_some() {
@@ -423,7 +176,7 @@ pub(super) fn perform(req: Request) -> Result<Response> {
Ok(
if let Some(retry_after) = parse_header(&response.headers, "retry-after")? {
- debug!("Ratelimited on route {:?} for {:?}ms", req.route, retry_after);
+ debug!("Ratelimited on route {:?} for {:?}ms", route, retry_after);
thread::sleep(Duration::from_millis(retry_after as u64));
true
@@ -432,7 +185,7 @@ pub(super) fn perform(req: Request) -> Result<Response> {
},
)
} else {
- lock.post_hook(&response, &req.route)
+ lock.post_hook(&response, &route)
};
if !redo.unwrap_or(true) {