mirror of
https://github.com/BetterCorp/BetterFrame.git
synced 2026-05-26 19:06:34 +00:00
perf(bundle): ETag content-hash — 304 Not Modified when bundle unchanged
This commit is contained in:
parent
890271d4c8
commit
0b3eaa3ef7
2 changed files with 38 additions and 5 deletions
|
|
@ -303,14 +303,20 @@ pub fn poll_claim(server: &str, code: &str) -> (String, String) {
|
||||||
|
|
||||||
/// Fetch bundle from server. Returns None on network/HTTP/parse failure.
|
/// Fetch bundle from server. Returns None on network/HTTP/parse failure.
|
||||||
/// On success, also writes the bundle to the on-disk cache.
|
/// On success, also writes the bundle to the on-disk cache.
|
||||||
|
/// Cached ETag from the last bundle fetch. Sent as If-None-Match so the
|
||||||
|
/// server can return 304 when the bundle hasn't changed.
|
||||||
|
static BUNDLE_ETAG: std::sync::Mutex<Option<String>> = std::sync::Mutex::new(None);
|
||||||
|
|
||||||
pub fn fetch_bundle(server: &str, key: &str) -> Option<KioskBundle> {
|
pub fn fetch_bundle(server: &str, key: &str) -> Option<KioskBundle> {
|
||||||
let client = reqwest::blocking::Client::new();
|
let client = reqwest::blocking::Client::new();
|
||||||
let resp = match client
|
let mut req = client
|
||||||
.get(format!("{server}/api/kiosk/bundle"))
|
.get(format!("{server}/api/kiosk/bundle"))
|
||||||
.header("Authorization", format!("Bearer {key}"))
|
.header("Authorization", format!("Bearer {key}"))
|
||||||
.timeout(Duration::from_secs(10))
|
.timeout(Duration::from_secs(10));
|
||||||
.send()
|
if let Some(etag) = BUNDLE_ETAG.lock().unwrap().as_deref() {
|
||||||
{
|
req = req.header("If-None-Match", etag);
|
||||||
|
}
|
||||||
|
let resp = match req.send() {
|
||||||
Ok(r) => r,
|
Ok(r) => r,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tracing::warn!("bundle fetch failed: {e}");
|
tracing::warn!("bundle fetch failed: {e}");
|
||||||
|
|
@ -318,11 +324,21 @@ pub fn fetch_bundle(server: &str, key: &str) -> Option<KioskBundle> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 304 Not Modified — bundle unchanged, use cached.
|
||||||
|
if resp.status().as_u16() == 304 {
|
||||||
|
return load_cached_bundle();
|
||||||
|
}
|
||||||
|
|
||||||
if !resp.status().is_success() {
|
if !resp.status().is_success() {
|
||||||
tracing::warn!("bundle fetch returned {}", resp.status());
|
tracing::warn!("bundle fetch returned {}", resp.status());
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Cache the ETag for next request.
|
||||||
|
if let Some(etag) = resp.headers().get("etag").and_then(|v| v.to_str().ok()) {
|
||||||
|
*BUNDLE_ETAG.lock().unwrap() = Some(etag.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
match resp.json::<KioskBundle>() {
|
match resp.json::<KioskBundle>() {
|
||||||
Ok(b) => {
|
Ok(b) => {
|
||||||
save_bundle(&b);
|
save_bundle(&b);
|
||||||
|
|
|
||||||
|
|
@ -297,7 +297,24 @@ function registerKioskRoutes(
|
||||||
const bundle = generateBundle(repo, secrets, kiosk.id, clusterKey);
|
const bundle = generateBundle(repo, secrets, kiosk.id, clusterKey);
|
||||||
if (!bundle) throw createError({ statusCode: 404, statusMessage: "Kiosk not found" });
|
if (!bundle) throw createError({ statusCode: 404, statusMessage: "Kiosk not found" });
|
||||||
|
|
||||||
return bundle;
|
// Content-hash ETag: kiosk sends If-None-Match on subsequent fetches.
|
||||||
|
// If bundle hasn't changed → 304 Not Modified (no body, saves bandwidth).
|
||||||
|
const json = JSON.stringify(bundle);
|
||||||
|
const hash = createHash("sha256").update(json).digest("hex").slice(0, 16);
|
||||||
|
const etag = `"${hash}"`;
|
||||||
|
const ifNoneMatch = getRequestHeader(event, "if-none-match");
|
||||||
|
if (ifNoneMatch === etag) {
|
||||||
|
return new Response(null, { status: 304 });
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Response(json, {
|
||||||
|
status: 200,
|
||||||
|
headers: {
|
||||||
|
"content-type": "application/json",
|
||||||
|
"etag": etag,
|
||||||
|
"x-bf-bundle-version": bundle.version,
|
||||||
|
},
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Heartbeat
|
// Heartbeat
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue