diff --git a/Cargo.toml b/Cargo.toml index 2eb523183..ba48f1c7c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,9 @@ tls = ["native-tls", "tokio-tls"] # openssl alpn = ["openssl", "openssl/v102", "openssl/v110", "tokio-openssl"] +# signals +signal = ["actix/signal"] + [dependencies] log = "0.3" failure = "0.1" diff --git a/examples/signals/Cargo.toml b/examples/signals/Cargo.toml new file mode 100644 index 000000000..d5a6f9235 --- /dev/null +++ b/examples/signals/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "signals" +version = "0.1.0" +authors = ["Nikolay Kim "] + +[[bin]] +name = "server" +path = "src/main.rs" + +[dependencies] +env_logger = "*" +futures = "0.1" +actix = "^0.3.5" + +#actix-web = { git = "https://github.com/actix/actix-web.git" } + +actix-web = { path="../../", features=["signal"] } diff --git a/examples/signals/README.md b/examples/signals/README.md new file mode 100644 index 000000000..0d2597fed --- /dev/null +++ b/examples/signals/README.md @@ -0,0 +1,4 @@ + +# Signals + +This example shows how to handle unix signals and properly stop http server diff --git a/examples/signals/src/main.rs b/examples/signals/src/main.rs new file mode 100644 index 000000000..500af1a79 --- /dev/null +++ b/examples/signals/src/main.rs @@ -0,0 +1,30 @@ +extern crate actix; +extern crate actix_web; +extern crate futures; +extern crate env_logger; + +use actix_web::*; +use actix::Arbiter; +use actix::actors::signal::{ProcessSignals, Subscribe}; + + +fn main() { + ::std::env::set_var("RUST_LOG", "actix_web=info"); + let _ = env_logger::init(); + let sys = actix::System::new("signals-example"); + + let addr = HttpServer::new(|| { + Application::new() + // enable logger + .middleware(middleware::Logger::default()) + .resource("/", |r| r.h(httpcodes::HTTPOk))}) + .bind("127.0.0.1:8080").unwrap() + .start(); + + // Subscribe to unix signals + let signals = Arbiter::system_registry().get::(); + signals.send(Subscribe(addr.subscriber())); + + println!("Started http server: 127.0.0.1:8080"); + let _ = sys.run(); +} diff --git a/src/server.rs b/src/server.rs index cf7276259..425f7eca9 100644 --- a/src/server.rs +++ b/src/server.rs @@ -33,6 +33,9 @@ use openssl::pkcs12::ParsedPkcs12; #[cfg(feature="alpn")] use tokio_openssl::{SslStream, SslAcceptorExt}; +#[cfg(feature="signal")] +use actix::actors::signal; + use helpers; use channel::{HttpChannel, HttpHandler, IntoHttpHandler}; @@ -108,7 +111,7 @@ pub struct HttpServer workers: Vec>>, sockets: HashMap, accept: Vec<(mio::SetReadiness, sync_mpsc::Sender)>, - spawned: bool, + exit: bool, } unsafe impl Sync for HttpServer where H: 'static {} @@ -152,7 +155,7 @@ impl HttpServer workers: Vec::new(), sockets: HashMap::new(), accept: Vec::new(), - spawned: false, + exit: false, } } @@ -202,6 +205,16 @@ impl HttpServer self } + #[cfg(feature="signal")] + /// Send `SystemExit` message to actix system + /// + /// `SystemExit` message stops currently running system arbiter and all + /// nested arbiters. + pub fn system_exit(mut self) -> Self { + self.exit = true; + self + } + /// Get addresses of bound sockets. pub fn addrs(&self) -> Vec { self.sockets.keys().cloned().collect() @@ -341,7 +354,7 @@ impl HttpServer /// } /// ``` pub fn spawn(mut self) -> SyncAddress { - self.spawned = true; + self.exit = true; let (tx, rx) = sync_mpsc::channel(); thread::spawn(move || { @@ -475,6 +488,41 @@ impl HttpServer } } +#[cfg(feature="signal")] +/// Unix Signals support +/// Handle `SIGINT`, `SIGTERM`, `SIGQUIT` signals and send `SystemExit(0)` +/// message to `System` actor. +impl Handler for HttpServer + where T: AsyncRead + AsyncWrite + 'static, + H: HttpHandler + 'static, + U: 'static, + A: 'static, +{ + fn handle(&mut self, msg: signal::Signal, ctx: &mut Context) + -> Response + { + match msg.0 { + signal::SignalType::Int => { + info!("SIGINT received, exiting"); + self.exit = true; + Handler::::handle(self, StopServer{graceful: false}, ctx); + } + signal::SignalType::Term => { + info!("SIGTERM received, stopping"); + self.exit = true; + Handler::::handle(self, StopServer{graceful: true}, ctx); + } + signal::SignalType::Quit => { + info!("SIGQUIT received, exiting"); + self.exit = true; + Handler::::handle(self, StopServer{graceful: false}, ctx); + } + _ => (), + }; + Self::empty() + } +} + #[derive(Message)] struct IoStream { io: T, @@ -522,7 +570,9 @@ pub struct ResumeServer; /// /// If server starts with `spawn()` method, then spawned thread get terminated. #[derive(Message)] -pub struct StopServer; +pub struct StopServer { + pub graceful: bool +} impl Handler for HttpServer where T: AsyncRead + AsyncWrite + 'static, @@ -571,7 +621,7 @@ impl Handler for HttpServer ctx.stop(); // we need to stop system if server was spawned - if self.spawned { + if self.exit { Arbiter::system().send(msgs::SystemExit(0)) } Self::empty()