use log::{debug,info,error};
use mime;
use new_mime_guess::from_path;
-use simplelog::{SimpleLogger,LevelFilter,Config};
+use regex::Regex;
+use simplelog::{SimpleLogger,LevelFilter};
+use std::collections::BTreeMap;
use std::error::Error;
+use std::env;
use std::fs::{File,metadata,remove_file};
use std::io::{Read,Write};
use std::os::unix::net::UnixListener;
const BUFSZ: usize = 4096;
+// Write a validation 4XX or 5XX back to the user
macro_rules! write_err_resp {
($req:expr,$status:expr,$statusmess:expr,$body:expr) => {
if let Err(e) =
}
}
-fn handle_req(mut req: Request) {
+fn handle_req(conf: &Config, mut req: Request) {
// for (k,v) in req.params() {
- // error!("key {} has value {}",k,v);
+ // info!("key {} has value {}",k,v);
// }
+ let Some(docroot) = req.param("DOCUMENT_ROOT") else {
+ write_err_resp!(req,"400","Bad Request","No document root was set");
+ return;
+ };
+
// require a method
let Some(method) = req.param("REQUEST_METHOD") else {
write_err_resp!(req,"405","Method not allowed","No method");
// 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();
+ let mut mimestr = guessed_mime.essence_str();
+
+ // check for a mime type override in config
+ let short_filename = &filename[docroot.len()..];
+ for idx in conf.mime_matches.keys() {
+ let (conf_mime_match,conf_mime_value) = &conf.mime_matches[idx];
+ if conf_mime_match.is_match(short_filename) {
+ mimestr = conf_mime_value;
+ }
+ }
// read std::fs::metadata including size
let Ok(metadata) = metadata(filename.clone()) else {
}
+struct Config {
+ socket_path: String,
+ mime_matches: BTreeMap<u8,(Regex,String)>,
+ // cors_origin_matches: BTreeMap<u8,(Regex,String)>,
+}
+
+impl Config {
+ fn init() -> Result<Config,Box<dyn Error>> {
+ let mut socket_path = "/tmp/fcgisocket".to_string();
+ let mut mime_matches = BTreeMap::new();
+ // let mut cors_origin_matches = BTreeMap::new();
+
+ for (k,v) in env::vars() {
+ if k == "FCGI_SRV_SOCKET_PATH" {
+ info!("config: found socket_path {}",v);
+ socket_path = v;
+ } else if k.starts_with("FCGI_SRV_MIME_MATCH_") {
+ let index = u8::from_str_radix(&k[20..],10)?;
+ info!("config: found mime match index {}",index);
+ let re = Regex::new(&v)?;
+ if let Ok(mtype) = env::var(format!("FCGI_SRV_MIME_TYPE_{}",index)) {
+ mime_matches.insert(index,(re,mtype));
+ };
+ }
+ }
+
+ let conf = Config {
+ socket_path: socket_path,
+ mime_matches: mime_matches,
+ // cors_origin_matches: cors_origin_matches,
+ };
+
+ Ok(conf)
+ }
+}
+
+
fn main() -> Result<(),Box<dyn Error>> {
// init logging
- SimpleLogger::init(LevelFilter::Info,Config::default())?;
+ SimpleLogger::init(LevelFilter::Info,simplelog::Config::default())?;
- let socketpath = "/tmp/fcgisocket";
+ let conf = Config::init()?;
// handle Ctrl+C
- set_command_line("/bin/rm",["-f",socketpath],[("FOO","")]);
+ set_command_line("/bin/rm",["-f",&conf.socket_path],[("FOO","")]);
let _ = set_handler(&[Signal::SIGINT],false);
// remove socket file
- let _ = remove_file(socketpath);
+ let _ = remove_file(&conf.socket_path);
info!("Entering main loop");
- let listener = UnixListener::bind(socketpath).unwrap();
- run_raw(handle_req,listener.as_raw_fd());
+ let listener = UnixListener::bind(&conf.socket_path).unwrap();
+ run_raw(
+ move |req| {handle_req(&conf,req)},
+ listener.as_raw_fd());
// Nothing after this is called