use std::{ collections::HashMap, io::{prelude::*, BufReader}, net::TcpStream, }; pub struct URL { raw: String, pub path: String, pub query: HashMap, } impl URL { pub fn new(url: String) -> Option { let raw = url; let mut split = raw.split('?'); // Query string parsing let mut query = HashMap::new(); let path = split.next()?.to_string(); let query_string = split.next(); for pair in query_string.unwrap_or("").split('&') { let mut key_value = pair.split('='); let key = match key_value.next() { Some(key) => key.to_string(), None => continue, }; let value = key_value.next().unwrap_or("").to_string(); query.insert(key, value); } // Drop trailing slash but not for root path let path = if path.ends_with("/") && path.len() > 1 { path[..path.len() - 1].to_string() } else { path }; Some(URL { path, query, raw }) } } pub struct Request { stream: TcpStream, pub method: String, pub url: URL, } pub enum RequestStatus { Ok(Request), MalformedHTTP, } impl<'a> Request { pub fn parse_stream(stream: TcpStream) -> RequestStatus { let reader = BufReader::new(&stream); let mut lines = reader.lines(); let request_line = match lines.next() { Some(Ok(line)) => line, _ => return RequestStatus::MalformedHTTP, }; let request_line_parts = request_line.split(" ").collect::>(); if request_line_parts.len() != 3 { return RequestStatus::MalformedHTTP; } let (method, url_str) = ( request_line_parts[0].to_string().clone(), request_line_parts[1].to_string().clone(), ); let url = match URL::new(url_str) { Some(path) => path, None => return RequestStatus::MalformedHTTP, }; RequestStatus::Ok(Request { stream, method, url }) } }