70 %
Chris Biscardi

Unsafely terminating tide servers in Rust by exiting from an http handler

Tide doesn't allow programmatic graceful shutdown or remove unix sockets either. If we add this handler, we can both ensure that we remove the socket and also exit with a successful error code by sending a request to /terminate.

rust
app.at("/terminate").get(|_| async {
let _result =
fs::remove_file("/var/tmp/toaster.sock");
process::exit(0);
#[allow(unreachable_code)]
Ok("done")
});

example curl command:

shell
curl --unix-socket /var/tmp/toaster.sock http://localhost/terminate

The handler in this case was added to this tide server.

rust
use async_std::task;
use futures::try_join;
use std::fs;
use std::io::{self, Write};
use std::process::{self, Command};
#[async_std::main]
async fn main() -> Result<(), io::Error> {
let mut app = tide::new();
let sock =
format!("http+unix://{}", "/var/tmp/toaster.sock");
app.at("/").get(|_| async { Ok("Hello, world!") });
let server = app.listen(sock);
// node script creation
let child = task::spawn(async {
println!("child");
let cmd =
Command::new("node").arg("index.js").output();
io::stdout()
.write_all(&cmd.unwrap().stdout)
.unwrap();
Ok(())
});
// results of both futures
// result is Result<((),()), Error>
let result = try_join!(server, child);
match result {
Err(e) => Err(e),
Ok(_) => Ok(()),
}
}

Alternatives

We could have also sent a sigterm to ourselves using the nix crate which results in a fatal exit code 130.

rust
use nix::sys::signal::{self, Signal};
use nix::unistd::{getpid, Pid};
...
signal::kill(getpid(), Signal::SIGINT).unwrap();

We could also have panic'ed which would call destructors, but this results in a nonzero exit code.


Rocket supports shutdown but it doesn't support unix sockets.