diff --git a/Cargo.lock b/Cargo.lock index 1f579e9..091c3f0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -111,6 +111,7 @@ dependencies = [ "criterion", "env_logger", "exitcode", + "httparse", "ipnetwork", "libc", "log", @@ -289,6 +290,12 @@ dependencies = [ "libc", ] +[[package]] +name = "httparse" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "496ce29bb5a52785b44e0f7ca2847ae0bb839c9bd28f69acac9b99d461c0c04c" + [[package]] name = "humantime" version = "2.1.0" diff --git a/Cargo.toml b/Cargo.toml index 978c892..97634c3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,9 @@ tempfile = "3.3" [dev-dependencies] criterion = "0.3" +# examples/tls +httparse = "1" + [[bench]] name = "clone3" harness = false diff --git a/examples/tls/http.rs b/examples/tls/http.rs new file mode 100644 index 0000000..0688f37 --- /dev/null +++ b/examples/tls/http.rs @@ -0,0 +1,61 @@ +use std::fs; +use std::io::{Read, Write}; +use std::net::TcpStream; +use std::path::PathBuf; + +pub(super) fn handler(mut stream: TcpStream) -> i32 { + println!("entered http handler"); + + let mut buf = Vec::new(); + loop { + let buf_len = buf.len(); + buf.resize_with(buf_len + 1024, Default::default); + + if stream.read(&mut buf[buf_len..]).unwrap() == 0 { + break; + } + + let mut headers = [httparse::EMPTY_HEADER; 64]; + let mut req = httparse::Request::new(&mut headers); + let result = req.parse(&buf).unwrap(); + + if result.is_partial() { + continue; + } + + let filename = if req.method != Some("GET") { + None + } else { + req.path + }; + + let status_line = if filename.is_some() { + "HTTP/1.1 200 OK" + } else { + "HTTP/1.1 404 NOT FOUND" + }; + + let contents = if let Some(filename) = filename { + fs::read_to_string( + PathBuf::from("/var/www/html/") + .join(filename.strip_prefix('/').unwrap_or(filename)), + ) + .unwrap() + } else { + "content not found\n".to_string() + }; + + let response_header = format!( + "{}\r\nContent-Length: {}\r\n\r\n", + status_line, + contents.len(), + ); + + stream.write_all(response_header.as_bytes()).unwrap(); + stream.write_all(contents.as_bytes()).unwrap(); + + break; + } + + exitcode::OK +} diff --git a/examples/tls/main.rs b/examples/tls/main.rs index 124bbd5..3b77a55 100644 --- a/examples/tls/main.rs +++ b/examples/tls/main.rs @@ -1,4 +1,7 @@ -use std::net::TcpListener; +mod http; + +use std::fs::File; +use std::net::{TcpListener, TcpStream}; fn main() { let mut args = std::env::args(); @@ -9,6 +12,7 @@ fn main() { match entrypoint { Some(s) => match s.as_str() { "connection_listener" => connection_listener_entrypoint(), + "http_handler" => http_handler_entrypoint(), _ => unimplemented!(), }, @@ -26,6 +30,13 @@ fn connection_listener_entrypoint() { let _bin = args.next(); let _entrypoint = args.next(); + let http_handler_trigger = args.next(); + let http_handler_trigger: RawFd = http_handler_trigger + .expect("request handler required") + .parse() + .expect("tcp listener should be a file descriptor"); + let http_handler_trigger = unsafe { File::from_raw_fd(http_handler_trigger) }; + let tcp_listener = args.next(); let tcp_listener: RawFd = tcp_listener .expect("tcp listener required") @@ -34,12 +45,12 @@ fn connection_listener_entrypoint() { let tcp_listener = unsafe { TcpListener::from_raw_fd(tcp_listener) }; // actual function body - fn connection_listener(tcp_listener: TcpListener) -> i32 { + fn connection_listener(http_handler_trigger: File, tcp_listener: TcpListener) -> i32 { println!("connection_listener entered"); // handle incoming connections for stream in tcp_listener.incoming() { - let _stream = match stream { + let stream = match stream { Ok(s) => s, Err(e) => { println!("connection listener: error: {}", e); @@ -48,11 +59,51 @@ fn connection_listener_entrypoint() { }; println!("received a new connection"); + http_handler(&http_handler_trigger, stream); } exitcode::OK } // run function - std::process::exit(connection_listener(tcp_listener)); + std::process::exit(connection_listener(http_handler_trigger, tcp_listener)); +} + +fn http_handler(trigger_socket: &File, stream: TcpStream) { + // imports + use nix::sys::socket::{sendmsg, ControlMessage, MsgFlags}; + use std::os::unix::io::AsRawFd; + + // send file descriptor(s) + let sockfd = trigger_socket.as_raw_fd(); + let fds = [stream.as_raw_fd()]; + + sendmsg::<()>( + sockfd, + &[], + &[ControlMessage::ScmRights(&fds)], + MsgFlags::empty(), + None, + ) + .unwrap(); +} + +fn http_handler_entrypoint() { + // imports + use std::os::unix::io::{FromRawFd, RawFd}; + + // argument parsing + let mut args = std::env::args(); + + let _bin = args.next(); + let _entrypoint = args.next(); + + let stream = args.next(); + let stream: RawFd = stream + .expect("request stream required") + .parse() + .expect("request stream should be a file descriptor"); + let stream = unsafe { TcpStream::from_raw_fd(stream) }; + + std::process::exit(http::handler(stream)); } diff --git a/examples/tls/spec.json b/examples/tls/spec.json index c2ce4ca..ece8101 100644 --- a/examples/tls/spec.json +++ b/examples/tls/spec.json @@ -4,6 +4,11 @@ "args": [ "BinaryName", "Entrypoint", + { + "FileSocket": { + "Tx": "http" + } + }, { "TcpListener": { "addr": "0.0.0.0:8443" @@ -30,6 +35,42 @@ } } ] + }, + "http_handler": { + "trigger": { + "FileSocket": "http" + }, + "args": [ + "BinaryName", + "Entrypoint", + "Trigger" + ], + "environment": [ + { + "Filesystem": { + "host_path": "/var/www/html", + "environment_path": "/var/www/html" + } + }, + { + "Filesystem": { + "host_path": "/lib/x86_64-linux-gnu/libgcc_s.so.1", + "environment_path": "/lib/libgcc_s.so.1" + } + }, + { + "Filesystem": { + "host_path": "/lib/x86_64-linux-gnu/libc.so.6", + "environment_path": "/lib/libc.so.6" + } + }, + { + "Filesystem": { + "host_path": "/lib64/ld-linux-x86-64.so.2", + "environment_path": "/lib64/ld-linux-x86-64.so.2" + } + } + ] } } } \ No newline at end of file