blob: 5ab934ace231a03868ba86130fcec6f40752c395 [file] [log] [blame]
Samuel Shuert71aa1362024-01-10 17:27:57 -05001use std::{io, collections::HashMap, env::args, os::unix::fs::MetadataExt};
2use bytes::BytesMut;
3use tokio::{net::{TcpListener, TcpStream}, io::{AsyncReadExt, AsyncWriteExt}, fs::File};
Samuel Shuert2ecef062024-01-10 16:31:55 -05004
codecrafters-bot67061582024-01-10 17:35:39 +00005
Samuel Shuert62c526f2024-01-10 15:30:45 -05006use itertools::Itertools;
Samuel Shuertf2efffb2024-01-10 16:14:01 -05007use nom::AsChar;
Samuel Shuert62c526f2024-01-10 15:30:45 -05008
Samuel Shuert9c09d872024-01-10 17:27:57 -05009#[derive(Debug)]
Samuel Shuert71aa1362024-01-10 17:27:57 -050010enum BodyTypes {
11 File(File),
12 String(String)
13}
14
15async fn process_socket(mut stream: TcpStream) -> Result<(), &'static str> {
Samuel Shuert2ecef062024-01-10 16:31:55 -050016 println!("accepted new connection");
Samuel Shuert2ecef062024-01-10 16:31:55 -050017 let mut request: HashMap<String, String> = HashMap::new();
18 let mut line_num: usize = 0;
19 loop {
20 let mut line = String::default();
Samuel Shuerte4ee8732024-01-10 14:42:09 -050021
Samuel Shuert2ecef062024-01-10 16:31:55 -050022 loop {
23 let mut buf: [u8; 1] = [0; 1];
24 stream.read_exact(&mut buf).await.unwrap();
Samuel Shuert3d880f72024-01-10 14:48:08 -050025
Samuel Shuert2ecef062024-01-10 16:31:55 -050026 let character = buf[0].as_char();
27 if character == '\n' {
28 break
29 } else if character != '\r' {
30 line.push(character);
Samuel Shuertf10dce92024-01-10 14:31:46 -050031 }
32 }
Samuel Shuert2ecef062024-01-10 16:31:55 -050033
34 if line_num == 0 {
35 let mut splits = line.split(' ');
36 request.insert("method".to_owned(), splits.next().unwrap().to_owned());
37 request.insert("path".to_owned(), splits.next().unwrap().to_owned());
38 } else {
Samuel Shuertbdb676c2024-01-10 16:31:55 -050039 let mut splits = line.split(":");
Samuel Shuert2ecef062024-01-10 16:31:55 -050040 let key = splits.next().unwrap();
Samuel Shuertb96d06c2024-01-10 16:31:55 -050041 let value = splits.next().unwrap_or_default().trim();
Samuel Shuert2ecef062024-01-10 16:31:55 -050042 request.insert(key.to_owned(), value.to_owned());
43 }
44
Samuel Shuert5d783332024-01-10 16:31:55 -050045 if request.get("path").unwrap() != "/user-agent" || request.contains_key("User-Agent") { break };
Samuel Shuert2ecef062024-01-10 16:31:55 -050046 line_num += 1;
47 }
48
Samuel Shuert2ecef062024-01-10 16:31:55 -050049 let mut path = request.get("path").unwrap().split("/");
50
51 let response_status: String;
52 let mut response_headers: Vec<String> = vec![];
Samuel Shuert71aa1362024-01-10 17:27:57 -050053 let response_body: BodyTypes;
Samuel Shuert2ecef062024-01-10 16:31:55 -050054
Samuel Shuert2ecef062024-01-10 16:31:55 -050055 path.next().unwrap();
56
57 match path.next().unwrap() {
58 "" => {
59 response_status = "200 OK".into();
Samuel Shuert71aa1362024-01-10 17:27:57 -050060 response_body = BodyTypes::String("".into());
Samuel Shuert2ecef062024-01-10 16:31:55 -050061 },
62 "echo" => {
63 response_status = "200 OK".into();
Samuel Shuert71aa1362024-01-10 17:27:57 -050064 response_body = BodyTypes::String(path.join("/"));
Samuel Shuert2ecef062024-01-10 16:31:55 -050065 response_headers.push("Content-Type: text/plain".into());
66 },
67 "user-agent" => {
68 response_status = "200 OK".into();
Samuel Shuert71aa1362024-01-10 17:27:57 -050069 response_body = BodyTypes::String(request.get("User-Agent").unwrap().into());
Samuel Shuert2ecef062024-01-10 16:31:55 -050070 response_headers.push("Content-Type: text/plain".into());
71 },
Samuel Shuert71aa1362024-01-10 17:27:57 -050072 "files" => {
73 let file_name = path.join("/");
74 let args: Vec<String> = args().collect();
Samuel Shuertb8096532024-01-10 17:27:57 -050075 let dir_arg = args.iter().find_position(|item| **item == "--directory");
76 let file_path = if dir_arg.is_some() {
77 args[dir_arg.unwrap().0+1].to_owned() + "/" + file_name.as_str()
Samuel Shuert71aa1362024-01-10 17:27:57 -050078 } else {
79 return Err("No directory Given".into())
80 };
81 let maybe_file = File::open(file_path).await;
82
83 match maybe_file {
84 Ok(file) => {
85 response_status = "200 OK".into();
86 response_body = BodyTypes::File(file);
Samuel Shuert9c09d872024-01-10 17:27:57 -050087 response_headers.push("Content-Type: application/octet-stream".into());
Samuel Shuert71aa1362024-01-10 17:27:57 -050088 },
89 Err(_) => {
90 response_status = "404 Not Found".into();
91 response_body = BodyTypes::String("".into());
92 }
93 }
94 },
Samuel Shuert2ecef062024-01-10 16:31:55 -050095 _ => {
96 response_status = "404 Not Found".into();
Samuel Shuert71aa1362024-01-10 17:27:57 -050097 response_body = BodyTypes::String("".into());
Samuel Shuert2ecef062024-01-10 16:31:55 -050098 }
99 }
100
Samuel Shuert71aa1362024-01-10 17:27:57 -0500101 match response_body {
102 BodyTypes::String(ref content) => {
103 if content.len() > 0 {
104 response_headers.push(format!("Content-Length: {}", content.len()));
105 };
106 },
107 BodyTypes::File(ref file) => {
108 response_headers.push(format!("Content-Length: {}", file.metadata().await.unwrap().size()));
109 }
Samuel Shuert2ecef062024-01-10 16:31:55 -0500110 }
111
Samuel Shuert71aa1362024-01-10 17:27:57 -0500112 stream.write(format!("HTTP/1.1 {response_status}\r\n{}\r\n\r\n", response_headers.join("\r\n")).as_bytes()).await.unwrap();
Samuel Shuert2ecef062024-01-10 16:31:55 -0500113
Samuel Shuert71aa1362024-01-10 17:27:57 -0500114 match response_body {
115 BodyTypes::String(ref content) => {
116 stream.write(content.as_bytes()).await.unwrap();
117 },
118 BodyTypes::File(mut file) => {
119 loop {
120 let mut buf = BytesMut::with_capacity(1024);
121
122 let read = file.read_buf(&mut buf).await.unwrap();
123
124 if read == 0 {
125 break;
126 }
127
128 stream.write(&mut buf).await.unwrap();
129 }
130 }
131 }
132
133 Ok(())
Samuel Shuert2ecef062024-01-10 16:31:55 -0500134}
135
136#[tokio::main]
Samuel Shuert71aa1362024-01-10 17:27:57 -0500137async fn main() -> io::Result<()> {
Samuel Shuert2ecef062024-01-10 16:31:55 -0500138 let listener = TcpListener::bind("127.0.0.1:4221").await?;
139 let mut futures = vec![];
140
141 loop {
142 let (socket, _) = listener.accept().await?;
Samuel Shuertb933bdd2024-01-10 16:31:55 -0500143 futures.push(tokio::spawn(process_socket(socket)));
Samuel Shuertf10dce92024-01-10 14:31:46 -0500144 }
codecrafters-bot67061582024-01-10 17:35:39 +0000145}