1use axum::{
2 http::header,
3 response::{IntoResponse, Response},
4};
5use rust_embed::Embed;
6
7pub async fn serve_sw<A: Embed>() -> Response {
11 match crate::assets::asset_bytes::<A>("sw.js") {
12 Some(data) => {
13 let mut builder = Response::builder()
14 .header(header::CONTENT_TYPE, "text/javascript; charset=utf-8")
15 .header(header::CACHE_CONTROL, "no-cache, no-store, must-revalidate")
16 .header("service-worker-allowed", "/");
17 for (name, value) in crate::types::asset_security_headers() {
20 builder = builder.header(name, value);
21 }
22 builder
23 .body(axum::body::Body::from(data))
24 .unwrap()
25 .into_response()
26 }
27 None => axum::http::StatusCode::NOT_FOUND.into_response(),
28 }
29}
30
31#[cfg(test)]
32mod tests {
33 use super::*;
34 use rust_embed::Embed;
35
36 #[derive(Embed)]
37 #[folder = "test_fixtures/"]
38 struct Fixtures;
39
40 #[tokio::test]
41 async fn serve_sw_sets_nosniff_and_no_store() {
42 let response = serve_sw::<Fixtures>().await;
43 let headers = response.headers();
44 assert_eq!(
45 headers
46 .get("x-content-type-options")
47 .map(|v| v.to_str().unwrap()),
48 Some("nosniff"),
49 "the service worker must not be sniffable into another type"
50 );
51 assert_eq!(
52 headers
53 .get(header::CONTENT_TYPE)
54 .map(|v| v.to_str().unwrap()),
55 Some("text/javascript; charset=utf-8")
56 );
57 assert!(headers
58 .get(header::CACHE_CONTROL)
59 .unwrap()
60 .to_str()
61 .unwrap()
62 .contains("no-store"));
63 }
64}