89 Commits

Author SHA1 Message Date
Shahar "Dawn" Or
1a31f005bc fix: hidden root path ignored by --ignore-hidden-files feature (#606)
* test: hidden base path not ignored
* fix: hidden base path not ignored
* feat: trace when ignoring a hidden file
2025-12-27 10:31:00 +01:00
Jose Quintana
308f0d26ce Merge commit from fork
* refactor: re-organize directory listing module

* chore: skip broken symlinks in directory listing
2025-12-08 02:58:12 +01:00
Jose Quintana
d48da4ca06 refactor: simplify public dir for docker image & default error pages (#579) 2025-11-15 21:48:54 +01:00
David Legrand
2c25d823f2 feat: content negotiation for markdown files via Accept header (#577)
* feat: add accept markdown

* docs: add accept markdown feature

* fix: cors error detected by clippy in CI

* fix: simplify markdown handling

* chore: add accept markdown integration tests

* chore: move accept markdown test files

* fix: after review
2025-11-06 15:44:20 +01:00
Jose Quintana
57025e3321 chore: update dependencies 23-10-2025 & MSRV to Rust 1.85.0 (2024 Edition) (#572)
* fix: update dependencies 23-10-2025
* chore: update dependencies 26-10-2025
* chore: update dependencies 26.10.2025 & bump up MSRV to 1.85.0
* fix: lint errors
* fix: style edition 2024

SWS now requires Rust 1.85.0 (Rust 2024 Edition) or later
https://blog.rust-lang.org/2025/02/20/Rust-1.85.0/
2025-10-26 02:04:21 +02:00
Jose Quintana
0b5577085d chore: remove public from Cache-Control header value (#562)
It removes `public` from `Cache-Control` value, leaving max-age=<VALUE>, which can prevent CDN and Basic Authentication issues. See details on #560.

This applies to the "Cache-Control Headers" feature, with no user-breaking changes expected.
2025-09-06 09:20:09 +02:00
Jose Quintana
8c435ad010 chore: update dependencies 21.07.2025 & fix clippy lint warnings (#552)
* fix: clippy lint warnings
* chore: update dependencies 21.07.2025
* core: rust version on FreeBSD CI has been bumped up to 1.88.0.
2025-07-21 21:43:51 +02:00
David Legrand
acd838877b feat: prefer a less-generic config file sws.toml as default (#551)
- `sws.toml` used if present
- But `config.toml` has priority if it exists
- A warning message is printed if `config.toml` is used, as it will be removed in a future release previous advice.
2025-07-02 11:09:04 +02:00
Jose Quintana
b56e3c4a96 chore: update dependencies 30.05.2025 and MSRV to 1.82.0 (#546)
* chore: update dependencies 30.05.2025
* chore: format and lint checks for test files on CI
* fix: clippy lint warnings in test files
2025-05-31 23:31:50 +02:00
ekangmonyet
89f5846ffa feat: support for downloading a directory as a compressed tarball (#544)
$ static-web-server -p 1234 -d ./public \
    --directory-listing --directory-listing-download=targz
2025-05-31 12:15:44 +02:00
Jose Quintana
7ca2785c82 chore: update dependencies 26.05.2025 and MSRV 1.81.0 (#545)
- BREAKING: End support for unmaintained Windows 7, 8, 8.1 platforms as previously announced on v2.36.1.
- SECURITY (RUSTSEC-2024-0437): Crash due to uncontrolled recursion in protobuf crate was temporarily solved in the previous release. However, this PR applies the dependency patches recently available.
- RESTORED: `experimental` Cargo feature for metrics and in-memory cache.
2025-05-27 00:25:29 +02:00
Jose Quintana
5fbd0c5696 fix: add missing Origin to the Vary header value when CORS enabled (#534)
reference: https://fetch.spec.whatwg.org/#example-vary-origin
2025-03-28 09:21:29 +01:00
Matthias Schoettle
2737f4c4ca refactor: prevent single wildcards from matching path separators for URL Rewrites (#506)
* refactor: prevent single wildcards from matching path separators for URL Rewrites

* Fix log output in documentation

BREAKING CHANGE: Up to version 2.33.1 the wildcard `*` was matching the path separator. For example, `/{*}/{*}/` matched `/assets/images/logo/`. In later versions, the default has changed such that `*` does not match the path separator. In contrast, double wildcard (`**`) can match also a path separator.
2024-12-02 06:54:10 +01:00
Matthias Schoettle
5516b6a4be chore: add additional tests for URL Redirects (#503) 2024-11-29 15:58:56 +01:00
Matthias Schoettle
96ed7df2aa refactor: prevent single wildcards from matching path separators for URL Redirects (#501)
* Enable literal_separator for redirects

This ensures that * does not match the path separator.

* Fix tests

* Update documentation

BREAKING CHANGE: Up to version 2.33.1 the wildcard `*` was matching the path separator. For example, `/{*}/{*}/` matched `/assets/images/logo/`. In later versions, the default has changed such that `*` does not match the path separator. In contrast, double wildcard (`**`) can match also a path separator.
2024-11-28 13:30:36 +01:00
Jose Quintana
93479ba042 chore: update dependencies 26-10-2024 (#490)
* chore: update dependencies 26-10-2024
* chore: update dependencies 2024-10-31
* fix: move out handler.rs tests
* chore: pin rust version to 1.81.0 for freebsd on ci due to issues on 1.82.0 for i386 toolchain
2024-11-01 18:05:33 +01:00
Jose Quintana
ec85abdf32 feat: add in-memory files cache to the experimental Cargo feature (#482) 2024-09-15 21:42:23 +02:00
Jose Quintana
a3d40b8c2f fix: issues when building SWS without default features (#480)
* fix: issues when building without default features
* fix: windows http1_cancel_recv build issues
2024-09-10 15:28:54 +02:00
Jose Quintana
5bdfcd4c88 feat: experimental in-memory files cache with eviction policy support (#328)
Via a new advanced configuration entry.

The feature also supports expiration policies such as Time To Live (TTL) and Time To Idle (TTI).

Admission to a cache is controlled by the Least Frequently Used (LFU) policy and the eviction from a cache is controlled by the Least Recently Used (LRU) policy.

Example:

```toml
[general]

[advanced]

[advanced.memory-cache]
# Maximum capacity entries of the memory cache-store. Default 256
capacity = 256
# Time to live in seconds of a cached file entry. Default 1h
ttl = 3600
# Time to idle in seconds of a cached file entry. Default 5min
tti = 300
# Maximum size in bytes for a file entry to be cached. Default 8MB
max-file-size = 8192
```

Note that this feature requires Rust 1.76.0 or newer.

This feature is **experimental**. However, when stabilized then a cargo feature will be available as well as a proper documentation page.
2024-09-04 00:15:55 +02:00
Jose Quintana
a72c7b3c6b fix: wrong Content-Encoding when compression & compression-static enabled (#471) 2024-08-13 01:32:41 +02:00
Jose Quintana
915d04012e fix: can not run tests via cargo when passing non-sws arguments (#466)
When running tests with additional non-sws arguments, tests execution
fails because sws is trying to parse cli arguments anyways.

Now, sws won't parse arguments by default when running tests.
For example, the following command will work as expected:

cargo test -- --test-threads 1 --nocapture
2024-07-22 21:32:26 +02:00
Jose Quintana
cfa3567099 chore: update dependencies 17.07.2024 (#463)
* chore: update dependencies 17.07.2024
* chore: update tokio-rustls 0.26
* chore: update dependencies 19.07.2024
2024-07-19 22:53:55 +02:00
Jose Quintana
4c805d626e refactor: remove redundant async function signatures (#457) 2024-06-27 01:35:02 +02:00
Jose Quintana
eeb88dab51 feat: disable symlinks option via --disable-symlinks (#454)
* feat: disable symlinks option

--disable-symlinks[=<DISABLE_SYMLINKS>]
  Prevent following files or directories if any path name component is
  a symbolic link [env: SERVER_DISABLE_SYMLINKS=] [default: false]
  [possible values: true, false]

* chore: add tests
* docs: feature page [skip ci]
2024-06-11 22:46:50 +02:00
Jose Quintana
3410365ed0 fix: duplicate Vary response headers for compression and compression-static features (#453)
it prevents duplicate `vary` headers in responses when enabling
`compression` and `compression-static`.

* fix: duplicate `vary` response headers for compression feature
* fix: failed tests for experimental features
2024-06-07 00:47:34 +02:00
Jose Quintana
6031a1b51d fix: incorrect Content-Encoding for pre-compressed zstd file requests (#452) 2024-05-29 06:53:07 +02:00
Tristan Kobusch
7f59da9c50 feat: look for all accepted encodings for pre-compressed files but keeping quality ordering (#439)
* search for all accepted static compression formats
* add test
* fix syntax issues and improve the test

---------

Co-authored-by: Jose Quintana <1700322+joseluisq@users.noreply.github.com>
2024-05-17 00:34:08 +02:00
Wladimir Palant
9cbf95b7c5 feat: compression level option support (#381)
* Make fastest compression level the default setting

* Use balanced compression levels by default

* Fixed formatting and addressed clippy warning

* Change default zlib compression level to 3

* Updated docs to mention zlib compression

* Fixed setting spelling in docs

* Don't expose CompressionLevel::into_algorithm_level()

* Updated documentation of the compression feature
2024-05-01 06:42:25 +03:00
Jose Quintana
5d66301584 fix: don't percent-encode unreserved characters for directory listing links (#371)
* fix: don't percent-encode unreserved chars in the directory listing links

those chars (a.k.a unreserved marks) are now not percent-encoded:
https://www.ietf.org/rfc/rfc3986.txt

* chore: revert partially previous functionality
2024-04-28 14:04:58 +02:00
Wladimir Palant
528ed08b32 fix: Accept-Encoding handling to work correctly if only two compression schemes are available (#361)
* Fix Accept-Encoding handling to work correctly if only two compression schemes are available
* Fixed typo and slightly extended test

---------

Co-authored-by: Jose Quintana <1700322+joseluisq@users.noreply.github.com>
2024-04-25 22:54:58 +02:00
Jose Quintana
183102d1bf fix: build error when using specific or no Cargo compression features (#339)
also, if only one compression feature is
enabled (e.g. compression-gzip) then the preferred encoding gets
re-evaluated and assigned to that compression feature format  but only if it
was part of the `accept-encoding` request header value.

now the following scenarios will work:

[dependencies]
static-web-server = { version = "2.28.0", default-features = false }

or

 [dependencies]
static-web-server = { version = "2.28.0", default-features = false, features = ["compression-brotli"] }

in addition, some tracing logs are added to reflect the changes.
2024-04-13 04:44:21 +02:00
Jose Quintana
c04357e176 fix: missing custom headers for directory requests (trailing slash) (#333)
It fixes an issue where custom headers were not applied to the response for directory requests (requests ending with the slash character).
2024-04-12 01:15:30 +02:00
Jose Quintana
1a6caa476a feat: add all and experimental Cargo feature flags (#313)
* feat: `all` and `experimental` cargo feature flags
  - the `all` will host all available features (`default`) plus the
    `experimental`.
  - the `experimental` will only hold unstable features like for example
    `metrics` (as of writing)
* chore: enable the `all` cargo feature for freebsd

this feature also fixes #312
2024-02-12 23:19:15 +01:00
Jose Quintana
d4427eb3e1 feat: experimental Tokio Runtime metrics for Prometheus (#307)
* feat: Prometheus metrics endpoint at /metrics

Signed-off-by: Tom Plant <tom@tplant.com.au>

* fix: add `experimental` prefix to metrics arg, env var, and logs
Signed-off-by: Tom Plant <tom@tplant.com.au>

* fix: disable tokio-metrics-collector on Windows
Signed-off-by: Tom Plant <tom@tplant.com.au>

* chore: address feedback

* refactor: rename feature to `experimental-metrics` and add test

* fix: freebsd ci tests

* refactor: move dependencies to the unix target section

---------

Signed-off-by: Tom Plant <tom@tplant.com.au>
Co-authored-by: Jose Quintana <joseluisquintana20@gmail.com>
2024-02-04 13:44:38 +01:00
Jose Quintana
71dd54f998 feat: support for Range requests out of bounds (#306)
* feat: support for `Range` requests out of bounds

SWS will make sure to return only what's available in that case which
seems to be a very common behavior across web servers.

Previously exceeding the length of a file returning `416 Requested
Range Not Satisfiable`. Now it will return what's available.

```sh
$ curl -IH "Range: bytes=50-9000" http://localhost/index.html
\# HTTP/1.1 206 Partial Content
\# Server: nginx/1.25.3
\# Date: Sun, 28 Jan 2024 22:09:20 GMT
\# Content-Type: text/html
\# Content-Length: 486
\# Last-Modified: Mon, 02 Oct 2023 04:49:01 GMT
\# Connection: keep-alive
\# ETag: "651a4bbd-218"
\# Content-Range: bytes 50-535/536
```

it resolves #295 and relates to https://github.com/orgs/static-web-server/discussions/145
2024-01-29 00:36:32 +01:00
Jose Quintana
42f52e899c fix: wrong glob brace expansion capture in url rewrites & redirects (#304)
* fix: wrong glob brace expansion capture in url redirects/rewrites

now an url redirect (or rewrite) `source` that uses glob groups with
brace expansions like `**/{*}.{jpg,jpeg}` will works as expected:

```toml
[advanced]

[[advanced.redirects]]
source = "**/{*}.{jpg,jpeg}"
destination = "http://localhost/new-images/$2.$3"
kind = 302
```

* chore: url rewrites/redirects test cases
2024-01-22 00:34:02 +01:00
Jose Quintana
83e4277ff0 refactor: migrate to tokio-rustls 0.25 (#303)
- rustls 0.22
- rustls-pemfile 2.0
- bytes 1.5
2024-01-20 00:15:15 +01:00
Jose Quintana
8c6ab533fd feat: optional Host URI support for the URL Redirects feature (#301)
* feat: optional `host` uri support for URL redirects

which allows redirecting based on a host's incoming uri making it
possible to perform for example www to non-www redirects.

config example:

```toml
[advanced]

[[advanced.redirects]]
host = "127.0.0.1:4433"
source = "/{*}"
destination = "https://localhost:4433/$1"
kind = 301
```

* chore: add test cases
2024-01-16 00:43:14 +01:00
Jose Quintana
f8e2d3e462 docs: improve new target page information [skip ci] 2023-11-09 01:31:22 +01:00
Jose Quintana
9e5049110c feat: maintenance mode support (#272)
maintenance mode support via new options:

--maintenance-mode=false
--maintenance-mode-status=503
--maintenance-mode-file="./my_maintenance.html"
2023-10-12 22:26:52 +02:00
Jose Quintana
efb2c0cd8a Multiple index files support (#267)
* feat: multiple index files support

option: --index-files="a.html, b.htm, etc"
env: SERVER_INDEX_FILES
default value: "index.html"
2023-10-09 03:25:27 +02:00
Jose Quintana
2e3e49f880 fix: rewrites/redirects do not capture glob groups correctly (#265)
by removing the non-capturing group that appeared during the glob to regex conversion.

the following glob example works now

```toml
[advanced]

[[advanced.rewrites]]
source = "/files/{*}"
destination = "/$1"
```
2023-09-19 06:41:24 +02:00
Mac Chaffee
7baf569ef4 feat: virtual hosting support (#252) 2023-08-09 07:43:33 +02:00
Jose Quintana
3cf13dc964 fix: url rewrites don't work without replacements (#244)
this fixes a regression #243 introduced by v2.20.0 when using url rewrites with out replacements.
2023-07-19 17:25:09 +02:00
Jose Quintana
7c66c5ccb6 feat: replacements support for URL Redirects destination (#239)
example:

```toml
[advanced]

[[advanced.redirects]]
source = "**/{*}.{jpg,jpeg,svg}"
destination = "http://localhost/images/$1.$2"
kind = 301
```
2023-07-11 23:32:53 +02:00
Jose Quintana
3a47ef6aed feat: replacements support for URL Rewrites destination (#235)
* feat: placeholders support for url rewrites destination

example:

```toml
[advanced]

[[advanced.rewrites]]
source = "**/{*}.{png,gif}"
destination = "/assets/$1.$2"

* docs: rewrite destination replacements description [skip ci]
2023-07-09 11:41:47 +02:00
Jose Quintana
b2e4e49a35 refactor: improve directory listing styling for html display 2023-06-28 00:40:15 +02:00
Jose Quintana
06955e9061 feat: redirect option for url rewrites feature (#231)
the values can be:

- `301` for "Moved Permanently"
- `302` for "Found" (Temporary Redirect)

example:

```toml
[[advanced.rewrites]]
source = "**/*.{jpg,jpeg}"
destination = "/images/generic.png"
redirect = 301
```
2023-06-26 21:59:29 +02:00
Jose Quintana
21c90db067 docs: improve documentation pages [skip ci] 2023-06-16 20:43:45 +02:00
Jose Quintana
79a93f69ed feat: directory-listing cargo feature (#220) 2023-06-14 00:35:08 +02:00