Skip to content

Commit 650e3ba

Browse files
committed
Add back in support for gzip compression
See #28 for context. This new code will serve up br compressed if supported, but will fall back to gzip compression if not.
1 parent 2d089c3 commit 650e3ba

File tree

3 files changed

+75
-19
lines changed

3 files changed

+75
-19
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ tokio = { version = "1.11", default-features = false, features = ["rt-multi-thre
2323
tower-http = { version = "0.3", features = ["compression-full", "fs", "set-header", "trace"] }
2424
tower = "0.4"
2525
fastrand = "1.5"
26+
flate2 = "1.0"
2627
brotli = { version = "3", default-features = false, features = ["std"]}
2728
rcgen = { version = "0.9", default-features = false }
2829

src/server.rs

Lines changed: 49 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
use std::collections::HashMap;
22
use std::net::SocketAddr;
3+
use std::str::from_utf8;
34

45
use axum::headers::HeaderName;
6+
use axum::http::header::ACCEPT_ENCODING;
57
use axum::http::{HeaderMap, HeaderValue, StatusCode, Uri};
68
use axum::response::{Html, IntoResponse, Response};
79
use axum::routing::{get, get_service};
@@ -59,7 +61,8 @@ pub async fn run_server(options: Options, output: WasmBindgenOutput) -> Result<(
5961
}
6062

6163
fn get_router(options: &Options, output: WasmBindgenOutput) -> Router {
62-
let WasmBindgenOutput { js, compressed_wasm, snippets, local_modules } = output;
64+
let WasmBindgenOutput { js, compressed_wasm, gzip_compressed_wasm, snippets, local_modules } =
65+
output;
6366

6467
let middleware_stack = ServiceBuilder::new()
6568
.layer(CompressionLayer::new())
@@ -87,7 +90,37 @@ fn get_router(options: &Options, output: WasmBindgenOutput) -> Router {
8790

8891
let serve_wasm = |headers: HeaderMap| async move {
8992
println!(" request headers: {:?}", headers);
90-
([("content-encoding", "br")], WithContentType("application/wasm", compressed_wasm))
93+
if let Some(accept_encoding) = headers.get(ACCEPT_ENCODING) {
94+
match from_utf8(accept_encoding.as_bytes()) {
95+
Ok(encodings) => {
96+
let split_encodings: Vec<&str> = encodings.split(",").map(str::trim).collect();
97+
println!("split_encodings: {:?}", split_encodings);
98+
if split_encodings.contains(&"br") {
99+
println!("serving br compressed wasm");
100+
Ok((
101+
[("content-encoding", "br")],
102+
WithContentType("application/wasm", compressed_wasm),
103+
))
104+
} else if split_encodings.contains(&"gzip") {
105+
println!("serving gzip compressed wasm");
106+
Ok((
107+
[("content-encoding", "gzip")],
108+
WithContentType("application/wasm", gzip_compressed_wasm),
109+
))
110+
} else {
111+
println!("No support for requested encoding!");
112+
Err((
113+
StatusCode::BAD_REQUEST,
114+
format!("Unsupported encoding(s): {:?}", split_encodings),
115+
))
116+
}
117+
}
118+
Err(err) => Err((StatusCode::BAD_REQUEST, err.to_string())),
119+
}
120+
} else {
121+
tracing::error!("Received request missing the accept-encoding header");
122+
Err((StatusCode::BAD_REQUEST, "Missing `accept-encoding` header".to_string()))
123+
}
91124
};
92125

93126
Router::new()
@@ -217,7 +250,13 @@ mod tests {
217250
println!("{:?}", &router);
218251
let client = TestClient::new(router);
219252

220-
// Test with br compression requested
253+
// Test without any supported compression
254+
let mut res =
255+
client.get("/api/wasm.wasm").header("accept-encoding", "deflate").send().await;
256+
assert_eq!(res.status(), StatusCode::BAD_REQUEST);
257+
let result = res.chunk().await.unwrap();
258+
println!("Bad Request result: {:?}", result);
259+
221260
let mut res = client
222261
.get("/api/wasm.wasm")
223262
.header("accept-encoding", "gzip, deflate, br")
@@ -227,17 +266,12 @@ mod tests {
227266
let result = res.chunk().await.unwrap();
228267
assert_ne!(result[0..3], Bytes::from(vec![0x1f, 0x8b, 0x08]));
229268

230-
// Test without br compression
231-
// let mut res = client
232-
// .get("/api/wasm.wasm")
233-
// .header("accept-encoding", "gzip, deflate")
234-
// .send()
235-
// .await;
236-
// assert_eq!(res.status(), StatusCode::OK);
237-
// let result = res.chunk().await.unwrap();
238-
// // This is the gzip 3-byte file header
239-
// assert_eq!(result[0..3], Bytes::from(vec![0x1f, 0x8b, 0x08]));
240-
241-
//tokio_test::block_on(server).unwrap();
269+
// Test without br compression, defaulting to gzip
270+
let mut res =
271+
client.get("/api/wasm.wasm").header("accept-encoding", "gzip, deflate").send().await;
272+
assert_eq!(res.status(), StatusCode::OK);
273+
let result = res.chunk().await.unwrap();
274+
// This is the gzip 3-byte file header
275+
assert_eq!(result[0..3], Bytes::from(vec![0x1f, 0x8b, 0x08]));
242276
}
243277
}

src/wasm_bindgen.rs

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,12 @@ use std::collections::HashMap;
55
use std::path::Path;
66
use tracing::debug;
77

8+
const COMPRESSION_LEVEL: u32 = 2;
9+
810
pub struct WasmBindgenOutput {
911
pub js: String,
1012
pub compressed_wasm: Vec<u8>,
13+
pub gzip_compressed_wasm: Vec<u8>,
1114
pub snippets: HashMap<String, Vec<String>>,
1215
pub local_modules: HashMap<String, String>,
1316
}
@@ -35,15 +38,20 @@ pub fn generate(options: &Options, wasm_file: &Path) -> Result<WasmBindgenOutput
3538
let wasm = output.wasm_mut().emit_wasm();
3639
debug!("emitting wasm took {:?}", start.elapsed());
3740

38-
debug!("compressing wasm...");
41+
debug!("br compressing wasm...");
3942
let start = std::time::Instant::now();
40-
let compressed_wasm = compress(&wasm).context("failed to compress wasm file")?;
43+
let compressed_wasm = br_compress(&wasm).context("failed to compress wasm file")?;
4144
debug!("compressing took {:?}", start.elapsed());
4245

43-
Ok(WasmBindgenOutput { js, compressed_wasm, snippets, local_modules })
46+
debug!("gzip compressing wasm...");
47+
let start = std::time::Instant::now();
48+
let gzip_compressed_wasm = gzip_compress(&wasm).context("failed to compress wasm file")?;
49+
debug!("compressing took {:?}", start.elapsed());
50+
51+
Ok(WasmBindgenOutput { js, compressed_wasm, gzip_compressed_wasm, snippets, local_modules })
4452
}
4553

46-
fn compress(mut bytes: &[u8]) -> Result<Vec<u8>, std::io::Error> {
54+
fn br_compress(mut bytes: &[u8]) -> Result<Vec<u8>, std::io::Error> {
4755
use brotli::enc::{self, BrotliEncoderParams};
4856

4957
let mut output = Vec::new();
@@ -54,3 +62,16 @@ fn compress(mut bytes: &[u8]) -> Result<Vec<u8>, std::io::Error> {
5462

5563
Ok(output)
5664
}
65+
66+
fn gzip_compress(bytes: &[u8]) -> Result<Vec<u8>, std::io::Error> {
67+
use flate2::write::GzEncoder;
68+
use flate2::Compression;
69+
use std::io::prelude::*;
70+
71+
let mut encoder = GzEncoder::new(Vec::new(), Compression::new(COMPRESSION_LEVEL));
72+
73+
encoder.write_all(bytes)?;
74+
let compressed = encoder.finish()?;
75+
76+
Ok(compressed)
77+
}

0 commit comments

Comments
 (0)