]> Humopery - fcgisrv.git/commitdiff
Can serve a basic file
authorErik Mackdanz <erikmack@gmail.com>
Wed, 25 Oct 2023 01:12:29 +0000 (20:12 -0500)
committerErik Mackdanz <erikmack@gmail.com>
Wed, 25 Oct 2023 01:12:29 +0000 (20:12 -0500)
Cargo.lock
Cargo.toml
src/main.rs

index de1ecd6f9b2ab70d2addd4c8be0f60ed9d09cdae..b9f9cd7fb9c9d215531ea20dfe1487861602ee0b 100644 (file)
@@ -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"
index 00215010c5ab5cdaa9f1c6719e81d9e1b623f4dd..62fe26ca3eeaea12f2d3d29706c898534870e7a2 100644 (file)
@@ -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"
index 1e21677fa6668890d1078a9c1b60215806e27aad..7a3cb8dce3b563147e961b1367be6fe11b20aab3 100644 (file)
-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);