Skip to content

Commit

Permalink
Implement --spa and --index flags
Browse files Browse the repository at this point in the history
  • Loading branch information
Antosser committed Feb 25, 2024
1 parent aaa642c commit dfc3c37
Show file tree
Hide file tree
Showing 10 changed files with 219 additions and 64 deletions.
94 changes: 50 additions & 44 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@

<div align="center">

[![Crates.io](https://img.shields.io/crates/v/http-server.svg)](https://crates.io/crates/http-server)
[![Documentation](https://docs.rs/http-server/badge.svg)](https://docs.rs/http-server)
![Build](https://github.com/http-server-rs/http-server/workflows/build/badge.svg)
![Clippy](https://github.com/http-server-rs/http-server/workflows/clippy/badge.svg)
![Formatter](https://github.com/http-server-rs/http-server/workflows/fmt/badge.svg)
![Tests](https://github.com/http-server-rs/http-server/workflows/test/badge.svg)
![Benchs](https://github.com/http-server-rs/http-server/workflows/bench/badge.svg)
[![Crates.io](https://img.shields.io/crates/v/http-server.svg)](https://crates.io/crates/http-server)
[![Documentation](https://docs.rs/http-server/badge.svg)](https://docs.rs/http-server)
![Build](https://github.com/http-server-rs/http-server/workflows/build/badge.svg)
![Clippy](https://github.com/http-server-rs/http-server/workflows/clippy/badge.svg)
![Formatter](https://github.com/http-server-rs/http-server/workflows/fmt/badge.svg)
![Tests](https://github.com/http-server-rs/http-server/workflows/test/badge.svg)
![Benches](https://github.com/http-server-rs/http-server/workflows/bench/badge.svg)

</div>

Expand Down Expand Up @@ -45,10 +45,12 @@ FLAGS:
--graceful-shutdown Waits for all requests to fulfill before shutting down the server
--gzip Enable GZip compression for HTTP Responses
--help Prints help information
--logger Prints HTTP request and response details to stdout
-l, --logger Prints HTTP request and response details to stdout
-q, --quiet Turns off stdout/stderr logging
--spa Route non-existent files to /index.html
--tls Enables HTTPS serving using TLS
-i, --index Route directories to index.html if present
-V, --version Prints version information
-q, --quiet Turns off stdout/stderr logging
OPTIONS:
-c, --config <config> Path to TOML configuration file
Expand All @@ -74,19 +76,21 @@ configurations will be used. You can always change this behavior by either
creating your own config with the [Configuration TOML](https://github.com/http-server-rs/http-server/blob/main/fixtures/config.toml) file
or by providing CLI arguments described in the [usage](#usage) section.

Name | Description | Default
--- | --- | ---
Host | Address to bind the server | `127.0.0.1`
Port | Port to bind the server | `7878`
Root Directory | The directory to serve files from | `CWD`
File Explorer UI | A File Explorer UI for the directory configured as the _Root Directory_ | Enabled
Configuration File | Specifies a configuration file. [Example](https://github.com/http-server-rs/http-server/blob/main/fixtures/config.toml) | Disabled
HTTPS (TLS) | HTTPS Secure connection configuration. Refer to [TLS (HTTPS)](https://github.com/http-server-rs/http-server#tls-https) reference | Disabled
CORS | Cross-Origin-Resource-Sharing headers support. Refer to [CORS](https://github.com/http-server-rs/http-server#cross-origin-resource-sharing-cors) reference | Disabled
Compression | GZip compression for HTTP Response Bodies. Refer to [Compression](https://github.com/http-server-rs/http-server#compression) reference | Disabled
Quiet | Don't print server details when running. This doesn't include any logging capabilities. | Disabled
Basic Authentication | Authorize requests using Basic Authentication. Refer to [Basic Authentication](https://github.com/http-server-rs/http-server#basic-authentication) | Disabled
Logger | Prints HTTP request and response details to stdout | Disabled
| Name | Description | Default |
| -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- |
| Host | Address to bind the server | `127.0.0.1` |
| Port | Port to bind the server | `7878` |
| Root Directory | The directory to serve files from | `CWD` |
| File Explorer UI | A File Explorer UI for the directory configured as the _Root Directory_ | Enabled |
| Configuration File | Specifies a configuration file. [Example](https://github.com/http-server-rs/http-server/blob/main/fixtures/config.toml) | Disabled |
| HTTPS (TLS) | HTTPS Secure connection configuration. Refer to [TLS (HTTPS)](https://github.com/http-server-rs/http-server#tls-https) reference | Disabled |
| CORS | Cross-Origin-Resource-Sharing headers support. Refer to [CORS](https://github.com/http-server-rs/http-server#cross-origin-resource-sharing-cors) reference | Disabled |
| Compression | GZip compression for HTTP Response Bodies. Refer to [Compression](https://github.com/http-server-rs/http-server#compression) reference | Disabled |
| Quiet | Don't print server details when running. This doesn't include any logging capabilities. | Disabled |
| Index | Route directories to index.html if present | Disabled |
| SPA | Route non-existent files to /index.html | Disabled |
| Basic Authentication | Authorize requests using Basic Authentication. Refer to [Basic Authentication](https://github.com/http-server-rs/http-server#basic-authentication) | Disabled |
| Logger | Prints HTTP request and response details to stdout | Disabled |

## Usage

Expand All @@ -102,15 +106,17 @@ Flags are provided without any values. For example:
http-server --help
```

Name | Short | Long | Description
--- | --- | --- | ---
Cross-Origin Resource Sharing | N/A | `--cors` | Enable Cross-Origin Resource Sharing allowing any origin
GZip Compression | N/A | `--gzip` | Enable GZip compression for responses
Graceful Shutdown | N/A | `--graceful-shutdown` | Wait for all requests to be fulfilled before shutting down the server
Help | N/A | `--help` | Print help information
Logger | `-l` | `--logger` | Print HTTP request and response details to stdout
Version | `-V` | `--version` | Print version information
Quiet | `-q` | `--quiet` | Don't print output to console
| Name | Short | Long | Description |
| ----------------------------- | ----- | --------------------- | --------------------------------------------------------------------- |
| Cross-Origin Resource Sharing | N/A | `--cors` | Enable Cross-Origin Resource Sharing allowing any origin |
| GZip Compression | N/A | `--gzip` | Enable GZip compression for responses |
| Graceful Shutdown | N/A | `--graceful-shutdown` | Wait for all requests to be fulfilled before shutting down the server |
| Help | N/A | `--help` | Print help information |
| Logger | `-l` | `--logger` | Print HTTP request and response details to stdout |
| Version | `-V` | `--version` | Print version information |
| Quiet | `-q` | `--quiet` | Don't print output to console |
| Index | `-i` | `--index` | Route directories to index.html if present |
| SPA | N/A | `--spa` | Route non-existent files to /index.html |

### Options

Expand All @@ -120,18 +126,18 @@ Options receive a value and support default values as well.
http-server --host 127.0.0.1
```

Name | Short | Long | Description | Default Value
--- | --- | --- | --- | ---
Host | `-h` | `--host` | Address to bind the server | `127.0.0.1`
Port | `-p` | `--port` | Port to bind the server | `7878`
Configuration File | `-c` | `--config` | Configuration file. [Example](https://github.com/http-server-rs/http-server/blob/main/fixtures/config.toml) | N/A
TLS | N/A | `--tls` | Enable TLS for HTTPS connections. Requires a Certificate and Key. [Reference](#tls-reference) | N/A
TLS Ceritificate | N/A | `--tls-cert` | Path to TLS certificate file. **Depends on `--tls`** | `cert.pem`
TLS Key | N/A | `--tls-key` | Path to TLS key file. **Depends on `--tls`** | `key.rsa`
TLS Key Algorithm | N/A | `--tls-key-algorithm` | Algorithm used to generate certificate key. **Depends on `--tls`** | `rsa`
Username | N/A | `--username` | Username to validate using basic authentication | N/A
Password | N/A | `--password` | Password to validate using basic authentication. **Depends on `--username`** | N/A
Proxy | N/A | `--proxy` | Proxy requests to the provided URL | N/A
| Name | Short | Long | Description | Default Value |
| ------------------ | ----- | --------------------- | ----------------------------------------------------------------------------------------------------------- | ------------- |
| Host | `-h` | `--host` | Address to bind the server | `127.0.0.1` |
| Port | `-p` | `--port` | Port to bind the server | `7878` |
| Configuration File | `-c` | `--config` | Configuration file. [Example](https://github.com/http-server-rs/http-server/blob/main/fixtures/config.toml) | N/A |
| TLS | N/A | `--tls` | Enable TLS for HTTPS connections. Requires a Certificate and Key. [Reference](#tls-reference) | N/A |
| TLS Certificate | N/A | `--tls-cert` | Path to TLS certificate file. **Depends on `--tls`** | `cert.pem` |
| TLS Key | N/A | `--tls-key` | Path to TLS key file. **Depends on `--tls`** | `key.rsa` |
| TLS Key Algorithm | N/A | `--tls-key-algorithm` | Algorithm used to generate certificate key. **Depends on `--tls`** | `rsa` |
| Username | N/A | `--username` | Username to validate using basic authentication | N/A |
| Password | N/A | `--password` | Password to validate using basic authentication. **Depends on `--username`** | N/A |
| Proxy | N/A | `--proxy` | Proxy requests to the provided URL | N/A |

## Request Handlers

Expand Down Expand Up @@ -285,7 +291,7 @@ open an issue to be assigned and track the progress there.

- [x] Logging
- [x] Request/Response Logging
- [x] Service Config Loggins
- [x] Service Config Logins
- [ ] File Explorer
- [x] Modified Date
- [x] File Size
Expand Down
2 changes: 2 additions & 0 deletions fixtures/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ port = 7878
# quiet = false
# root_dir = "./"
# graceful_shutdown = false
# index = false
# spa = false

# [tls]
# cert = "cert.pem"
Expand Down
87 changes: 69 additions & 18 deletions src/addon/file_server/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ mod scoped_file_system;

use chrono::{DateTime, Local};

pub use file::File;
use humansize::{format_size, DECIMAL};

pub use scoped_file_system::{Entry, ScopedFileSystem};

use anyhow::{Context, Result};
Expand All @@ -20,6 +22,7 @@ use std::path::{Component, Path, PathBuf};
use std::str::FromStr;
use std::sync::Arc;

use crate::config::Config;
use crate::utils::url_encode::{decode_uri, encode_uri, PERCENT_ENCODE_SET};

use self::directory_entry::{BreadcrumbItem, DirectoryEntry, DirectoryIndex, Sort};
Expand All @@ -33,18 +36,20 @@ pub struct FileServer {
root_dir: PathBuf,
handlebars: Arc<Handlebars<'static>>,
scoped_file_system: ScopedFileSystem,
config: Arc<Config>,
}

impl<'a> FileServer {
/// Creates a new instance of the `FileExplorer` with the provided `root_dir`
pub fn new(root_dir: PathBuf) -> Self {
pub fn new(root_dir: PathBuf, config: Arc<Config>) -> Self {
let handlebars = FileServer::make_handlebars_engine();
let scoped_file_system = ScopedFileSystem::new(root_dir.clone()).unwrap();

FileServer {
root_dir,
handlebars,
scoped_file_system,
config,
}
}

Expand Down Expand Up @@ -128,33 +133,79 @@ impl<'a> FileServer {
/// If the matched path resolves to a file, attempts to render it if the
/// MIME type is supported, otherwise returns the binary (downloadable file)
pub async fn resolve(&self, req_path: String) -> Result<Response<Body>> {
use std::io::ErrorKind;

let (path, query_params) = FileServer::parse_path(req_path.as_str())?;

match self.scoped_file_system.resolve(path).await {
Ok(entry) => match entry {
Entry::Directory(dir) => {
Entry::Directory(dir) => 'dir: {
if self.config.index() {
let mut filepath = dir.path();

filepath.push("index.html");
if let Ok(file) = tokio::fs::File::open(&filepath).await {
break 'dir make_http_file_response(
File {
path: filepath,
metadata: file.metadata().await?,
file,
},
CacheControlDirective::MaxAge(2500),
)
.await;
}
}
self.render_directory_index(dir.path(), query_params).await
}
Entry::File(file) => {
make_http_file_response(*file, CacheControlDirective::MaxAge(2500)).await
}
},
Err(err) => match err.kind() {
ErrorKind::NotFound => Ok(HttpResponseBuilder::new()
.status(StatusCode::NOT_FOUND)
.body(Body::from(err.to_string()))
.expect("Failed to build response")),
ErrorKind::PermissionDenied => Ok(HttpResponseBuilder::new()
.status(StatusCode::FORBIDDEN)
.body(Body::from(err.to_string()))
.expect("Failed to build response")),
_ => Ok(HttpResponseBuilder::new()
.status(StatusCode::BAD_REQUEST)
.body(Body::from(err.to_string()))
.expect("Failed to build response")),
},
Err(err) => {
if self.config.spa() {
make_http_file_response(
{
let mut path = self.config.root_dir();
path.push("index.html");

let file = tokio::fs::File::open(&path).await?;

let metadata = file.metadata().await?;

File {
path,
metadata,
file,
}
},
CacheControlDirective::MaxAge(2500),
)
.await
} else {
let status = match err.kind() {
std::io::ErrorKind::NotFound => hyper::StatusCode::NOT_FOUND,
std::io::ErrorKind::PermissionDenied => hyper::StatusCode::FORBIDDEN,
_ => hyper::StatusCode::BAD_REQUEST,
};

let code = match err.kind() {
std::io::ErrorKind::NotFound => "404",
std::io::ErrorKind::PermissionDenied => "403",
_ => "400",
};

let response = hyper::Response::builder()
.status(status)
.header(http::header::CONTENT_TYPE, "text/html")
.body(hyper::Body::from(
handlebars::Handlebars::new().render_template(
include_str!("./template/error.hbs"),
&serde_json::json!({"error": err.to_string(), "code": code}),
)?,
))?;

Ok(response)
}
}
}
}

Expand Down
4 changes: 4 additions & 0 deletions src/addon/file_server/template/error.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<center>
<h1>{{code}}</h1>
<p>{{error}}</p>
</center>
Loading

0 comments on commit dfc3c37

Please sign in to comment.