diff --git a/Cargo.lock b/Cargo.lock index 0f65e0a..521ab66 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -68,6 +68,15 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "crc32fast" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" +dependencies = [ + "cfg-if", +] + [[package]] name = "diff" version = "0.1.13" @@ -80,6 +89,16 @@ version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +[[package]] +name = "flate2" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + [[package]] name = "gimli" version = "0.27.3" @@ -98,6 +117,7 @@ version = "0.1.0" dependencies = [ "anyhow", "bytes", + "flate2", "itertools", "nom", "pretty_assertions", diff --git a/Cargo.toml b/Cargo.toml index 321f7f7..85bf04c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ thiserror = "1.0.38" # error handling tokio = { version = "1.23.0", features = ["full"] } # async networking nom = "7.1.3" # parser combinators itertools = "0.11.0" # General iterator helpers +flate2 = "1.0.30" [dev-dependencies] pretty_assertions = "1.3.0" # nicer looking assertions - diff --git a/src/main.rs b/src/main.rs index 8e89c93..c1fe5ce 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,6 @@ // #![feature(if_let_guard)] -use std::{ collections::HashMap, path::PathBuf, sync::Arc }; +use std::{ collections::HashMap, io::Write, path::PathBuf, sync::Arc }; use anyhow::{bail, Result}; use itertools::Itertools; @@ -10,6 +10,8 @@ use tokio::{ net::{ TcpListener, TcpStream }, }; +use flate2::{ write::GzEncoder, Compression }; + mod utils; use utils::*; @@ -72,9 +74,6 @@ async fn process (mut stream: TcpStream, args: A) -> Result<()> { let headers = Headers::parse(&mut data).await; let encoding = Encoding::parse(headers.get("Accept-Encoding")); - println!("{:?}", headers); - println!("{:?}", encoding); - use Method as M; let response = match (method, target.as_str()) { (M::GET, "/") => Response::Empty, @@ -104,8 +103,6 @@ async fn process (mut stream: TcpStream, args: A) -> Result<()> { _ => Response::_404, }; - println!("{:?}", String::from_utf8_lossy(&response.clone().build())); - let _ = stream.write_all(&response.build()).await; let _ = stream.flush().await; @@ -143,7 +140,7 @@ impl Headers { } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] enum Encoding { Gzip, Invalid, @@ -215,17 +212,31 @@ enum Response { impl Response { fn build (self) -> Vec { - let headers = self.headers().join("\r\n"); + let mut headers = self.headers(); let code = self.code(); - let mut v: Vec = f!("HTTP/1.1 {code}\r\n{headers}\r\n\r\n").into(); + let mut v: Vec = vec![]; + let mut write_response_header = |headers: Vec| v.extend_from_slice(format!("HTTP/1.1 {code}\r\n{}\r\n\r\n", headers.join("\r\n")).as_bytes()); match self { Self::OctetStream(bytes) => { + write_response_header(headers); v.extend_from_slice(&bytes); }, - Self::TextPlain(text, _) => { - v.extend_from_slice(text.as_bytes()); + Self::TextPlain(text, encodings) => { + if encodings.contains(&Encoding::Gzip) { + let mut enc = GzEncoder::new(vec![], Compression::default()); + enc.write_all(text.as_bytes()).unwrap(); + let b = enc.finish().unwrap(); + headers.push(format!("Content-Length: {}", b.len())); + write_response_header(headers); + v.extend_from_slice(&b); + } else { + + write_response_header(headers); + v.extend_from_slice(text.as_bytes()); + } + }, _ => () } @@ -248,10 +259,11 @@ impl Response { Self::TextPlain(text, enc) => { let mut v = vec![ f!("Content-Type: text/plain"), - format!("Content-Length: {}", text.len()), ]; + if !enc.contains(&Encoding::Gzip) { v.push(format!("Content-Length: {}", text.len())) } let enc = enc.into_iter().map(Encoding::header).filter(|e| !e.is_empty()).join(", "); if !enc.is_empty() { v.push(f!("Content-Encoding: {enc}")) } + v }, Self::OctetStream(bytes) => vec![