diff --git a/Cargo.toml b/Cargo.toml
index 6280d02a..7f8e099c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -25,7 +25,7 @@ term-painter = "0.2"
url = "1.5"
# Below is for the serve cmd
actix = "~0.5"
-actix-web = "0.6"
+actix-web = "~0.6"
notify = "4"
ws = "0.7"
ctrlc = "3"
diff --git a/components/site/src/lib.rs b/components/site/src/lib.rs
index 53ebc9d0..b217795a 100644
--- a/components/site/src/lib.rs
+++ b/components/site/src/lib.rs
@@ -526,6 +526,7 @@ impl Site {
if self.config.generate_rss {
self.render_rss_feed()?;
}
+ self.render_404()?;
self.render_robots()?;
// `render_categories` and `render_tags` will check whether the config allows
// them to render or not
@@ -660,6 +661,15 @@ impl Site {
Ok(())
}
+ /// Renders 404.html
+ pub fn render_404(&self) -> Result<()> {
+ ensure_directory_exists(&self.output_path)?;
+ create_file(
+ &self.output_path.join("404.html"),
+ &render_template("404.html", &self.tera, &Context::new(), &self.config.theme)?
+ )
+ }
+
/// Renders robots.txt
pub fn render_robots(&self) -> Result<()> {
ensure_directory_exists(&self.output_path)?;
diff --git a/components/templates/src/builtins/404.html b/components/templates/src/builtins/404.html
new file mode 100644
index 00000000..f7d50b1a
--- /dev/null
+++ b/components/templates/src/builtins/404.html
@@ -0,0 +1,10 @@
+
+
+
+ File Not Found: 404.
+
+
+ Oops!
+ File Not Found: 404.
+
+
diff --git a/components/templates/src/lib.rs b/components/templates/src/lib.rs
index 1c494038..ce0fe912 100644
--- a/components/templates/src/lib.rs
+++ b/components/templates/src/lib.rs
@@ -24,6 +24,7 @@ lazy_static! {
pub static ref GUTENBERG_TERA: Tera = {
let mut tera = Tera::default();
tera.add_raw_templates(vec![
+ ("404.html", include_str!("builtins/404.html")),
("rss.xml", include_str!("builtins/rss.xml")),
("sitemap.xml", include_str!("builtins/sitemap.xml")),
("robots.txt", include_str!("builtins/robots.txt")),
diff --git a/src/cmd/serve.rs b/src/cmd/serve.rs
index 1ae7a01c..a5158e5a 100644
--- a/src/cmd/serve.rs
+++ b/src/cmd/serve.rs
@@ -22,8 +22,8 @@
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
use std::env;
-use std::fs::remove_dir_all;
-use std::io;
+use std::fs::{remove_dir_all, File};
+use std::io::{self, Read};
use std::path::{Path, PathBuf};
use std::sync::mpsc::channel;
use std::time::{Instant, Duration};
@@ -31,7 +31,8 @@ use std::thread;
use chrono::prelude::*;
use actix;
-use actix_web::{fs, server, App, HttpRequest, HttpResponse, Responder};
+use actix_web::{self, fs, http, server, App, HttpRequest, HttpResponse, Responder};
+use actix_web::middleware::{Middleware, Started, Response};
use notify::{Watcher, RecursiveMode, watcher};
use ws::{WebSocket, Sender, Message};
use ctrlc;
@@ -58,6 +59,33 @@ enum ChangeKind {
// errors
const LIVE_RELOAD: &'static str = include_str!("livereload.js");
+struct NotFoundHandler {
+ rendered_template: PathBuf,
+}
+
+impl Middleware for NotFoundHandler {
+ fn start(&self, _req: &mut HttpRequest) -> actix_web::Result {
+ Ok(Started::Done)
+ }
+
+ fn response(
+ &self,
+ _req: &mut HttpRequest,
+ mut resp: HttpResponse,
+ ) -> actix_web::Result {
+ if http::StatusCode::NOT_FOUND == resp.status() {
+ let mut fh = File::open(&self.rendered_template)?;
+ let mut buf: Vec = vec![];
+ let _ = fh.read_to_end(&mut buf)?;
+ resp.replace_body(buf);
+ resp.headers_mut().insert(
+ http::header::CONTENT_TYPE,
+ http::header::HeaderValue::from_static("text/html"),
+ );
+ }
+ Ok(Response::Done(resp))
+ }
+}
fn livereload_handler(_: HttpRequest) -> &'static str {
LIVE_RELOAD
@@ -155,6 +183,7 @@ pub fn serve(interface: &str, port: &str, output_dir: &str, base_url: &str, conf
let sys = actix::System::new("http-server");
server::new(move || {
App::new()
+ .middleware(NotFoundHandler { rendered_template: static_root.join("404.html") })
.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)