riven/config.rs
1//! Configuration of RiotApi.
2use reqwest::header::{HeaderMap, HeaderValue};
3use reqwest::ClientBuilder;
4
5use crate::time::Duration;
6
7/// Configuration for instantiating RiotApi.
8#[derive(Debug)]
9pub struct RiotApiConfig {
10 pub(crate) base_url: String,
11 pub(crate) retries: u8,
12 pub(crate) app_rate_usage_factor: f32,
13 pub(crate) method_rate_usage_factor: f32,
14 pub(crate) burst_factor: f32,
15 pub(crate) duration_overhead: Duration,
16 pub(crate) client_builder: Option<ClientBuilder>,
17 pub(crate) rso_clear_header: Option<String>,
18}
19
20impl RiotApiConfig {
21 /// Request header name for the Riot API key, `"X-Riot-Token"`.
22 ///
23 /// When using `set_client_builder`, the supplied builder should include
24 /// this default header with the Riot API key as the value.
25 pub const RIOT_KEY_HEADER: &'static str = "X-Riot-Token";
26
27 /// `"https://{}.api.riotgames.com"`
28 ///
29 /// Default base URL, including `{}` placeholder for region platform.
30 pub const DEFAULT_BASE_URL: &'static str = "https://{}.api.riotgames.com";
31
32 /// `3`
33 ///
34 /// Default number of retries.
35 pub const DEFAULT_RETRIES: u8 = 3;
36
37 /// `1.0`
38 ///
39 /// Default rate limit usage factor.
40 pub const DEFAULT_RATE_USAGE_FACTOR: f32 = 1.0;
41
42 /// `0.99`
43 ///
44 /// Default `burst_factor`, also used by `preconfig_burst`.
45 pub const PRECONFIG_BURST_BURST_FACTOR: f32 = 0.99;
46 /// `989` ms
47 ///
48 /// Default `duration_overhead`, also used by `preconfig_burst`.
49 pub const PRECONFIG_BURST_DURATION_OVERHEAD: Duration = Duration::from_millis(989);
50
51 /// `0.47`
52 ///
53 /// `burst_factor` used by `preconfig_throughput`.
54 pub const PRECONFIG_THROUGHPUT_BURST_FACTOR: f32 = 0.47;
55 /// `10` ms.
56 ///
57 /// `duration_overhead` used by `preconfig_throughput`.
58 pub const PRECONFIG_THROUGHPUT_DURATION_OVERHEAD: Duration = Duration::from_millis(10);
59
60 /// Creates a new `RiotApiConfig` with the given `api_key` with the following
61 /// configuration:
62 ///
63 /// * `retries = 3` (`RiotApiConfig::DEFAULT_RETRIES`).
64 /// * `burst_factor = 0.99` (`preconfig_burst`).
65 /// * `duration_overhead = 989 ms` (`preconfig_burst`).
66 ///
67 /// `api_key` should be a Riot Games API key from
68 /// [https://developer.riotgames.com/](https://developer.riotgames.com/),
69 /// and should look like `"RGAPI-01234567-89ab-cdef-0123-456789abcdef"`.
70 pub fn with_key(api_key: impl AsRef<[u8]>) -> Self {
71 let mut default_headers = HeaderMap::new();
72 default_headers.insert(
73 Self::RIOT_KEY_HEADER,
74 HeaderValue::from_bytes(api_key.as_ref()).unwrap(),
75 );
76
77 Self {
78 base_url: Self::DEFAULT_BASE_URL.into(),
79 retries: Self::DEFAULT_RETRIES,
80 app_rate_usage_factor: Self::DEFAULT_RATE_USAGE_FACTOR,
81 method_rate_usage_factor: Self::DEFAULT_RATE_USAGE_FACTOR,
82 burst_factor: Self::PRECONFIG_BURST_BURST_FACTOR,
83 duration_overhead: Self::PRECONFIG_BURST_DURATION_OVERHEAD,
84 client_builder: Some(ClientBuilder::new().default_headers(default_headers)),
85 rso_clear_header: Some(Self::RIOT_KEY_HEADER.to_owned()),
86 }
87 }
88
89 /// Creates a new `RiotApiConfig` with the given client builder.
90 ///
91 /// The client builder default headers should include a value for
92 /// [`RiotApiConfig::RIOT_KEY_HEADER`] (`"X-Riot-Token"`), otherwise authentication will fail.
93 ///
94 /// * `retries = 3` (`RiotApiConfig::DEFAULT_RETRIES`).
95 /// * `burst_factor = 0.99` (`preconfig_burst`).
96 /// * `duration_overhead = 989 ms` (`preconfig_burst`).
97 pub fn with_client_builder(client_builder: ClientBuilder) -> Self {
98 Self {
99 base_url: Self::DEFAULT_BASE_URL.to_owned(),
100 retries: Self::DEFAULT_RETRIES,
101 app_rate_usage_factor: Self::DEFAULT_RATE_USAGE_FACTOR,
102 method_rate_usage_factor: Self::DEFAULT_RATE_USAGE_FACTOR,
103 burst_factor: Self::PRECONFIG_BURST_BURST_FACTOR,
104 duration_overhead: Self::PRECONFIG_BURST_DURATION_OVERHEAD,
105 client_builder: Some(client_builder),
106 rso_clear_header: Some(Self::RIOT_KEY_HEADER.to_owned()),
107 }
108 }
109
110 /// Sets rate limiting settings to preconfigured values optimized for burst,
111 /// low latency:
112 ///
113 /// * `burst_factor = 0.99` (`PRECONFIG_BURST_BURST_FACTOR`).
114 /// * `duration_overhead = 989 ms` (`PRECONFIG_BURST_DURATION_OVERHEAD_MILLIS`).
115 ///
116 /// # Returns
117 /// `self`, for chaining.
118 pub fn preconfig_burst(mut self) -> Self {
119 self.burst_factor = Self::PRECONFIG_BURST_BURST_FACTOR;
120 self.duration_overhead = Self::PRECONFIG_BURST_DURATION_OVERHEAD;
121 self
122 }
123
124 /// Sets the rate limiting settings to preconfigured values optimized for
125 /// high throughput:
126 ///
127 /// * `burst_factor = 0.47` (`PRECONFIG_THROUGHPUT_BURST_FACTOR`).
128 /// * `duration_overhead = 10 ms` (`PRECONFIG_THROUGHPUT_DURATION_OVERHEAD_MILLIS`).
129 ///
130 /// # Returns
131 /// `self`, for chaining.
132 pub fn preconfig_throughput(mut self) -> Self {
133 self.burst_factor = Self::PRECONFIG_THROUGHPUT_BURST_FACTOR;
134 self.duration_overhead = Self::PRECONFIG_THROUGHPUT_DURATION_OVERHEAD;
135 self
136 }
137
138 /// Set the base url for requests. The string should contain a `"{}"`
139 /// literal which will be replaced with the region platform name. (However
140 /// multiple or zero `"{}"`s may be included if needed).
141 ///
142 /// # Returns
143 /// `self`, for chaining.
144 pub fn set_base_url(mut self, base_url: impl Into<String>) -> Self {
145 self.base_url = base_url.into();
146 self
147 }
148
149 /// Set number of times to retry requests. Naturally, only retryable requests
150 /// will be retried: responses with status codes 5xx or 429 (after waiting
151 /// for retry-after headers). A value of `0` means one request will be sent
152 /// and it will not be retried if it fails.
153 ///
154 /// # Returns
155 /// `self`, for chaining.
156 pub fn set_retries(mut self, retries: u8) -> Self {
157 self.retries = retries;
158 self
159 }
160
161 /// The rate limit usage percentage controls how much of the API key's rate
162 /// limit will be used. The default value of `1.0` means the entirety of
163 /// the rate limit may be used if it is needed. This applies to both the
164 /// API key's rate limit (per route) _and_ to endpoint method rate limits.
165 ///
166 /// Setting a value lower than `1.0` can be useful if you are running
167 /// multiple API instances on the same API key.
168 ///
169 /// For example, four instances, possibly running on different machines,
170 /// could each have a value of `0.25` to share an API key's rate limit
171 /// evenly.
172 ///
173 /// Note that if you have multiple instances hitting _different_ methods,
174 /// you should use [Self::set_app_rate_usage_factor()] and [Self::set_method_rate_usage_factor()]
175 /// separately, as this sets both.
176 ///
177 /// This also can be used to reduce the chance of hitting 429s, although
178 /// 429s should be rare even with this set to `1.0`.
179 ///
180 /// # Panics
181 /// If `rate_usage_factor` is not in range (0, 1].
182 ///
183 /// # Returns
184 /// `self`, for chaining.
185 pub fn set_rate_usage_factor(mut self, rate_usage_factor: f32) -> Self {
186 // Use inverted check to handle NaN.
187 if 0.0 < rate_usage_factor && rate_usage_factor <= 1.0 {
188 self.app_rate_usage_factor = rate_usage_factor;
189 self.method_rate_usage_factor = rate_usage_factor;
190 return self;
191 }
192 panic!(
193 "rate_usage_factor \"{}\" not in range (0, 1].",
194 rate_usage_factor
195 );
196 }
197
198 /// See [Self::set_rate_usage_factor]. Setting this is useful if you have multiple
199 /// instances sharing the app rate limit, but are hitting distinct methods
200 /// and therefore do not need their method usage decreased.
201 ///
202 /// # Panics
203 /// If `app_rate_usage_factor` is not in range (0, 1\].
204 ///
205 /// # Returns
206 /// `self`, for chaining.
207 pub fn set_app_rate_usage_factor(mut self, app_rate_usage_factor: f32) -> Self {
208 // Use inverted check to handle NaN.
209 if 0.0 < app_rate_usage_factor && app_rate_usage_factor <= 1.0 {
210 self.app_rate_usage_factor = app_rate_usage_factor;
211 return self;
212 }
213 panic!(
214 "app_rate_usage_factor \"{}\" not in range (0, 1].",
215 app_rate_usage_factor
216 );
217 }
218
219 /// See [Self::set_rate_usage_factor] and [Self::set_app_rate_usage_factor].
220 /// This method is mainly provided for completeness, though it may be
221 /// useful in advanced use cases.
222 ///
223 /// # Panics
224 /// If `method_rate_usage_factor` is not in range (0, 1\].
225 ///
226 /// # Returns
227 /// `self`, for chaining.
228 pub fn set_method_rate_usage_factor(mut self, method_rate_usage_factor: f32) -> Self {
229 // Use inverted check to handle NaN.
230 if 0.0 < method_rate_usage_factor && method_rate_usage_factor <= 1.0 {
231 self.method_rate_usage_factor = method_rate_usage_factor;
232 return self;
233 }
234 panic!(
235 "method_rate_usage_factor \"{}\" not in range (0, 1].",
236 method_rate_usage_factor
237 );
238 }
239
240 /// Burst percentage controls how many burst requests are allowed and
241 /// therefore how requests are spread out. Higher equals more burst,
242 /// less spread. Lower equals less burst, more spread.
243 ///
244 /// The value must be in the range (0, 1];
245 /// Between 0, exclusive, and 1, inclusive. However values should generally
246 /// be larger than 0.25.
247 ///
248 /// Burst percentage behaves as follows:<br>
249 /// A burst percentage of x% means, for each token bucket, "x% of the
250 /// tokens can be used in x% of the bucket duration." So, for example, if x
251 /// is 90%, a bucket would allow 90% of the requests to be made without
252 /// any delay. Then, after waiting 90% of the bucket's duration, the
253 /// remaining 10% of requests could be made.
254 ///
255 /// A burst percentage of 100% results in no request spreading, which would
256 /// allow for the largest bursts and lowest latency, but could result in
257 /// 429s as bucket boundaries occur.
258 ///
259 /// A burst percentage of near 0% results in high spreading causing
260 /// temporally equidistant requests. This prevents 429s but has the highest
261 /// latency. Additionally, if the number of tokens is high, this may lower
262 /// the overall throughput due to the rate at which requests can be
263 /// scheduled.
264 ///
265 /// Therefore, for interactive applications like summoner & match history
266 /// lookup, a higher percentage may be better. For data-collection apps
267 /// like champion winrate aggregation, a medium-low percentage may be
268 /// better.
269 ///
270 /// # Panics
271 /// If `burst_factor` is not in range (0, 1\].
272 ///
273 /// # Returns
274 /// `self`, for chaining.
275 pub fn set_burst_factor(mut self, burst_factor: f32) -> Self {
276 // Use inverted check to handle NaN.
277 if 0.0 < burst_factor && burst_factor <= 1.0 {
278 self.burst_factor = burst_factor;
279 return self;
280 }
281 panic!("burst_factor \"{}\" not in range (0, 1].", burst_factor);
282 }
283
284 /// Sets the additional bucket duration to consider when rate limiting.
285 /// Increasing this value will decrease the chances of 429s, but will lower
286 /// the overall throughput.
287 ///
288 /// In a sense, the `duration_overhead` is how much to "widen" the temporal
289 /// width of buckets.
290 ///
291 /// Given a particular Riot Game API rate limit bucket that allows N requests
292 /// per D duration, when counting requests this library will consider requests
293 /// sent in the past `D + duration_overhead` duration.
294 ///
295 /// # Returns
296 /// `self`, for chaining.
297 pub fn set_duration_overhead(mut self, duration_overhead: Duration) -> Self {
298 self.duration_overhead = duration_overhead;
299 self
300 }
301
302 /// Sets the header to clear for RSO requests (if `Some`), or will not override any headers (if
303 /// `None`).
304 ///
305 /// This is a bit of a hack. The client used by Riven is expected to include the API key as a
306 /// default header. However, if the API key is included in an [RSO](https://developer.riotgames.com/docs/lol#rso-integration)
307 /// request the server responds with a 400 "Bad request - Invalid authorization specified"
308 /// error. To avoid this the `rso_clear_header` header is overridden to be empty for RSO
309 /// requests.
310 ///
311 /// This is set to `Some(`[`Self::RIOT_KEY_HEADER`]`)` by default.
312 ///
313 /// # Returns
314 /// `self`, for chaining.
315 pub fn set_rso_clear_header(mut self, rso_clear_header: Option<String>) -> Self {
316 self.rso_clear_header = rso_clear_header;
317 self
318 }
319}
320
321impl<T: AsRef<[u8]>> From<T> for RiotApiConfig {
322 fn from(api_key: T) -> Self {
323 Self::with_key(api_key)
324 }
325}