From 629d57de50d0bae770810eacaf2fb3d190223746 Mon Sep 17 00:00:00 2001 From: YK Date: Sat, 11 May 2024 01:04:51 +0300 Subject: [PATCH] stage 7 attempt 1 --- src/main.rs | 84 ++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 61 insertions(+), 23 deletions(-) diff --git a/src/main.rs b/src/main.rs index ab7b6f8..f836c02 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,30 +1,50 @@ // #![feature(if_let_guard)] -use std::collections::HashMap; +use std::{ collections::HashMap, path::PathBuf, sync::Arc }; use anyhow::Result; use tokio::{ - io::{ AsyncBufReadExt, AsyncWriteExt, BufReader, Lines }, - net::{ TcpListener, TcpStream } + fs::File, + io::{ AsyncBufReadExt, AsyncReadExt, AsyncWriteExt, BufReader }, + net::{ TcpListener, TcpStream }, }; mod utils; use utils::*; +#[derive(Debug, Clone)] +struct Args { + pub directory: PathBuf, +} + +type A = Arc; + + +fn parse_args () -> Args { + let directory = std::env::args().position(|e| e == "--directory").unwrap() + 1; + let directory = PathBuf::from(std::env::args().nth(directory).unwrap()); + + Args { + directory + } +} + #[tokio::main] async fn main() -> Result<()> { let listener = TcpListener::bind("127.0.0.1:4221").await.unwrap(); + let args = Arc::new(parse_args()); loop { + let args = args.clone(); let Ok((socket, info)) = listener.accept().await else { continue; } ; println!("Connection from {:?} accepted, processing…", info); - let _ = process(socket).await; + let _ = process(socket, args).await; } } -async fn process (mut stream: TcpStream) -> Result<()> { +async fn process (mut stream: TcpStream, args: A) -> Result<()> { let buf_reader = BufReader::new(&mut stream); let mut data = buf_reader @@ -40,20 +60,27 @@ async fn process (mut stream: TcpStream) -> Result<()> { (method, path, ver) }; - let headers = Headers::parse(data.into_inner()).await; let response = match path.as_str() { "/" => Response::Empty, - "/user-agent" => Response::TextPlain(headers.get("User-Agent")), + "/user-agent" => Response::TextPlain(headers.get("User-Agent").to_owned()), // p if let Some(echo) = p.strip_prefix("/echo/") => Response::TextPlain(echo), // a nicer way to do that, not available in stable yet - p if p.starts_with("/echo/") => Response::TextPlain(p.trim_start_matches("/echo/")), + p if p.starts_with("/echo/") => Response::TextPlain(p.trim_start_matches("/echo/").to_owned()), + p if p.starts_with("/files/") => { + let path = args.directory.join(p.trim_start_matches("/files/")); + let mut buf = vec![]; + let mut f = File::open(path).await?; + + + let _ = f.read_to_end(&mut buf).await; + + Response::OctetStream(buf) + }, _ => Response::_404, }; - println!("{:?}", response); - - let _ = stream.write_all(response.build().as_bytes()).await; + let _ = stream.write_all(&response.build()).await; let _ = stream.flush().await; Ok(()) @@ -68,7 +95,6 @@ impl Headers { let mut buf = String::new(); while let Ok(_) = reader.read_line(&mut buf).await { if let Some((k, v)) = buf.split_once(":") { - println!("{:?}", (k, v)); map.insert(k.trim().to_lowercase(), v.trim().to_owned()); } else { break; @@ -85,31 +111,43 @@ impl Headers { } #[derive(Debug, Clone)] -enum Response <'a> { +enum Response { _404, Empty, - TextPlain (&'a str), + TextPlain (String), + OctetStream (Vec) } #[allow(non_upper_case_globals)] -impl Response <'_> { - fn build (self) -> String { - - let (code, body) = match self { - Self::_404 => ("404 Not Found", d!()), - Self::Empty => ("200 OK", d!()), - Self::TextPlain(text) => ("200 OK", text) - }; +impl Response { + fn build (self) -> Vec { let headers = self.headers().join("\r\n"); - f!("HTTP/1.1 {code}\r\n{headers}\r\n\r\n{body}").into() + let code = match self { + Self::_404 => "404 Not Found", + _ => "200 OK", + }; + + let mut v: Vec = f!("HTTP/1.1 {code}\r\n{headers}\r\n\r\n").into(); + match self { + Self::OctetStream(bytes) => { + v.extend_from_slice(&bytes); + }, + Self::TextPlain(text) => { + v.extend_from_slice(text.as_bytes()); + }, + _ => () + } + + v } fn headers (&self) -> Vec { match self { Self::TextPlain(text) => vec![f!("Content-Type: text/plain"), format!("Content-Length: {}", text.len())], + Self::OctetStream(bytes) => vec![f!("Content-Type: application/octet-stream"), format!("Content-Length: {}", bytes.len())], _ => d!() } }