twilight_http_ratelimiting/ticket.rs
1//! Flow for managing ratelimit tickets.
2//!
3//! Tickets are the [`Ratelimiter`]'s method of managing approval for a consumer
4//! to be able to send a request.
5//!
6//! # Ratelimit Consumer
7//!
8//! ## 1. Requesting a ticket
9//!
10//! Consumers of a ratelimiter will call [`Ratelimiter::ticket`].
11//!
12//! ## 2. Waiting for approval
13//!
14//! In return consumers will receive a [`TicketReceiver`]. This must be polled
15//! in order to know when the ratelimiter has approved a ticket.
16//!
17//! ## 3. Receiving approval
18//!
19//! When a ticket is approved and the future resolves, a [`TicketSender`] is
20//! provided. This must be used to provide the ratelimiter with the response's
21//! ratelimit headers.
22//!
23//! ## 4. Performing the request
24//!
25//! Consumers may now execute the HTTP request associated with the ticket. Once
26//! a response (or lack of one) is received, the headers [must be parsed] and
27//! sent to the ratelimiter via [`TicketSender::headers`]. This completes the
28//! cycle.
29//!
30//! # Ratelimiter
31//!
32//! ## 1. Initializing a ticket's channels
33//!
34//! Ratelimiters will accept a request for a ticket when [`Ratelimiter::ticket`]
35//! is called. You must call [`channel`] to create a channel between the
36//! ratelimiter and the consumer.
37//!
38//! ## 2. Keeping the consumer waiting
39//!
40//! [`channel`] will return two halves: [`TicketNotifier`] and
41//! [`TicketReceiver`]. Ratelimiters must keep the notifier and give the user
42//! the receiver in return.
43//!
44//! ## 3. Notifying the consumer of ticket approval
45//!
46//! When any ratelimits have passed and a user is free to perform their request,
47//! call [`TicketNotifier::available`]. If the user hasn't canceled their
48//! request for a ticket, you will receive a [`TicketHeaders`].
49//!
50//! ## 4. Receiving the response's headers
51//!
52//! The consumer will perform their HTTP request and parse the response's
53//! headers. Once the headers (or lack of headers) are available the user will
54//! send them along the channel. Poll the provided [`TicketHeaders`] for those
55//! headers to complete the cycle.
56//!
57//! [`Ratelimiter::ticket`]: super::Ratelimiter::ticket
58//! [`Ratelimiter`]: super::Ratelimiter
59//! [must be parsed]: super::headers
60
61use crate::headers::RatelimitHeaders;
62use std::{
63 future::Future,
64 pin::Pin,
65 task::{Context, Poll},
66};
67use tokio::sync::oneshot::{self, error::RecvError, Receiver, Sender};
68
69/// Receiver to wait for the headers sent by the API consumer.
70///
71/// You must poll the future in order to process the headers. If the future
72/// results to an error, then the API consumer dropped the sernding half of the
73/// channel. You should treat this as if the request happened.
74#[derive(Debug)]
75pub struct TicketHeaders(Receiver<Option<RatelimitHeaders>>);
76
77impl Future for TicketHeaders {
78 type Output = Result<Option<RatelimitHeaders>, RecvError>;
79
80 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
81 Pin::new(&mut self.0).poll(cx)
82 }
83}
84
85/// Indicate to the ratelimit consumer that their ticket has been granted and
86/// they may now send a request.
87#[derive(Debug)]
88pub struct TicketNotifier(Sender<Sender<Option<RatelimitHeaders>>>);
89
90impl TicketNotifier {
91 /// Signal to the ratelimiter consumer (an HTTP client) that a request may
92 /// now be performed.
93 ///
94 /// A receiver is returned. This must be stored and awaited so that
95 /// ratelimiting backends can handle the headers that the API consumer will
96 /// send back, thus completing the cycle.
97 ///
98 /// Returns a `None` if the consumer has dropped their
99 /// [`TicketReceiver`] half. The ticket is considered canceled.
100 #[must_use]
101 pub fn available(self) -> Option<TicketHeaders> {
102 let (tx, rx) = oneshot::channel();
103
104 self.0.send(tx).ok()?;
105
106 Some(TicketHeaders(rx))
107 }
108}
109
110/// Channel receiver to wait for availability of a ratelimit ticket.
111///
112/// This is used by the ratelimiter consumer (such as an API client) to wait for
113/// an available ratelimit ticket.
114///
115/// Once one is available, a [`TicketSender`] will be produced which can be used to
116/// send the associated HTTP response's ratelimit headers.
117#[derive(Debug)]
118pub struct TicketReceiver(Receiver<Sender<Option<RatelimitHeaders>>>);
119
120impl Future for TicketReceiver {
121 type Output = Result<TicketSender, RecvError>;
122
123 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
124 Pin::new(&mut self.0).poll(cx).map_ok(TicketSender)
125 }
126}
127
128/// Channel sender to send response ratelimit information to the ratelimiter.
129///
130/// This is used by the ratelimiter consumer (such as an API client) once a
131/// request has been granted via [`TicketReceiver`].
132///
133/// If a response results in available ratelimit headers, send them via
134/// [`headers`] to the ratelimiter backend. If a response results in an
135/// error - such as a server error or request cancellation - send `None`.
136///
137/// [`headers`]: Self::headers
138#[derive(Debug)]
139pub struct TicketSender(Sender<Option<RatelimitHeaders>>);
140
141impl TicketSender {
142 /// Send the response's ratelimit headers to the ratelimiter.
143 ///
144 /// This will allow the ratelimiter to complete the cycle and acknowledge
145 /// that the request has been completed. This must be done so that the
146 /// ratelimiter can process information such as whether there's a global
147 /// ratelimit.
148 ///
149 /// # Errors
150 ///
151 /// Returns the input headers if the ratelimiter has dropped the receiver
152 /// half. This may happen if the ratelimiter is dropped or if a timeout has
153 /// occurred.
154 pub fn headers(
155 self,
156 headers: Option<RatelimitHeaders>,
157 ) -> Result<(), Option<RatelimitHeaders>> {
158 self.0.send(headers)
159 }
160}
161
162/// Produce a new channel consisting of a sender and receiver.
163///
164/// The notifier is to be used by the ratelimiter while the receiver is to be
165/// provided to the consumer.
166///
167/// Refer to the [module-level] documentation for more information.
168///
169/// [module-level]: self
170#[must_use]
171pub fn channel() -> (TicketNotifier, TicketReceiver) {
172 let (tx, rx) = oneshot::channel();
173
174 (TicketNotifier(tx), TicketReceiver(rx))
175}