http compressions stage 3

This commit is contained in:
YK 2024-05-11 09:01:57 +03:00
parent 16b90fe61c
commit be3740f6ed
3 changed files with 45 additions and 13 deletions

20
Cargo.lock generated
View File

@ -68,6 +68,15 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 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]] [[package]]
name = "diff" name = "diff"
version = "0.1.13" version = "0.1.13"
@ -80,6 +89,16 @@ version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" 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]] [[package]]
name = "gimli" name = "gimli"
version = "0.27.3" version = "0.27.3"
@ -98,6 +117,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bytes", "bytes",
"flate2",
"itertools", "itertools",
"nom", "nom",
"pretty_assertions", "pretty_assertions",

View File

@ -25,7 +25,7 @@ thiserror = "1.0.38" # error handling
tokio = { version = "1.23.0", features = ["full"] } # async networking tokio = { version = "1.23.0", features = ["full"] } # async networking
nom = "7.1.3" # parser combinators nom = "7.1.3" # parser combinators
itertools = "0.11.0" # General iterator helpers itertools = "0.11.0" # General iterator helpers
flate2 = "1.0.30"
[dev-dependencies] [dev-dependencies]
pretty_assertions = "1.3.0" # nicer looking assertions pretty_assertions = "1.3.0" # nicer looking assertions

View File

@ -1,6 +1,6 @@
// #![feature(if_let_guard)] // #![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 anyhow::{bail, Result};
use itertools::Itertools; use itertools::Itertools;
@ -10,6 +10,8 @@ use tokio::{
net::{ TcpListener, TcpStream }, net::{ TcpListener, TcpStream },
}; };
use flate2::{ write::GzEncoder, Compression };
mod utils; mod utils;
use utils::*; use utils::*;
@ -72,9 +74,6 @@ async fn process (mut stream: TcpStream, args: A) -> Result<()> {
let headers = Headers::parse(&mut data).await; let headers = Headers::parse(&mut data).await;
let encoding = Encoding::parse(headers.get("Accept-Encoding")); let encoding = Encoding::parse(headers.get("Accept-Encoding"));
println!("{:?}", headers);
println!("{:?}", encoding);
use Method as M; use Method as M;
let response = match (method, target.as_str()) { let response = match (method, target.as_str()) {
(M::GET, "/") => Response::Empty, (M::GET, "/") => Response::Empty,
@ -104,8 +103,6 @@ async fn process (mut stream: TcpStream, args: A) -> Result<()> {
_ => Response::_404, _ => Response::_404,
}; };
println!("{:?}", String::from_utf8_lossy(&response.clone().build()));
let _ = stream.write_all(&response.build()).await; let _ = stream.write_all(&response.build()).await;
let _ = stream.flush().await; let _ = stream.flush().await;
@ -143,7 +140,7 @@ impl Headers {
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone, PartialEq, Eq)]
enum Encoding { enum Encoding {
Gzip, Gzip,
Invalid, Invalid,
@ -215,17 +212,31 @@ enum Response {
impl Response { impl Response {
fn build (self) -> Vec<u8> { fn build (self) -> Vec<u8> {
let headers = self.headers().join("\r\n"); let mut headers = self.headers();
let code = self.code(); let code = self.code();
let mut v: Vec<u8> = f!("HTTP/1.1 {code}\r\n{headers}\r\n\r\n").into(); let mut v: Vec<u8> = vec![];
let mut write_response_header = |headers: Vec<String>| v.extend_from_slice(format!("HTTP/1.1 {code}\r\n{}\r\n\r\n", headers.join("\r\n")).as_bytes());
match self { match self {
Self::OctetStream(bytes) => { Self::OctetStream(bytes) => {
write_response_header(headers);
v.extend_from_slice(&bytes); v.extend_from_slice(&bytes);
}, },
Self::TextPlain(text, _) => { Self::TextPlain(text, encodings) => {
v.extend_from_slice(text.as_bytes()); 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) => { Self::TextPlain(text, enc) => {
let mut v = vec![ let mut v = vec![
f!("Content-Type: text/plain"), 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(", "); let enc = enc.into_iter().map(Encoding::header).filter(|e| !e.is_empty()).join(", ");
if !enc.is_empty() { v.push(f!("Content-Encoding: {enc}")) } if !enc.is_empty() { v.push(f!("Content-Encoding: {enc}")) }
v v
}, },
Self::OctetStream(bytes) => vec![ Self::OctetStream(bytes) => vec![