From 934b0e2c4027af916945ed5d4aecfa8249ec2b35 Mon Sep 17 00:00:00 2001 From: Jose Quintana Date: Wed, 2 Apr 2025 00:23:31 +0200 Subject: [PATCH 1/9] docs: v2.36.1 [skip ci] --- docs/content/download-and-install.md | 106 +++++++++++++-------------- scripts/installer.sh | 2 +- 2 files changed, 54 insertions(+), 54 deletions(-) diff --git a/docs/content/download-and-install.md b/docs/content/download-and-install.md index 24f7f9e5..eae08368 100644 --- a/docs/content/download-and-install.md +++ b/docs/content/download-and-install.md @@ -1,13 +1,13 @@ # Download and Install -Latest **v2.36.0** release `2025-02-10` ([changelog](https://github.com/static-web-server/static-web-server/releases/tag/v2.36.0), [sha256sum](https://github.com/static-web-server/static-web-server/releases/download/v2.36.0/static-web-server-v2.36.0-SHA256SUM)) +Latest **v2.36.1** release `2025-04-02` ([changelog](https://github.com/static-web-server/static-web-server/releases/tag/v2.36.1), [sha256sum](https://github.com/static-web-server/static-web-server/releases/download/v2.36.1/static-web-server-v2.36.1-SHA256SUM)) @@ -31,7 +31,7 @@ curl --proto '=https' --tlsv1.2 -sSfL https://get.static-web-server.net | sh Alternatively, you can install a specific version of SWS to a custom location by setting environment variables. ```sh -export SWS_INSTALL_VERSION="2.36.0" # full list at https://github.com/static-web-server/static-web-server/tags +export SWS_INSTALL_VERSION="2.36.1" # full list at https://github.com/static-web-server/static-web-server/tags export SWS_INSTALL_DIR="~/.local/bin" curl --proto '=https' --tlsv1.2 -sSfL https://get.static-web-server.net | sh ``` @@ -111,69 +111,69 @@ Pre-compiled binaries grouped by CPU architectures. ### x86_64 -- [static-web-server-v2.36.0-x86_64-apple-darwin.tar.gz](https://github.com/static-web-server/static-web-server/releases/download/v2.36.0/static-web-server-v2.36.0-x86_64-apple-darwin.tar.gz)
-**SHA256SUM:** `83f5f35197ef9b35475019eba5cd44004f72cecccc61957f2045b26881790c9b` -- [static-web-server-v2.36.0-x86_64-pc-windows-gnu.zip](https://github.com/static-web-server/static-web-server/releases/download/v2.36.0/static-web-server-v2.36.0-x86_64-pc-windows-gnu.zip)
-**SHA256SUM:** `73992f047066aa39d6d28421429b05bbd877c428420a029e8cca5251fc4b1af7` -- [static-web-server-v2.36.0-x86_64-pc-windows-msvc.zip](https://github.com/static-web-server/static-web-server/releases/download/v2.36.0/static-web-server-v2.36.0-x86_64-pc-windows-msvc.zip)
-**SHA256SUM:** `2a0071fd3978bd5fbb09a154e0c1d09672cacd6f9356ebeb25e76bb4d7ee1af9` -- [static-web-server-v2.36.0-x86_64-unknown-freebsd.tar.gz](https://github.com/static-web-server/static-web-server/releases/download/v2.36.0/static-web-server-v2.36.0-x86_64-unknown-freebsd.tar.gz)
-**SHA256SUM:** `862741bb4490a2770325b5b08c475c92bf23c7a297c856ed2df2f9ec631d31e6` -- [static-web-server-v2.36.0-x86_64-unknown-linux-gnu.tar.gz](https://github.com/static-web-server/static-web-server/releases/download/v2.36.0/static-web-server-v2.36.0-x86_64-unknown-linux-gnu.tar.gz)
-**SHA256SUM:** `170d3b99e5f08c61e38caa8335e91fca0c0156c6f3fba1c00b3a2763e16dc7d4` -- [static-web-server-v2.36.0-x86_64-unknown-linux-musl.tar.gz](https://github.com/static-web-server/static-web-server/releases/download/v2.36.0/static-web-server-v2.36.0-x86_64-unknown-linux-musl.tar.gz)
-**SHA256SUM:** `2c047e9e58a6c62a31a2a86f15e45543db1bac6c3e0781b656e39e321d94a618` -- [static-web-server-v2.36.0-x86_64-unknown-netbsd.tar.gz](https://github.com/static-web-server/static-web-server/releases/download/v2.36.0/static-web-server-v2.36.0-x86_64-unknown-netbsd.tar.gz)
-**SHA256SUM:** `6d16bd2fea21186e03e641e043acd49fea76d0993a2fee65c9f7153ecf8bccbf` -- [static-web-server-v2.36.0-x86_64-unknown-illumos.tar.gz](https://github.com/static-web-server/static-web-server/releases/download/v2.36.0/static-web-server-v2.36.0-x86_64-unknown-illumos.tar.gz)
-**SHA256SUM:** `f45facda3d4164e3f6cd3be9b9b38633d5f36e30ecb3a8100c061e4af9ff26e3` +- [static-web-server-v2.36.1-x86_64-apple-darwin.tar.gz](https://github.com/static-web-server/static-web-server/releases/download/v2.36.1/static-web-server-v2.36.1-x86_64-apple-darwin.tar.gz)
+**SHA256SUM:** `7d67d6dc1931805c624280595836843b6b09ad8663690848d1c8779ae5db1669` +- [static-web-server-v2.36.1-x86_64-pc-windows-gnu.zip](https://github.com/static-web-server/static-web-server/releases/download/v2.36.1/static-web-server-v2.36.1-x86_64-pc-windows-gnu.zip)
+**SHA256SUM:** `9278bb9d89a243aeef27dfd80cc4dfc9745717ca2f9a297d0dbc6999ca10eca8` +- [static-web-server-v2.36.1-x86_64-pc-windows-msvc.zip](https://github.com/static-web-server/static-web-server/releases/download/v2.36.1/static-web-server-v2.36.1-x86_64-pc-windows-msvc.zip)
+**SHA256SUM:** `dcf1105a016d1e185edfb0830065e12eea23bb35af5eae64f661117c10970593` +- [static-web-server-v2.36.1-x86_64-unknown-freebsd.tar.gz](https://github.com/static-web-server/static-web-server/releases/download/v2.36.1/static-web-server-v2.36.1-x86_64-unknown-freebsd.tar.gz)
+**SHA256SUM:** `bf609d43ff593fdb50e3f7b51d012a681c9dcb3742157ee9e75baa57a2df0bcd` +- [static-web-server-v2.36.1-x86_64-unknown-linux-gnu.tar.gz](https://github.com/static-web-server/static-web-server/releases/download/v2.36.1/static-web-server-v2.36.1-x86_64-unknown-linux-gnu.tar.gz)
+**SHA256SUM:** `81cac7e086ddce2528bcd3a99ced7b25c92d58e1be24d19717157e551e0f967c` +- [static-web-server-v2.36.1-x86_64-unknown-linux-musl.tar.gz](https://github.com/static-web-server/static-web-server/releases/download/v2.36.1/static-web-server-v2.36.1-x86_64-unknown-linux-musl.tar.gz)
+**SHA256SUM:** `28d353599c3a2407d1cceab6f58cf6aab40886835c226563969629af7e1c7e63` +- [static-web-server-v2.36.1-x86_64-unknown-netbsd.tar.gz](https://github.com/static-web-server/static-web-server/releases/download/v2.36.1/static-web-server-v2.36.1-x86_64-unknown-netbsd.tar.gz)
+**SHA256SUM:** `f2ffb2bd10a98a80d28554f77ca800bddad7833bc9447f84418db173015e550e` +- [static-web-server-v2.36.1-x86_64-unknown-illumos.tar.gz](https://github.com/static-web-server/static-web-server/releases/download/v2.36.1/static-web-server-v2.36.1-x86_64-unknown-illumos.tar.gz)
+**SHA256SUM:** `2f0a32a7fbc16a54644e045760ec952b79f3a98b7771d21c571e64b400074759` ### ARM64 -- [static-web-server-v2.36.0-aarch64-unknown-linux-gnu.tar.gz](https://github.com/static-web-server/static-web-server/releases/download/v2.36.0/static-web-server-v2.36.0-aarch64-unknown-linux-gnu.tar.gz)
-**SHA256SUM:** `166fcac258a7d6bd644d427da005bde2212c57031c88a34526f25b859c3ef2a8` -- [static-web-server-v2.36.0-aarch64-unknown-linux-musl.tar.gz](https://github.com/static-web-server/static-web-server/releases/download/v2.36.0/static-web-server-v2.36.0-aarch64-unknown-linux-musl.tar.gz)
-**SHA256SUM:** `c61e8ecad98309c8a82f101f6a2b43932bcc0fdddaa06231968bd17e56e9f488` -- [static-web-server-v2.36.0-aarch64-apple-darwin.tar.gz](https://github.com/static-web-server/static-web-server/releases/download/v2.36.0/static-web-server-v2.36.0-aarch64-apple-darwin.tar.gz)
-**SHA256SUM:** `4cb4170bb221edaea86fcdc67152aeb125764054b54da0f1afeec3cd7e737b5e` -- [static-web-server-v2.36.0-aarch64-linux-android.tar.gz](https://github.com/static-web-server/static-web-server/releases/download/v2.36.0/static-web-server-v2.36.0-aarch64-linux-android.tar.gz)
-**SHA256SUM:** `f77a638c606ea830fcd14685f27778deda1ffdc6a6e97058e51f9e77acb66833` -- [static-web-server-v2.36.0-aarch64-pc-windows-msvc.zip](https://github.com/static-web-server/static-web-server/releases/download/v2.36.0/static-web-server-v2.36.0-aarch64-pc-windows-msvc.zip)
-**SHA256SUM:** `db8440d04ab0a2e8a229197da5c61344bb4760b7f1add33ba21443e323b8b8f1` +- [static-web-server-v2.36.1-aarch64-unknown-linux-gnu.tar.gz](https://github.com/static-web-server/static-web-server/releases/download/v2.36.1/static-web-server-v2.36.1-aarch64-unknown-linux-gnu.tar.gz)
+**SHA256SUM:** `c201cf1979ec29abfc093202613f6daa2c88b36b041e4804bd38b8e0a1318f6f` +- [static-web-server-v2.36.1-aarch64-unknown-linux-musl.tar.gz](https://github.com/static-web-server/static-web-server/releases/download/v2.36.1/static-web-server-v2.36.1-aarch64-unknown-linux-musl.tar.gz)
+**SHA256SUM:** `21108f2b359086e56faea8a4b3b9170207ad3361ee35b02def45e1b5b71a9880` +- [static-web-server-v2.36.1-aarch64-apple-darwin.tar.gz](https://github.com/static-web-server/static-web-server/releases/download/v2.36.1/static-web-server-v2.36.1-aarch64-apple-darwin.tar.gz)
+**SHA256SUM:** `25e98b9b3b0f71d7055a3a40c3fca4adf00608eb1685a0adf99beb4f02cd63b9` +- [static-web-server-v2.36.1-aarch64-linux-android.tar.gz](https://github.com/static-web-server/static-web-server/releases/download/v2.36.1/static-web-server-v2.36.1-aarch64-linux-android.tar.gz)
+**SHA256SUM:** `47fdd8061a59dd3cef5fc136eb896d01406fc1b6c8cca098e87ef5330497dcba` +- [static-web-server-v2.36.1-aarch64-pc-windows-msvc.zip](https://github.com/static-web-server/static-web-server/releases/download/v2.36.1/static-web-server-v2.36.1-aarch64-pc-windows-msvc.zip)
+**SHA256SUM:** `d6bbddb5ca5b55cb4142e6e30ce1a9f06525595ba5cec9f4bf936ce109f09aae` ### x86 -- [static-web-server-v2.36.0-i686-pc-windows-msvc.zip](https://github.com/static-web-server/static-web-server/releases/download/v2.36.0/static-web-server-v2.36.0-i686-pc-windows-msvc.zip)
-**SHA256SUM:** `5cdd9933ebf7c1621744412a83263ecc5cd0fd6a15d75f3aeb12698e72bd7553` -- [static-web-server-v2.36.0-i686-unknown-freebsd.tar.gz](https://github.com/static-web-server/static-web-server/releases/download/v2.36.0/static-web-server-v2.36.0-i686-unknown-freebsd.tar.gz)
-**SHA256SUM:** `f84d9548f82a044fc4d7a07e341e79afb7c8b8bf5ad01ed4ce584c21493b569a` -- [static-web-server-v2.36.0-i686-unknown-linux-gnu.tar.gz](https://github.com/static-web-server/static-web-server/releases/download/v2.36.0/static-web-server-v2.36.0-i686-unknown-linux-gnu.tar.gz)
-**SHA256SUM:** `9dd732810c6ab9132d17683fac8927811f656c1b45917adfadfa7e96fdc5e89e` -- [static-web-server-v2.36.0-i686-unknown-linux-musl.tar.gz](https://github.com/static-web-server/static-web-server/releases/download/v2.36.0/static-web-server-v2.36.0-i686-unknown-linux-musl.tar.gz)
-**SHA256SUM:** `7b5fe36b8ca2affc4c535ca3d2649ba5960b23be5f769005907c376dac504ae0` +- [static-web-server-v2.36.1-i686-pc-windows-msvc.zip](https://github.com/static-web-server/static-web-server/releases/download/v2.36.1/static-web-server-v2.36.1-i686-pc-windows-msvc.zip)
+**SHA256SUM:** `e7b5a98df19f08f59fd710c6adca3cfe9feb839032f30624f7b8461afabc243d` +- [static-web-server-v2.36.1-i686-unknown-freebsd.tar.gz](https://github.com/static-web-server/static-web-server/releases/download/v2.36.1/static-web-server-v2.36.1-i686-unknown-freebsd.tar.gz)
+**SHA256SUM:** `c6c0050366401bde4882afb85cea1962f02b1c42b06c66cfc8e346f47323e0b5` +- [static-web-server-v2.36.1-i686-unknown-linux-gnu.tar.gz](https://github.com/static-web-server/static-web-server/releases/download/v2.36.1/static-web-server-v2.36.1-i686-unknown-linux-gnu.tar.gz)
+**SHA256SUM:** `f494b9ba0a57fe53390fccc6dc2771f0960e79ce14c37ac37d261ee9228edefa` +- [static-web-server-v2.36.1-i686-unknown-linux-musl.tar.gz](https://github.com/static-web-server/static-web-server/releases/download/v2.36.1/static-web-server-v2.36.1-i686-unknown-linux-musl.tar.gz)
+**SHA256SUM:** `a1ad328bf942598f967d823bae501465f6feb18da062118d1c60b28d9f1474ae` ### ARM -- [static-web-server-v2.36.0-arm-unknown-linux-gnueabihf.tar.gz](https://github.com/static-web-server/static-web-server/releases/download/v2.36.0/static-web-server-v2.36.0-arm-unknown-linux-gnueabihf.tar.gz)
-**SHA256SUM:** `9ad5935882f4fdea5cd990ba4341662141e6ab22ed86f5df1700be43a3944d6d` -- [static-web-server-v2.36.0-arm-unknown-linux-musleabihf.tar.gz](https://github.com/static-web-server/static-web-server/releases/download/v2.36.0/static-web-server-v2.36.0-arm-unknown-linux-musleabihf.tar.gz)
-**SHA256SUM:** `260f17e12c7a23dbd191db596421e76caa1532d29dde0f6861c56b25e2c9f8da` -- [static-web-server-v2.36.0-armv7-unknown-linux-musleabihf.tar.gz](https://github.com/static-web-server/static-web-server/releases/download/v2.36.0/static-web-server-v2.36.0-armv7-unknown-linux-musleabihf.tar.gz)
-**SHA256SUM:** `1066219a8dd30bfc9760b2ba4eeb381effeddc435feaa060f4d3d7f7785c9e5d` +- [static-web-server-v2.36.1-arm-unknown-linux-gnueabihf.tar.gz](https://github.com/static-web-server/static-web-server/releases/download/v2.36.1/static-web-server-v2.36.1-arm-unknown-linux-gnueabihf.tar.gz)
+**SHA256SUM:** `81c34b3819ce2a139a664501589323d3fb1d2565c4454bc16d5c2a7f66a0e6da` +- [static-web-server-v2.36.1-arm-unknown-linux-musleabihf.tar.gz](https://github.com/static-web-server/static-web-server/releases/download/v2.36.1/static-web-server-v2.36.1-arm-unknown-linux-musleabihf.tar.gz)
+**SHA256SUM:** `429567c305444bff1f38869fc31f8be3eb688e7c2f76c3f05b5f883fc0d0254d` +- [static-web-server-v2.36.1-armv7-unknown-linux-musleabihf.tar.gz](https://github.com/static-web-server/static-web-server/releases/download/v2.36.1/static-web-server-v2.36.1-armv7-unknown-linux-musleabihf.tar.gz)
+**SHA256SUM:** `1c640ce9c380d3c82e9ed9304255af721d1405ffeb2a5fe76a20bdb68610e1dc` ### PowerPC -- [static-web-server-v2.36.0-powerpc64le-unknown-linux-gnu.tar.gz](https://github.com/static-web-server/static-web-server/releases/download/v2.36.0/static-web-server-v2.36.0-powerpc64le-unknown-linux-gnu.tar.gz)
-**SHA256SUM:** `0e0064c7317c549cec0efff824aa0f0f1900e0375777eac7a1ff64633200b542` +- [static-web-server-v2.36.1-powerpc64le-unknown-linux-gnu.tar.gz](https://github.com/static-web-server/static-web-server/releases/download/v2.36.1/static-web-server-v2.36.1-powerpc64le-unknown-linux-gnu.tar.gz)
+**SHA256SUM:** `a179b59dc57e756ba419ee2c6a5b84dc0ab327fe6e8cba013de861114bd3d6f6` ### S390X -- [static-web-server-v2.36.0-s390x-unknown-linux-gnu.tar.gz](https://github.com/static-web-server/static-web-server/releases/download/v2.36.0/static-web-server-v2.36.0-s390x-unknown-linux-gnu.tar.gz)
-**SHA256SUM:** `6e49ff4e2c1eed474212e68927ab26856d92a94ec8adfdceb25abf3dcbc32c67` +- [static-web-server-v2.36.1-s390x-unknown-linux-gnu.tar.gz](https://github.com/static-web-server/static-web-server/releases/download/v2.36.1/static-web-server-v2.36.1-s390x-unknown-linux-gnu.tar.gz)
+**SHA256SUM:** `0fb67cdcfb77f1fd0c0031795e00579a164289584526a395a3ba8e4deef30499` ## Source files -- [static-web-server-2.36.0.tar.gz](https://github.com/static-web-server/static-web-server/archive/refs/tags/v2.36.0.tar.gz)
-**SHA256SUM:** `bb99fd25835050e9572ea4589f66b94a64d1724712a2f4881ab35f29d1d8f2a9` -- [static-web-server-2.36.0.zip](https://github.com/static-web-server/static-web-server/archive/refs/tags/v2.36.0.zip)
-**SHA256SUM:** `c03d487ab8b925e482a43fd147106819b2f977ff19fe074ca61f91f2115ccdd4` +- [static-web-server-2.36.1.tar.gz](https://github.com/static-web-server/static-web-server/archive/refs/tags/v2.36.1.tar.gz)
+**SHA256SUM:** `e242e21b3e4b46395bda21b351438df6b5c54b1319a41a86b52eb49ed5567a40` +- [static-web-server-2.36.1.zip](https://github.com/static-web-server/static-web-server/archive/refs/tags/v2.36.1.zip)
+**SHA256SUM:** `6b81abd065f9dfe328f3af365a4b13f7df0a7c3d0fc266abd2b472931e0c833c` diff --git a/scripts/installer.sh b/scripts/installer.sh index ce26409a..68e43886 100755 --- a/scripts/installer.sh +++ b/scripts/installer.sh @@ -23,7 +23,7 @@ fi set -u # SWS latest version -version=${SWS_INSTALL_VERSION:-"2.36.0"} +version=${SWS_INSTALL_VERSION:-"2.36.1"} # Default directory where SWS will be installed local_bin=${SWS_INSTALL_DIR:-"/usr/local/bin"} From 5d1eaac0b55c987f5113a2789c13cd9c445b9bd9 Mon Sep 17 00:00:00 2001 From: Jose Quintana <1700322+joseluisq@users.noreply.github.com> Date: Thu, 10 Apr 2025 01:09:08 +0200 Subject: [PATCH 2/9] chore: automate post-release updates using ci (#538) It just automates the post-release updates needed after every release, like documentation and installer script updates, as well as checksum generation. --- .github/workflows/release.updates.yml | 33 ++++++++++++++++++++------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/.github/workflows/release.updates.yml b/.github/workflows/release.updates.yml index 70b9ae91..b4b17dfa 100644 --- a/.github/workflows/release.updates.yml +++ b/.github/workflows/release.updates.yml @@ -3,12 +3,15 @@ on: release: types: - published + permissions: contents: write + pull-requests: write jobs: - checksum_file: + release_updates: runs-on: ubuntu-22.04 + environment: release-updates-static-web-server steps: - name: Checkout uses: actions/checkout@v4 @@ -18,8 +21,9 @@ jobs: - name: Prepare shell: bash run: | - echo "SERVER_VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV - echo "version is: ${{ env.SERVER_VERSION }}" + echo "RELEASE_VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV + echo "version is: ${{ env.RELEASE_VERSION }}" + echo "PR_BRANCH_NAME=release-docs-${{ env.RELEASE_VERSION }}-$(date -u '+%d-%m-%Y')" >> $GITHUB_ENV - name: Post release updates shell: bash @@ -29,17 +33,30 @@ jobs: run: | scripts/ci/post_release_updates.sh - - name: Commit post release updates changes + - name: Commit post release updates shell: bash env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | git config --global user.name 'github-actions[bot]' git config --global user.email '41898282+github-actions[bot]@users.noreply.github.com' + git add scripts/installer.sh git add docs/content/download-and-install.md git add docs/content/download-and-install.template.md - git commit --verbose -m "docs: ${{ env.SERVER_VERSION }} [skip ci]" - git fetch origin - git rebase --strategy-option=theirs origin/master --verbose - git push --verbose + + git commit --verbose -m "docs: ${{ env.RELEASE_VERSION }} [skip ci]" + git checkout -b ${{ env.PR_BRANCH_NAME }} + git push --set-upstream origin --verbose ${{ env.PR_BRANCH_NAME }} + + - name: Create pull request for release updates + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh pr create \ + --title "Post release ${{ env.RELEASE_VERSION }} updates" \ + --body "An automated PR containing documentation and script installer updates corresponding to the current release \`${{ env.RELEASE_VERSION }}\`.\n\nSee https://static-web-server.net/download-and-install/ for more details." \ + --base "master" \ + --head "${{ env.PR_BRANCH_NAME }}" \ + --label "v2" --label "enhancement" --label "documentation" --label "automated" \ + --draft From a384d9230ae977df6e32cd06645b5d74361eaf87 Mon Sep 17 00:00:00 2001 From: Jose Quintana <1700322+joseluisq@users.noreply.github.com> Date: Wed, 16 Apr 2025 21:38:03 +0200 Subject: [PATCH 3/9] chore: update Alpine (3.20.6) and Debian (12.10) Docker images (#539) --- docker/alpine/Dockerfile | 4 ++-- docker/debian/Dockerfile | 4 ++-- docker/devel/Dockerfile.alpine | 2 +- docker/devel/Dockerfile.debian | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docker/alpine/Dockerfile b/docker/alpine/Dockerfile index 05ac0dd9..08796dc6 100644 --- a/docker/alpine/Dockerfile +++ b/docker/alpine/Dockerfile @@ -1,4 +1,4 @@ -FROM --platform=$BUILDPLATFORM alpine:3.19.6 as build +FROM --platform=$BUILDPLATFORM alpine:3.20.6 AS build ARG TARGETPLATFORM ARG SERVER_VERSION=0.0.0 @@ -30,7 +30,7 @@ RUN set -ex \ && file /usr/local/bin/static-web-server \ && true -FROM alpine:3.19.6 +FROM alpine:3.20.6 ARG SERVER_VERSION=0.0.0 ENV SERVER_VERSION=${SERVER_VERSION} diff --git a/docker/debian/Dockerfile b/docker/debian/Dockerfile index 62d53583..1a19304e 100644 --- a/docker/debian/Dockerfile +++ b/docker/debian/Dockerfile @@ -1,4 +1,4 @@ -FROM --platform=$BUILDPLATFORM debian:12.9-slim as build +FROM --platform=$BUILDPLATFORM debian:12.10-slim AS build ARG TARGETPLATFORM ARG SERVER_VERSION=0.0.0 @@ -48,7 +48,7 @@ RUN set -ex \ && file /usr/local/bin/static-web-server \ && true -FROM debian:12.9-slim +FROM debian:12.10-slim ARG SERVER_VERSION=0.0.0 ENV SERVER_VERSION=${SERVER_VERSION} diff --git a/docker/devel/Dockerfile.alpine b/docker/devel/Dockerfile.alpine index 82692575..1d7c12cb 100644 --- a/docker/devel/Dockerfile.alpine +++ b/docker/devel/Dockerfile.alpine @@ -1,4 +1,4 @@ -FROM alpine:3.19.6 +FROM alpine:3.20.6 ENV SERVER_VERSION=devel diff --git a/docker/devel/Dockerfile.debian b/docker/devel/Dockerfile.debian index 262b819f..bc9d1fc1 100644 --- a/docker/devel/Dockerfile.debian +++ b/docker/devel/Dockerfile.debian @@ -1,4 +1,4 @@ -FROM debian:12.9-slim +FROM debian:12.10-slim ENV SERVER_VERSION=devel From cb1999550e4b08a957d12f4338434426b1d9e3ad Mon Sep 17 00:00:00 2001 From: Jose Quintana <1700322+joseluisq@users.noreply.github.com> Date: Thu, 8 May 2025 01:36:19 +0200 Subject: [PATCH 4/9] fix: generic server log info output even on higher log levels (#542) It fixes an issue (previously an ad-hoc and inconsistent behavior) where SWS printed INFO-level messages at startup despite setting up a higher log level. E.g., `WARN` or `ERROR`. Notice: For consistency reasons, SWS now won't persistently show server information at startup independently of the log level as it did before. Instead, those info log entries are non-persistent and under the normal `INFO` log level. For example, to show those logs again then use the `INFO` log level. $ static-web-server -p 8787 -d public/ -g info --- src/basic_auth.rs | 2 +- src/bin/server.rs | 6 ++--- src/compression.rs | 2 +- src/compression_static.rs | 2 +- src/control_headers.rs | 2 +- src/cors.rs | 2 +- src/directory_listing.rs | 6 ++--- src/fallback_page.rs | 2 +- src/health.rs | 2 +- src/log_addr.rs | 8 +++--- src/logger.rs | 29 +--------------------- src/maintenance_mode.rs | 6 ++--- src/mem_cache/cache.rs | 4 +-- src/metrics.rs | 2 +- src/security_headers.rs | 2 +- src/server.rs | 52 +++++++++++++++++++-------------------- src/settings/mod.rs | 2 +- 17 files changed, 51 insertions(+), 80 deletions(-) diff --git a/src/basic_auth.rs b/src/basic_auth.rs index 924de1c4..7a9c9ca7 100644 --- a/src/basic_auth.rs +++ b/src/basic_auth.rs @@ -15,7 +15,7 @@ use crate::{error_page, handler::RequestHandlerOpts, http_ext::MethodExt, Error} /// Initializes `Basic` HTTP Authorization handling pub(crate) fn init(credentials: &str, handler_opts: &mut RequestHandlerOpts) { credentials.trim().clone_into(&mut handler_opts.basic_auth); - server_info!( + tracing::info!( "basic authentication: enabled={}", !handler_opts.basic_auth.is_empty() ); diff --git a/src/bin/server.rs b/src/bin/server.rs index 95bf3866..4cdd82b4 100644 --- a/src/bin/server.rs +++ b/src/bin/server.rs @@ -25,8 +25,6 @@ fn main() -> Result { } if let Some(commands) = opts.general.commands { - use static_web_server::server_info; - match commands { #[cfg(windows)] Commands::Install {} => { @@ -45,13 +43,13 @@ fn main() -> Result { let mut comp_dir = out_dir.clone(); comp_dir.push("completions"); clap_allgen::render_shell_completions::(&comp_dir)?; - server_info!("wrote completions to {}", comp_dir.to_string_lossy()); + tracing::info!("wrote completions to {}", comp_dir.to_string_lossy()); } if man_pages || !completions { let mut man_dir = out_dir.clone(); man_dir.push("man"); clap_allgen::render_manpages::(&man_dir)?; - server_info!("wrote man pages to {}", man_dir.to_string_lossy()); + tracing::info!("wrote man pages to {}", man_dir.to_string_lossy()); } return Ok(()); } diff --git a/src/compression.rs b/src/compression.rs index f51885fd..cbf88888 100644 --- a/src/compression.rs +++ b/src/compression.rs @@ -82,7 +82,7 @@ pub fn init(enabled: bool, level: CompressionLevel, handler_opts: &mut RequestHa #[cfg(any(feature = "compression", feature = "compression-zstd"))] "zstd", ]; - server_info!( + tracing::info!( "auto compression: enabled={enabled}, formats={}, compression level={level:?}", FORMATS.join(",") ); diff --git a/src/compression_static.rs b/src/compression_static.rs index b1ec5150..e64f581c 100644 --- a/src/compression_static.rs +++ b/src/compression_static.rs @@ -31,7 +31,7 @@ pub struct CompressedFileVariant { /// Initializes static compression. pub fn init(enabled: bool, handler_opts: &mut RequestHandlerOpts) { handler_opts.compression_static = enabled; - server_info!("compression static: enabled={enabled}"); + tracing::info!("compression static: enabled={enabled}"); } /// Post-processing to add Vary header if necessary. diff --git a/src/control_headers.rs b/src/control_headers.rs index 231ca124..670480f2 100644 --- a/src/control_headers.rs +++ b/src/control_headers.rs @@ -26,7 +26,7 @@ const CACHE_EXT_ONE_YEAR: [&str; 32] = [ pub(crate) fn init(enabled: bool, handler_opts: &mut RequestHandlerOpts) { handler_opts.cache_control_headers = enabled; - server_info!("cache control headers: enabled={enabled}"); + tracing::info!("cache control headers: enabled={enabled}"); } /// Appends `Cache-Control` header to a response if necessary diff --git a/src/cors.rs b/src/cors.rs index 6fd9a21e..c6885136 100644 --- a/src/cors.rs +++ b/src/cors.rs @@ -71,7 +71,7 @@ pub fn new( }; if cors_res.is_some() { - server_info!( + tracing::info!( "cors enabled=true, allow_methods=[GET,HEAD,OPTIONS], allow_origins={}, allow_headers=[{}], expose_headers=[{}]", origins_str, allow_headers_str, diff --git a/src/directory_listing.rs b/src/directory_listing.rs index 460d0581..d2f6d489 100644 --- a/src/directory_listing.rs +++ b/src/directory_listing.rs @@ -61,13 +61,13 @@ pub struct DirListOpts<'a> { /// Initializes directory listings. pub fn init(enabled: bool, order: u8, format: DirListFmt, handler_opts: &mut RequestHandlerOpts) { handler_opts.dir_listing = enabled; - server_info!("directory listing: enabled={enabled}"); + tracing::info!("directory listing: enabled={enabled}"); handler_opts.dir_listing_order = order; - server_info!("directory listing order code: {order}"); + tracing::info!("directory listing order code: {order}"); handler_opts.dir_listing_format = format; - server_info!( + tracing::info!( "directory listing format: {:?}", handler_opts.dir_listing_format ); diff --git a/src/fallback_page.rs b/src/fallback_page.rs index 89630a1c..2743b00d 100644 --- a/src/fallback_page.rs +++ b/src/fallback_page.rs @@ -26,7 +26,7 @@ pub(crate) fn init(file_path: &Path, handler_opts: &mut RequestHandlerOpts) { tracing::debug!("fallback page path not found or not a regular file"); } - server_info!( + tracing::info!( "fallback page: enabled={}, value=\"{}\"", found, file_path.display() diff --git a/src/health.rs b/src/health.rs index 99f89469..a4b0f100 100644 --- a/src/health.rs +++ b/src/health.rs @@ -14,7 +14,7 @@ use crate::{handler::RequestHandlerOpts, Error}; /// Initializes the health endpoint. pub fn init(enabled: bool, handler_opts: &mut RequestHandlerOpts) { handler_opts.health = enabled; - server_info!("health endpoint: enabled={enabled}"); + tracing::info!("health endpoint: enabled={enabled}"); } /// Handles health requests. diff --git a/src/log_addr.rs b/src/log_addr.rs index f6bc388a..85595be2 100644 --- a/src/log_addr.rs +++ b/src/log_addr.rs @@ -20,16 +20,16 @@ pub(crate) fn init(enabled: bool, handler_opts: &mut RequestHandlerOpts) { format!("{:?}", handler_opts.trusted_proxies) }; - server_info!("log requests with remote IP addresses: enabled={enabled}"); - server_info!( + tracing::info!("log requests with remote IP addresses: enabled={enabled}"); + tracing::info!( "log X-Real-IP header: enabled={}", handler_opts.log_forwarded_for ); - server_info!( + tracing::info!( "log X-Forwarded-For header: enabled={}", handler_opts.log_forwarded_for ); - server_info!("trusted IPs for X-Forwarded-For: {trusted}"); + tracing::info!("trusted IPs for X-Forwarded-For: {trusted}"); } /// It logs remote and real IP addresses if available. diff --git a/src/logger.rs b/src/logger.rs index 6595ec11..47f3f503 100644 --- a/src/logger.rs +++ b/src/logger.rs @@ -35,12 +35,7 @@ fn configure(level: &str) -> Result { .with_writer(std::io::stderr) .with_span_events(FmtSpan::CLOSE) .with_ansi(enable_ansi) - .with_filter( - Targets::default() - .with_default(level) - .with_target("static_web_server::info", Level::INFO) - .with_target("static_web_server::warn", Level::WARN), - ); + .with_filter(Targets::default().with_default(level)); match tracing_subscriber::registry() .with(filtered_layer) @@ -50,25 +45,3 @@ fn configure(level: &str) -> Result { _ => Ok(()), } } - -/// Custom info level macro. -#[macro_export] -macro_rules! server_info { - ($($arg:tt)*) => { - tracing::info!( - target: "static_web_server::info", - $($arg)* - ) - }; -} - -/// Custom warn level macro. -#[macro_export] -macro_rules! server_warn { - ($($arg:tt)*) => { - tracing::warn!( - target: "static_web_server::warn", - $($arg)* - ) - }; -} diff --git a/src/maintenance_mode.rs b/src/maintenance_mode.rs index 3218456a..f488a23c 100644 --- a/src/maintenance_mode.rs +++ b/src/maintenance_mode.rs @@ -25,15 +25,15 @@ pub(crate) fn init( handler_opts.maintenance_mode = maintenance_mode; handler_opts.maintenance_mode_status = maintenance_mode_status; handler_opts.maintenance_mode_file = maintenance_mode_file; - server_info!( + tracing::info!( "maintenance mode: enabled={}", handler_opts.maintenance_mode ); - server_info!( + tracing::info!( "maintenance mode status: {}", handler_opts.maintenance_mode_status.as_str() ); - server_info!( + tracing::info!( "maintenance mode file: \"{}\"", handler_opts.maintenance_mode_file.display() ); diff --git a/src/mem_cache/cache.rs b/src/mem_cache/cache.rs index a2e55e9e..0db4dc63 100644 --- a/src/mem_cache/cache.rs +++ b/src/mem_cache/cache.rs @@ -67,7 +67,7 @@ pub(crate) fn init(handler_opts: &mut RequestHandlerOpts) -> Result { // Default 8mb let max_file_size = opts.max_file_size.unwrap_or(8192); - server_info!( + tracing::info!( "in-memory cache (experimental): enabled=true, capacity={capacity}, ttl={ttl}, tti={tti}, max_file_size={max_file_size}" ); @@ -91,7 +91,7 @@ pub(crate) fn init(handler_opts: &mut RequestHandlerOpts) -> Result { } } - server_info!("in-memory cache (experimental): enabled=false"); + tracing::info!("in-memory cache (experimental): enabled=false"); Ok(()) } diff --git a/src/metrics.rs b/src/metrics.rs index ed2725e0..037af722 100644 --- a/src/metrics.rs +++ b/src/metrics.rs @@ -15,7 +15,7 @@ use crate::{handler::RequestHandlerOpts, http_ext::MethodExt, Error}; /// Initializes the metrics endpoint. pub fn init(enabled: bool, handler_opts: &mut RequestHandlerOpts) { handler_opts.experimental_metrics = enabled; - server_info!("metrics endpoint (experimental): enabled={enabled}"); + tracing::info!("metrics endpoint (experimental): enabled={enabled}"); if enabled { default_registry() diff --git a/src/security_headers.rs b/src/security_headers.rs index e9c62234..874dd413 100644 --- a/src/security_headers.rs +++ b/src/security_headers.rs @@ -15,7 +15,7 @@ use crate::{handler::RequestHandlerOpts, Error}; pub(crate) fn init(enabled: bool, handler_opts: &mut RequestHandlerOpts) { handler_opts.security_headers = enabled; - server_info!("security headers: enabled={enabled}"); + tracing::info!("security headers: enabled={enabled}"); } /// Appends security headers to a response if necessary diff --git a/src/server.rs b/src/server.rs index eaa5c26e..f65fec43 100644 --- a/src/server.rs +++ b/src/server.rs @@ -139,19 +139,19 @@ impl Server { F: FnOnce(), { tracing::trace!("starting web server"); - server_info!("{} {}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION")); + tracing::info!("{} {}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION")); // Config "general" options let general = self.opts.general; // Config-file "advanced" options let advanced_opts = self.opts.advanced; - server_info!("log level: {}", general.log_level); + tracing::info!("log level: {}", general.log_level); // Config file option let config_file = general.config_file; if config_file.is_file() { - server_info!("config file used: {}", config_file.display()); + tracing::info!("config file used: {}", config_file.display()); } else { tracing::debug!( "config file path not found or not a regular file: {}", @@ -167,7 +167,7 @@ impl Server { tcp_listener = ListenFd::from_env() .take_tcp_listener(fd)? .with_context(|| "failed to convert inherited 'fd' into a 'tcp' listener")?; - server_info!( + tracing::info!( "converted inherited file descriptor {} to a 'tcp' listener", fd ); @@ -181,16 +181,16 @@ impl Server { tcp_listener = TcpListener::bind(addr) .with_context(|| format!("failed to bind to {addr} address"))?; addr_str = addr.to_string(); - server_info!("server bound to tcp socket {}", addr_str); + tracing::info!("server bound to tcp socket {}", addr_str); } } // Number of worker threads option let threads = self.worker_threads; - server_info!("runtime worker threads: {}", threads); + tracing::info!("runtime worker threads: {}", threads); // Maximum number of blocking threads - server_info!( + tracing::info!( "runtime max blocking threads: {}", general.max_blocking_threads ); @@ -236,22 +236,22 @@ impl Server { // Log redirect trailing slash option let redirect_trailing_slash = general.redirect_trailing_slash; - server_info!( + tracing::info!( "redirect trailing slash: enabled={}", redirect_trailing_slash ); // Ignore hidden files option let ignore_hidden_files = general.ignore_hidden_files; - server_info!("ignore hidden files: enabled={}", ignore_hidden_files); + tracing::info!("ignore hidden files: enabled={}", ignore_hidden_files); // Disable symlinks option let disable_symlinks = general.disable_symlinks; - server_info!("disable symlinks: enabled={}", disable_symlinks); + tracing::info!("disable symlinks: enabled={}", disable_symlinks); // Grace period option let grace_period = general.grace_period; - server_info!("grace period before graceful shutdown: {}s", grace_period); + tracing::info!("grace period before graceful shutdown: {}s", grace_period); // Index files option let index_files = general @@ -262,7 +262,7 @@ impl Server { if index_files.is_empty() { bail!("index files list is empty, provide at least one index file") } - server_info!("index files: {}", general.index_files); + tracing::info!("index files: {}", general.index_files); // Request handler options, some settings will be filled in by modules let mut handler_opts = RequestHandlerOpts { @@ -370,11 +370,11 @@ impl Server { #[cfg(windows)] let ctrlc_task = tokio::spawn(async move { if !general.windows_service { - server_info!("installing graceful shutdown ctrl+c signal handler"); + tracing::info!("installing graceful shutdown ctrl+c signal handler"); tokio::signal::ctrl_c() .await .expect("failed to install ctrl+c signal handler"); - server_info!("installing graceful shutdown ctrl+c signal handler"); + tracing::info!("installing graceful shutdown ctrl+c signal handler"); let _ = sender.send(()); } }); @@ -384,16 +384,16 @@ impl Server { if general.http2 { // HTTP to HTTPS redirect option let https_redirect = general.https_redirect; - server_info!("http to https redirect: enabled={}", https_redirect); - server_info!( + tracing::info!("http to https redirect: enabled={}", https_redirect); + tracing::info!( "http to https redirect host: {}", general.https_redirect_host ); - server_info!( + tracing::info!( "http to https redirect from port: {}", general.https_redirect_from_port ); - server_info!( + tracing::info!( "http to https redirect from hosts: {}", general.https_redirect_from_hosts ); @@ -466,7 +466,7 @@ impl Server { } }); - server_info!( + tracing::info!( parent: tracing::info_span!("Server::start_server", ?addr_str, ?threads), "http2 server is listening on https://{}", addr_str @@ -481,7 +481,7 @@ impl Server { let addr = SocketAddr::from((ip, general.https_redirect_from_port)); let tcp_listener = TcpListener::bind(addr) .with_context(|| format!("failed to bind to {addr} address"))?; - server_info!( + tracing::info!( parent: tracing::info_span!("Server::start_server", ?addr, ?threads), "http1 redirect server is listening on http://{}", addr @@ -568,7 +568,7 @@ impl Server { } }); - server_info!("press ctrl+c to shut down the servers"); + tracing::info!("press ctrl+c to shut down the servers"); #[cfg(windows)] tokio::try_join!(ctrlc_task, server_task, redirect_server_task)?; @@ -578,7 +578,7 @@ impl Server { #[cfg(unix)] redirect_handle.close(); } else { - server_info!("press ctrl+c to shut down the server"); + tracing::info!("press ctrl+c to shut down the server"); http2_server.await?; } @@ -588,7 +588,7 @@ impl Server { #[cfg(windows)] _cancel_fn(); - server_warn!("termination signal caught, shutting down the server execution"); + tracing::warn!("termination signal caught, shutting down the server execution"); return Ok(()); } @@ -631,13 +631,13 @@ impl Server { signals::wait_for_ctrl_c(http1_cancel_recv, grace_period).await; }); - server_info!( + tracing::info!( parent: tracing::info_span!("Server::start_server", ?addr_str, ?threads), "http1 server is listening on http://{}", addr_str ); - server_info!("press ctrl+c to shut down the server"); + tracing::info!("press ctrl+c to shut down the server"); #[cfg(unix)] http1_server.await?; @@ -658,7 +658,7 @@ impl Server { #[cfg(unix)] handle.close(); - server_warn!("termination signal caught, shutting down the server execution"); + tracing::warn!("termination signal caught, shutting down the server execution"); Ok(()) } } diff --git a/src/settings/mod.rs b/src/settings/mod.rs index 554cccbe..afd4bb16 100644 --- a/src/settings/mod.rs +++ b/src/settings/mod.rs @@ -422,7 +422,7 @@ impl Settings { tracing::debug!("config file path resolved: {}", config_file.display()); if !has_general_settings { - server_warn!( + tracing::warn!( "config file empty or no `general` settings found, using default values" ); } From 0236980bc5f8b97e71bac2f2d38dcf0afb7ae631 Mon Sep 17 00:00:00 2001 From: Jose Quintana <1700322+joseluisq@users.noreply.github.com> Date: Sat, 17 May 2025 14:39:37 +0200 Subject: [PATCH 5/9] feat: control log ANSI output via boolean `--log-with-ansi` option (#543) This PR adds support for controlling ANSI escape codes for colors and other text formatting when logging via a new boolean --log-with-ansi CLI option and its equivalent SERVER_LOG_WITH_ANSI env. No ANSI by default: Note that from now on, SWS will be no-ansi by default. If you want colored log output, then use --log-with-ansi. For example: ``` static-web-server -p 8788 -d ./public/ -g trace -z --log-with-ansi ``` --- docs/content/configuration/environment-variables.md | 3 +++ docs/content/features/logging.md | 11 +++++++++++ src/logger.rs | 11 +++-------- src/settings/cli.rs | 12 ++++++++++++ src/settings/file.rs | 4 +++- src/settings/mod.rs | 9 +++++++-- 6 files changed, 39 insertions(+), 11 deletions(-) diff --git a/docs/content/configuration/environment-variables.md b/docs/content/configuration/environment-variables.md index 60d4af02..da643fbd 100644 --- a/docs/content/configuration/environment-variables.md +++ b/docs/content/configuration/environment-variables.md @@ -27,6 +27,9 @@ Defines a grace period in seconds after a `SIGTERM` signal is caught which will ### SERVER_LOG_LEVEL Specify a logging level in lowercase. Possible values are `error`, `warn`, `info`, `debug` or `trace`. Default `error`. +### SERVER_LOG_WITH_ANSI +Enable or disable ANSI escape codes for colors and other text formatting of the log output. + ### SERVER_LOG_REMOTE_ADDRESS Log incoming request information along with its Remote Address (IP) if available using the `info` log level. Default `false`. diff --git a/docs/content/features/logging.md b/docs/content/features/logging.md index db3e75d9..25bb087d 100644 --- a/docs/content/features/logging.md +++ b/docs/content/features/logging.md @@ -15,6 +15,16 @@ static-web-server \ > Note: The log format is not well defined and is subject to change. +## Log output with ANSI + +SWS does not output ANSI escape codes by default. However, If you want ANSI escape for colors and other text formatting when logging then use the boolean `--log-with-ansi` CLI option and its equivalent [SERVER_LOG_WITH_ANSI](./../configuration/environment-variables.md#server_log_with_ansi) env. + +For example, if you want colored log output then use the `--log-with-ansi` option as follows: + +```sh +static-web-server -p 8788 -d ./public/ -g trace -z --log-with-ansi +``` + ## Log Remote Addresses SWS provides *Remote Address (IP)* logging for every request via an `INFO` log level. @@ -76,6 +86,7 @@ Since the content of the `X-Forwarded-For` header can be changed by all proxies To restrict the logging to only requests that originate from trusted proxy IPs, you can use the `--trusted-proxies` option, or the equivalent [SERVER_TRUSTED_PROXIES](../configuration/environment-variables.md#server_trusted_proxies) env. This should be a list of IPs, separated by commas. An empty list (the default) indicates that all IPs should be trusted. Command used for the following examples: + ```sh static-web-server -a "::" --log-forwarded-for=true --trusted-proxies="::1" -p 8080 -d docker/public/ -g info ``` diff --git a/src/logger.rs b/src/logger.rs index 47f3f503..1684a463 100644 --- a/src/logger.rs +++ b/src/logger.rs @@ -12,25 +12,20 @@ use tracing_subscriber::{filter::Targets, fmt::format::FmtSpan, prelude::*}; use crate::{Context, Result}; /// Logging system initialization -pub fn init(log_level: &str) -> Result { +pub fn init(log_level: &str, log_with_ansi: bool) -> Result { let log_level = log_level.to_lowercase(); - configure(&log_level).with_context(|| "failed to initialize logging")?; + configure(&log_level, log_with_ansi).with_context(|| "failed to initialize logging")?; Ok(()) } /// Initialize logging builder with its levels. -fn configure(level: &str) -> Result { +fn configure(level: &str, enable_ansi: bool) -> Result { let level = level .parse::() .with_context(|| "failed to parse log level")?; - #[cfg(not(windows))] - let enable_ansi = true; - #[cfg(windows)] - let enable_ansi = false; - let filtered_layer = tracing_subscriber::fmt::layer() .with_writer(std::io::stderr) .with_span_events(FmtSpan::CLOSE) diff --git a/src/settings/cli.rs b/src/settings/cli.rs index 7935828d..1e311075 100644 --- a/src/settings/cli.rs +++ b/src/settings/cli.rs @@ -123,6 +123,18 @@ pub struct General { /// Specify a logging level in lower case. Values: error, warn, info, debug or trace pub log_level: String, + #[arg( + long, + default_value = "false", + default_missing_value("true"), + num_args(0..=1), + require_equals(false), + action = clap::ArgAction::Set, + env = "SERVER_LOG_WITH_ANSI", + )] + /// Enable or disable ANSI escape codes for colors and other text formatting of the log output. + pub log_with_ansi: bool, + #[arg( long, short = 'c', diff --git a/src/settings/file.rs b/src/settings/file.rs index 180c7d34..276dd85b 100644 --- a/src/settings/file.rs +++ b/src/settings/file.rs @@ -205,8 +205,10 @@ pub struct General { /// Root directory path. pub root: Option, - /// Logging. + /// Logging level. pub log_level: Option, + /// Enable/disable ANSI escape codes for log output. + pub log_with_ansi: Option, /// Cache Control headers. pub cache_control_headers: Option, diff --git a/src/settings/mod.rs b/src/settings/mod.rs index afd4bb16..b178b5ab 100644 --- a/src/settings/mod.rs +++ b/src/settings/mod.rs @@ -127,6 +127,7 @@ impl Settings { let mut port = opts.port; let mut root = opts.root; let mut log_level = opts.log_level; + let mut log_with_ansi = opts.log_with_ansi; let mut config_file = opts.config_file.clone(); let mut cache_control_headers = opts.cache_control_headers; @@ -242,6 +243,9 @@ impl Settings { if let Some(ref v) = general.log_level { log_level = v.name().to_lowercase(); } + if let Some(v) = general.log_with_ansi { + log_with_ansi = v; + } if let Some(v) = general.cache_control_headers { cache_control_headers = v } @@ -414,7 +418,7 @@ impl Settings { // Logging system initialization in config file context if log_init { - logger::init(log_level.as_str())?; + logger::init(log_level.as_str(), log_with_ansi)?; } tracing::debug!("config file read successfully"); @@ -596,7 +600,7 @@ impl Settings { } } else if log_init { // Logging system initialization on demand - logger::init(log_level.as_str())?; + logger::init(log_level.as_str(), log_with_ansi)?; } Ok(Settings { @@ -606,6 +610,7 @@ impl Settings { port, root, log_level, + log_with_ansi, config_file, cache_control_headers, #[cfg(any( From 7ca2785c8252af1cedee04040e1a30d81494ee8e Mon Sep 17 00:00:00 2001 From: Jose Quintana <1700322+joseluisq@users.noreply.github.com> Date: Tue, 27 May 2025 00:25:29 +0200 Subject: [PATCH 6/9] 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. --- .cargo/audit.toml | 8 --- .github/workflows/devel.yml | 8 +-- .github/workflows/release.yml | 6 +- Cargo.lock | 88 +++++++++++++++++----------- Cargo.toml | 17 ++---- build.rs | 7 ++- docs/content/building-from-source.md | 4 +- tests/experimental_metrics.rs | 5 +- 8 files changed, 78 insertions(+), 65 deletions(-) diff --git a/.cargo/audit.toml b/.cargo/audit.toml index 9f684112..b28486df 100644 --- a/.cargo/audit.toml +++ b/.cargo/audit.toml @@ -1,9 +1 @@ [advisories] -ignore = [ - # NOTE: - # Ignored and disabled from the `static-web-server` final binary (experimental feature) temporarily. - # See - # - https://github.com/tikv/rust-prometheus/issues/538 - # - https://github.com/Hanaasagi/tokio-metrics-collector/issues/26 - "RUSTSEC-2024-0437" -] diff --git a/.github/workflows/devel.yml b/.github/workflows/devel.yml index 0e10984d..f35ef767 100644 --- a/.github/workflows/devel.yml +++ b/.github/workflows/devel.yml @@ -74,7 +74,7 @@ jobs: # We test against the latest and minimum Rust stable version. - build: pinned os: ubuntu-22.04 - rust: 1.76.0 + rust: 1.81.0 # Some of our release builds are generated by a nightly compiler to take # advantage of the latest optimizations/compile time improvements. - build: linux-musl @@ -148,11 +148,11 @@ jobs: target: aarch64-apple-darwin - build: windows-msvc os: windows-2022 - rust: 1.77.2 + rust: stable target: x86_64-pc-windows-msvc - build: windows-msvc-i686 os: windows-2022 - rust: 1.77.2 + rust: stable target: i686-pc-windows-msvc - build: windows-msvc-arm64 os: windows-2022 @@ -160,7 +160,7 @@ jobs: target: aarch64-pc-windows-msvc - build: windows-pc-gnu os: windows-2022 - rust: 1.77.2 + rust: stable target: x86_64-pc-windows-gnu steps: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bac3448f..65cfcd94 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -142,11 +142,11 @@ jobs: target: aarch64-apple-darwin - build: windows-msvc os: windows-2022 - rust: 1.77.2 + rust: stable target: x86_64-pc-windows-msvc - build: windows-msvc-i686 os: windows-2022 - rust: 1.77.2 + rust: stable target: i686-pc-windows-msvc - build: windows-msvc-arm64 os: windows-2022 @@ -154,7 +154,7 @@ jobs: target: aarch64-pc-windows-msvc - build: windows-pc-gnu os: windows-2022 - rust: 1.77.2 + rust: stable target: x86_64-pc-windows-gnu steps: diff --git a/Cargo.lock b/Cargo.lock index 2bf2ec9c..69445361 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -368,7 +368,7 @@ dependencies = [ "clap_complete_fig", "clap_complete_nushell", "clap_mangen", - "thiserror", + "thiserror 1.0.69", ] [[package]] @@ -460,12 +460,6 @@ dependencies = [ "static_assertions", ] -[[package]] -name = "const_fn" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f8a2ca5ac02d09563609681103aada9e1777d54fc57a5acd7a41404f9c93b6e" - [[package]] name = "const_format" version = "0.2.34" @@ -727,9 +721,9 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "git2" -version = "0.19.0" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b903b73e45dc0c6c596f2d37eccece7c1c8bb6e4407b001096387c63d0d93724" +checksum = "2deb07a133b1520dc1a5690e9bd08950108873d7ed5de38dcc74d3b5ebffa110" dependencies = [ "bitflags", "libc", @@ -1120,9 +1114,9 @@ checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" [[package]] name = "libgit2-sys" -version = "0.17.0+1.8.1" +version = "0.18.1+1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10472326a8a6477c3c20a64547b0059e4b0d086869eee31e6d7da728a8eb7224" +checksum = "e1dcb20f84ffcdd825c7a311ae347cce604a6f084a767dec4a4929829645290e" dependencies = [ "cc", "libc", @@ -1411,9 +1405,9 @@ dependencies = [ [[package]] name = "prometheus" -version = "0.13.4" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d33c28a30771f7f96db69893f78b857f7450d7e0237e9c8fc6427a81bae7ed1" +checksum = "3ca5326d8d0b950a9acd87e6a3f94745394f62e4dae1b1ee22b2bc0c394af43a" dependencies = [ "cfg-if", "fnv", @@ -1421,14 +1415,28 @@ dependencies = [ "memchr", "parking_lot", "protobuf", - "thiserror", + "thiserror 2.0.12", ] [[package]] name = "protobuf" -version = "2.28.0" +version = "3.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d65a1d4ddae7d8b5de68153b48f6aa3bba8cb002b243dbdbc55a5afbc98f99f4" +dependencies = [ + "once_cell", + "protobuf-support", + "thiserror 1.0.69", +] + +[[package]] +name = "protobuf-support" +version = "3.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" +checksum = "3e36c2f31e0a47f9280fb347ef5e461ffcd2c52dd520d8e216b52f93b0b0d7d6" +dependencies = [ + "thiserror 1.0.69", +] [[package]] name = "pulldown-cmark" @@ -1697,9 +1705,9 @@ dependencies = [ [[package]] name = "shadow-rs" -version = "0.36.1" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd2f59f8b166e94269530e0f47323c8b2a5b2d82ef90363cc7ce1e517e063f78" +checksum = "6d5625ed609cf66d7e505e7d487aca815626dc4ebb6c0dd07637ca61a44651a6" dependencies = [ "const_format", "git2", @@ -1823,7 +1831,6 @@ dependencies = [ "hyper", "lazy_static", "listenfd", - "litemap", "maud", "mime_guess", "mini-moka", @@ -1848,8 +1855,6 @@ dependencies = [ "tracing", "tracing-subscriber", "windows-service", - "zerofrom", - "zerofrom-derive", ] [[package]] @@ -1917,7 +1922,16 @@ version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl 2.0.12", ] [[package]] @@ -1931,6 +1945,17 @@ dependencies = [ "syn", ] +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "thread_local" version = "1.1.8" @@ -2046,9 +2071,9 @@ dependencies = [ [[package]] name = "tokio-metrics-collector" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a78b1d1a001e75e5f29669ac7b9288f44ad1af7afcbd6ea77add41214db716" +checksum = "f360c6fa7fd188e65904979ea07f1f44c57f0759ab18ecca88551c26acdf4cd6" dependencies = [ "lazy_static", "parking_lot", @@ -2197,18 +2222,15 @@ checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" [[package]] name = "tz-rs" -version = "0.6.14" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33851b15c848fad2cf4b105c6bb66eb9512b6f6c44a4b13f57c53c73c707e2b4" -dependencies = [ - "const_fn", -] +checksum = "e1450bf2b99397e72070e7935c89facaa80092ac812502200375f1f7d33c71a1" [[package]] name = "tzdb" -version = "0.6.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b580f6b365fa89f5767cdb619a55d534d04a4e14c2d7e5b9a31e94598687fb1" +checksum = "0be2ea5956f295449f47c0b825c5e109022ff1a6a53bb4f77682a87c2341fbf5" dependencies = [ "iana-time-zone", "tz-rs", @@ -2217,9 +2239,9 @@ dependencies = [ [[package]] name = "tzdb_data" -version = "0.1.4" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4471adcfcbd3052e8c5b5890a04a559837444b3be26b9cbbd622063171cec9d" +checksum = "9c4c81d75033770e40fbd3643ce7472a1a9fd301f90b7139038228daf8af03ec" dependencies = [ "tz-rs", ] diff --git a/Cargo.toml b/Cargo.toml index c746ec53..a03284d5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "static-web-server" version = "2.36.1" edition = "2021" -rust-version = "1.76.0" +rust-version = "1.81.0" authors = ["Jose Quintana "] license = "MIT OR Apache-2.0" description = "A cross-platform, high-performance and asynchronous web server for static files-serving." @@ -40,7 +40,7 @@ doc = false # All features enabled by default default = ["compression", "http2", "directory-listing", "basic-auth", "fallback-page"] # Include all features (used when building SWS binaries) -all = ["default"] +all = ["default", "experimental"] # HTTP2 http2 = ["tokio-rustls", "rustls-pemfile"] # Compression @@ -89,7 +89,7 @@ serde = { version = "1.0", default-features = false, features = ["derive"] } serde_ignored = "0.1" serde_json = "1.0" serde_repr = "0.1" -shadow-rs = "0.36" +shadow-rs = "1.1.1" tokio = { version = "1", default-features = false, features = ["rt-multi-thread", "macros", "fs", "io-util", "signal"] } tokio-rustls = { version = "0.26", optional = true, default-features = false, features = ["logging", "tls12", "ring"] } tokio-util = { version = "0.7", default-features = false, features = ["io"] } @@ -97,19 +97,14 @@ toml = "0.8" tracing = { version = "0.1", default-features = false, features = ["std"] } tracing-subscriber = { version = "0.3", default-features = false, features = ["smallvec", "registry", "parking_lot", "fmt", "ansi", "tracing-log"] } -# NOTE: pinning dependencies temporarily (MSRV 1.76.0) because 0.1.6+ requires rustc 1.81 or newer -zerofrom = "=0.1.5" -zerofrom-derive = "=0.1.5" -litemap = "=0.7.4" - [target.'cfg(all(target_env = "musl", target_pointer_width = "64"))'.dependencies.tikv-jemallocator] version = "0.6" [target.'cfg(unix)'.dependencies] signal-hook = { version = "0.3", features = ["extended-siginfo"] } signal-hook-tokio = { version = "0.3", features = ["futures-v0_3"], default-features = false } -tokio-metrics-collector = { version = "0.3", optional = true } -prometheus = { version = "0.13.4", default-features = false, optional = true } +tokio-metrics-collector = { version = "0.3.1", optional = true } +prometheus = { version = "0.14.0", default-features = false, optional = true } [target.'cfg(windows)'.dependencies] windows-service = "0.7" @@ -119,7 +114,7 @@ bytes = "1.10" serde_json = "1.0" [build-dependencies] -shadow-rs = "0.36" +shadow-rs = "1.1.1" [profile.release] codegen-units = 1 diff --git a/build.rs b/build.rs index 4a0dfc45..607cefa7 100644 --- a/build.rs +++ b/build.rs @@ -1,3 +1,6 @@ -fn main() -> shadow_rs::SdResult<()> { - shadow_rs::new() +use shadow_rs::{SdResult, ShadowBuilder}; + +fn main() -> SdResult<()> { + ShadowBuilder::builder().build()?; + Ok(()) } diff --git a/docs/content/building-from-source.md b/docs/content/building-from-source.md index 39710ddd..ec1b0e19 100644 --- a/docs/content/building-from-source.md +++ b/docs/content/building-from-source.md @@ -6,7 +6,7 @@ Follow these instructions to either build **`SWS`** project from the source or t If you want to build **SWS** from the source, all you need is a [Rust 2021 Edition](https://blog.rust-lang.org/2021/05/11/edition-2021.html) installed. -So make sure to install Rust [1.76.0](https://blog.rust-lang.org/2024/02/08/Rust-1.76.0.html) or newer (or nightly) along with [the toolchain(s)](https://rust-lang.github.io/rustup/concepts/toolchains.html) of your preference. +So make sure to install Rust [1.81.0](https://blog.rust-lang.org/2024/09/05/Rust-1.81.0/) or newer (or nightly) along with [the toolchain(s)](https://rust-lang.github.io/rustup/concepts/toolchains.html) of your preference. Then clone the repository and use [Cargo](https://doc.rust-lang.org/cargo/) to build the project from the source. @@ -16,7 +16,7 @@ cd static-web-server cargo build --release ``` -Finally, the release binary should be available at `target/https://blog.rust-lang.org/2024/02/08/Rust-1.76.0.htmlrelease/static-web-server` or under your toolchain directory chosen. +Finally, the release binary should be available at `target/release/static-web-server` or under your toolchain directory chosen. !!! info "Don't use the project's `Makefile`" Please don't use the project's `Makefile` since it's only intended for development and some on-demand tasks. diff --git a/tests/experimental_metrics.rs b/tests/experimental_metrics.rs index 16bde81a..91addee7 100644 --- a/tests/experimental_metrics.rs +++ b/tests/experimental_metrics.rs @@ -9,13 +9,14 @@ pub mod tests { use std::net::SocketAddr; use static_web_server::testing::fixtures::{ - fixture_req_handler, fixture_settings, REMOTE_ADDR, + fixture_req_handler, fixture_req_handler_opts, fixture_settings, REMOTE_ADDR, }; #[tokio::test] async fn experimental_metrics_enabled() { let opts = fixture_settings("toml/experimental_metrics.toml"); - let req_handler = fixture_req_handler(opts.general, opts.advanced); + let req_handler_opts = fixture_req_handler_opts(opts.general, opts.advanced); + let req_handler = fixture_req_handler(req_handler_opts); let remote_addr = Some(REMOTE_ADDR.parse::().unwrap()); let mut req = Request::default(); From 89f5846ffad2cba17baf7c2c6efe1b3ea64cf5ea Mon Sep 17 00:00:00 2001 From: ekangmonyet Date: Sat, 31 May 2025 18:15:44 +0800 Subject: [PATCH 7/9] feat: support for downloading a directory as a compressed tarball (#544) $ static-web-server -p 1234 -d ./public \ --directory-listing --directory-listing-download=targz --- Cargo.lock | 417 +++++++++++++++++- Cargo.toml | 7 +- .../configuration/command-line-arguments.md | 2 + docs/content/configuration/config-file.md | 3 + .../configuration/environment-variables.md | 3 + docs/content/features/directory-listing.md | 12 + src/directory_listing.rs | 27 +- src/directory_listing_download.rs | 212 +++++++++ src/handler.rs | 13 + src/lib.rs | 3 + src/server.rs | 8 + src/settings/cli.rs | 21 + src/settings/file.rs | 8 + src/settings/mod.rs | 9 + src/static_files.rs | 45 ++ src/testing.rs | 2 + tests/dir_listing.rs | 115 +++++ tests/dir_listing_download.rs | 361 +++++++++++++++ tests/static_files.rs | 66 +++ 19 files changed, 1322 insertions(+), 12 deletions(-) create mode 100644 src/directory_listing_download.rs create mode 100644 tests/dir_listing_download.rs diff --git a/Cargo.lock b/Cargo.lock index 69445361..2fe7b362 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -112,6 +112,29 @@ version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" +[[package]] +name = "async-channel" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +dependencies = [ + "concurrent-queue", + "event-listener 2.5.3", + "futures-core", +] + +[[package]] +name = "async-channel" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + [[package]] name = "async-compression" version = "0.4.21" @@ -128,6 +151,155 @@ dependencies = [ "zstd-safe", ] +[[package]] +name = "async-executor" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb812ffb58524bdd10860d7d974e2f01cc0950c2438a74ee5ec2e2280c6c4ffa" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "pin-project-lite", + "slab", +] + +[[package]] +name = "async-global-executor" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" +dependencies = [ + "async-channel 2.3.1", + "async-executor", + "async-io", + "async-lock", + "blocking", + "futures-lite", + "once_cell", +] + +[[package]] +name = "async-io" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059" +dependencies = [ + "async-lock", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix 0.38.44", + "slab", + "tracing", + "windows-sys 0.59.0", +] + +[[package]] +name = "async-lock" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +dependencies = [ + "event-listener 5.4.0", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-process" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63255f1dc2381611000436537bbedfe83183faa303a5a0edaf191edef06526bb" +dependencies = [ + "async-channel 2.3.1", + "async-io", + "async-lock", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener 5.4.0", + "futures-lite", + "rustix 0.38.44", + "tracing", +] + +[[package]] +name = "async-signal" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "637e00349800c0bdf8bfc21ebbc0b6524abea702b0da4168ac00d070d0c0b9f3" +dependencies = [ + "async-io", + "async-lock", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix 0.38.44", + "signal-hook-registry", + "slab", + "windows-sys 0.59.0", +] + +[[package]] +name = "async-std" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "730294c1c08c2e0f85759590518f6333f0d5a0a766a27d519c1b244c3dfd8a24" +dependencies = [ + "async-channel 1.9.0", + "async-global-executor", + "async-io", + "async-lock", + "async-process", + "crossbeam-utils", + "futures-channel", + "futures-core", + "futures-io", + "futures-lite", + "gloo-timers", + "kv-log-macro", + "log", + "memchr", + "once_cell", + "pin-project-lite", + "pin-utils", + "slab", + "wasm-bindgen-futures", +] + +[[package]] +name = "async-tar" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a42f905d4f623faf634bbd1e001e84e0efc24694afa64be9ad239bf6ca49e1f8" +dependencies = [ + "async-std", + "filetime", + "libc", + "pin-project", + "redox_syscall 0.2.16", + "xattr", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "autocfg" version = "1.4.0" @@ -174,6 +346,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "bitflags" version = "2.9.0" @@ -189,6 +367,19 @@ dependencies = [ "generic-array", ] +[[package]] +name = "blocking" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" +dependencies = [ + "async-channel 2.3.1", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + [[package]] name = "blowfish" version = "0.9.1" @@ -460,6 +651,15 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "const_format" version = "0.2.34" @@ -597,12 +797,51 @@ dependencies = [ "version_check", ] +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "event-listener" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener 5.4.0", + "pin-project-lite", +] + [[package]] name = "fastrand" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +[[package]] +name = "filetime" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" +dependencies = [ + "cfg-if", + "libc", + "libredox", + "windows-sys 0.59.0", +] + [[package]] name = "flate2" version = "1.1.0" @@ -643,6 +882,25 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-lite" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + [[package]] name = "futures-macro" version = "0.3.31" @@ -725,7 +983,7 @@ version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2deb07a133b1520dc1a5690e9bd08950108873d7ed5de38dcc74d3b5ebffa110" dependencies = [ - "bitflags", + "bitflags 2.9.0", "libc", "libgit2-sys", "log", @@ -752,6 +1010,18 @@ dependencies = [ "serde", ] +[[package]] +name = "gloo-timers" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + [[package]] name = "h2" version = "0.3.26" @@ -813,6 +1083,12 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + [[package]] name = "http" version = "0.2.12" @@ -1100,6 +1376,15 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "kv-log-macro" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" +dependencies = [ + "log", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -1124,6 +1409,17 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.9.0", + "libc", + "redox_syscall 0.5.10", +] + [[package]] name = "libz-sys" version = "1.1.22" @@ -1136,6 +1432,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + [[package]] name = "linux-raw-sys" version = "0.9.3" @@ -1174,6 +1476,9 @@ name = "log" version = "0.4.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" +dependencies = [ + "value-bag", +] [[package]] name = "maud" @@ -1309,6 +1614,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + [[package]] name = "parking_lot" version = "0.12.3" @@ -1327,7 +1638,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.5.10", "smallvec", "windows-targets", ] @@ -1370,12 +1681,38 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "piper" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + [[package]] name = "pkg-config" version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +[[package]] +name = "polling" +version = "3.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi", + "pin-project-lite", + "rustix 0.38.44", + "tracing", + "windows-sys 0.59.0", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -1444,7 +1781,7 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b" dependencies = [ - "bitflags", + "bitflags 2.9.0", "memchr", "unicase", ] @@ -1464,13 +1801,22 @@ version = "5.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "redox_syscall" version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1" dependencies = [ - "bitflags", + "bitflags 2.9.0", ] [[package]] @@ -1528,16 +1874,29 @@ version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags 2.9.0", + "errno", + "libc", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + [[package]] name = "rustix" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e56a18552996ac8d29ecc3b190b4fdbb2d91ca4ec396de7bbffaf43f3d637e96" dependencies = [ - "bitflags", + "bitflags 2.9.0", "errno", "libc", - "linux-raw-sys", + "linux-raw-sys 0.9.3", "windows-sys 0.59.0", ] @@ -1816,6 +2175,7 @@ dependencies = [ "aho-corasick", "anyhow", "async-compression", + "async-tar", "bcrypt", "bytes", "chrono", @@ -1912,7 +2272,7 @@ dependencies = [ "fastrand", "getrandom 0.3.2", "once_cell", - "rustix", + "rustix 1.0.3", "windows-sys 0.59.0", ] @@ -2111,6 +2471,7 @@ checksum = "6b9590b93e6fcc1739458317cccd391ad3955e2bde8913edf6f95f9e65a8f034" dependencies = [ "bytes", "futures-core", + "futures-io", "futures-sink", "pin-project-lite", "tokio", @@ -2317,6 +2678,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" +[[package]] +name = "value-bag" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "943ce29a8a743eb10d6082545d861b24f9d1b160b7d741e0f2cdf726bec909c5" + [[package]] name = "vcpkg" version = "0.2.15" @@ -2389,6 +2756,19 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.100" @@ -2421,6 +2801,16 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "web-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "widestring" version = "1.2.0" @@ -2479,7 +2869,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d24d6bcc7f734a4091ecf8d7a64c5f7d7066f45585c1861eba06449909609c8a" dependencies = [ - "bitflags", + "bitflags 2.9.0", "widestring", "windows-sys 0.52.0", ] @@ -2581,7 +2971,7 @@ version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ - "bitflags", + "bitflags 2.9.0", ] [[package]] @@ -2596,6 +2986,15 @@ version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" +[[package]] +name = "xattr" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d1526bbe5aaeb5eb06885f4d987bcdfa5e23187055de9b83fe00156a821fabc" +dependencies = [ + "libc", +] + [[package]] name = "yoke" version = "0.7.5" diff --git a/Cargo.toml b/Cargo.toml index a03284d5..b0d4fede 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,7 +38,7 @@ doc = false [features] # All features enabled by default -default = ["compression", "http2", "directory-listing", "basic-auth", "fallback-page"] +default = ["compression", "http2", "directory-listing", "directory-listing-download", "basic-auth", "fallback-page"] # Include all features (used when building SWS binaries) all = ["default", "experimental"] # HTTP2 @@ -51,6 +51,8 @@ compression-gzip = ["async-compression/deflate"] compression-zstd = ["async-compression/zstd"] # Directory listing directory-listing = ["chrono", "maud"] +# Directory listing download +directory-listing-download = ["async-tar", "compression-gzip", "directory-listing"] # Basic HTTP Authorization basic-auth = ["bcrypt"] # Fallback Page @@ -63,6 +65,7 @@ experimental = ["tokio-metrics-collector", "prometheus", "compact_str", "mini-mo aho-corasick = "1.1" anyhow = "1.0" async-compression = { version = "0.4", default-features = false, optional = true, features = ["brotli", "deflate", "gzip", "zstd", "tokio"] } +async-tar = { version = "0.5", optional = true } bcrypt = { version = "0.17", optional = true } bytes = "1.10" chrono = { version = "0.4", default-features = false, features = ["std", "clock"], optional = true } @@ -92,7 +95,7 @@ serde_repr = "0.1" shadow-rs = "1.1.1" tokio = { version = "1", default-features = false, features = ["rt-multi-thread", "macros", "fs", "io-util", "signal"] } tokio-rustls = { version = "0.26", optional = true, default-features = false, features = ["logging", "tls12", "ring"] } -tokio-util = { version = "0.7", default-features = false, features = ["io"] } +tokio-util = { version = "0.7", default-features = false, features = ["compat", "io"] } toml = "0.8" tracing = { version = "0.1", default-features = false, features = ["std"] } tracing-subscriber = { version = "0.3", default-features = false, features = ["smallvec", "registry", "parking_lot", "fmt", "ansi", "tracing-log"] } diff --git a/docs/content/configuration/command-line-arguments.md b/docs/content/configuration/command-line-arguments.md index eaf93fd3..4abc4443 100644 --- a/docs/content/configuration/command-line-arguments.md +++ b/docs/content/configuration/command-line-arguments.md @@ -72,6 +72,8 @@ Options: Specify a default code number to order directory listing entries per `Name`, `Last modified` or `Size` attributes (columns). Code numbers supported: 0 (Name asc), 1 (Name desc), 2 (Last modified asc), 3 (Last modified desc), 4 (Size asc), 5 (Size desc). Default 6 (unordered) [env: SERVER_DIRECTORY_LISTING_ORDER=] [default: 6] --directory-listing-format Specify a content format for directory listing entries. Formats supported: "html" or "json". Default "html" [env: SERVER_DIRECTORY_LISTING_FORMAT=] [default: html] [possible values: html, json] + --directory-listing-download= + Specify list of enabled format(s) for directory download. Format supported: `targz`. Default to empty list (disabled) [env: SERVER_DIRECTORY_LISTING_DOWNLOAD=] [possible values: targz] --security-headers [] Enable security headers by default when HTTP/2 feature is activated. Headers included: "Strict-Transport-Security: max-age=63072000; includeSubDomains; preload" (2 years max-age), "X-Frame-Options: DENY" and "Content-Security-Policy: frame-ancestors 'self'" [env: SERVER_SECURITY_HEADERS=] [default: false] [possible values: true, false] -e, --cache-control-headers [] diff --git a/docs/content/configuration/config-file.md b/docs/content/configuration/config-file.md index d6058962..0cc698cb 100644 --- a/docs/content/configuration/config-file.md +++ b/docs/content/configuration/config-file.md @@ -57,6 +57,9 @@ directory-listing-order = 1 #### Directory listing content format directory-listing-format = "html" +#### Directory listing download format +directory-listing-download = [] + #### Basic Authentication # basic-auth = "" diff --git a/docs/content/configuration/environment-variables.md b/docs/content/configuration/environment-variables.md index da643fbd..154009ed 100644 --- a/docs/content/configuration/environment-variables.md +++ b/docs/content/configuration/environment-variables.md @@ -107,6 +107,9 @@ Specify a default code number to order directory listing entries per `Name`, `La ### SERVER_DIRECTORY_LISTING_FORMAT Specify a content format for the directory listing entries. Formats supported: `html` or `json`. Default `html`. +### SERVER_DIRECTORY_LISTING_DOWNLOAD +Specify list of enabled format(s) for directory download. Format supported: `targz`. Default to empty list (disabled). + ### SERVER_SECURITY_HEADERS Enable security headers by default when the HTTP/2 feature is activated. Headers included: `Strict-Transport-Security: max-age=63072000; includeSubDomains; preload` (2 years max-age), `X-Frame-Options: DENY` and `Content-Security-Policy: frame-ancestors 'self'`. Default `false` (disabled). diff --git a/docs/content/features/directory-listing.md b/docs/content/features/directory-listing.md index 3f82f907..533282c5 100644 --- a/docs/content/features/directory-listing.md +++ b/docs/content/features/directory-listing.md @@ -126,3 +126,15 @@ curl -iH "content-type: application/json" http://localhost:8787 # [{"name":"spécial directöry","type":"directory","mtime":"2022-10-07T00:53:50Z"},{"name":"index.html.gz","type":"file","mtime":"2022-09-27T22:44:34Z","size":332}]⏎ ``` + +## Directory Download +**`SWS`** supports downloading the content of a directory as a single file when **Directory Listing** feature is enabled. To activate, specify the list of download format to enable using the `--directory-listing-download` flag or the equivalent [SERVER_DIRECTORY_LISTING_DOWNLOAD](./../configuration/environment-variables.md#server_directory_listing_download) env. Currently, `targz` format is supported. + +```sh +static-web-server \ + --directory-listing=true \ + --directory-listing-download=targz +``` + +When **Directory Download** is enabled, append `?download` to a directory URL to download it. A link will also be added to the top part of **HTML** output format. + diff --git a/src/directory_listing.rs b/src/directory_listing.rs index d2f6d489..02d4cef1 100644 --- a/src/directory_listing.rs +++ b/src/directory_listing.rs @@ -17,6 +17,9 @@ use std::ffi::{OsStr, OsString}; use std::io; use std::path::Path; +#[cfg(feature = "directory-listing-download")] +use crate::directory_listing_download::{DirDownloadFmt, DOWNLOAD_PARAM_KEY}; + use crate::{handler::RequestHandlerOpts, http_ext::MethodExt, Context, Result}; /// Non-alphanumeric characters to be percent-encoded @@ -52,6 +55,9 @@ pub struct DirListOpts<'a> { pub dir_listing_order: u8, /// Directory listing format. pub dir_listing_format: &'a DirListFmt, + #[cfg(feature = "directory-listing-download")] + /// Directory listing download. + pub dir_listing_download: &'a [DirDownloadFmt], /// Ignore hidden files (dotfiles). pub ignore_hidden_files: bool, /// Prevent following symlinks for files and directories. @@ -96,6 +102,8 @@ pub fn auto_index(opts: DirListOpts<'_>) -> Result, StatusCode> { content_format: opts.dir_listing_format, ignore_hidden_files: opts.ignore_hidden_files, disable_symlinks: opts.disable_symlinks, + #[cfg(feature = "directory-listing-download")] + download: opts.dir_listing_download, }; match read_dir_entries(dir_opts) { Ok(resp) => Ok(resp), @@ -184,6 +192,8 @@ struct DirEntryOpts<'a> { content_format: &'a DirListFmt, ignore_hidden_files: bool, disable_symlinks: bool, + #[cfg(feature = "directory-listing-download")] + download: &'a [DirDownloadFmt], } /// It reads a list of directory entries and create an index page content. @@ -337,6 +347,8 @@ fn read_dir_entries(mut opt: DirEntryOpts<'_>) -> Result> { files_count, &mut file_entries, opt.order_code, + #[cfg(feature = "directory-listing-download")] + opt.download, ) } }; @@ -388,12 +400,25 @@ fn html_auto_index<'a>( files_count: usize, entries: &'a mut [FileEntry], order_code: u8, + #[cfg(feature = "directory-listing-download")] download: &'a [DirDownloadFmt], ) -> String { use maud::{html, DOCTYPE}; let sort_attrs = sort_file_entries(entries, order_code); let current_path = percent_decode_str(base_path).decode_utf8_lossy(); + #[cfg(feature = "directory-listing-download")] + let download_directory_elem = match download.is_empty() { + true => html! {}, + false => html! { + ", " a href={ "?" (DOWNLOAD_PARAM_KEY) } { + "download tar.gz" + } + }, + }; + #[cfg(not(feature = "directory-listing-download"))] + let download_directory_elem = html! {}; + html! { (DOCTYPE) html { @@ -413,7 +438,7 @@ fn html_auto_index<'a>( } p { small { - "directories: " (dirs_count) ", files: " (files_count) + "directories: " (dirs_count) ", files: " (files_count) (download_directory_elem) } } hr; diff --git a/src/directory_listing_download.rs b/src/directory_listing_download.rs new file mode 100644 index 00000000..cfe25545 --- /dev/null +++ b/src/directory_listing_download.rs @@ -0,0 +1,212 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// This file is part of Static Web Server. +// See https://static-web-server.net/ for more information +// Copyright (C) 2019-present Jose Quintana + +//! Compress content of a directory into a tarball +//! + +use async_compression::tokio::write::GzipEncoder; +use async_tar::Builder; +use bytes::BytesMut; +use clap::ValueEnum; +use headers::{ContentType, HeaderMapExt}; +use http::{HeaderValue, Method, Response}; +use hyper::{body::Sender, Body}; +use mime_guess::Mime; +use std::fmt::Display; +use std::path::Path; +use std::path::PathBuf; +use std::str::FromStr; +use std::task::Poll::{Pending, Ready}; +use tokio::fs; +use tokio::io; +use tokio::io::AsyncWriteExt; +use tokio_util::compat::TokioAsyncWriteCompatExt; + +use crate::handler::RequestHandlerOpts; +use crate::http_ext::MethodExt; +use crate::Result; + +/// query parameter key to download directory as tar.gz +pub const DOWNLOAD_PARAM_KEY: &str = "download"; + +/// Download format for directory +#[derive(Debug, Serialize, Deserialize, Clone, ValueEnum, Eq, Hash, PartialEq)] +#[serde(rename_all = "lowercase")] +pub enum DirDownloadFmt { + /// Gunzip-compressed tarball (.tar.gz) + Targz, +} + +impl Display for DirDownloadFmt { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Debug::fmt(self, f) + } +} + +/// Directory download options. +pub struct DirDownloadOpts<'a> { + /// Request method. + pub method: &'a Method, + /// Prevent following symlinks for files and directories. + pub disable_symlinks: bool, + /// Ignore hidden files (dotfiles). + pub ignore_hidden_files: bool, +} + +/// Initializes directory listing download +pub fn init(formats: &Vec, handler_opts: &mut RequestHandlerOpts) { + for fmt in formats { + // Use naive implementation since the list is not expected to be long + if !handler_opts.dir_listing_download.contains(fmt) { + tracing::info!("directory listing download: enabled format {}", &fmt); + handler_opts.dir_listing_download.push(fmt.to_owned()); + } + } + tracing::info!( + "directory listing download: enabled={}", + !handler_opts.dir_listing_download.is_empty() + ); +} + +/// impl AsyncWrite for hyper::Body::Sender +pub struct ChannelBuffer { + s: Sender, +} + +impl tokio::io::AsyncWrite for ChannelBuffer { + fn poll_write( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + buf: &[u8], + ) -> std::task::Poll> { + let this = self.get_mut(); + let b = BytesMut::from(buf); + match this.s.poll_ready(cx) { + Ready(r) => match r { + Ok(()) => match this.s.try_send_data(b.freeze()) { + Ok(_) => Ready(Ok(buf.len())), + Err(_) => Pending, + }, + Err(e) => Ready(Err(io::Error::new(io::ErrorKind::BrokenPipe, e))), + }, + Pending => Pending, + } + } + + fn poll_flush( + self: std::pin::Pin<&mut Self>, + _cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + std::task::Poll::Ready(Ok(())) + } + + fn poll_shutdown( + self: std::pin::Pin<&mut Self>, + _cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + std::task::Poll::Ready(Ok(())) + } +} + +async fn archive( + path: PathBuf, + src_path: PathBuf, + cb: ChannelBuffer, + follow_symlinks: bool, + ignore_hidden: bool, +) -> Result { + let gz = GzipEncoder::with_quality(cb, async_compression::Level::Default); + let mut a = Builder::new(gz.compat_write()); + a.follow_symlinks(follow_symlinks); + + // NOTE: Since it is not possible to handle error gracefully, we will + // just stop writing when error occurs. It is also not possible to call + // sender.abort() as it is protected behind the Builder to ensure + // finish() is successfully called. + + // adapted from async_tar::Builder::append_dir_all + let mut stack = vec![(src_path.to_path_buf(), true, false)]; + while let Some((src, is_dir, is_symlink)) = stack.pop() { + let dest = path.join(src.strip_prefix(&src_path)?); + + // In case of a symlink pointing to a directory, is_dir is false, but src.is_dir() will return true + if is_dir || (is_symlink && follow_symlinks && src.is_dir()) { + let mut entries = fs::read_dir(&src).await?; + while let Some(entry) = entries.next_entry().await? { + // Check and ignore the current hidden file/directory (dotfile) if feature enabled + let name = entry.file_name(); + if ignore_hidden && name.as_encoded_bytes().first().is_some_and(|c| *c == b'.') { + continue; + } + + let file_type = entry.file_type().await?; + stack.push((entry.path(), file_type.is_dir(), file_type.is_symlink())); + } + if dest != Path::new("") { + a.append_dir(&dest, &src).await?; + } + } else { + // use append_path_with_name to handle symlink + a.append_path_with_name(src, &dest).await?; + } + } + + a.finish().await?; + // this is required to emit gzip CRC trailer + a.into_inner().await?.into_inner().shutdown().await?; + + Ok(()) +} + +/// Reply with archived directory content in compressed tarball format. +/// The content from `src_path` on server filesystem will be stored to `path` +/// within the tarball. +/// An async task will be spawned to asynchronously write compressed data to the +/// response body. +pub fn archive_reply(path: P, src_path: Q, opts: DirDownloadOpts<'_>) -> Response +where + P: AsRef, + Q: AsRef, +{ + let archive_name = path.as_ref().with_extension("tar.gz"); + let mut resp = Response::new(Body::empty()); + + resp.headers_mut().typed_insert(ContentType::from( + // since this satisfies the required format: `*/*`, it should not fail + Mime::from_str("application/gzip").unwrap(), + )); + let hvals = format!( + "attachment; filename=\"{}\"", + archive_name.to_string_lossy() + ); + match HeaderValue::from_str(hvals.as_str()) { + Ok(hval) => { + resp.headers_mut() + .insert(hyper::header::CONTENT_DISPOSITION, hval); + } + Err(err) => { + // not fatal, most browser is able to handle the download since + // content-type is set + tracing::error!("can't make content disposition from {}: {:?}", hvals, err); + } + } + + // We skip the body for HEAD requests + if opts.method.is_head() { + return resp; + } + + let (tx, body) = Body::channel(); + tokio::task::spawn(archive( + path.as_ref().into(), + src_path.as_ref().into(), + ChannelBuffer { s: tx }, + !opts.disable_symlinks, + opts.ignore_hidden_files, + )); + *resp.body_mut() = body; + + resp +} diff --git a/src/handler.rs b/src/handler.rs index 24aade74..2992b85e 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -47,6 +47,9 @@ use crate::{ #[cfg(feature = "directory-listing")] use crate::directory_listing::DirListFmt; +#[cfg(feature = "directory-listing-download")] +use crate::directory_listing_download::DirDownloadFmt; + /// It defines options for a request handler. pub struct RequestHandlerOpts { // General options @@ -80,6 +83,10 @@ pub struct RequestHandlerOpts { #[cfg_attr(docsrs, doc(cfg(feature = "directory-listing")))] /// Directory listing format feature. pub dir_listing_format: DirListFmt, + /// Directory listing download feature. + #[cfg(feature = "directory-listing-download")] + #[cfg_attr(docsrs, doc(cfg(feature = "directory-listing-download")))] + pub dir_listing_download: Vec, /// CORS feature. pub cors: Option, /// Security headers feature. @@ -150,6 +157,8 @@ impl Default for RequestHandlerOpts { dir_listing_order: 6, // unordered #[cfg(feature = "directory-listing")] dir_listing_format: DirListFmt::Html, + #[cfg(feature = "directory-listing-download")] + dir_listing_download: Vec::new(), cors: None, #[cfg(feature = "experimental")] memory_cache: None, @@ -200,6 +209,8 @@ impl RequestHandler { let dir_listing_order = self.opts.dir_listing_order; #[cfg(feature = "directory-listing")] let dir_listing_format = &self.opts.dir_listing_format; + #[cfg(feature = "directory-listing-download")] + let dir_listing_download = &self.opts.dir_listing_download; let redirect_trailing_slash = self.opts.redirect_trailing_slash; let compression_static = self.opts.compression_static; let ignore_hidden_files = self.opts.ignore_hidden_files; @@ -286,6 +297,8 @@ impl RequestHandler { dir_listing_order, #[cfg(feature = "directory-listing")] dir_listing_format, + #[cfg(feature = "directory-listing-download")] + dir_listing_download, redirect_trailing_slash, compression_static, ignore_hidden_files, diff --git a/src/lib.rs b/src/lib.rs index 921e1218..3cc8f13e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -156,6 +156,9 @@ pub mod custom_headers; #[cfg(feature = "directory-listing")] #[cfg_attr(docsrs, doc(cfg(feature = "directory-listing")))] pub mod directory_listing; +#[cfg(feature = "directory-listing-download")] +#[cfg_attr(docsrs, doc(cfg(feature = "directory-listing-download")))] +pub mod directory_listing_download; pub mod error_page; #[cfg(feature = "fallback-page")] #[cfg_attr(docsrs, doc(cfg(feature = "fallback-page")))] diff --git a/src/server.rs b/src/server.rs index f65fec43..599c105c 100644 --- a/src/server.rs +++ b/src/server.rs @@ -29,6 +29,10 @@ use { #[cfg(feature = "directory-listing")] use crate::directory_listing; + +#[cfg(feature = "directory-listing-download")] +use crate::directory_listing_download; + #[cfg(feature = "fallback-page")] use crate::fallback_page; @@ -290,6 +294,10 @@ impl Server { &mut handler_opts, ); + // Directory listing download options + #[cfg(feature = "directory-listing-download")] + directory_listing_download::init(&general.directory_listing_download, &mut handler_opts); + // Fallback page option #[cfg(feature = "fallback-page")] fallback_page::init(&general.page_fallback, &mut handler_opts); diff --git a/src/settings/cli.rs b/src/settings/cli.rs index 1e311075..b2b7f6fe 100644 --- a/src/settings/cli.rs +++ b/src/settings/cli.rs @@ -11,6 +11,10 @@ use std::{net::IpAddr, path::PathBuf}; #[cfg(feature = "directory-listing")] use crate::directory_listing::DirListFmt; + +#[cfg(feature = "directory-listing-download")] +use crate::directory_listing_download::DirDownloadFmt; + use crate::Result; /// General server configuration available in CLI and config file options. @@ -361,6 +365,23 @@ pub struct General { /// Specify a content format for directory listing entries. Formats supported: "html" or "json". Default "html". pub directory_listing_format: DirListFmt, + #[cfg(feature = "directory-listing-download")] + #[cfg_attr(docsrs, doc(cfg(feature = "directory-listing-download")))] + #[arg( + long, + value_delimiter(','), + value_enum, + requires_ifs([ + ("targz", "directory_listing"), + ]), + require_equals(true), + action = clap::ArgAction::Set, + env = "SERVER_DIRECTORY_LISTING_DOWNLOAD", + ignore_case(true) + )] + /// Specify list of enabled format(s) for directory download. Format supported: `targz`. Default to empty list (disabled). + pub directory_listing_download: Vec, + #[arg( long, default_value = "false", diff --git a/src/settings/file.rs b/src/settings/file.rs index 276dd85b..75d70d72 100644 --- a/src/settings/file.rs +++ b/src/settings/file.rs @@ -15,6 +15,9 @@ use std::{collections::BTreeSet, path::PathBuf}; #[cfg(feature = "directory-listing")] use crate::directory_listing::DirListFmt; +#[cfg(feature = "directory-listing-download")] +use crate::directory_listing_download::DirDownloadFmt; + use crate::{helpers, Context, Result}; #[derive(Debug, Serialize, Deserialize, Clone)] @@ -334,6 +337,11 @@ pub struct General { #[cfg_attr(docsrs, doc(cfg(feature = "directory-listing")))] pub directory_listing_format: Option, + /// Directory listing download feature. + #[cfg(feature = "directory-listing-download")] + #[cfg_attr(docsrs, doc(cfg(feature = "directory-listing-download")))] + pub directory_listing_download: Option>, + /// Basic Authentication feature. #[cfg(feature = "basic-auth")] #[cfg_attr(docsrs, doc(cfg(feature = "basic-auth")))] diff --git a/src/settings/mod.rs b/src/settings/mod.rs index b178b5ab..b6b8c3e8 100644 --- a/src/settings/mod.rs +++ b/src/settings/mod.rs @@ -186,6 +186,9 @@ impl Settings { #[cfg(feature = "directory-listing")] let mut directory_listing_format = opts.directory_listing_format; + #[cfg(feature = "directory-listing-download")] + let mut directory_listing_download = opts.directory_listing_download; + #[cfg(feature = "basic-auth")] let mut basic_auth = opts.basic_auth; @@ -347,6 +350,10 @@ impl Settings { if let Some(v) = general.directory_listing_format { directory_listing_format = v } + #[cfg(feature = "directory-listing-download")] + if let Some(v) = general.directory_listing_download { + directory_listing_download = v + } #[cfg(feature = "basic-auth")] if let Some(ref v) = general.basic_auth { v.clone_into(&mut basic_auth) @@ -663,6 +670,8 @@ impl Settings { directory_listing_order, #[cfg(feature = "directory-listing")] directory_listing_format, + #[cfg(feature = "directory-listing-download")] + directory_listing_download, #[cfg(feature = "basic-auth")] basic_auth, fd, diff --git a/src/static_files.rs b/src/static_files.rs index f4ff425f..9aa7b449 100644 --- a/src/static_files.rs +++ b/src/static_files.rs @@ -40,6 +40,11 @@ use crate::{ directory_listing::{DirListFmt, DirListOpts}, }; +#[cfg(feature = "directory-listing-download")] +use crate::directory_listing_download::{ + archive_reply, DirDownloadFmt, DirDownloadOpts, DOWNLOAD_PARAM_KEY, +}; + const DEFAULT_INDEX_FILES: &[&str; 1] = &["index.html"]; /// Defines all options needed by the static-files handler. @@ -71,6 +76,10 @@ pub struct HandleOpts<'a> { #[cfg(feature = "directory-listing")] #[cfg_attr(docsrs, doc(cfg(feature = "directory-listing")))] pub dir_listing_format: &'a DirListFmt, + /// Directory listing download feature. + #[cfg(feature = "directory-listing-download")] + #[cfg_attr(docsrs, doc(cfg(feature = "directory-listing-download")))] + pub dir_listing_download: &'a [DirDownloadFmt], /// Redirect trailing slash feature. pub redirect_trailing_slash: bool, /// Compression static feature. @@ -185,6 +194,40 @@ pub async fn handle(opts: &HandleOpts<'_>) -> Result) -> Result { + let mut res = result.resp; + assert_eq!(res.status(), 200); + assert_eq!(res.headers()["content-type"], "text/html; charset=utf-8"); + + let body = hyper::body::to_bytes(res.body_mut()) + .await + .expect("unexpected bytes error during `body` conversion"); + let body_str = std::str::from_utf8(&body).unwrap(); + + if method == Method::GET { + assert!(body_str.contains("download tar.gz")) + } else { + assert!(body_str.is_empty()); + } + } + Err(status) => { + assert!(method != Method::GET && method != Method::HEAD); + assert_eq!(status, StatusCode::METHOD_NOT_ALLOWED); + } + } + } + } + + #[cfg(feature = "directory-listing-download")] + #[tokio::test] + async fn dir_listing_has_no_download_link_when_disabled() { + for method in METHODS { + match static_files::handle(&HandleOpts { + method: &method, + headers: &HeaderMap::new(), + base_path: &root_dir("tests/fixtures/public"), + uri_path: "/", + uri_query: None, + #[cfg(feature = "experimental")] + memory_cache: None, + dir_listing: true, + dir_listing_order: 1, + dir_listing_format: &DirListFmt::Html, + redirect_trailing_slash: true, + compression_static: false, + ignore_hidden_files: true, + disable_symlinks: false, + index_files: &[], + dir_listing_download: &[], + }) + .await + { + Ok(result) => { + let mut res = result.resp; + assert_eq!(res.status(), 200); + assert_eq!(res.headers()["content-type"], "text/html; charset=utf-8"); + + let body = hyper::body::to_bytes(res.body_mut()) + .await + .expect("unexpected bytes error during `body` conversion"); + let body_str = std::str::from_utf8(&body).unwrap(); + + if method == Method::GET { + assert!(!body_str.contains("download tar.gz")) + } else { + assert!(body_str.is_empty()); + } + } + Err(status) => { + assert!(method != Method::GET && method != Method::HEAD); + assert_eq!(status, StatusCode::METHOD_NOT_ALLOWED); + } + } + } + } } diff --git a/tests/dir_listing_download.rs b/tests/dir_listing_download.rs new file mode 100644 index 00000000..9ec12f09 --- /dev/null +++ b/tests/dir_listing_download.rs @@ -0,0 +1,361 @@ +#![forbid(unsafe_code)] +#![deny(warnings)] +#![deny(rust_2018_idioms)] +#![deny(dead_code)] + +#[cfg(feature = "directory-listing-download")] +#[cfg(test)] +mod tests { + use async_compression::tokio::bufread::GzipDecoder; + use async_tar::Archive; + use futures_util::StreamExt; + use headers::HeaderMap; + use http::{Method, StatusCode}; + use std::{ + collections::HashSet, + path::{Path, PathBuf}, + pin::Pin, + }; + use tokio::{fs, io::AsyncReadExt}; + use tokio_util::compat::{FuturesAsyncReadCompatExt, TokioAsyncReadCompatExt}; + + use static_web_server::{ + directory_listing::DirListFmt, + directory_listing_download::DirDownloadOpts, + static_files::{self, HandleOpts}, + }; + + use static_web_server::directory_listing_download::{DirDownloadFmt, DOWNLOAD_PARAM_KEY}; + + const METHODS: [Method; 8] = [ + Method::CONNECT, + Method::DELETE, + Method::GET, + Method::HEAD, + Method::PATCH, + Method::POST, + Method::PUT, + Method::TRACE, + ]; + + fn root_dir>(dir: P) -> PathBuf + where + PathBuf: From

, + { + PathBuf::from(dir) + } + + async fn inspect_tarball_content( + prefix: PathBuf, + body: &[u8], + validate: bool, + ) -> HashSet { + let reader = Archive::new(GzipDecoder::new(body).compat()); + + let mut content = HashSet::new(); + // adapted from async_tar::Archive::unpack + let mut entries = reader.entries().unwrap(); + let mut pinned = Pin::new(&mut entries); + while let Some(entry) = pinned.next().await { + let file = entry.unwrap(); + let path: PathBuf = file.header().path().unwrap().to_path_buf().into(); + + // validate content + if validate + && (file.header().entry_type() == async_tar::EntryType::Link + || file.header().entry_type() == async_tar::EntryType::Regular + || file.header().entry_type() == async_tar::EntryType::Symlink) + { + let on_disk_path = prefix.join(&path); + // in case of symlink, skip dir + let meta = std::fs::metadata(&on_disk_path).unwrap(); + if !meta.is_dir() { + let on_disk = std::fs::read(&on_disk_path).unwrap(); + let mut compressed = Vec::new(); + file.compat().read_to_end(&mut compressed).await.unwrap(); + assert_eq!(on_disk, compressed); + } + } + + content.insert(path); + } + content + } + + async fn get_dir_content( + path: PathBuf, + src_path: PathBuf, + opts: DirDownloadOpts<'_>, + ) -> HashSet { + let mut content = HashSet::new(); + let mut stack = vec![(src_path.to_path_buf(), true, false)]; + + while let Some((src, is_dir, is_symlink)) = stack.pop() { + let dest = path.join(src.strip_prefix(&src_path).unwrap()); + + // In case of a symlink pointing to a directory, is_dir is false, but src.is_dir() will return true + if is_dir || (is_symlink && !opts.disable_symlinks && src.is_dir()) { + let mut entries = fs::read_dir(&src).await.unwrap(); + while let Some(entry) = entries.next_entry().await.unwrap() { + // Check and ignore the current hidden file/directory (dotfile) if feature enabled + let name = entry.file_name(); + if opts.ignore_hidden_files + && name.as_encoded_bytes().first().is_some_and(|c| *c == b'.') + { + continue; + } + + let file_type = entry.file_type().await.unwrap(); + stack.push((entry.path(), file_type.is_dir(), file_type.is_symlink())); + } + if dest != Path::new("") { + content.insert(dest); + } + } else { + content.insert(dest); + } + } + + content + } + + #[tokio::test] + async fn dir_listing_download_targz() { + let base_path = root_dir("tests/fixtures/public"); + let disable_symlinks = false; + for method in METHODS { + match static_files::handle(&HandleOpts { + method: &method, + headers: &HeaderMap::new(), + base_path: &base_path, + uri_path: "/", + uri_query: Some(DOWNLOAD_PARAM_KEY), + #[cfg(feature = "experimental")] + memory_cache: None, + dir_listing: true, + dir_listing_order: 1, + dir_listing_format: &DirListFmt::Html, + redirect_trailing_slash: true, + compression_static: false, + ignore_hidden_files: false, + disable_symlinks, + index_files: &[], + dir_listing_download: &vec![DirDownloadFmt::Targz], + }) + .await + { + Ok(result) => { + let mut res = result.resp; + assert_eq!(res.status(), 200); + assert_eq!(res.headers()["content-type"], "application/gzip"); + assert!(res.headers()["content-disposition"] + .to_str() + .unwrap() + .starts_with("attachment")); + + let body = hyper::body::to_bytes(res.body_mut()) + .await + .expect("unexpected bytes error during `body` conversion"); + + if method == Method::GET { + let mut prefix = base_path.clone(); + prefix.pop(); + let left = inspect_tarball_content(prefix, &body, true).await; + let right = get_dir_content( + PathBuf::from(base_path.file_name().unwrap()), + base_path.clone(), + DirDownloadOpts { + method: &method, + disable_symlinks, + ignore_hidden_files: false, + }, + ) + .await; + + if left != right { + eprintln!("left - right {:?}", (left.difference(&right))); + eprintln!("right - left {:?}", (right.difference(&left))); + } + + assert_eq!(left, right); + } else { + assert!(body.len() == 0); + } + } + Err(status) => { + assert!(method != Method::GET && method != Method::HEAD); + assert_eq!(status, StatusCode::METHOD_NOT_ALLOWED); + } + } + } + } + + #[tokio::test] + async fn dir_listing_download_targz_no_hidden() { + let base_path = root_dir("tests/fixtures/public"); + for method in METHODS { + match static_files::handle(&HandleOpts { + method: &method, + headers: &HeaderMap::new(), + base_path: &base_path, + uri_path: "/", + uri_query: Some(DOWNLOAD_PARAM_KEY), + #[cfg(feature = "experimental")] + memory_cache: None, + dir_listing: true, + dir_listing_order: 1, + dir_listing_format: &DirListFmt::Html, + redirect_trailing_slash: true, + compression_static: false, + ignore_hidden_files: true, + disable_symlinks: false, + index_files: &[], + dir_listing_download: &vec![DirDownloadFmt::Targz], + }) + .await + { + Ok(result) => { + let mut res = result.resp; + assert_eq!(res.status(), 200); + assert_eq!(res.headers()["content-type"], "application/gzip"); + assert!(res.headers()["content-disposition"] + .to_str() + .unwrap() + .starts_with("attachment")); + + let body = hyper::body::to_bytes(res.body_mut()) + .await + .expect("unexpected bytes error during `body` conversion"); + + if method == Method::GET { + let mut prefix = base_path.clone(); + prefix.pop(); + assert!(inspect_tarball_content(prefix, &body, false) + .await + .iter() + .find(|path| path.file_name().unwrap() == ".dotfile") + .is_none()); + } else { + assert!(body.len() == 0); + } + } + Err(status) => { + assert!(method != Method::GET && method != Method::HEAD); + assert_eq!(status, StatusCode::METHOD_NOT_ALLOWED); + } + } + } + } + + #[tokio::test] + async fn dir_listing_download_targz_no_symlinks() { + let base_path = root_dir("tests/fixtures/public"); + let disable_symlinks = true; + for method in METHODS { + match static_files::handle(&HandleOpts { + method: &method, + headers: &HeaderMap::new(), + base_path: &base_path, + uri_path: "/", + uri_query: Some(DOWNLOAD_PARAM_KEY), + #[cfg(feature = "experimental")] + memory_cache: None, + dir_listing: true, + dir_listing_order: 1, + dir_listing_format: &DirListFmt::Html, + redirect_trailing_slash: true, + compression_static: false, + ignore_hidden_files: false, + disable_symlinks, + index_files: &[], + dir_listing_download: &vec![DirDownloadFmt::Targz], + }) + .await + { + Ok(result) => { + let mut res = result.resp; + assert_eq!(res.status(), 200); + assert_eq!(res.headers()["content-type"], "application/gzip"); + assert!(res.headers()["content-disposition"] + .to_str() + .unwrap() + .starts_with("attachment")); + + let body = hyper::body::to_bytes(res.body_mut()) + .await + .expect("unexpected bytes error during `body` conversion"); + + if method == Method::GET { + let mut prefix = base_path.clone(); + prefix.pop(); + let left = inspect_tarball_content(prefix, &body, false).await; + let right = get_dir_content( + PathBuf::from(base_path.file_name().unwrap()), + base_path.clone(), + DirDownloadOpts { + method: &method, + disable_symlinks, + ignore_hidden_files: false, + }, + ) + .await; + + if left != right { + eprintln!("left - right {:?}", (left.difference(&right))); + eprintln!("right - left {:?}", (right.difference(&left))); + } + + assert_eq!(left, right); + } else { + assert!(body.len() == 0); + } + } + Err(status) => { + assert!(method != Method::GET && method != Method::HEAD); + assert_eq!(status, StatusCode::METHOD_NOT_ALLOWED); + } + } + } + } + + #[tokio::test] + async fn dir_listing_download_when_disabled() { + for method in METHODS { + match static_files::handle(&HandleOpts { + method: &method, + headers: &HeaderMap::new(), + base_path: &root_dir("tests/fixtures/public"), + uri_path: "/", + uri_query: Some(DOWNLOAD_PARAM_KEY), + #[cfg(feature = "experimental")] + memory_cache: None, + dir_listing: true, + dir_listing_order: 1, + dir_listing_format: &DirListFmt::Html, + redirect_trailing_slash: true, + compression_static: false, + ignore_hidden_files: false, + disable_symlinks: false, + index_files: &[], + dir_listing_download: &vec![], + }) + .await + { + Ok(result) => { + let res = result.resp; + assert_eq!(res.status(), 200); + assert_eq!(res.headers()["content-type"], "text/html; charset=utf-8"); + assert!(res + .headers() + .iter() + .find(|(k, _v)| *k == "content-disposition") + .is_none()); + } + Err(status) => { + assert!(method != Method::GET && method != Method::HEAD); + assert_eq!(status, StatusCode::METHOD_NOT_ALLOWED); + } + } + } + } +} diff --git a/tests/static_files.rs b/tests/static_files.rs index 74f4d9ad..efdf5945 100644 --- a/tests/static_files.rs +++ b/tests/static_files.rs @@ -55,6 +55,8 @@ mod tests { dir_listing_order: 6, #[cfg(feature = "directory-listing")] dir_listing_format: &DirListFmt::Html, + #[cfg(feature = "directory-listing-download")] + dir_listing_download: &[], redirect_trailing_slash: true, compression_static: false, ignore_hidden_files: false, @@ -101,6 +103,8 @@ mod tests { dir_listing_order: 6, #[cfg(feature = "directory-listing")] dir_listing_format: &DirListFmt::Html, + #[cfg(feature = "directory-listing-download")] + dir_listing_download: &[], redirect_trailing_slash: true, compression_static: false, ignore_hidden_files: false, @@ -148,6 +152,8 @@ mod tests { dir_listing_order: 6, #[cfg(feature = "directory-listing")] dir_listing_format: &DirListFmt::Html, + #[cfg(feature = "directory-listing-download")] + dir_listing_download: &[], redirect_trailing_slash: true, compression_static: false, ignore_hidden_files: false, @@ -182,6 +188,8 @@ mod tests { dir_listing_order: 0, #[cfg(feature = "directory-listing")] dir_listing_format: &DirListFmt::Html, + #[cfg(feature = "directory-listing-download")] + dir_listing_download: &[], redirect_trailing_slash: true, compression_static: false, ignore_hidden_files: false, @@ -218,6 +226,8 @@ mod tests { dir_listing_order: 0, #[cfg(feature = "directory-listing")] dir_listing_format: &DirListFmt::Html, + #[cfg(feature = "directory-listing-download")] + dir_listing_download: &[], redirect_trailing_slash: true, compression_static: false, ignore_hidden_files: false, @@ -253,6 +263,8 @@ mod tests { dir_listing_order: 0, #[cfg(feature = "directory-listing")] dir_listing_format: &DirListFmt::Html, + #[cfg(feature = "directory-listing-download")] + dir_listing_download: &[], redirect_trailing_slash: false, compression_static: false, ignore_hidden_files: false, @@ -293,6 +305,8 @@ mod tests { dir_listing_order: 6, #[cfg(feature = "directory-listing")] dir_listing_format: &DirListFmt::Html, + #[cfg(feature = "directory-listing-download")] + dir_listing_download: &[], redirect_trailing_slash: true, compression_static: false, ignore_hidden_files: false, @@ -348,6 +362,8 @@ mod tests { dir_listing_order: 6, #[cfg(feature = "directory-listing")] dir_listing_format: &DirListFmt::Html, + #[cfg(feature = "directory-listing-download")] + dir_listing_download: &[], redirect_trailing_slash: true, compression_static: false, ignore_hidden_files: false, @@ -385,6 +401,8 @@ mod tests { dir_listing_order: 6, #[cfg(feature = "directory-listing")] dir_listing_format: &DirListFmt::Html, + #[cfg(feature = "directory-listing-download")] + dir_listing_download: &[], redirect_trailing_slash: true, compression_static: false, ignore_hidden_files: false, @@ -424,6 +442,8 @@ mod tests { dir_listing_order: 6, #[cfg(feature = "directory-listing")] dir_listing_format: &DirListFmt::Html, + #[cfg(feature = "directory-listing-download")] + dir_listing_download: &[], redirect_trailing_slash: true, compression_static: false, ignore_hidden_files: false, @@ -464,6 +484,8 @@ mod tests { dir_listing_order: 6, #[cfg(feature = "directory-listing")] dir_listing_format: &DirListFmt::Html, + #[cfg(feature = "directory-listing-download")] + dir_listing_download: &[], redirect_trailing_slash: true, compression_static: false, ignore_hidden_files: false, @@ -507,6 +529,8 @@ mod tests { dir_listing_order: 6, #[cfg(feature = "directory-listing")] dir_listing_format: &DirListFmt::Html, + #[cfg(feature = "directory-listing-download")] + dir_listing_download: &[], redirect_trailing_slash: true, compression_static: false, ignore_hidden_files: false, @@ -548,6 +572,8 @@ mod tests { dir_listing_order: 6, #[cfg(feature = "directory-listing")] dir_listing_format: &DirListFmt::Html, + #[cfg(feature = "directory-listing-download")] + dir_listing_download: &[], redirect_trailing_slash: true, compression_static: false, ignore_hidden_files: false, @@ -587,6 +613,8 @@ mod tests { dir_listing_order: 6, #[cfg(feature = "directory-listing")] dir_listing_format: &DirListFmt::Html, + #[cfg(feature = "directory-listing-download")] + dir_listing_download: &[], redirect_trailing_slash: true, compression_static: false, ignore_hidden_files: false, @@ -625,6 +653,8 @@ mod tests { dir_listing_order: 6, #[cfg(feature = "directory-listing")] dir_listing_format: &DirListFmt::Html, + #[cfg(feature = "directory-listing-download")] + dir_listing_download: &[], redirect_trailing_slash: true, compression_static: false, ignore_hidden_files: false, @@ -667,6 +697,8 @@ mod tests { dir_listing_order: 6, #[cfg(feature = "directory-listing")] dir_listing_format: &DirListFmt::Html, + #[cfg(feature = "directory-listing-download")] + dir_listing_download: &[], redirect_trailing_slash: true, compression_static: false, ignore_hidden_files: false, @@ -752,6 +784,8 @@ mod tests { dir_listing_order: 6, #[cfg(feature = "directory-listing")] dir_listing_format: &DirListFmt::Html, + #[cfg(feature = "directory-listing-download")] + dir_listing_download: &[], redirect_trailing_slash: true, compression_static: false, ignore_hidden_files: false, @@ -825,6 +859,8 @@ mod tests { dir_listing_order: 6, #[cfg(feature = "directory-listing")] dir_listing_format: &DirListFmt::Html, + #[cfg(feature = "directory-listing-download")] + dir_listing_download: &[], redirect_trailing_slash: true, compression_static: false, ignore_hidden_files: false, @@ -877,6 +913,8 @@ mod tests { dir_listing_order: 6, #[cfg(feature = "directory-listing")] dir_listing_format: &DirListFmt::Html, + #[cfg(feature = "directory-listing-download")] + dir_listing_download: &[], redirect_trailing_slash: true, compression_static: false, ignore_hidden_files: false, @@ -929,6 +967,8 @@ mod tests { dir_listing_order: 6, #[cfg(feature = "directory-listing")] dir_listing_format: &DirListFmt::Html, + #[cfg(feature = "directory-listing-download")] + dir_listing_download: &[], redirect_trailing_slash: true, compression_static: false, ignore_hidden_files: false, @@ -982,6 +1022,8 @@ mod tests { dir_listing_order: 6, #[cfg(feature = "directory-listing")] dir_listing_format: &DirListFmt::Html, + #[cfg(feature = "directory-listing-download")] + dir_listing_download: &[], redirect_trailing_slash: true, compression_static: false, ignore_hidden_files: false, @@ -1027,6 +1069,8 @@ mod tests { dir_listing_order: 6, #[cfg(feature = "directory-listing")] dir_listing_format: &DirListFmt::Html, + #[cfg(feature = "directory-listing-download")] + dir_listing_download: &[], redirect_trailing_slash: true, compression_static: false, ignore_hidden_files: false, @@ -1082,6 +1126,8 @@ mod tests { dir_listing_order: 6, #[cfg(feature = "directory-listing")] dir_listing_format: &DirListFmt::Html, + #[cfg(feature = "directory-listing-download")] + dir_listing_download: &[], redirect_trailing_slash: true, compression_static: false, ignore_hidden_files: false, @@ -1134,6 +1180,8 @@ mod tests { dir_listing_order: 6, #[cfg(feature = "directory-listing")] dir_listing_format: &DirListFmt::Html, + #[cfg(feature = "directory-listing-download")] + dir_listing_download: &[], redirect_trailing_slash: true, compression_static: false, ignore_hidden_files: false, @@ -1186,6 +1234,8 @@ mod tests { dir_listing_order: 6, #[cfg(feature = "directory-listing")] dir_listing_format: &DirListFmt::Html, + #[cfg(feature = "directory-listing-download")] + dir_listing_download: &[], redirect_trailing_slash: true, compression_static: false, ignore_hidden_files: false, @@ -1241,6 +1291,8 @@ mod tests { dir_listing_order: 6, #[cfg(feature = "directory-listing")] dir_listing_format: &DirListFmt::Html, + #[cfg(feature = "directory-listing-download")] + dir_listing_download: &[], redirect_trailing_slash: true, compression_static: false, ignore_hidden_files: false, @@ -1286,6 +1338,8 @@ mod tests { dir_listing_order: 6, #[cfg(feature = "directory-listing")] dir_listing_format: &DirListFmt::Html, + #[cfg(feature = "directory-listing-download")] + dir_listing_download: &[], redirect_trailing_slash: true, compression_static: false, ignore_hidden_files: false, @@ -1330,6 +1384,8 @@ mod tests { dir_listing_order: 6, #[cfg(feature = "directory-listing")] dir_listing_format: &DirListFmt::Html, + #[cfg(feature = "directory-listing-download")] + dir_listing_download: &[], redirect_trailing_slash: true, compression_static: false, ignore_hidden_files: false, @@ -1389,6 +1445,8 @@ mod tests { dir_listing_order: 6, #[cfg(feature = "directory-listing")] dir_listing_format: &DirListFmt::Html, + #[cfg(feature = "directory-listing-download")] + dir_listing_download: &[], redirect_trailing_slash: true, compression_static: false, ignore_hidden_files: false, @@ -1440,6 +1498,8 @@ mod tests { dir_listing_order: 6, #[cfg(feature = "directory-listing")] dir_listing_format: &DirListFmt::Html, + #[cfg(feature = "directory-listing-download")] + dir_listing_download: &[], redirect_trailing_slash: true, compression_static: true, ignore_hidden_files: true, @@ -1482,6 +1542,8 @@ mod tests { dir_listing_order: 6, #[cfg(feature = "directory-listing")] dir_listing_format: &DirListFmt::Html, + #[cfg(feature = "directory-listing-download")] + dir_listing_download: &[], redirect_trailing_slash: true, compression_static: true, ignore_hidden_files: true, @@ -1526,6 +1588,8 @@ mod tests { dir_listing_order: 6, #[cfg(feature = "directory-listing")] dir_listing_format: &DirListFmt::Html, + #[cfg(feature = "directory-listing-download")] + dir_listing_download: &[], redirect_trailing_slash: true, compression_static: true, ignore_hidden_files: true, @@ -1560,6 +1624,8 @@ mod tests { dir_listing_order: 6, #[cfg(feature = "directory-listing")] dir_listing_format: &DirListFmt::Html, + #[cfg(feature = "directory-listing-download")] + dir_listing_download: &[], redirect_trailing_slash: true, compression_static: true, ignore_hidden_files: true, From b56e3c4a96062d4691a2559d5a7cdb0df4c0e9fc Mon Sep 17 00:00:00 2001 From: Jose Quintana <1700322+joseluisq@users.noreply.github.com> Date: Sat, 31 May 2025 23:31:50 +0200 Subject: [PATCH 8/9] 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 --- .cirrus.yml | 4 +- .github/workflows/devel.yml | 5 +- Cargo.lock | 529 ++++++++++++++------------- Cargo.toml | 4 +- docs/content/building-from-source.md | 2 +- tests/dir_listing_download.rs | 24 +- 6 files changed, 295 insertions(+), 273 deletions(-) diff --git a/.cirrus.yml b/.cirrus.yml index 4fdb836e..7e88628e 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -8,7 +8,7 @@ freebsd_instance: task: only_if: $CIRRUS_TAG == '' && changesIncludeOnly('.cirrus.yml', 'src/**', 'tests/**', 'Cargo.toml', 'Cargo.lock', '.cargo/config.toml') env: - TOOLCHAIN: "1.81.0" + TOOLCHAIN: "1.82.0" RUSTFLAGS: -Dwarnings --cfg tokio_unstable CARGO_FEATURES: "--features=all" RUST_BACKTRACE: "1" @@ -45,7 +45,7 @@ task: task: only_if: $CIRRUS_TAG != '' env: - TOOLCHAIN: "1.81.0" + TOOLCHAIN: "1.82.0" RUSTFLAGS: -Dwarnings --cfg tokio_unstable CARGO_FEATURES: "--features=all" GITHUB_TOKEN: ENCRYPTED[d1766ef328d83729917d2ffb875d64c35d1c0177edf8f32e66ec464daf5c1b7b145d65fc6c044a73fffe2235d3b38349] diff --git a/.github/workflows/devel.yml b/.github/workflows/devel.yml index f35ef767..ffcacaf6 100644 --- a/.github/workflows/devel.yml +++ b/.github/workflows/devel.yml @@ -74,7 +74,7 @@ jobs: # We test against the latest and minimum Rust stable version. - build: pinned os: ubuntu-22.04 - rust: 1.81.0 + rust: 1.82.0 # Some of our release builds are generated by a nightly compiler to take # advantage of the latest optimizations/compile time improvements. - build: linux-musl @@ -319,11 +319,12 @@ jobs: - name: Check formatting run: | - cargo fmt --all -- --check + cargo fmt --all -- --check tests/*.rs - name: Check via Clippy run: | cargo clippy --all-features -- -D warnings + cargo clippy --all-features --tests -- -D warnings - name: Check crate docs run: | diff --git a/Cargo.lock b/Cargo.lock index 2fe7b362..385854e2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -97,20 +97,20 @@ dependencies = [ [[package]] name = "anstyle-wincon" -version = "3.0.7" +version = "3.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" +checksum = "6680de5231bd6ee4c6191b8a1325daa282b415391ec9d3a37bd34f2060dc73fa" dependencies = [ "anstyle", - "once_cell", + "once_cell_polyfill", "windows-sys 0.59.0", ] [[package]] name = "anyhow" -version = "1.0.97" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" [[package]] name = "async-channel" @@ -137,9 +137,9 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.21" +version = "0.4.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0cf008e5e1a9e9e22a7d3c9a4992e21a350290069e36d8fb72304ed17e8f2d2" +checksum = "b37fc50485c4f3f736a4fb14199f6d5f5ba008d7f28fe710306c92780f004c07" dependencies = [ "brotli", "flate2", @@ -182,9 +182,9 @@ dependencies = [ [[package]] name = "async-io" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059" +checksum = "1237c0ae75a0f3765f58910ff9cdd0a12eeb39ab2f4c7de23262f337f0aacbb3" dependencies = [ "async-lock", "cfg-if", @@ -193,7 +193,7 @@ dependencies = [ "futures-lite", "parking", "polling", - "rustix 0.38.44", + "rustix", "slab", "tracing", "windows-sys 0.59.0", @@ -212,9 +212,9 @@ dependencies = [ [[package]] name = "async-process" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63255f1dc2381611000436537bbedfe83183faa303a5a0edaf191edef06526bb" +checksum = "cde3f4e40e6021d7acffc90095cbd6dc54cb593903d1de5832f435eb274b85dc" dependencies = [ "async-channel 2.3.1", "async-io", @@ -225,15 +225,15 @@ dependencies = [ "cfg-if", "event-listener 5.4.0", "futures-lite", - "rustix 0.38.44", + "rustix", "tracing", ] [[package]] name = "async-signal" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "637e00349800c0bdf8bfc21ebbc0b6524abea702b0da4168ac00d070d0c0b9f3" +checksum = "d7605a4e50d4b06df3898d5a70bf5fde51ed9059b0434b73105193bc27acce0d" dependencies = [ "async-io", "async-lock", @@ -241,7 +241,7 @@ dependencies = [ "cfg-if", "futures-core", "futures-io", - "rustix 0.38.44", + "rustix", "signal-hook-registry", "slab", "windows-sys 0.59.0", @@ -308,9 +308,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "backtrace" -version = "0.3.74" +version = "0.3.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" dependencies = [ "addr2line", "cfg-if", @@ -341,7 +341,7 @@ checksum = "92758ad6077e4c76a6cadbce5005f666df70d4f13b19976b1a8062eef880040f" dependencies = [ "base64 0.22.1", "blowfish", - "getrandom 0.3.2", + "getrandom 0.3.3", "subtle", "zeroize", ] @@ -354,9 +354,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" [[package]] name = "block-buffer" @@ -392,9 +392,9 @@ dependencies = [ [[package]] name = "brotli" -version = "7.0.0" +version = "8.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd" +checksum = "9991eea70ea4f293524138648e41ee89b0b2b12ddef3b255effa43c8056e0e0d" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -403,9 +403,9 @@ dependencies = [ [[package]] name = "brotli-decompressor" -version = "4.0.2" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74fa05ad7d803d413eb8380983b092cbbaf9a85f151b871360e7b00cd7060b37" +checksum = "874bb8112abecc98cbd6d81ea4fa7e94fb9449648c93cc89aa40c81c24d7de03" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -413,9 +413,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.11.3" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0" +checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4" dependencies = [ "memchr", "serde", @@ -500,9 +500,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.17" +version = "1.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fcb57c740ae1daf453ae85f16e37396f672b039e00d9d866e07ddb24e328e3a" +checksum = "d0fc897dc1e865cc67c0e05a836d9d3f1df3cbe442aa4a9473b18e12624a4951" dependencies = [ "jobserver", "libc", @@ -517,9 +517,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.40" +version = "0.4.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c" +checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" dependencies = [ "android-tzdata", "iana-time-zone", @@ -539,9 +539,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.32" +version = "4.5.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6088f3ae8c3608d19260cd7445411865a485688711b78b5be70d78cd96136f83" +checksum = "fd60e63e9be68e5fb56422e397cf9baddded06dae1d2e523401542383bc72a9f" dependencies = [ "clap_builder", "clap_derive", @@ -564,9 +564,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.32" +version = "4.5.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22a7ef7f676155edfb82daa97f99441f3ebf4a58d5e32f295a56259f1b6facc8" +checksum = "89cc6392a1f72bbeb820d71f32108f61fdaf18bc526e1d23954168a67759ef51" dependencies = [ "anstream", "anstyle", @@ -576,9 +576,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.47" +version = "4.5.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06f5378ea264ad4f82bbc826628b5aad714a75abf6ece087e923010eb937fb6" +checksum = "1a554639e42d0c838336fc4fbedb9e2df3ad1fa4acda149f9126b4ccfcd7900f" dependencies = [ "clap", ] @@ -595,9 +595,9 @@ dependencies = [ [[package]] name = "clap_complete_nushell" -version = "4.5.5" +version = "4.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6a8b1593457dfc2fe539002b795710d022dc62a65bf15023f039f9760c7b18a" +checksum = "9801fe85d7986742027c6d365728a6a4ecb6d2b09866de18be836fef7ebf7df1" dependencies = [ "clap", "clap_complete", @@ -706,9 +706,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.14" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ba6d68e24814cb8de6bb986db8222d3a027d15872cabc0d18817bc3c0e4471" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" dependencies = [ "crossbeam-utils", ] @@ -744,9 +744,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.4.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28cfac68e08048ae1883171632c2aef3ebc555621ae56fbccce1cbf22dd7f058" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" dependencies = [ "powerfmt", ] @@ -780,9 +780,9 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.10" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18" dependencies = [ "libc", "windows-sys 0.59.0", @@ -844,9 +844,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11faaf5a5236997af9848be0bef4db95824b1d534ebc64d0f0c6cf3e67bd38dc" +checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" dependencies = [ "crc32fast", "miniz_oxide", @@ -950,9 +950,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "libc", @@ -961,9 +961,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ "cfg-if", "libc", @@ -983,7 +983,7 @@ version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2deb07a133b1520dc1a5690e9bd08950108873d7ed5de38dcc74d3b5ebffa110" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "libc", "libgit2-sys", "log", @@ -1049,9 +1049,9 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "hashbrown" -version = "0.15.2" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" [[package]] name = "headers" @@ -1085,9 +1085,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.4.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" +checksum = "f154ce46856750ed433c8649605bf7ed2de3bc35fd9d2a9f30cddd873c80cb08" [[package]] name = "http" @@ -1159,14 +1159,15 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.61" +version = "0.1.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", + "log", "wasm-bindgen", "windows-core", ] @@ -1182,21 +1183,22 @@ dependencies = [ [[package]] name = "icu_collections" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" dependencies = [ "displaydoc", + "potential_utf", "yoke", "zerofrom", "zerovec", ] [[package]] -name = "icu_locid" -version = "1.5.0" +name = "icu_locale_core" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" dependencies = [ "displaydoc", "litemap", @@ -1205,31 +1207,11 @@ dependencies = [ "zerovec", ] -[[package]] -name = "icu_locid_transform" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_locid_transform_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_locid_transform_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" - [[package]] name = "icu_normalizer" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" dependencies = [ "displaydoc", "icu_collections", @@ -1237,67 +1219,54 @@ dependencies = [ "icu_properties", "icu_provider", "smallvec", - "utf16_iter", - "utf8_iter", - "write16", "zerovec", ] [[package]] name = "icu_normalizer_data" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" [[package]] name = "icu_properties" -version = "1.5.1" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" dependencies = [ "displaydoc", "icu_collections", - "icu_locid_transform", + "icu_locale_core", "icu_properties_data", "icu_provider", - "tinystr", + "potential_utf", + "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" -version = "1.5.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" [[package]] name = "icu_provider" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" dependencies = [ "displaydoc", - "icu_locid", - "icu_provider_macros", + "icu_locale_core", "stable_deref_trait", "tinystr", "writeable", "yoke", "zerofrom", + "zerotrie", "zerovec", ] -[[package]] -name = "icu_provider_macros" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "idna" version = "1.0.3" @@ -1311,9 +1280,9 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" dependencies = [ "icu_normalizer", "icu_properties", @@ -1321,12 +1290,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ "equivalent", - "hashbrown 0.15.2", + "hashbrown 0.15.3", "serde", ] @@ -1359,10 +1328,11 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jobserver" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" dependencies = [ + "getrandom 0.3.3", "libc", ] @@ -1393,9 +1363,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.171" +version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" [[package]] name = "libgit2-sys" @@ -1415,9 +1385,9 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "libc", - "redox_syscall 0.5.10", + "redox_syscall 0.5.12", ] [[package]] @@ -1434,15 +1404,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.4.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" - -[[package]] -name = "linux-raw-sys" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe7db12097d22ec582439daf8618b8fdd1a7bef6270e9af3b1ebcd30893cf413" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" [[package]] name = "listenfd" @@ -1457,15 +1421,15 @@ dependencies = [ [[package]] name = "litemap" -version = "0.7.4" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" [[package]] name = "lock_api" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" dependencies = [ "autocfg", "scopeguard", @@ -1473,9 +1437,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.26" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" dependencies = [ "value-bag", ] @@ -1541,22 +1505,22 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.8.5" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" +checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" dependencies = [ "adler2", ] [[package]] name = "mio" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" dependencies = [ "libc", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1604,9 +1568,15 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.21.1" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc" +checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" [[package]] name = "overload" @@ -1622,9 +1592,9 @@ checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" [[package]] name = "parking_lot" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" dependencies = [ "lock_api", "parking_lot_core", @@ -1632,13 +1602,13 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.10" +version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.10", + "redox_syscall 0.5.12", "smallvec", "windows-targets", ] @@ -1700,19 +1670,28 @@ checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "polling" -version = "3.7.4" +version = "3.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f" +checksum = "b53a684391ad002dd6a596ceb6c74fd004fdce75f4be2e3f615068abbea5fd50" dependencies = [ "cfg-if", "concurrent-queue", "hermit-abi", "pin-project-lite", - "rustix 0.38.44", + "rustix", "tracing", "windows-sys 0.59.0", ] +[[package]] +name = "potential_utf" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +dependencies = [ + "zerovec", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -1721,9 +1700,9 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "proc-macro2" -version = "1.0.94" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] @@ -1781,7 +1760,7 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "memchr", "unicase", ] @@ -1812,11 +1791,11 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.10" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1" +checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", ] [[package]] @@ -1856,7 +1835,7 @@ checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.15", + "getrandom 0.2.16", "libc", "untrusted", "windows-sys 0.52.0", @@ -1876,35 +1855,22 @@ checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustix" -version = "0.38.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" -dependencies = [ - "bitflags 2.9.0", - "errno", - "libc", - "linux-raw-sys 0.4.15", - "windows-sys 0.59.0", -] - -[[package]] -name = "rustix" -version = "1.0.3" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e56a18552996ac8d29ecc3b190b4fdbb2d91ca4ec396de7bbffaf43f3d637e96" +checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "errno", "libc", - "linux-raw-sys 0.9.3", + "linux-raw-sys", "windows-sys 0.59.0", ] [[package]] name = "rustls" -version = "0.23.25" +version = "0.23.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "822ee9188ac4ec04a2f0531e55d035fb2de73f18b41a63c70c2712503b6fb13c" +checksum = "730944ca083c1c233a75c09f199e973ca499344a2b7ba9e755c457e86fb4a321" dependencies = [ "log", "once_cell", @@ -1926,15 +1892,18 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" +checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +dependencies = [ + "zeroize", +] [[package]] name = "rustls-webpki" -version = "0.103.0" +version = "0.103.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0aa4eeac2588ffff23e9d7a7e9b3f971c5fb5b7ebc9452745e0c232c64f83b2f" +checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435" dependencies = [ "ring", "rustls-pki-types", @@ -1943,9 +1912,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.20" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" +checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" [[package]] name = "ryu" @@ -1999,9 +1968,9 @@ dependencies = [ [[package]] name = "serde_ignored" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "566da67d80e92e009728b3731ff0e5360cb181432b8ca73ea30bb1d170700d76" +checksum = "b516445dac1e3535b6d658a7b528d771153dfb272ed4180ca4617a20550365ff" dependencies = [ "serde", ] @@ -2092,9 +2061,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" +checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2" dependencies = [ "cc", "libc", @@ -2103,9 +2072,9 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.2" +version = "1.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" dependencies = [ "libc", ] @@ -2148,15 +2117,15 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" +checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" [[package]] name = "socket2" -version = "0.5.8" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" dependencies = [ "libc", "windows-sys 0.52.0", @@ -2237,9 +2206,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.100" +version = "2.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" dependencies = [ "proc-macro2", "quote", @@ -2248,9 +2217,9 @@ dependencies = [ [[package]] name = "synstructure" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", @@ -2265,14 +2234,14 @@ checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417" [[package]] name = "tempfile" -version = "3.19.1" +version = "3.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" +checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" dependencies = [ "fastrand", - "getrandom 0.3.2", + "getrandom 0.3.3", "once_cell", - "rustix 1.0.3", + "rustix", "windows-sys 0.59.0", ] @@ -2381,9 +2350,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.7.6" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" dependencies = [ "displaydoc", "zerovec", @@ -2391,9 +2360,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.44.1" +version = "1.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f382da615b842244d4b8738c82ed1275e6c5dd90c459a30941cd07080b06c91a" +checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779" dependencies = [ "backtrace", "bytes", @@ -2419,9 +2388,9 @@ dependencies = [ [[package]] name = "tokio-metrics" -version = "0.4.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb2bb07a8451c4c6fa8b3497ad198510d8b8dffa5df5cfb97a64102a58b113c8" +checksum = "7817b32d36c9b94744d7aa3f8fc13526aa0f5112009d7045f3c659413a6e44ac" dependencies = [ "futures-util", "pin-project-lite", @@ -2465,9 +2434,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.14" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b9590b93e6fcc1739458317cccd391ad3955e2bde8913edf6f95f9e65a8f034" +checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" dependencies = [ "bytes", "futures-core", @@ -2479,9 +2448,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.20" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148" +checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae" dependencies = [ "serde", "serde_spanned", @@ -2491,26 +2460,33 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.22.24" +version = "0.22.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" +checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" dependencies = [ "indexmap", "serde", "serde_spanned", "toml_datetime", + "toml_write", "winnow", ] +[[package]] +name = "toml_write" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076" + [[package]] name = "tower-service" version = "0.3.3" @@ -2648,12 +2624,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "utf16_iter" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" - [[package]] name = "utf8_iter" version = "1.0.4" @@ -2668,9 +2638,13 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" +checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d" +dependencies = [ + "js-sys", + "wasm-bindgen", +] [[package]] name = "valuable" @@ -2850,11 +2824,37 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-core" -version = "0.52.0" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" dependencies = [ - "windows-targets", + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -2863,15 +2863,33 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-service" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d24d6bcc7f734a4091ecf8d7a64c5f7d7066f45585c1861eba06449909609c8a" +checksum = "193cae8e647981c35bc947fdd57ba7928b1fa0d4a79305f6dd2dc55221ac35ac" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "widestring", - "windows-sys 0.52.0", + "windows-sys 0.59.0", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link", ] [[package]] @@ -2958,9 +2976,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.7.4" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36" +checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" dependencies = [ "memchr", ] @@ -2971,20 +2989,14 @@ version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", ] -[[package]] -name = "write16" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" - [[package]] name = "writeable" -version = "0.5.5" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" [[package]] name = "xattr" @@ -2997,9 +3009,9 @@ dependencies = [ [[package]] name = "yoke" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" dependencies = [ "serde", "stable_deref_trait", @@ -3009,9 +3021,9 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", @@ -3021,18 +3033,18 @@ dependencies = [ [[package]] name = "zerofrom" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", @@ -3046,11 +3058,22 @@ version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +[[package]] +name = "zerotrie" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + [[package]] name = "zerovec" -version = "0.10.4" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" dependencies = [ "yoke", "zerofrom", @@ -3059,9 +3082,9 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.10.3" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index b0d4fede..3e78af66 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "static-web-server" version = "2.36.1" edition = "2021" -rust-version = "1.81.0" +rust-version = "1.82.0" authors = ["Jose Quintana "] license = "MIT OR Apache-2.0" description = "A cross-platform, high-performance and asynchronous web server for static files-serving." @@ -110,7 +110,7 @@ tokio-metrics-collector = { version = "0.3.1", optional = true } prometheus = { version = "0.14.0", default-features = false, optional = true } [target.'cfg(windows)'.dependencies] -windows-service = "0.7" +windows-service = "0.8" [dev-dependencies] bytes = "1.10" diff --git a/docs/content/building-from-source.md b/docs/content/building-from-source.md index ec1b0e19..9a5fd4a2 100644 --- a/docs/content/building-from-source.md +++ b/docs/content/building-from-source.md @@ -6,7 +6,7 @@ Follow these instructions to either build **`SWS`** project from the source or t If you want to build **SWS** from the source, all you need is a [Rust 2021 Edition](https://blog.rust-lang.org/2021/05/11/edition-2021.html) installed. -So make sure to install Rust [1.81.0](https://blog.rust-lang.org/2024/09/05/Rust-1.81.0/) or newer (or nightly) along with [the toolchain(s)](https://rust-lang.github.io/rustup/concepts/toolchains.html) of your preference. +So make sure to install Rust [1.82.0](https://blog.rust-lang.org/2024/10/17/Rust-1.82.0/) or newer (or nightly) along with [the toolchain(s)](https://rust-lang.github.io/rustup/concepts/toolchains.html) of your preference. Then clone the repository and use [Cargo](https://doc.rust-lang.org/cargo/) to build the project from the source. diff --git a/tests/dir_listing_download.rs b/tests/dir_listing_download.rs index 9ec12f09..74fd9c33 100644 --- a/tests/dir_listing_download.rs +++ b/tests/dir_listing_download.rs @@ -140,7 +140,7 @@ mod tests { ignore_hidden_files: false, disable_symlinks, index_files: &[], - dir_listing_download: &vec![DirDownloadFmt::Targz], + dir_listing_download: &[DirDownloadFmt::Targz], }) .await { @@ -179,7 +179,7 @@ mod tests { assert_eq!(left, right); } else { - assert!(body.len() == 0); + assert!(body.is_empty()); } } Err(status) => { @@ -210,7 +210,7 @@ mod tests { ignore_hidden_files: true, disable_symlinks: false, index_files: &[], - dir_listing_download: &vec![DirDownloadFmt::Targz], + dir_listing_download: &[DirDownloadFmt::Targz], }) .await { @@ -230,13 +230,12 @@ mod tests { if method == Method::GET { let mut prefix = base_path.clone(); prefix.pop(); - assert!(inspect_tarball_content(prefix, &body, false) + assert!(!inspect_tarball_content(prefix, &body, false) .await .iter() - .find(|path| path.file_name().unwrap() == ".dotfile") - .is_none()); + .any(|path| path.file_name().unwrap() == ".dotfile")); } else { - assert!(body.len() == 0); + assert!(body.is_empty()); } } Err(status) => { @@ -268,7 +267,7 @@ mod tests { ignore_hidden_files: false, disable_symlinks, index_files: &[], - dir_listing_download: &vec![DirDownloadFmt::Targz], + dir_listing_download: &[DirDownloadFmt::Targz], }) .await { @@ -307,7 +306,7 @@ mod tests { assert_eq!(left, right); } else { - assert!(body.len() == 0); + assert!(body.is_empty()); } } Err(status) => { @@ -337,7 +336,7 @@ mod tests { ignore_hidden_files: false, disable_symlinks: false, index_files: &[], - dir_listing_download: &vec![], + dir_listing_download: &[], }) .await { @@ -345,11 +344,10 @@ mod tests { let res = result.resp; assert_eq!(res.status(), 200); assert_eq!(res.headers()["content-type"], "text/html; charset=utf-8"); - assert!(res + assert!(!res .headers() .iter() - .find(|(k, _v)| *k == "content-disposition") - .is_none()); + .any(|(k, _v)| *k == "content-disposition")); } Err(status) => { assert!(method != Method::GET && method != Method::HEAD); From b67202baea7836ffbb38e7e90dda340a7685df31 Mon Sep 17 00:00:00 2001 From: Jose Quintana Date: Tue, 3 Jun 2025 22:31:24 +0200 Subject: [PATCH 9/9] v2.37.0 --- CHANGELOG.md | 66 ++++++++++++++++++- Cargo.lock | 2 +- Cargo.toml | 2 +- .../configuration/command-line-arguments.md | 2 + docs/content/download-and-install.md | 9 ++- 5 files changed, 76 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d8137c6..b7754764 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,10 +2,72 @@ All notable changes to this project will be documented in this file. -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -_**Note:** See changelog for v1 under the [1.x](https://github.com/static-web-server/static-web-server/blob/1.x/CHANGELOG.md) branch._ +## v2.37.0 - 2025-06-03 + +This new `v2.37.0` release brings several security and bug fixes. New features like the possibility to download directories as tarballs, control for server log ANSI output, end of support for a few unmaintained Windows platforms and other improvements. + +**End of support for unmaintained Windows 7, 8, 8.1 platforms** + +As anticipated in [v2.36.1][v2361], SWS no longer supports Windows 7, 8, and 8.1 platforms. +SWS now requires Rust `1.82.0` or later to build and the minimum Windows supported platform is *Windows 10*. + +**Fixes** + +- [b56e3c4][b56e3c4] Bugfix/security dependency updates including tokio, rustls, chrono, flate2, windows-service, serde and other crates. SWS now requires Rust `1.82.0` or later to build. PR [#546][546], [#545][545] by [@joseluisq][joseluisq] +- [a384d92][a384d92] Update Alpine `3.20.6` and Debian `12.10` Docker images. PR [#539][539] by [@joseluisq][joseluisq] +- [cb19995][cb19995] Generic server log info output even on higher log levels. PR [#542][542] fixes [#541][541] reported by [@Tasssadar][Tasssadar]. + +[cb19995]: https://github.com/static-web-server/static-web-server/commit/cb19995 + +[541]: https://github.com/static-web-server/static-web-server/issues/541 +[542]: https://github.com/static-web-server/static-web-server/pull/542 + +**Features** + +- [89f5846][89f5846] Support for downloading a directory as a compressed tarball (`tar.gz`) via new `--directory-listing-download=targz` option. PR [#544][544] by [@ekangmonyet][ekangmonyet] resolves [#67][67] suggested by [@shirshak55][shirshak55]. See [docs][directory-download]. +- [0236980][0236980] Control log ANSI output via boolean new `--log-with-ansi=true` option (SWS is now no-ANSI by default). PR [#543][543] resolves [#540][540] suggested by [@Tasssadar][Tasssadar]. See [docs][log-output-with-ansi]. + +[89f5846]: https://github.com/static-web-server/static-web-server/commit/89f5846 +[0236980]: https://github.com/static-web-server/static-web-server/commit/0236980 + +[67]: https://github.com/static-web-server/static-web-server/issues/67 +[540]: https://github.com/static-web-server/static-web-server/issues/540 +[544]: https://github.com/static-web-server/static-web-server/pull/544 +[543]: https://github.com/static-web-server/static-web-server/pull/543 +[log-output-with-ansi]: https://static-web-server.net/features/logging#log-output-with-ansi +[directory-download]: https://static-web-server.net/features/directory-listing#directory-download + +**Refactorings** + +- Misc: [5d1eaac][5d1eaac] Automate post-release updates using CI. PR [#538][538] by [@joseluisq][joseluisq] + +[b56e3c4]: https://github.com/static-web-server/static-web-server/commit/b56e3c4 +[7ca2785]: https://github.com/static-web-server/static-web-server/commit/7ca2785 +[a384d92]: https://github.com/static-web-server/static-web-server/commit/a384d92 +[5d1eaac]: https://github.com/static-web-server/static-web-server/commit/5d1eaac + +[546]: https://github.com/static-web-server/static-web-server/pull/546 +[545]: https://github.com/static-web-server/static-web-server/pull/545 +[539]: https://github.com/static-web-server/static-web-server/pull/539 +[538]: https://github.com/static-web-server/static-web-server/pull/538 + +[joseluisq]: https://github.com/joseluisq +[ekangmonyet]: https://github.com/ekangmonyet +[Tasssadar]: https://github.com/Tasssadar +[shirshak55]: https://github.com/shirshak55 +[v2361]: https://github.com/static-web-server/static-web-server/releases/tag/v2.36.1 + +For more details see the [v2.37.0 milestone][v2370-milestone] and the full changelog [v2.36.1...v2.37.0][v2370-diff]. + +[v2370-diff]: https://github.com/static-web-server/static-web-server/compare/v2.36.1...v2.37.0 +[v2370-milestone]: https://github.com/static-web-server/static-web-server/milestone/29?closed=1 + +__Acknowledgments__ + +Thanks to our new donor [@mrkesu](https://github.com/mrkesu) for supporting the project. ## v2.36.1 - 2025-03-02 diff --git a/Cargo.lock b/Cargo.lock index 385854e2..cac7cdd1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2139,7 +2139,7 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "static-web-server" -version = "2.36.1" +version = "2.37.0" dependencies = [ "aho-corasick", "anyhow", diff --git a/Cargo.toml b/Cargo.toml index 3e78af66..6ecdfafe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "static-web-server" -version = "2.36.1" +version = "2.37.0" edition = "2021" rust-version = "1.82.0" authors = ["Jose Quintana "] diff --git a/docs/content/configuration/command-line-arguments.md b/docs/content/configuration/command-line-arguments.md index 4abc4443..734a8a9f 100644 --- a/docs/content/configuration/command-line-arguments.md +++ b/docs/content/configuration/command-line-arguments.md @@ -38,6 +38,8 @@ Options: A HTML file path (not relative to the root) used for GET requests when the requested path doesn't exist. The fallback page is served with a 200 status code, useful when using client routers. If the path doesn't exist then the feature is not activated [env: SERVER_FALLBACK_PAGE=] [default: ] -g, --log-level Specify a logging level in lower case. Values: error, warn, info, debug or trace [env: SERVER_LOG_LEVEL=] [default: error] + --log-with-ansi [] + Enable or disable ANSI escape codes for colors and other text formatting of the log output [env: SERVER_LOG_WITH_ANSI=] [default: false] [possible values: true, false] -c, --cors-allow-origins Specify an optional CORS list of allowed origin hosts separated by commas. Host ports or protocols aren't being checked. Use an asterisk (*) to allow any host [env: SERVER_CORS_ALLOW_ORIGINS=] [default: ] -j, --cors-allow-headers diff --git a/docs/content/download-and-install.md b/docs/content/download-and-install.md index eae08368..a1d0d931 100644 --- a/docs/content/download-and-install.md +++ b/docs/content/download-and-install.md @@ -77,7 +77,14 @@ nix-env -iA nixpkgs.static-web-server ### MacOS -Via [Homebrew](https://brew.sh/) (also Linux) +Using [Homebrew Formulae](https://formulae.brew.sh/formula/static-web-server/) (also Linux) + +```sh +# Build from source +brew install static-web-server +``` + +Or using the [SWS Homebrew Tap](https://github.com/static-web-server/homebrew-tap) (also Linux) ```sh brew tap static-web-server/static-web-server