update main handler, add comments

This commit is contained in:
yggverse 2025-02-23 23:27:21 +02:00
parent 156a2c40dc
commit 31e33bca28

View file

@ -59,45 +59,69 @@ fn handle(
match connection { match connection {
Ok(mut stream) => { Ok(mut stream) => {
// server should work with large files without memory overload, // server should work with large files without memory overload,
// because of that incoming data read partially, using chunks; // because of that, incoming data read partially, using chunks;
// collect header bytes first to route the request // collect header bytes first to route the request by its type.
let mut header = Vec::with_capacity(HEADER_MAX_LEN); let mut header_buffer = Vec::with_capacity(HEADER_MAX_LEN);
loop { loop {
let mut buffer = vec![0]; let mut header_chunk = vec![0];
match stream.read(&mut buffer) { match stream.read(&mut header_chunk) {
Ok(0) => println!("[{}] [warning] [{peer}] Peer closed connection.", now()), Ok(0) => println!("[{}] [warning] [{peer}] Peer closed connection.", now()),
Ok(_) => { Ok(l) => {
header.push(buffer[0]); // validate header buffer, break on its length reached protocol limits
// header bytes collected if header_buffer.len() + l > HEADER_MAX_LEN {
if header.len() > HEADER_MAX_LEN || buffer[0] == b'\n' { return send(
// detect controller for the request by parse its header bytes
return match Request::from_bytes(&header) {
Ok(request) => match request {
Request::Gemini(this) => {
gemini(this, &argument, &peer, &mut stream)
}
Request::Titan(this) => {
titan(this, &argument, &peer, &mut stream)
}
},
Err(e) => send(
&response::failure::permanent::BadRequest { &response::failure::permanent::BadRequest {
message: Some("Invalid request".to_string()), message: Some("Bad request".to_string()),
} }
.into_bytes(), .into_bytes(),
&mut stream, &mut stream,
|result| match result { |result| match result {
Ok(()) => println!("[{}] [warning] [{peer}] {e}", now()), Ok(()) => {
println!("[{}] [warning] [{peer}] Bad request", now())
}
Err(e) => println!("[{}] [error] [{peer}] {e}", now()), Err(e) => println!("[{}] [error] [{peer}] {e}", now()),
}, },
), );
}; }
// take chunk bytes at this point
header_buffer.extend(header_chunk);
// ending header byte received
if header_buffer.last().is_some_and(|&b| b == b'\n') {
// header bytes contain valid Gemini **request**
if let Ok(request) = request::Gemini::from_bytes(&header_buffer) {
return gemini(request, &argument, &peer, &mut stream);
}
// header bytes contain valid Titan **meta**
// * yet, no data has been received to parse the entire Titan request,
// the data will be handled separately upon success, in chunks.
if let Ok(meta) = request::titan::Meta::from_bytes(&header_buffer) {
return titan(meta, &argument, &peer, &mut stream);
}
// header bytes received but yet could not be parsed,
// complete with request failure
return send(
&response::failure::permanent::BadRequest {
message: Some("Bad request".to_string()),
}
.into_bytes(),
&mut stream,
|result| match result {
Ok(()) => {
println!("[{}] [warning] [{peer}] Bad request", now())
}
Err(e) => println!("[{}] [error] [{peer}] {e}", now()),
},
);
} }
} }
Err(e) => { Err(e) => {
return send( return send(
&response::failure::permanent::BadRequest { &response::failure::permanent::BadRequest {
message: Some("Invalid request".to_string()), message: Some("Bad request".to_string()),
} }
.into_bytes(), .into_bytes(),
&mut stream, &mut stream,
@ -174,16 +198,16 @@ fn gemini(
} }
fn titan( fn titan(
titan: titanite::request::Titan, meta: titanite::request::titan::Meta,
argument: &Argument, argument: &Argument,
peer: &SocketAddr, peer: &SocketAddr,
stream: &mut TlsStream<TcpStream>, stream: &mut TlsStream<TcpStream>,
) { ) {
use titanite::*; use titanite::*;
println!("[{}] [info] [{peer}] Request: {}", now(), titan.meta.url); println!("[{}] [info] [{peer}] Request: {}", now(), meta.url);
// require content type for application, // require content type for application,
// even MIME value is optional by Titan specification // even MIME value is optional by Titan specification
let mime = match titan.meta.mime { let mime = match meta.mime {
Some(mime) => mime, Some(mime) => mime,
None => { None => {
const MESSAGE: &str = "Content type is required"; const MESSAGE: &str = "Content type is required";
@ -236,7 +260,7 @@ fn titan(
} }
// validate client-side limits (from header) // validate client-side limits (from header)
if total > titan.meta.size { if total > meta.size {
if let Err(e) = tmp.delete() { if let Err(e) = tmp.delete() {
println!("[{}] [error] [{peer}] {e}", now()); println!("[{}] [error] [{peer}] {e}", now());
} }
@ -278,12 +302,12 @@ fn titan(
} }
// just to make sure // just to make sure
if total > titan.meta.size { if total > meta.size {
panic!() panic!()
} }
// all data received // all data received
if titan.meta.size == total { if meta.size == total {
return match tmp.commit() { return match tmp.commit() {
Ok(pmt) => send( Ok(pmt) => send(
&response::redirect::Permanent { &response::redirect::Permanent {