ars_server/
embedded_asset.rs1use std::sync::Arc;
2use ars_package::Asset;
3use axum::{http::HeaderValue, http::StatusCode, http::header, response::IntoResponse};
4use bytes::Bytes;
5
6use std::io::Write;
7use flate2::write::GzEncoder;
8use flate2::Compression;
9
10fn gzip_encode_raw(base_raw: &[u8]) -> Vec<u8> {
11 let mut e = GzEncoder::new(Vec::new(), Compression::default());
12 e.write_all(base_raw).unwrap();
13 let compressed_bytes = e.finish();
14 compressed_bytes.unwrap()
15}
16
17#[derive(Clone)]
18pub struct EmbeddedAsset {
19 body: Bytes,
20 mime: Arc<str>,
21 status: u16,
22 cache: bool,
23}
24
25impl EmbeddedAsset {
26 pub fn new(asset: &Asset, cache: bool, target_url: &str, public_url: &str) -> anyhow::Result<Self> {
27 let is_empty = target_url == "/";
28 Ok(match asset.mime.as_ref() {
29 "application/javascript" | "text/css" | "text/html; charset=utf-8" => {
30 let body = if !is_empty {
31 let next_asset = std::str::from_utf8(asset.bytes.as_ref())?
32 .replace(target_url, public_url);
33 Bytes::from(gzip_encode_raw(next_asset.as_bytes()))
34 } else {
35 Bytes::from(gzip_encode_raw(&asset.bytes))
36 };
37 Self {
38 body,
39 mime: asset.mime.clone(),
40 status: 200,
41 cache,
42 }
43 }
44 _ => {
45 let body = Bytes::from(gzip_encode_raw(asset.bytes.as_ref()));
46 Self {
47 body,
48 mime: asset.mime.clone(),
49 status: 200,
50 cache,
51 }
52 }
53 })
54 }
55
56 pub fn json<T>(body: &T) -> anyhow::Result<Self> where T: serde::ser::Serialize {
57 let t = serde_json::to_vec(body)?;
58 let body = Bytes::from(gzip_encode_raw(&t));
59 Ok(Self {
60 status: 200,
61 body,
62 mime: Arc::from("application/json; charset=utf-8"),
63 cache: true,
64 })
65 }
66
67 pub fn not_found(body: Bytes, mime: Arc<str>) -> Self {
68 Self {
69 status: 404,
70 body,
71 mime,
72 cache: false,
73 }
74 }
75}
76
77impl IntoResponse for EmbeddedAsset {
78 fn into_response(self) -> axum::response::Response {
79 let mut response = self.body.into_response();
80 *response.status_mut() = StatusCode::from_u16(self.status).unwrap();
81 if self.status == 200 {
82 response.headers_mut().insert(
83 header::CONTENT_ENCODING,
84 HeaderValue::from_static("gzip"),
85 );
86 }
87 if self.cache {
88 response.headers_mut().insert(
89 header::CACHE_CONTROL,
90 HeaderValue::from_static("public, max-age=604800"),
91 );
92 } else {
93 response.headers_mut().insert(
94 header::CACHE_CONTROL,
95 HeaderValue::from_static(
96 "no-store, no-cache, max-age=0, must-revalidate, proxy-revalidate",
97 ),
98 );
99 }
100 response.headers_mut().insert(
101 header::CONTENT_TYPE,
102 HeaderValue::from_bytes(self.mime.as_bytes()).unwrap(),
103 );
104 response
105 }
106}