diff options
| author | Zeyla Hellyer <[email protected]> | 2018-08-05 11:07:21 -0700 |
|---|---|---|
| committer | Zeyla Hellyer <[email protected]> | 2018-08-05 11:07:21 -0700 |
| commit | bbbf63868a8ef3c0f21c1896f7afb96f4d8fbcc1 (patch) | |
| tree | a04c4f18f56d5c5d34a7c2e31de52c1cb87966de /src/http/ratelimiting.rs | |
| parent | Make http::wind perform requests (diff) | |
| download | serenity-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.rs | 283 |
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) { |