aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoromagdy7 <omar.professional8777@gmail.com>2024-06-05 22:42:05 +0300
committeromagdy7 <omar.professional8777@gmail.com>2024-06-05 22:42:05 +0300
commitfe378c20c645ca1cb6cc04e95c9afd4c2de5c0e8 (patch)
treeb5848c95a5c12597b952934bae0b85e4b292816c
parentc119c9cc4c1b3b01a7c1851993dde0264ad06733 (diff)
downloadtiny-server-fe378c20c645ca1cb6cc04e95c9afd4c2de5c0e8.tar.xz
tiny-server-fe378c20c645ca1cb6cc04e95c9afd4c2de5c0e8.zip
feat: Handled Gzip compression
-rw-r--r--Cargo.lock27
-rw-r--r--Cargo.toml2
-rw-r--r--src/main.rs41
-rw-r--r--src/response.rs27
-rw-r--r--src/server.rs4
-rw-r--r--src/utils.rs15
6 files changed, 95 insertions, 21 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 931bb35..096c35d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -54,6 +54,12 @@ dependencies = [
]
[[package]]
+name = "base64"
+version = "0.22.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
+
+[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -78,6 +84,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
+name = "crc32fast"
+version = "1.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
name = "diff"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -90,6 +105,16 @@ 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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -106,7 +131,9 @@ name = "http-server-starter-rust"
version = "0.1.0"
dependencies = [
"anyhow",
+ "base64",
"bytes",
+ "flate2",
"itertools",
"nom",
"pretty_assertions",
diff --git a/Cargo.toml b/Cargo.toml
index 6a19fc8..9858303 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -26,6 +26,8 @@ tokio = { version = "1.23.0", features = ["full"] } # async networking
nom = "7.1.3" # parser combinators
itertools = "0.11.0" # General iterator helpers
regex = "1.10.4"
+flate2 = "1.0.30"
+base64 = "0.22.1"
[dev-dependencies]
pretty_assertions = "1.3.0" # nicer looking assertions
diff --git a/src/main.rs b/src/main.rs
index 24c75a1..250f94b 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -23,11 +23,7 @@ fn handle_echo(request: &Request, ctx: Option<&HashMap<String, String>>) -> Resp
Method::Get(route) | Method::Post(route) | Method::Put(route) => route,
};
- if let Some(encoding) = request.get_tag("Accept-Encoding") {
- if encoding.contains("gzip") {
- headers.insert("Content-Encoding".to_string(), "gzip".to_string());
- }
- }
+ let mut body = vec![];
for ch in route.chars().skip(1).skip_while(|&ch| ch != '/').skip(1) {
echo_string.push(ch);
@@ -35,10 +31,35 @@ fn handle_echo(request: &Request, ctx: Option<&HashMap<String, String>>) -> Resp
if echo_string.chars().last().unwrap() == '/' {
echo_string.pop();
}
- let len = echo_string.len().to_string();
+
+ if let Some(encoding) = request.get_tag("Accept-Encoding") {
+ if encoding.contains("gzip") {
+ headers.insert("Content-Encoding".to_string(), "gzip".to_string());
+ match encode_gzip_string(echo_string.as_str()) {
+ Ok(encoded_bytes) => {
+ println!("In succses");
+ let len = encoded_bytes.len();
+ body = encoded_bytes;
+ headers.insert("Content-Length".to_string(), len.to_string());
+ }
+ Err(err) => {
+ println!("In error {}", &echo_string);
+ println!("Error: {err}");
+ }
+ }
+ } else {
+ let len = echo_string.len();
+ headers.insert("Content-Length".to_string(), len.to_string());
+ body = echo_string.as_bytes().to_owned();
+ }
+ } else {
+ let len = echo_string.len();
+ headers.insert("Content-Length".to_string(), len.to_string());
+ body = echo_string.as_bytes().to_owned();
+ }
+
headers.insert("Content-Type".to_string(), "text/plain".to_string());
- headers.insert("Content-Length".to_string(), len);
- let body = echo_string;
+
Response::new(
"1.1".to_string(),
StatusCode::Ok,
@@ -106,7 +127,7 @@ fn handle_files(request: &Request, ctx: Option<&HashMap<String, String>>) -> Res
"application/octet-stream".to_string(),
);
headers.insert("Content-Length".to_string(), bytes.len().to_string());
- let body = String::from_utf8(bytes).unwrap();
+ let body = bytes;
Response::new(
"1.1".to_string(),
StatusCode::Ok,
@@ -124,7 +145,7 @@ fn handle_user_agent(request: &Request, ctx: Option<&HashMap<String, String>>) -
let len = user_agent.len().to_string();
headers.insert("Content-Type".to_string(), "text/plain".to_string());
headers.insert("Content-Length".to_string(), len);
- let body = user_agent.to_string();
+ let body = user_agent.as_bytes().to_owned();
Response::new(
"1.1".to_string(),
StatusCode::Ok,
diff --git a/src/response.rs b/src/response.rs
index c3540d2..80424a3 100644
--- a/src/response.rs
+++ b/src/response.rs
@@ -5,7 +5,7 @@ pub struct Response {
version: String,
status: StatusCode,
headers: Option<Headers>,
- body: Option<String>,
+ body: Option<Vec<u8>>,
}
impl Response {
@@ -13,7 +13,7 @@ impl Response {
version: String,
status: StatusCode,
headers: Option<Headers>,
- body: Option<String>,
+ body: Option<Vec<u8>>,
) -> Self {
Response {
version,
@@ -24,18 +24,25 @@ impl Response {
}
}
-impl Into<String> for Response {
- fn into(self) -> String {
- let status_line = format!("HTTP/{} {}", self.version, String::from(self.status));
+impl Into<Vec<u8>> for Response {
+ fn into(self) -> Vec<u8> {
+ let status_line = format!("HTTP/{} {}", self.version, String::from(self.status))
+ .as_bytes()
+ .to_owned();
let headers = self
.headers
.unwrap_or(Headers(HashMap::new()))
.0
.iter()
.map(|(key, value)| format!("{key}: {value}\r\n"))
- .collect::<String>();
- let body = self.body.unwrap_or("".to_string());
- format!("{status_line}\r\n{headers}\r\n{body}")
+ .collect::<String>()
+ .as_bytes()
+ .to_owned();
+ let body = self.body.unwrap_or(vec![]);
+ let crlf = "\r\n".as_bytes().to_owned();
+ vec![status_line, crlf.clone(), headers, crlf.clone(), body]
+ .concat()
+ .to_owned()
}
}
@@ -74,12 +81,12 @@ impl Into<Response> for &str {
};
// Parse body
- let body: Option<String> = {
+ let body: Option<Vec<u8>> = {
let remaining_lines: Vec<&str> = lines.collect();
if remaining_lines.is_empty() {
None
} else {
- Some(remaining_lines.join("\n"))
+ Some(remaining_lines.join("\n").as_bytes().to_owned())
}
};
diff --git a/src/server.rs b/src/server.rs
index c0d7ab6..9c60d33 100644
--- a/src/server.rs
+++ b/src/server.rs
@@ -6,6 +6,8 @@ use std::{
net::{SocketAddr, TcpListener},
};
+use nom::AsBytes;
+
use crate::request::Request;
use crate::router::Router;
@@ -40,7 +42,7 @@ impl Server {
println!("Request after parsing:\n{}", request_string);
dbg!(&request.method);
- let response: String = router.handle(&request, ctx).into();
+ let response: Vec<u8> = router.handle(&request, ctx).into();
stream.write(response.as_bytes())
}
Err(_) => todo!(),
diff --git a/src/utils.rs b/src/utils.rs
index e9d2ef1..925669c 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -1,6 +1,21 @@
+use flate2::write::GzEncoder;
+use flate2::Compression;
use std::fs::File;
use std::io::{self, Read, Write};
+pub fn encode_gzip_string(input: &str) -> std::io::Result<Vec<u8>> {
+ // Create a buffer to hold the compressed data
+ let mut encoder = GzEncoder::new(Vec::new(), Compression::default());
+
+ // Write the input string into the encoder
+ encoder.write_all(input.as_bytes())?;
+
+ // Finish the encoding process and get the compressed data
+ let compressed_data = encoder.finish()?;
+
+ Ok(compressed_data)
+}
+
pub fn save_bytes_to_file(bytes: &[u8], file_path: &str) -> io::Result<()> {
let mut file = File::create(file_path)?;
file.write_all(bytes)?;