From: Erik Mackdanz Date: Wed, 25 Oct 2023 01:12:29 +0000 (-0500) Subject: Can serve a basic file X-Git-Url: https://git.humopery.space/?a=commitdiff_plain;h=f82f5ce681420d949380973e34d8ad071620da3f;p=fcgisrv.git Can serve a basic file --- diff --git a/Cargo.lock b/Cargo.lock index de1ecd6..b9f9cd7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -37,6 +37,8 @@ name = "fcgisrv" version = "0.1.0" dependencies = [ "fastcgi", + "mime", + "new_mime_guess", "signal", ] @@ -46,6 +48,22 @@ version = "0.2.149" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "new_mime_guess" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2d684d1b59e0dc07b37e2203ef576987473288f530082512aff850585c61b1f" +dependencies = [ + "mime", + "unicase", +] + [[package]] name = "nix" version = "0.14.1" @@ -69,6 +87,21 @@ dependencies = [ "nix", ] +[[package]] +name = "unicase" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" +dependencies = [ + "version_check", +] + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + [[package]] name = "void" version = "1.0.2" diff --git a/Cargo.toml b/Cargo.toml index 0021501..62fe26c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,4 +7,6 @@ edition = "2021" [dependencies] fastcgi = "1.0.0" +mime = "0.3.17" +new_mime_guess = "4.0.1" signal = "0.7.0" diff --git a/src/main.rs b/src/main.rs index 1e21677..7a3cb8d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,32 +1,107 @@ -extern crate fastcgi; +use fastcgi::{run_raw,Request}; +use mime; +use new_mime_guess::from_path; use std::error::Error; -use std::fs::remove_file; -use std::io::Write; +use std::fs::{File,metadata,remove_file}; +use std::io::{Read,Write}; use std::os::unix::net::UnixListener; use std::os::fd::AsRawFd; use signal::exec_handler::{set_command_line,set_handler}; use signal::Signal; +const BUFSZ: usize = 4096; + +fn handle_req(mut req: Request) { + // for (k,v) in req.params() { + // println!("key {} has value {}",k,v); + // } + + // require a method + let Some(method) = req.param("REQUEST_METHOD") else { + if let Err(e) = write!(&mut req.stdout(), "Status: 405 Method Not Allowed\nContent-Type: text/plain\n\nNo method") { + println!("Error was {}",e); + } + return; + }; + // require method is GET + if method != "GET" { + if let Err(e) = write!(&mut req.stdout(), "Status: 405 Method Not Allowed\nContent-Type: text/plain\n\nBad method") { + println!("Error was {}",e); + } + return; + }; + // require a filename + let Some(filename) = req.param("SCRIPT_FILENAME") else { + if let Err(e) = write!(&mut req.stdout(), "Status: 400 Bad Request\nContent-Type: text/plain\n\nNo filename sent") { + println!("Error was {}",e); + } + return; + }; + + // determine content-type + let guess = from_path(filename.clone()); + let guessed_mime = guess.first().unwrap_or(mime::TEXT_PLAIN); + let mimestr = guessed_mime.essence_str(); + + // read std::fs::metadata including size + let Ok(metadata) = metadata(filename.clone()) else { + if let Err(e) = write!(&mut req.stdout(), "Status: 400 Bad Request\nContent-Type: text/plain\n\nfile metadata can't be read") { + println!("Error was {}",e); + } + return; + }; + + println!("len: {}, perms: {:?}, mime: {}",metadata.len(),metadata.permissions(),mimestr); + + // std::fs::read to one large buffer + let mut buf = [0; BUFSZ]; + + let Ok(mut f) = File::open(filename) else { + if let Err(e) = write!(&mut req.stdout(), "Status: 500 Server Error\nContent-Type: text/plain\n\ncan't open file") { + println!("Error was {}",e); + } + return; + }; + + let mut is_first_write = true; + + while let Ok(n) = f.read(&mut buf) { + if is_first_write { + if let Err(e) = write!(&mut req.stdout(), "Content-Type: {}\nContent-Length: {}\n\n", + mimestr,metadata.len()) { + println!("Error was {}",e); + } + } + is_first_write = false; + + if n == 0 { + break; + } + + match req.stdout().write(&buf[0..n]) { + Ok(x) => { + println!("wrote {} bytes",x); + }, + Err(e) => { + println!("Error was {}",e); + } + } + } + +} + fn main() -> Result<(),Box> { let socketpath = "/tmp/fcgisocket"; + // handle Ctrl+C set_command_line("/bin/rm",["-f",socketpath],[("FOO","")]); let _ = set_handler(&[Signal::SIGINT],false); let _ = remove_file(socketpath); let listener = UnixListener::bind(socketpath).unwrap(); - fastcgi::run_raw(|mut req| { - for (k,v) in req.params() { - println!("key {} has value {}",k,v); - } - - if let Err(e) = write!(&mut req.stdout(), "Status: 404 Not Found\nContent-Type: text/plain\n\nHello, world!") { - println!("Error was {}",e); - } - - }, listener.as_raw_fd()); + run_raw(handle_req,listener.as_raw_fd()); let _ = remove_file(socketpath);