feat(selfhost-web): make webapp-server timeouts configurable (#6147)

Signed-off-by: Rodrigo Kellermann <kellermann@gmail.com>
Co-authored-by: James George <25279263+jamesgeorge007@users.noreply.github.com>
This commit is contained in:
Rodrigo Kellermann 2026-04-27 16:15:41 -03:00 committed by GitHub
parent 9861ee84ad
commit 40ac84c115
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 48 additions and 14 deletions

View file

@ -16,15 +16,18 @@ docker build -t hoppscotch-webapp-server .
## Configuration
| Variable | Description | Default |
|----------------------------------|------------------------------------|------------------------------------------------|
| `WEBAPP_SERVER_PORT` | Server port | `3200` |
| `FRONTEND_PATH` | Path to frontend assets | `/site/selfhost-web` (prod) or `../dist` (dev) |
| `WEBAPP_SERVER_SIGNING_SECRET` | Secret string for key derivation | None |
| `WEBAPP_SERVER_SIGNING_SEED` | Base64 encoded 32-byte seed | None |
| `WEBAPP_SERVER_SIGNING_KEY` | Base64 encoded 64-byte private key | None |
| `WEBAPP_SERVER_SIGNING_KEY_FILE` | Custom path for key file | `/data/webapp-server/signing.key` |
| `GO_ENV` | Set to `development` for dev mode | None |
| Variable | Description | Default |
|----------------------------------|------------------------------------------------------------|------------------------------------------------|
| `WEBAPP_SERVER_PORT` | Server port | `3200` |
| `WEBAPP_SERVER_READ_TIMEOUT` | HTTP read timeout (Go duration, e.g. `30s`; `0` disables) | `15s` |
| `WEBAPP_SERVER_WRITE_TIMEOUT` | HTTP write timeout (Go duration, e.g. `30s`; `0` disables) | `15s` |
| `WEBAPP_SERVER_IDLE_TIMEOUT` | HTTP idle timeout (Go duration, e.g. `2m`; `0` disables) | `60s` |
| `FRONTEND_PATH` | Path to frontend assets | `/site/selfhost-web` (prod) or `../dist` (dev) |
| `WEBAPP_SERVER_SIGNING_SECRET` | Secret string for key derivation | None |
| `WEBAPP_SERVER_SIGNING_SEED` | Base64 encoded 32-byte seed | None |
| `WEBAPP_SERVER_SIGNING_KEY` | Base64 encoded 64-byte private key | None |
| `WEBAPP_SERVER_SIGNING_KEY_FILE` | Custom path for key file | `/data/webapp-server/signing.key` |
| `GO_ENV` | Set to `development` for dev mode | None |
## Signing Key Persistence

View file

@ -4,17 +4,43 @@ import (
"log"
"os"
"strconv"
"time"
)
const (
DefaultPort = 3200
DefaultFrontendPath = "/site/selfhost-web"
DevFrontendPath = "../dist"
DefaultReadTimeout = 15 * time.Second
DefaultWriteTimeout = 15 * time.Second
DefaultIdleTimeout = 60 * time.Second
)
type Config struct {
Port int
FrontendPath string
ReadTimeout time.Duration
WriteTimeout time.Duration
IdleTimeout time.Duration
}
// parseDuration reads a Go duration string from an env var (e.g. "30s", "2m", "0").
// A value of "0" is valid and disables the timeout (net/http semantics).
// If the variable is unset or invalid, it logs a warning and returns the fallback.
func parseDuration(envKey string, fallback time.Duration) time.Duration {
if s := os.Getenv(envKey); s != "" {
if d, err := time.ParseDuration(s); err == nil && d >= 0 {
if d == 0 {
log.Printf("Using %s from environment: %s (timeout disabled)", envKey, s)
} else {
log.Printf("Using %s from environment: %s", envKey, s)
}
return d
}
log.Printf("Warning: Invalid %s value '%s', using default %v", envKey, s, fallback)
}
return fallback
}
// Load reads config from env vars with sensible defaults
@ -46,5 +72,9 @@ func Load() *Config {
log.Println("Running in production mode, using frontend path: /site/selfhost-web")
}
cfg.ReadTimeout = parseDuration("WEBAPP_SERVER_READ_TIMEOUT", DefaultReadTimeout)
cfg.WriteTimeout = parseDuration("WEBAPP_SERVER_WRITE_TIMEOUT", DefaultWriteTimeout)
cfg.IdleTimeout = parseDuration("WEBAPP_SERVER_IDLE_TIMEOUT", DefaultIdleTimeout)
return cfg
}

View file

@ -62,14 +62,15 @@ func main() {
addr := fmt.Sprintf(":%d", cfg.Port)
// NOTE: these timeouts are pretty conservative
// bump them if you're serving huge bundles over slow connections
// NOTE: timeouts default to conservative values but can be overridden
// via WEBAPP_SERVER_READ_TIMEOUT, WEBAPP_SERVER_WRITE_TIMEOUT, WEBAPP_SERVER_IDLE_TIMEOUT
// (Go duration strings, e.g. "30s", "2m", "0" to disable)
httpServer := &http.Server{
Addr: addr,
Handler: mux,
ReadTimeout: 15 * time.Second,
WriteTimeout: 15 * time.Second,
IdleTimeout: 60 * time.Second,
ReadTimeout: cfg.ReadTimeout,
WriteTimeout: cfg.WriteTimeout,
IdleTimeout: cfg.IdleTimeout,
}
go func() {