-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<dyn Error>> {
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);