Skip to content

Commit 736c18c

Browse files
committed
Split delay computation into its own method and add test.
1 parent f62f3a7 commit 736c18c

File tree

1 file changed

+52
-15
lines changed

1 file changed

+52
-15
lines changed

src/internet_identity/src/openid/generic.rs

Lines changed: 52 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ const HTTP_STATUS_OK: u8 = 200;
3838

3939
// Fetch the certs every fifteen minutes, the responses are always
4040
// valid for a couple of hours so that should be enough margin.
41-
#[cfg(not(test))]
4241
const FETCH_CERTS_INTERVAL: u64 = 60 * 15; // 15 minutes in seconds
4342

4443
// A JWT is only valid for a very small window, even if the JWT itself says it's valid for longer,
@@ -177,6 +176,29 @@ impl Provider {
177176
}
178177
}
179178

179+
fn compute_next_certs_fetch_delay<T, E>(
180+
result: &Result<T, E>,
181+
current_delay: Option<u64>,
182+
) -> Option<u64> {
183+
use std::cmp::{max, min};
184+
185+
const MAX_DELAY: u64 = FETCH_CERTS_INTERVAL;
186+
const MIN_DELAY: u64 = 60;
187+
188+
match result {
189+
// Reset delay to None so default (`FETCH_CERTS_INTERVAL`) delay is used.
190+
Ok(_) => None,
191+
// Try again earlier with backoff if fetch failed, the HTTP outcall responses
192+
// aren't the same across nodes when we fetch at the moment of key rotation.
193+
//
194+
// The delay should be at most `MAX_DELAY` and at minimum `MIN_DELAY` * 2.
195+
Err(_) => Some(min(
196+
MAX_DELAY,
197+
max(MIN_DELAY, current_delay.unwrap_or(MIN_DELAY)) * 2,
198+
)),
199+
}
200+
}
201+
180202
#[cfg(not(test))]
181203
fn schedule_fetch_certs(
182204
jwks_uri: String,
@@ -185,26 +207,18 @@ fn schedule_fetch_certs(
185207
) {
186208
use ic_cdk::spawn;
187209
use ic_cdk_timers::set_timer;
188-
use std::cmp::{max, min};
189210
use std::time::Duration;
190211

191212
set_timer(
192213
Duration::from_secs(delay.unwrap_or(FETCH_CERTS_INTERVAL)),
193214
move || {
194215
spawn(async move {
195-
let new_delay = match fetch_certs(jwks_uri.clone()).await {
196-
Ok(certs) => {
197-
certs_reference.replace(certs);
198-
// Reset delay to None so default `FETCH_CERTS_INTERVAL` delay is used.
199-
None
200-
}
201-
// Try again earlier with backoff if fetch failed, the HTTP outcall responses
202-
// aren't the same across nodes when we fetch at the moment of key rotation.
203-
//
204-
// The delay should be at most `FETCH_CERTS_INTERVAL` and at minimum 60 * 2.
205-
Err(_) => Some(min(FETCH_CERTS_INTERVAL, max(60, delay.unwrap_or(60)) * 2)),
206-
};
207-
schedule_fetch_certs(jwks_uri, certs_reference, new_delay);
216+
let result = fetch_certs(jwks_uri.clone()).await;
217+
let next_delay = compute_next_certs_fetch_delay(&result, delay);
218+
if let Ok(certs) = result {
219+
certs_reference.replace(certs);
220+
}
221+
schedule_fetch_certs(jwks_uri, certs_reference, next_delay);
208222
});
209223
},
210224
);
@@ -647,3 +661,26 @@ fn should_return_error_when_name_too_long() {
647661
))
648662
);
649663
}
664+
#[test]
665+
fn should_compute_next_certs_fetch_delay() {
666+
let success: Result<(), ()> = Ok(());
667+
let error: Result<(), ()> = Err(());
668+
669+
for (current_delay, expected_next_delay_on_error) in [
670+
(None, Some(120)),
671+
(Some(0), Some(120)),
672+
(Some(120), Some(240)),
673+
(Some(240), Some(480)),
674+
(Some(480), Some(FETCH_CERTS_INTERVAL)),
675+
(Some(FETCH_CERTS_INTERVAL * 2), Some(FETCH_CERTS_INTERVAL)),
676+
] {
677+
assert_eq!(
678+
compute_next_certs_fetch_delay(&success, current_delay),
679+
None
680+
);
681+
assert_eq!(
682+
compute_next_certs_fetch_delay(&error, current_delay),
683+
expected_next_delay_on_error
684+
);
685+
}
686+
}

0 commit comments

Comments
 (0)