Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
fdef431
fix(server): harden HTTP to HTTPS redirects against host header abuse
jkyberneees Mar 8, 2026
5b7473c
fix(cache): enforce true no-cache mode and honor entry ttl
jkyberneees Mar 8, 2026
8e99c71
perf(handler): reduce cache hit overhead and cold-miss filesystem work
jkyberneees Mar 8, 2026
9ae60e2
ci(security): pin GitHub Actions and add govulncheck
jkyberneees Mar 8, 2026
9326f63
build(benchmark): fix bun static target and lower default load
jkyberneees Mar 8, 2026
98e782a
perf(server): add startup preloading, zero-alloc fast path, path cach…
jkyberneees Mar 8, 2026
538cff5
docs: update all documentation and landing page for preload performance
jkyberneees Mar 8, 2026
7a34136
chore: add static-web binary to .gitignore
jkyberneees Mar 8, 2026
944bd10
build(benchmark): add preload variant and update suite for 5 servers
jkyberneees Mar 8, 2026
9a8bf21
build(benchmark): add bare-metal script with benchmark-mode, preload,…
jkyberneees Mar 8, 2026
0d4def4
refactor(server): remove benchmark-mode in favor of production preload
jkyberneees Mar 8, 2026
d174c26
docs: update performance claims with reproducible bare-metal numbers
jkyberneees Mar 8, 2026
71847e4
perf(server): migrate HTTP layer from net/http to fasthttp
jkyberneees Mar 8, 2026
9596966
build(benchmark): add fasthttp/net-http hello world baselines and upd…
jkyberneees Mar 8, 2026
65cfb13
docs: update performance numbers and architecture for fasthttp migration
jkyberneees Mar 8, 2026
0fd9649
fix(config): remove dead read_header_timeout setting (fasthttp has no…
jkyberneees Mar 8, 2026
b90ffa6
docs: correct zero-alloc claims to near-zero (fasthttp []byte→string …
jkyberneees Mar 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

- name: Set up Go
uses: actions/setup-go@v5
uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5
with:
go-version-file: go.mod
cache: true
Expand All @@ -28,3 +28,9 @@ jobs:

- name: Test
run: go test -race ./...

- name: Install govulncheck
run: go install golang.org/x/vuln/cmd/govulncheck@latest

- name: Run govulncheck
run: govulncheck ./...
6 changes: 3 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
fetch-depth: 0

- name: Set up Go
uses: actions/setup-go@v5
uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5
with:
go-version-file: go.mod
cache: true
Expand All @@ -28,7 +28,7 @@ jobs:
run: go test -race ./...

- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v6
uses: goreleaser/goreleaser-action@e435ccd777264be153ace6237001ef4d979d3a7a # v6
with:
distribution: goreleaser
version: "~> v2"
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Build output
/bin/
/dist/
/static-web

# Test and coverage artifacts
coverage.out
Expand Down
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,21 @@
## v1.3.0 (2026-03-08)

### Perf

- **server**: migrate HTTP layer from net/http to fasthttp — ~141k req/sec (55% faster than Bun)
- **server**: use `tcp4` listener to eliminate dual-stack overhead (2x throughput gain on macOS)

### Refactor

- **handler**: replace `http.ServeContent` with custom `parseRange()`/`serveRange()` for byte-range requests
- **compress**: convert gzip middleware from wrapping `ResponseWriter` to post-processing response body
- **security**: use `ctx.SetStatusCode()`+`ctx.SetBodyString()` instead of `ctx.Error()` to preserve headers
- **cache**: change `CachedFile` header fields from `[]string` to `string`

### Build

- **benchmark**: add fasthttp/net-http hello world baselines and update baremetal script

## v1.2.0 (2026-03-07)

### Feat
Expand Down
9 changes: 8 additions & 1 deletion CLI.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ Grouped by concern for readability. All flags are optional; unset flags do not o
|------|------|---------|--------------|
| `--host` | string | `` (all interfaces) | `server.addr` (host part) |
| `--port`, `-p` | int | `8080` | `server.addr` (port part) |
| `--redirect-host` | string | — | `server.redirect_host` |
| `--tls-cert` | string | — | `server.tls_cert` |
| `--tls-key` | string | — | `server.tls_key` |
| `--tls-port` | int | `8443` | `server.tls_addr` (port part) |
Expand All @@ -205,6 +206,8 @@ Grouped by concern for readability. All flags are optional; unset flags do not o
|------|------|---------|--------------|
| `--no-cache` | bool | `false` | `cache.enabled = false` |
| `--cache-size` | string | `256MB` | `cache.max_bytes` (parses `256MB`, `64MB`, `1GB`) |
| `--preload` | bool | `false` | `cache.preload` — load all files into cache at startup |
| `--gc-percent` | int | `0` | `cache.gc_percent` — Go GC target % (0 = default; try 400 for throughput) |

#### Compression

Expand Down Expand Up @@ -244,6 +247,7 @@ static-web --dir-listing --no-dotfile-block ~/Downloads

# Serve with TLS (HTTPS on :443, HTTP redirect on :80)
static-web --port 80 --tls-port 443 \
--redirect-host static.example.com \
--tls-cert /etc/ssl/cert.pem \
--tls-key /etc/ssl/key.pem \
./public
Expand All @@ -265,6 +269,9 @@ static-web
# Disable caching (useful during local development to see file changes immediately)
static-web --no-cache ./dist

# Maximum throughput: preload all files + tune GC
static-web --preload --gc-percent 400 ./dist

# Print version info
static-web version
```
Expand Down Expand Up @@ -385,7 +392,7 @@ The CLI was implemented using Go stdlib `flag.FlagSet` — no external framework
- **`--host` + `--port` merging**: `net.SplitHostPort` / `net.JoinHostPort` used to decompose and reconstruct `server.addr`.
- **`parseBytes()`**: a small helper that parses `256MB`, `1GB`, etc. with `B`/`KB`/`MB`/`GB` suffixes (case-insensitive).
- **`//go:embed config.toml.example`**: the example config is embedded in `cmd/static-web/` at compile time. The binary is fully self-contained.
- **`--quiet`**: passes `io.Discard` to a `loggingMiddlewareWithWriter` variant, suppressing access log output with zero overhead.
- **`--quiet`**: skips access-log middleware entirely, removing per-request logging overhead.
- **`--verbose`**: calls `logConfig(cfg)` after all overrides are applied, so you see the final resolved values.
- **Version injection**: `internal/version.Version`, `Commit`, `Date` are set via `-ldflags` at build time. Default to `"dev"`, `"none"`, `"unknown"` for `go run`.

Expand Down
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: build run test bench lint precompress clean release install commit bump changelog benchmark benchmark-keep benchmark-down
.PHONY: build run test bench lint precompress clean release install commit bump changelog benchmark benchmark-keep benchmark-down benchmark-baremetal

# Binary output path and name
BIN := bin/static-web
Expand Down Expand Up @@ -83,3 +83,7 @@ benchmark-keep:
## benchmark-down: tear down any running benchmark containers
benchmark-down:
docker compose -f benchmark/docker-compose.benchmark.yml down --remove-orphans

## benchmark-baremetal: run bare-metal benchmark (static-web production vs Bun, no Docker)
benchmark-baremetal:
@bash benchmark/baremetal.sh
Loading