Files
rustguac/Cargo.toml
T
pletch e1cc4a9887 fix(http): let the browser find out a page has been rebuilt
The branded HTML handlers returned `Html(...)` and nothing else: no ETag,
no Last-Modified, no Cache-Control. With no freshness information at all
Chrome caches heuristically and does not revalidate, so a restart served
the old page for hours -- incognito was the only reliable way to see a
change. The static assets had the same gap from the other side: ServeDir
already sent ETag and Last-Modified and honoured both, it was just never
asked, so a rebuilt /guac/*.js was invisible too.

The pages are branded once at startup into a map, so the tag is computed
there as well -- one sha256 over each, against a rewrite already walking
the same strings. A request is then a string compare, and an unchanged
page costs a 304 rather than 136K of client.html or 255K of
connections.html. `no-cache` is the revalidate directive, not
do-not-store: the browser keeps the body and asks. Tags are
content-derived, not boot-derived, so a restart that changes nothing
still answers 304.

Revalidating every asset is correct but not free, and this is reached
over a WAN. The same startup pass therefore rewrites the 36 script tags
and the stylesheet to carry a hash of the bytes on disk, and a URL
bearing one is served with a year and `immutable`. The round trip it
saves is a single one, since the browser-to-proxy leg is HTTP/2 and the
conditional requests go out together -- but client.html reloads on every
session launch and relaunch, so it is a round trip paid exactly when the
link is worst. Only a 2xx or 304 earns the long directive: a 404 under a
versioned URL is a deployment that has gone wrong, and pinning it in the
cache until next year would make it outlive its cause.

Query strings rather than hashed filenames because there is no build step
to rename anything and the rewrite already existed; the old caution about
proxies declining to cache them has not been true in many years.

The cost is that editing an asset now needs a restart where a reload was
enough, since the URL and the bytes it names are produced together --
hence RUSTGUAC_NO_ASSET_VERSIONING, which leaves everything revalidating.

Verified against a running instance: matching and weak If-None-Match give
304 on the HTML, a stale tag gives 200, a versioned asset is immutable on
both its 200 and its 304, a bare one and a 404 are not, the kill switch
emits bare URLs, and API responses are untouched -- the middleware is on
the ServeDir fallback only.

(cherry picked from commit 966aba765c8a8db043b5f62603abdb6a18dd0eb5)
2026-10-03 11:57:45 +10:00

102 lines
2.2 KiB
TOML

[package]
name = "rustguac"
version = "1.10.1"
edition = "2021"
description = "Lightweight Rust replacement for Apache Guacamole client"
[dependencies]
# Web framework
axum = { version = "0.8", features = ["ws"] }
tower = "0.5"
tower-http = { version = "0.7", features = ["fs", "cors", "trace", "set-header"] }
# Async runtime
tokio = { version = "1", features = ["full"] }
tokio-util = "0.7"
# Serialization
serde = { version = "1", features = ["derive"] }
serde_json = "1"
# Utilities
uuid = { version = "1", features = ["v4", "serde"] }
chrono = { version = "0.4", features = ["serde"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
# Streaming
futures-util = "0.3"
# Config
toml = "1"
# Crypto/random
rand = "0.10"
hex = "0.4"
sha2 = "0.11"
subtle = "2"
aes = "0.9"
cbc = "0.2"
pbkdf2 = { version = "0.13", default-features = false }
hmac = "0.13"
sha1 = "0.11"
# Database
rusqlite = { version = "0.40", features = ["bundled"] }
# CLI
clap = { version = "4", features = ["derive"] }
# IP matching
ipnetwork = "0.21"
# TLS
axum-server = { version = "0.8", features = ["tls-rustls"] }
rcgen = "0.14"
tokio-rustls = "0.26"
rustls = { version = "0.23", default-features = false, features = ["ring", "std", "tls12"] }
rustls-pemfile = "2"
# OIDC
openidconnect = "4"
url = "2"
tower-cookies = "0.11"
# SSH key generation
ssh-key = { version = "0.6", features = ["ed25519"] }
# Rate limiting
tower_governor = "0.8"
# Base64 decoding (JWT payload for OIDC groups)
base64 = "0.23"
# HTTP client (Vault API)
reqwest = { version = "0.12", features = ["json", "rustls-tls-native-roots"], default-features = false }
webpki-roots = "1"
# SSH tunneling (jump host / bastion)
russh = "0.63"
# Docker (VDI container management)
bollard = "0.21"
# LUKS drive management (uid/gid for chown)
libc = "0.2"
# Socket-level options (TCP keepalive)
socket2 = "0.6"
# URL encoding/decoding (quick-connect redirect)
urlencoding = "2"
serde_urlencoded = "0.7"
[build-dependencies]
pulldown-cmark = "0.13"
[dev-dependencies]
assert_cmd = "2.2.2"
# Integration tests read the SQLite the CLI wrote. Already a runtime
# dependency, so this adds nothing new to the tree.
rusqlite = { version = "0.40", features = ["bundled"] }