Skip to content

Commit c6af4b8

Browse files
authored
Merge pull request #23 from hnez/directory-listings
http_server: Add directory listings, caching and compression
2 parents aef6538 + 6ddf0e7 commit c6af4b8

File tree

10 files changed

+506
-46
lines changed

10 files changed

+506
-46
lines changed

Cargo.lock

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ futures = "0.3"
2727
futures-lite = "1.12"
2828
futures-util = "0.3"
2929
gpio-cdev = "0.5"
30+
html-escape = "0.2"
3031
industrial-io = { version = "0.5", default-features = false }
3132
log = { version = "0.4", features = ["release_max_level_warn"]}
3233
mqtt-protocol = "0.11"

build.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use std::env::var_os;
1919
use std::fs::{read_to_string, write};
2020
use std::path::Path;
2121
use std::process::Command;
22+
use std::time::SystemTime;
2223

2324
use chrono::prelude::Utc;
2425

@@ -88,7 +89,19 @@ fn generate_version_string() {
8889
)
8990
}
9091

92+
/// Store the build date and time to have a lower bound on HTTP Last-Modified
93+
/// for files with faked timestamps.
94+
fn generate_build_date() {
95+
let timestamp = SystemTime::now()
96+
.duration_since(SystemTime::UNIX_EPOCH)
97+
.unwrap()
98+
.as_secs();
99+
100+
println!("cargo:rustc-env=BUILD_TIMESTAMP={}", timestamp);
101+
}
102+
91103
fn main() {
92-
generate_version_string();
93104
generate_openapi_include();
105+
generate_version_string();
106+
generate_build_date();
94107
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Hello!

src/http_server.rs

Lines changed: 13 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -15,30 +15,31 @@
1515
// with this program; if not, write to the Free Software Foundation, Inc.,
1616
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
1717

18-
use std::convert::AsRef;
1918
use std::fs::write;
20-
use std::io::ErrorKind;
2119
use std::net::TcpListener;
22-
use std::path::Path;
2320

24-
use log::warn;
2521
use tide::{Body, Response, Server};
2622

23+
mod serve_dir;
24+
use serve_dir::serve_dir;
25+
2726
#[cfg(feature = "demo_mode")]
2827
mod consts {
2928
pub const WEBUI_DIR: &str = "web/build";
29+
pub const EXTRA_DIR: &str = "demo_files/srv/www";
3030
pub const FS_PREFIX: &str = "demo_files";
3131
pub const FALLBACK_PORT: &str = "[::]:8080";
3232
}
3333

3434
#[cfg(not(feature = "demo_mode"))]
3535
mod consts {
3636
pub const WEBUI_DIR: &str = "/usr/share/tacd/webui";
37+
pub const EXTRA_DIR: &str = "/srv/www";
3738
pub const FS_PREFIX: &str = "";
3839
pub const FALLBACK_PORT: &str = "[::]:80";
3940
}
4041

41-
use consts::{FALLBACK_PORT, FS_PREFIX, WEBUI_DIR};
42+
use consts::{EXTRA_DIR, FALLBACK_PORT, FS_PREFIX, WEBUI_DIR};
4243

4344
// openapi.json is generated by build.rs from openapi.yaml
4445
const OPENAPI_JSON: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/openapi.json"));
@@ -74,8 +75,8 @@ impl HttpServer {
7475
);
7576

7677
this.expose_openapi_json();
77-
this.expose_dir(WEBUI_DIR, "/");
78-
this.expose_dir(FS_PREFIX.to_owned() + "/srv/www", "/srv/");
78+
this.expose_dir(WEBUI_DIR, "/", false);
79+
this.expose_dir(EXTRA_DIR, "/srv", true);
7980

8081
for (fs_path, web_path) in EXPOSED_FILES_RW {
8182
let fs_path = FS_PREFIX.to_owned() + *fs_path;
@@ -99,45 +100,12 @@ impl HttpServer {
99100
}
100101

101102
/// Serve a directory from disk for reading
102-
fn expose_dir(&mut self, fs_path: impl AsRef<Path>, web_path: &str) {
103-
if let Err(e) = self.server.at(web_path).serve_dir(&fs_path) {
104-
// Don't crash if the directory does not exist.
105-
// Just print a warning.
106-
match e.kind() {
107-
ErrorKind::NotFound => {
108-
warn!(
109-
"Can not serve {} at {}: Directory not found",
110-
fs_path.as_ref().display(),
111-
web_path
112-
);
113-
}
114-
_ => Err(e).unwrap(),
115-
}
116-
}
103+
fn expose_dir(&mut self, fs_path: &'static str, web_path: &str, directory_listings: bool) {
104+
let handler = move |req| async move { serve_dir(fs_path, directory_listings, req).await };
117105

118-
// Serve an index.html if the bare directory path is requested.
119-
// This only works for the top level. If we want to serve index.htmls
120-
// from sub-directories we would have to modify serve_dir().
121-
// Which is something we will likely want anyways as it does not
122-
// support compression, caching headers or directory listings.
123-
if web_path.ends_with('/') {
124-
let index_html = fs_path.as_ref().join("index.html");
125-
126-
if let Err(e) = self.server.at(web_path).serve_file(&index_html) {
127-
// Don't crash if the directory does not exist. Just print a
128-
// warning.
129-
match e.kind() {
130-
ErrorKind::NotFound => {
131-
warn!(
132-
"Can not serve {} at {}: File not found",
133-
index_html.display(),
134-
web_path
135-
);
136-
}
137-
_ => Err(e).unwrap(),
138-
}
139-
}
140-
}
106+
self.server.at(web_path).get(handler);
107+
self.server.at(web_path).at("").get(handler);
108+
self.server.at(web_path).at("*rel_path").get(handler);
141109
}
142110

143111
/// Serve a file from disk for reading and writing

0 commit comments

Comments
 (0)