Fixes #279 switch from iron to actix-web.
This implementation seems to match the behavior of the previous iron server. Static files are rendered as-is, and when a directory is requested, we attempt to render a `index.html` found inside, or 404 if it's not present. The actix docs mention using channels to send a message to the server to shut it down gracefully while running in another thread (as we're doing here), but it seems like there would have to be some reorganization in order to manage this effectively, perhaps holding the channel sender inside `main.rs` so we can push a message through to the server when the call to `cmd::serve()` finally returns. For the time being, I left things without any careful attempts to cleanup the server thread. This more or less matches the old iron implementation as far as I can see. The static file handling in actix is _just_ a little off from what we'd want. I left some comments in the source regarding why we can't just use their hook for directory index redirection.
This commit is contained in:
parent
8e8cdfeb7f
commit
908f16855a
919
Cargo.lock
generated
919
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -24,9 +24,8 @@ term-painter = "0.2"
|
||||||
# Used in init to ensure the url given as base_url is a valid one
|
# Used in init to ensure the url given as base_url is a valid one
|
||||||
url = "1.5"
|
url = "1.5"
|
||||||
# Below is for the serve cmd
|
# Below is for the serve cmd
|
||||||
staticfile = "0.5"
|
actix = "0.5"
|
||||||
iron = "0.6"
|
actix-web = "0.6"
|
||||||
mount = "0.4"
|
|
||||||
notify = "4"
|
notify = "4"
|
||||||
ws = "0.7"
|
ws = "0.7"
|
||||||
ctrlc = "3"
|
ctrlc = "3"
|
||||||
|
|
|
@ -23,15 +23,15 @@
|
||||||
|
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::fs::remove_dir_all;
|
use std::fs::remove_dir_all;
|
||||||
use std::path::Path;
|
use std::io;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::mpsc::channel;
|
use std::sync::mpsc::channel;
|
||||||
use std::time::{Instant, Duration};
|
use std::time::{Instant, Duration};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
use chrono::prelude::*;
|
use chrono::prelude::*;
|
||||||
use iron::{Iron, Request, IronResult, Response, status};
|
use actix;
|
||||||
use mount::Mount;
|
use actix_web::{fs, server, App, HttpRequest, HttpResponse, Responder};
|
||||||
use staticfile::Static;
|
|
||||||
use notify::{Watcher, RecursiveMode, watcher};
|
use notify::{Watcher, RecursiveMode, watcher};
|
||||||
use ws::{WebSocket, Sender, Message};
|
use ws::{WebSocket, Sender, Message};
|
||||||
use ctrlc;
|
use ctrlc;
|
||||||
|
@ -59,8 +59,8 @@ enum ChangeKind {
|
||||||
const LIVE_RELOAD: &'static str = include_str!("livereload.js");
|
const LIVE_RELOAD: &'static str = include_str!("livereload.js");
|
||||||
|
|
||||||
|
|
||||||
fn livereload_handler(_: &mut Request) -> IronResult<Response> {
|
fn livereload_handler(_: HttpRequest) -> &'static str {
|
||||||
Ok(Response::with((status::Ok, LIVE_RELOAD.to_string())))
|
LIVE_RELOAD
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rebuild_done_handling(broadcaster: &Sender, res: Result<()>, reload_path: &str) {
|
fn rebuild_done_handling(broadcaster: &Sender, res: Result<()>, reload_path: &str) {
|
||||||
|
@ -102,6 +102,24 @@ fn create_new_site(interface: &str, port: &str, output_dir: &str, base_url: &str
|
||||||
Ok((site, address))
|
Ok((site, address))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Attempt to render `index.html` when a directory is requested.
|
||||||
|
///
|
||||||
|
/// The default "batteries included" mechanisms for actix to handle directory
|
||||||
|
/// listings rely on redirection which behaves oddly (the location headers
|
||||||
|
/// seem to use relative paths for some reason).
|
||||||
|
/// They also mean that the address in the browser will include the
|
||||||
|
/// `index.html` on a successful redirect (rare), which is unsightly.
|
||||||
|
///
|
||||||
|
/// Rather than deal with all of that, we can hijack a hook for presenting a
|
||||||
|
/// custom directory listing response and serve it up using their
|
||||||
|
/// `NamedFile` responder.
|
||||||
|
fn handle_directory<'a, 'b>(dir: &'a fs::Directory, req: &'b HttpRequest) -> io::Result<HttpResponse> {
|
||||||
|
let mut path = PathBuf::from(&dir.base);
|
||||||
|
path.push(&dir.path);
|
||||||
|
path.push("index.html");
|
||||||
|
Ok(fs::NamedFile::open(path).respond_to(req).unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn serve(interface: &str, port: &str, output_dir: &str, base_url: &str, config_file: &str) -> Result<()> {
|
pub fn serve(interface: &str, port: &str, output_dir: &str, base_url: &str, config_file: &str) -> Result<()> {
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
let (mut site, address) = create_new_site(interface, port, output_dir, base_url, config_file)?;
|
let (mut site, address) = create_new_site(interface, port, output_dir, base_url, config_file)?;
|
||||||
|
@ -128,15 +146,28 @@ pub fn serve(interface: &str, port: &str, output_dir: &str, base_url: &str, conf
|
||||||
let _ = watcher.watch("sass/", RecursiveMode::Recursive);
|
let _ = watcher.watch("sass/", RecursiveMode::Recursive);
|
||||||
|
|
||||||
let ws_address = format!("{}:{}", interface, site.live_reload.unwrap());
|
let ws_address = format!("{}:{}", interface, site.live_reload.unwrap());
|
||||||
|
let output_path = Path::new(output_dir).to_path_buf();
|
||||||
|
|
||||||
// Start a webserver that serves the `output_dir` directory
|
// output path is going to need to be moved later on, so clone it for the
|
||||||
let mut mount = Mount::new();
|
// http closure to avoid contention.
|
||||||
mount.mount("/", Static::new(Path::new(output_dir)));
|
let static_root = output_path.clone();
|
||||||
mount.mount("/livereload.js", livereload_handler);
|
thread::spawn(move || {
|
||||||
// Starts with a _ to not trigger the unused lint
|
let sys = actix::System::new("http-server");
|
||||||
// we need to assign to a variable otherwise it will block
|
server::new(move || {
|
||||||
let _iron = Iron::new(mount).http(address.as_str())
|
App::new()
|
||||||
.chain_err(|| "Can't start the webserver")?;
|
.resource(r"/livereload.js", |r| r.f(livereload_handler))
|
||||||
|
// Start a webserver that serves the `output_dir` directory
|
||||||
|
.handler(r"/", fs::StaticFiles::new(&static_root)
|
||||||
|
.show_files_listing()
|
||||||
|
.files_listing_renderer(handle_directory))
|
||||||
|
})
|
||||||
|
.bind(&address)
|
||||||
|
.expect("Can't start the webserver")
|
||||||
|
.shutdown_timeout(20)
|
||||||
|
.start();
|
||||||
|
println!("Web server is available at http://{}", &address);
|
||||||
|
let _ = sys.run();
|
||||||
|
});
|
||||||
|
|
||||||
// The websocket for livereload
|
// The websocket for livereload
|
||||||
let ws_server = WebSocket::new(|output: Sender| {
|
let ws_server = WebSocket::new(|output: Sender| {
|
||||||
|
@ -169,10 +200,9 @@ pub fn serve(interface: &str, port: &str, output_dir: &str, base_url: &str, conf
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("Listening for changes in {}/{{{}}}", pwd, watchers.join(", "));
|
println!("Listening for changes in {}/{{{}}}", pwd, watchers.join(", "));
|
||||||
println!("Web server is available at http://{}", address);
|
|
||||||
println!("Press Ctrl+C to stop\n");
|
println!("Press Ctrl+C to stop\n");
|
||||||
// Delete the output folder on ctrl+C
|
// Delete the output folder on ctrl+C
|
||||||
let output_path = Path::new(output_dir).to_path_buf();
|
|
||||||
ctrlc::set_handler(move || {
|
ctrlc::set_handler(move || {
|
||||||
remove_dir_all(&output_path).expect("Failed to delete output directory");
|
remove_dir_all(&output_path).expect("Failed to delete output directory");
|
||||||
::std::process::exit(0);
|
::std::process::exit(0);
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
|
extern crate actix;
|
||||||
|
extern crate actix_web;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate clap;
|
extern crate clap;
|
||||||
extern crate chrono;
|
extern crate chrono;
|
||||||
extern crate term_painter;
|
|
||||||
extern crate staticfile;
|
|
||||||
extern crate iron;
|
|
||||||
extern crate mount;
|
|
||||||
extern crate notify;
|
extern crate notify;
|
||||||
|
extern crate term_painter;
|
||||||
extern crate url;
|
extern crate url;
|
||||||
extern crate ws;
|
extern crate ws;
|
||||||
extern crate ctrlc;
|
extern crate ctrlc;
|
||||||
|
|
Loading…
Reference in a new issue