http server
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing

This commit is contained in:
Jake Hillion 2022-05-21 17:18:28 +01:00
parent b7abf4fa07
commit 8f0b1a8083
5 changed files with 167 additions and 4 deletions

7
Cargo.lock generated
View File

@ -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"

View File

@ -25,6 +25,9 @@ tempfile = "3.3"
[dev-dependencies]
criterion = "0.3"
# examples/tls
httparse = "1"
[[bench]]
name = "clone3"
harness = false

61
examples/tls/http.rs Normal file
View File

@ -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
}

View File

@ -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));
}

View File

@ -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"
}
}
]
}
}
}