Hi
I'm currently trying to build a simple static file server as part of an ongoing research project. The only real complexity (I thought) was going to be setting an arbitrary TCP socket option I've introduced into a custom Linux kernel based on 4.18. Until the need to set the socket option became apparent I had been using a simple NodeJS fileserver using Node's built in http
module.
The problem I have with my Rust server is the browser does not receive all data written to the TcpStream
- if I request the same file multiple times I see different amounts of data transferred using Firefox's network tab. This is despite the Content-Length
header being set to the correct value.
My server is largely based on the single threaded web server example in the Rust book and I've included representative code below. I've omitted the various use
statements and a simple function (parse_path
) that converts the received request into a relative filesystem path for brevity.
At this point I can't work out why I'm not seeing the expected results so help is incredibly welcome.
Edit: I forgot to mention - this only happens when triggering a file download of at least a few tens of kilobytes using a browser, smaller files (on the order of 3-5 kilobytes) are fine using a browser and using a command-line tool like curl even the larger files download successfully.
const CUSTOM_TCP_SOCKOPT: c_int = 37;
fn main() -> io::Result {
let server = TcpListener::bind("0.0.0.0:30080")?;
for stream in server.incoming() {
handle_stream(stream?);
}
Ok(())
}
fn handle_stream(mut stream: TcpStream) {
let request_path = parse_path(&mut stream);
let socket = stream.as_raw_fd();
let sock_opt_val: c_int = 1;
unsafe {
libc::setsockopt(
fd,
IPPROTO_TCP,
CUSTOM_TCP_SOCKOPT,
&sock_opt_val as *const _ as *const c_void,
mem::size_of_val(&sock_opt_val).try_into().unwrap(),
).unwrap();
}
let mut response_body = fs::read(&request_path).unwrap_or(vec![]);
let mut response_head = format!(
"HTTP/1.1 200 OK\r\nContent-Type: application/octet-stream\r\nContent-Length: {}\r\n\r\n",
&response_body.len()
);
let response = response_head.into_bytes();
response.append(&mut response_body);
stream.write_all(&response).unwrap();
stream.flush().unwrap();
}