socktop-webterm/src/server.rs

91 lines
2.9 KiB
Rust
Raw Normal View History

2019-07-02 02:33:49 +00:00
#[macro_use]
extern crate lazy_static;
2019-03-29 13:48:31 +00:00
use actix_files;
use actix_web::{App, HttpServer};
2019-07-02 02:33:49 +00:00
use structopt::StructOpt;
2019-03-29 13:48:31 +00:00
use webterm::WebTermExt;
use std::net::TcpListener;
2019-03-29 15:32:35 +00:00
use std::process::Command;
2019-07-02 02:33:49 +00:00
#[derive(StructOpt, Debug)]
#[structopt(name = "webterm-server")]
struct Opt {
/// The port to listen on
#[structopt(short, long, default_value = "8082")]
2019-07-02 02:33:49 +00:00
port: u16,
/// The host or IP to listen on
#[structopt(short, long, default_value = "localhost")]
host: String,
/// The command to execute
#[structopt(short, long, default_value = "/bin/sh")]
command: String,
}
lazy_static! {
static ref OPT: Opt = Opt::from_args();
}
2019-03-29 13:48:31 +00:00
fn main() {
pretty_env_logger::init();
// Normalize common hostnames that sometimes resolve to IPv6-only addresses
// which can cause platform-specific bind failures. Mapping `localhost` to
// 127.0.0.1 makes behavior predictable on systems where `::1` would otherwise
// be selected.
let host = if OPT.host == "localhost" {
"127.0.0.1".to_string()
} else {
OPT.host.clone()
};
let bind_addr = format!("{}:{}", host, OPT.port);
println!("Starting webterm server on http://{}", bind_addr);
// Single factory closure variable that we reuse for HttpServer::new.
// The closure does not capture any stack variables (it references the static
// `OPT`), so it can act as a simple, repeated factory for the server.
let factory = || {
2019-03-29 13:48:31 +00:00
App::new()
.service(actix_files::Files::new("/assets", "./static"))
.service(actix_files::Files::new("/static", "./node_modules"))
2019-03-29 15:32:35 +00:00
.webterm_socket("/websocket", |_req| {
// Use the static OPT inside the handler; this does not make the
// outer `factory` closure capture stack variables, so factory
// remains a zero-capture closure (a function item/type).
2019-07-02 02:33:49 +00:00
let mut cmd = Command::new(OPT.command.clone());
2019-03-29 15:32:35 +00:00
cmd.env("TERM", "xterm");
cmd
})
.webterm_ui("/", "/websocket", "/static")
};
// Bind a std::net::TcpListener ourselves and hand it to actix via `listen`.
// This avoids actix's address parser producing EINVAL on some platforms.
let listener = match TcpListener::bind(&bind_addr) {
Ok(l) => l,
Err(e) => {
eprintln!("Failed to bind TcpListener to {}: {}", bind_addr, e);
eprintln!("Try `--host 0.0.0.0` or `--host 127.0.0.1` to bind explicitly.");
std::process::exit(1);
}
};
let server = HttpServer::new(factory)
.listen(listener)
.unwrap_or_else(|e| {
eprintln!("Failed to listen on {}: {}", bind_addr, e);
std::process::exit(1);
});
println!("Listening on http://{}", bind_addr);
if let Err(e) = server.run() {
eprintln!("Server run failed: {}", e);
std::process::exit(1);
}
2019-03-29 13:48:31 +00:00
}