Add further impl for mainloop

This commit is contained in:
2026-08-20 00:47:52 +08:00
parent 137028ac03
commit eaf76dfe29
5 changed files with 102 additions and 64 deletions
+9
View File
@@ -0,0 +1,9 @@
{
"recommendations": [
"rust-lang.rust-analyzer",
"vadimcn.vscode-lldb",
"tamasfe.even-better-toml",
"vscodevim.vim",
"streetsidesoftware.code-spell-checker"
]
}
+7
View File
@@ -0,0 +1,7 @@
{
"cSpell.words": [
"color",
"conn",
"resp"
]
}
+54 -46
View File
@@ -1,63 +1,24 @@
mod request; mod request;
mod response; mod response;
use request::{Header, ReqLine}; use color_eyre::eyre;
use request::{Request, Header, ReqLine};
use response::{Response, Version, Status}; use response::{Response, Version, Status};
use std::io::{BufReader, BufWriter, Read, Write}; use std::io::{BufReader, BufWriter, ErrorKind, Read, Write};
use std::net::TcpStream; use std::net::TcpStream;
use std::fs::File; use std::fs::File;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use log::{debug, info}; use log::{debug, info};
pub fn handle_connection(conn: TcpStream) -> color_eyre::Result<()> { pub fn handle_connection(conn: TcpStream) -> eyre::Result<()> {
info!("Reveiced new connection from {}", conn.peer_addr()?); info!("Received new connection from {}", conn.peer_addr()?);
let reader = BufReader::new(conn.try_clone()?); let mut reader = BufReader::new(conn.try_clone()?);
let mut writer = BufWriter::new(conn); let mut writer = BufWriter::new(conn);
let mut req = Vec::<String>::with_capacity(0x10); let resp = handle_request(reader.by_ref());
loop {
let mut buffer = String::with_capacity(0x100);
for byte in reader.get_ref().bytes() {
buffer.push(byte? as char);
if buffer.ends_with("\r\n") {
break;
}
}
if buffer.trim().is_empty() {
break;
}
buffer.shrink_to_fit();
req.push(buffer);
}
let req_line: ReqLine = req
.get(0)
.ok_or(std::io::Error::other("Missing request line."))?
.trim()
.parse()?;
let headers: std::io::Result<Vec<Header>> =
req.iter().skip(1).map(|line| line.parse()).collect();
debug!(
"{} {} {}",
req_line.method, req_line.route, req_line.version
);
for line in headers? {
debug!("{}: {}", line.key, line.value);
}
let file_path: Option<PathBuf> = req_line.route.starts_with("/").then(|| {
req_line.route.split("/").map(|s| Path::new(a)).collect()
});
let file_path = file_path.map(|p_buf| p_buf.canonicalize());
let file = file_path.map(|f_path| {
f_path.map(|f_path| File::open(f_path))
});
let count: Result<usize, _> = resp.as_str_list().into_iter() let count: Result<usize, _> = resp.as_str_list().into_iter()
.map(|s| writer.write(s.as_bytes())) .map(|s| writer.write(s.as_bytes()))
@@ -68,3 +29,50 @@ pub fn handle_connection(conn: TcpStream) -> color_eyre::Result<()> {
Ok(()) Ok(())
} }
fn handle_request(reader: &mut BufReader<TcpStream>) -> Response {
let mut req = Vec::<String>::with_capacity(0x10);
loop {
let mut buffer = String::with_capacity(0x100);
for byte in reader.bytes() {
match byte {
Err(err) if err.kind() == ErrorKind::Interrupted => continue,
Ok(byte) => {
buffer.push(byte as char);
if buffer.ends_with("\r\n") {
break;
}
}
Err(_) => return Response::invalid_request()
}
}
if buffer.trim().is_empty() {
break;
}
buffer.shrink_to_fit();
req.push(buffer);
}
let req_line = if let Some(req_line) = req.get(0) {
if let Ok(req_line) = req_line.parse::<ReqLine>() {
req_line
} else {
return Response::invalid_request();
}
} else {
return Response::invalid_request();
};
let mut headers: Vec<Header> = Vec::with_capacity(req.len() - 1);
for line in req.iter().skip(1) {
if let Ok(header) = line.parse() {
headers.push(header);
} else {
return Response::invalid_request();
}
}
Response::valid_request()
}
+13 -7
View File
@@ -1,10 +1,10 @@
use std::str::FromStr; use std::str::FromStr;
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct ReqLine { pub struct ReqLine {
pub(crate) method: String, pub method: String,
pub(crate) route: String, pub route: String,
pub(crate) version: String, pub version: String,
} }
impl FromStr for ReqLine { impl FromStr for ReqLine {
@@ -26,9 +26,9 @@ impl FromStr for ReqLine {
} }
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct Header { pub struct Header {
pub(crate) key: String, pub key: String,
pub(crate) value: String, pub value: String,
} }
impl FromStr for Header { impl FromStr for Header {
@@ -44,3 +44,9 @@ impl FromStr for Header {
}) })
} }
} }
#[derive(Debug)]
pub struct Request {
pub req_line: ReqLine,
pub headers: Vec<Header>,
}
+19 -11
View File
@@ -1,11 +1,11 @@
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub(crate) enum Version { pub enum Version {
#[default] #[default]
V11, V11,
} }
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub(crate) enum Status { pub enum Status {
#[default] #[default]
OK, OK,
NotFound, NotFound,
@@ -37,26 +37,26 @@ impl AsStaticStr for Status {
} }
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub(crate) struct Header { pub struct Header {
pub(crate) key: String, pub key: String,
pub(crate) value: String, pub value: String,
} }
impl Header { impl Header {
pub(crate) fn as_str(&self) -> (&str, &str) { pub fn as_str(&self) -> (&str, &str) {
(self.key.as_str(), self.value.as_str()) (self.key.as_str(), self.value.as_str())
} }
} }
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub(crate) struct Response { pub struct Response {
pub(crate) version: Version, pub version: Version,
pub(crate) status: Status, pub status: Status,
pub(crate) headers: Vec<Header>, pub headers: Vec<Header>,
} }
impl Response { impl Response {
pub(crate) fn as_str_list(&self) -> Vec<&str> { pub fn as_str_list(&self) -> Vec<&str> {
let mut ret = vec!( let mut ret = vec!(
self.version.as_str(), self.version.as_str(),
" ", " ",
@@ -76,4 +76,12 @@ impl Response {
ret.push("\r\n"); ret.push("\r\n");
ret ret
} }
pub fn valid_request() -> Self {
Self { ..Default::default() }
}
pub fn invalid_request() -> Self {
Self { version: Version::V11, status: Status::BadRequest, ..Default::default() }
}
} }