Skip to content

Commit 7aa5949

Browse files
committed
feat: file explorer
Implements `FileExplorer`. A file system explorer for the "root_dir" (the root directory specified when running the HTTP server). The `FileExplorer` list directory files and serves HTML, CSS, JavaScript, and WASM files. Other files are returned as binary files (prompt for download).
1 parent 49e4cd7 commit 7aa5949

File tree

19 files changed

+929
-15
lines changed

19 files changed

+929
-15
lines changed

Cargo.lock

Lines changed: 188 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "http-server"
3-
version = "0.1.0"
3+
version = "0.0.9"
44
authors = ["Esteban Borai <estebanborai@gmail.com>"]
55
edition = "2018"
66
license = "MIT"
@@ -12,4 +12,7 @@ categories = ["authentication", "encoding", "web-programming", "web-programming:
1212
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
1313

1414
[dependencies]
15+
ascii = "0.8"
1516
clap = "2.33.3"
17+
mime_guess = "2.0"
18+
tiny_http = "0.6"

src/cli/app.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ use clap::{crate_version, App};
44
/// Creates a CLAP `App` instance
55
pub fn make_app() -> App<'static, 'static> {
66
App::new("http-server")
7-
.author("Esteban Borai (https://github.com/EstebanBorai)")
87
.about("A simple, zero-configuration command-line HTTP server")
8+
.author("Authors: https://github.com/EstebanBorai/http-server/blob/main/AUTHORS")
99
.version(crate_version!())
1010
.setting(clap::AppSettings::ColoredHelp)
1111
.args(&make_args())

src/cli/args.rs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,21 @@ use clap::Arg;
77
/// the element `1` represents the `long` name.
88
pub type CommandLineOption<'a> = (&'a str, &'a str);
99

10+
/// The address to bind the HTTP server to
1011
pub const ADDRESS: CommandLineOption = ("a", "address");
12+
/// The port to bind the HTTP server to
1113
pub const PORT: CommandLineOption = ("p", "port");
14+
/// The root directory to serve files from
15+
pub const ROOT_DIR: CommandLineOption = ("", "root_dir");
16+
/// If `true` every output will not be logged
1217
pub const SILENT: CommandLineOption = ("s", "silent");
1318

1419
trait IntoArg {
15-
/// Creates a `clap::Arg` from a `CommandLineOption`
16-
/// where the element at index `0` of the tuple will be the `short`
17-
/// argument name and the element at index `1` will be the `long`
18-
/// argument name
20+
/// Creates an argument with the `short` and `long` names
21+
/// defined in the `CommandLineOption` tuple
1922
fn into_arg(&self) -> Arg<'static, 'static>;
23+
/// Creates a positional argument with the provided `index` and `name`
24+
fn into_positional(&self, index: u64) -> Arg<'static, 'static>;
2025
}
2126

2227
impl IntoArg for CommandLineOption<'static> {
@@ -25,6 +30,10 @@ impl IntoArg for CommandLineOption<'static> {
2530
.short(self.0)
2631
.long(self.1)
2732
}
33+
34+
fn into_positional(&self, index: u64) -> Arg<'static, 'static> {
35+
Arg::with_name(self.1).index(index).required(false)
36+
}
2837
}
2938

3039
/// Creates and returns a vector of `Arg` instances
@@ -42,6 +51,9 @@ pub fn make_args() -> Vec<Arg<'static, 'static>> {
4251
.takes_value(true)
4352
.default_value("7878")
4453
.validator(validate_port),
54+
ROOT_DIR
55+
.into_positional(1)
56+
.help("Directory to serve files from"),
4557
SILENT.into_arg().help("Disable outputs").takes_value(false),
4658
]
4759
}

src/cli/validator.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@ pub fn validate_address(value: String) -> Result<(), String> {
1313
pub fn validate_port(value: String) -> Result<(), String> {
1414
match value.parse::<u16>() {
1515
Ok(_) => Ok(()),
16-
Err(_) => Err(format!(
16+
Err(_) => Err(
1717
"The provided value must be a number and must be a 16-bit integer (maximum: 65535)"
18-
)),
18+
.to_string(),
19+
),
1920
}
2021
}

src/config/config.rs renamed to src/config.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
use crate::cli::{ADDRESS, PORT, SILENT};
1+
use crate::cli::{ADDRESS, PORT, ROOT_DIR, SILENT};
22
use clap::App;
3+
use std::env::current_dir;
34
use std::net::{IpAddr, SocketAddr};
5+
use std::path::PathBuf;
46
use std::str::FromStr;
57

68
/// Configuration for the HTTP/S Server
@@ -9,8 +11,8 @@ pub struct Config {
911
pub address: IpAddr,
1012
pub port: u16,
1113
pub socket_address: SocketAddr,
14+
pub root_dir: PathBuf,
1215
pub silent: bool,
13-
// pub input: PathBuf,
1416
}
1517

1618
impl From<App<'static, 'static>> for Config {
@@ -19,13 +21,20 @@ impl From<App<'static, 'static>> for Config {
1921
let address = IpAddr::from_str(matches.value_of(ADDRESS.1).unwrap()).unwrap();
2022
let port = matches.value_of(PORT.1).unwrap().parse::<u16>().unwrap();
2123
let socket_address = SocketAddr::new(address, port);
24+
let root_dir = if let Some(root_dir) = matches.value_of(ROOT_DIR.1) {
25+
PathBuf::from_str(root_dir).unwrap()
26+
} else {
27+
current_dir().unwrap()
28+
};
29+
2230
let silent = matches.is_present(SILENT.1);
2331

2432
// at this point the values provided to the config are validated by the CLI
2533
Self {
2634
address,
2735
port,
2836
socket_address,
37+
root_dir,
2938
silent,
3039
}
3140
}

src/config/mod.rs

Lines changed: 0 additions & 3 deletions
This file was deleted.

0 commit comments

Comments
 (0)