Skip to content

Commit 865a3f1

Browse files
committed
Minimise network traffic with ETags
1 parent dfa6cea commit 865a3f1

File tree

4 files changed

+88
-6
lines changed

4 files changed

+88
-6
lines changed

bundler/Cargo.lock

Lines changed: 48 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bundler/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@ edition = "2021"
66
[dependencies]
77
anyhow = "1.0.75"
88
axum = { version = "0.7.2" }
9+
axum-extra = { version = "0.9.0", features = ["typed-header"] }
910
clap = { version = "4.4.11", features = ["derive", "env"] }
1011
dotenvy = { version = "0.15.7" }
1112
flate2 = { version = "1.0.28" }
13+
headers = { version = "0.4.0" }
1214
humantime = { version = "2.1.0" }
1315
serde = { version = "1.0.193", features = ["derive"] }
1416
serde_json = { version = "1.0.108" }

bundler/src/bundle.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ where
7979
Ok(Self::new(metadata, proposals, sessions))
8080
}
8181

82+
pub fn revision(&self) -> &str {
83+
&self.manifest.revision
84+
}
85+
8286
pub fn to_tar_gz(&self) -> Result<Vec<u8>, anyhow::Error> {
8387
let mut bundle_builder = tar::Builder::new(GzEncoder::new(Vec::new(), Compression::best()));
8488

bundler/src/main.rs

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,24 @@ mod bundle;
33
mod permissionables;
44

55
use crate::bundle::{Bundle, NoMetadata};
6-
use axum::{body::Bytes, extract::State, routing::get, serve, Router};
6+
use axum::{
7+
body::Bytes,
8+
extract::State,
9+
http::{HeaderMap, StatusCode},
10+
response::IntoResponse,
11+
routing::get,
12+
serve, Router,
13+
};
14+
use axum_extra::TypedHeader;
715
use clap::Parser;
16+
use headers::{ETag, HeaderMapExt, IfNoneMatch};
817
use serde::Serialize;
918
use sqlx::{mysql::MySqlPoolOptions, MySqlPool};
1019
use std::{
1120
hash::Hash,
12-
marker::PhantomData,
1321
net::{Ipv4Addr, SocketAddr, SocketAddrV4},
1422
ops::Add,
23+
str::FromStr,
1524
sync::Arc,
1625
time::Duration,
1726
};
@@ -28,8 +37,8 @@ struct BundleFile<Metadata>
2837
where
2938
Metadata: Serialize,
3039
{
40+
bundle: Bundle<Metadata>,
3141
file: Bytes,
32-
_metadata: PhantomData<Metadata>,
3342
}
3443

3544
impl<Metadata> TryFrom<Bundle<Metadata>> for BundleFile<Metadata>
@@ -41,7 +50,7 @@ where
4150
fn try_from(bundle: Bundle<Metadata>) -> Result<Self, Self::Error> {
4251
Ok(Self {
4352
file: bundle.to_tar_gz()?.into(),
44-
_metadata: PhantomData,
53+
bundle,
4554
})
4655
}
4756
}
@@ -115,6 +124,25 @@ async fn update_bundle(
115124
}
116125
}
117126

118-
async fn bundle_endpoint(State(current_bundle): State<CurrentBundle>) -> Bytes {
119-
current_bundle.as_ref().read().await.file.clone()
127+
async fn bundle_endpoint(
128+
State(current_bundle): State<CurrentBundle>,
129+
if_none_match: Option<TypedHeader<IfNoneMatch>>,
130+
) -> impl IntoResponse {
131+
let etag = ETag::from_str(&format!(
132+
r#""{}""#,
133+
current_bundle.as_ref().read().await.bundle.revision()
134+
))
135+
.unwrap();
136+
let mut headers = HeaderMap::new();
137+
headers.typed_insert(etag.clone());
138+
match if_none_match {
139+
Some(TypedHeader(if_none_match)) if !if_none_match.precondition_passes(&etag) => {
140+
(StatusCode::NOT_MODIFIED, headers, Bytes::new())
141+
}
142+
_ => (
143+
StatusCode::OK,
144+
headers,
145+
current_bundle.as_ref().read().await.file.clone(),
146+
),
147+
}
120148
}

0 commit comments

Comments
 (0)