diff --git a/Cargo.toml b/Cargo.toml index 6cb8d295..f6b62232 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,7 @@ members = [ "redis-session", "run-in-thread", "rustls", + "self-shutdown-route", "server-sent-events", "simple-auth-server", "state", diff --git a/self-shutdown-route/Cargo.toml b/self-shutdown-route/Cargo.toml new file mode 100644 index 00000000..4b3a1210 --- /dev/null +++ b/self-shutdown-route/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "self-shutdown-route" +version = "2.0.0" +authors = ["Rob Ede "] +edition = "2018" +description = "Send a request to the server to shut it down" + +[dependencies] +actix-web = "2.0.0" +actix-rt = "1.0.0" +env_logger = "0.7" +futures = "0.3" diff --git a/self-shutdown-route/README.md b/self-shutdown-route/README.md new file mode 100644 index 00000000..709069e2 --- /dev/null +++ b/self-shutdown-route/README.md @@ -0,0 +1,24 @@ +# self-shutdown-route + +> Demonstrates how to shutdown the web server using a route hosted by the server itself using channels. + +This technique can be easily modified to support shutting down the server using other kinds of external events. +Created in response to actix/actix-web#1315. + +## Usage + +### Running The Server + +```bash +cargo run --bin self-shutdown-route + +# Starting 8 workers +# Starting "actix-web-service-127.0.0.1:8080" service on 127.0.0.1:8080 +``` + +### Available Routes + +- [GET /hello](http://localhost:8080/hello) + - Regular hello world route +- [POST /stop](http://localhost:8080/stop) + - Calling this will shutdown the server and exit diff --git a/self-shutdown-route/src/main.rs b/self-shutdown-route/src/main.rs new file mode 100644 index 00000000..a55feb9d --- /dev/null +++ b/self-shutdown-route/src/main.rs @@ -0,0 +1,56 @@ +use std::{sync::mpsc, thread}; + +use futures::executor; + +use actix_web::{get, middleware, post, web, App, HttpResponse, HttpServer}; + +#[get("/hello")] +async fn hello() -> &'static str { + "Hello world!" +} + +#[post("/stop")] +async fn stop(stopper: web::Data>) -> HttpResponse { + // make request that sends message through the Sender + stopper.send(()).unwrap(); + + HttpResponse::NoContent().finish() +} + +#[actix_rt::main] +async fn main() -> std::io::Result<()> { + std::env::set_var("RUST_LOG", "actix_server=debug,actix_web=debug"); + env_logger::init(); + + // create a channel + let (tx, rx) = mpsc::channel::<()>(); + + let bind = "127.0.0.1:8080"; + + // start server as normal but don't .await after .run() yet + let server = HttpServer::new(move || { + // give the server a Sender in .data + let stopper = tx.clone(); + + App::new() + .data(stopper) + .wrap(middleware::Logger::default()) + .service(hello) + .service(stop) + }) + .bind(&bind)? + .run(); + + // clone the Server handle + let srv = server.clone(); + thread::spawn(move || { + // wait for shutdown signal + rx.recv().unwrap(); + + // stop server gracefully + executor::block_on(srv.stop(true)) + }); + + // run server + server.await +}