Skip to content

Commit 8ddfebf

Browse files
committed
Improve etag caching
1 parent 84b09ee commit 8ddfebf

5 files changed

Lines changed: 101 additions & 13 deletions

File tree

Cargo.lock

Lines changed: 72 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

registry_wizard/Cargo.toml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "registry_wizard"
3-
version = "0.4.18"
3+
version = "0.4.19"
44
edition = "2024"
55

66
[dependencies]
@@ -18,6 +18,7 @@ tokio = { version = "1.50.0", features = ["rt-multi-thread", "signal"], optional
1818
axum = { version = "0.8.8", optional = true }
1919
rust-embed = { version = "8.11.0", optional = true }
2020
mime_guess = { version = "2.0.5", optional = true }
21+
tower-http = { version = "0.6.8", features = ["compression-br", "compression-gzip"], optional = true }
2122

2223
rpki = {version = "0.19.2", features = ["rtr", "crypto"], optional = true}
2324
tokio-stream = { version = "0.1.18" , features = ["net"], optional = true}
@@ -29,7 +30,8 @@ explorer = [
2930
"tokio",
3031
"axum",
3132
"rust-embed",
32-
"mime_guess"
33+
"mime_guess",
34+
"tower-http"
3335
]
3436
rtr-server = [
3537
"tokio",

registry_wizard/src/modules/explorer/handlers.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
use std::collections::HashMap;
22
use std::sync::{Arc, RwLock};
33
use axum::extract::{Query, State};
4-
use axum::http::{HeaderMap, HeaderValue, StatusCode, Uri};
4+
use axum::http::{header, HeaderMap, HeaderValue, StatusCode, Uri};
55
use axum::response::IntoResponse;
66
use crate::modules::explorer::{static_files, AppState};
77

8-
pub(super) async fn root_handler(uri: Uri) -> impl IntoResponse {
8+
pub(super) async fn root_handler(request_headers: HeaderMap, uri: Uri) -> impl IntoResponse {
99
let mut path = uri.path().trim_start_matches('/').to_owned();
1010
if path.is_empty() {
1111
path = String::from("index.html");
1212
}
13-
static_files::StaticFile(path)
13+
static_files::StaticFile { path, if_none_match: request_headers.get(header::IF_NONE_MATCH).cloned()}
1414
}
1515

1616
pub(super) async fn index_handler(request_headers: HeaderMap, State(u): State<Arc<RwLock<AppState>>>) -> impl IntoResponse {

registry_wizard/src/modules/explorer/mod.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use std::path::{Path, PathBuf};
66
use std::sync::{Arc, RwLock};
77
use tokio::sync::broadcast;
88
use tokio::sync::broadcast::channel;
9+
use tower_http::compression::CompressionLayer;
910
use crate::modules::explorer::state::AppState;
1011
use crate::modules::util::os_signals::{signal_listener, CustomSignal};
1112

@@ -79,10 +80,10 @@ async fn start_server(app_state: Arc<RwLock<AppState>>, port: u16, mut sig_chan_
7980
}
8081

8182
let app = Router::new()
82-
.route("/", get(handlers::root_handler))
83-
.route("/{*path}", get(handlers::root_handler))
84-
.route("/api/index/", get(handlers::index_handler))
85-
.route("/api/object/", get(handlers::get_object))
83+
.route("/", get(handlers::root_handler).layer(CompressionLayer::new()))
84+
.route("/{*path}", get(handlers::root_handler).layer(CompressionLayer::new()))
85+
.route("/api/index/", get(handlers::index_handler).layer(CompressionLayer::new()))
86+
.route("/api/object/", get(handlers::get_object).layer(CompressionLayer::new()))
8687
.route("/api/roa/v4/", get(handlers::roa_handler_v4))
8788
.route("/api/roa/v6/", get(handlers::roa_handler_v6))
8889
.route("/api/roa/json/", get(handlers::roa_handler_json))

registry_wizard/src/modules/explorer/static_files.rs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,40 @@
11
use axum::body::Body;
2-
use axum::http::{header, StatusCode};
2+
use axum::http::{header, HeaderValue, StatusCode};
33
use axum::response::{IntoResponse, Response};
44
use rust_embed::RustEmbed;
55

66
#[derive(RustEmbed)]
77
#[folder = "src/modules/explorer/static/"]
88
struct StaticFiles;
99

10-
pub(super) struct StaticFile<T>(pub T);
10+
pub(super) struct StaticFile<T> {
11+
pub path: T,
12+
pub if_none_match : Option<HeaderValue>,
13+
}
1114
impl<T> IntoResponse for StaticFile<T>
1215
where
1316
T: Into<String>,
1417
{
1518
fn into_response(self) -> Response {
16-
let path = self.0.into();
19+
let path = self.path.into();
1720

1821
match StaticFiles::get(path.as_str()) {
1922
Some(content) => {
2023
let body = Body::from(content.data);
24+
let hash = content.metadata.sha256_hash()
25+
.iter()
26+
.map(|b| format!("{:02x}", b))
27+
.collect::<String>();
28+
let etag = format!(r#""{}""#, hash);
29+
30+
if self.if_none_match.as_ref().and_then(|h| h.to_str().ok()) == Some(&etag) {
31+
return StatusCode::NOT_MODIFIED.into_response();
32+
}
33+
2134
let mime = mime_guess::from_path(path).first_or_octet_stream();
2235
Response::builder()
2336
.header(header::CONTENT_TYPE, mime.as_ref())
37+
.header(header::ETAG, etag)
2438
.header(header::CACHE_CONTROL, "max-age=3600, public, stale-if-error=86400")
2539
.body(body)
2640
.unwrap()

0 commit comments

Comments
 (0)