// Package config loads engine settings from the environment, replacing the // hard-coded values in the original config.js / HTTPServer.js. package config import ( "net" "os" "strconv" "time" ) // Config holds all runtime settings. type Config struct { Host string // bind address, e.g. "0.0.0.0" Port int // listen port, default 7707 PublicDir string // static assets directory (default "./public") ScriptDir string // legacy compiled SDK bundle directory (default "./script") SDKDir string // ES-module SDK directory (default "./sdk") ReadHeaderTimeout time.Duration // HTTP read-header timeout ShutdownTimeout time.Duration // grace period for in-flight work on shutdown TermOutput bool // verbose terminal logging (the old `termoutput` flag) // --- scale / throughput knobs (defaults kept deliberately high) --------- // // The engine is built for very high connection counts and endless message // traffic, so these are tuned generous. They are env-overridable both up (more // headroom) and down (tighter memory on small hosts). See the memory note on // OutboundBuffer below. Conn ConnConfig } // ConnConfig groups per-connection transport tuning. type ConnConfig struct { // OutboundBuffer is how many frames may queue for one client before further // frames are dropped (best-effort relay; the connection is kept alive). // // Memory note: the channel backing this is preallocated per connection at // ~24 bytes/slot, so the default 1024 costs ~24 KiB/connection (~2.4 GiB at // 100k connections) even while idle. Lower MWSE_OUTBOUND_BUFFER on memory- // constrained hosts; raise it to tolerate burstier producers. OutboundBuffer int // MaxMessageSize caps a single inbound frame. High by default to support the // large tunneled payloads used for file transfer (#30). MaxMessageSize int64 ReadBufferSize int // gorilla per-connection read buffer WriteBufferSize int // gorilla write buffer size (pooled across connections) PingInterval time.Duration // heartbeat period (the original used 10s) PongWait time.Duration // how long to wait for a pong before dropping WriteWait time.Duration // deadline for a single socket write } // Load reads configuration from the environment, applying defaults that match the // original server. Recognised variables: // // MWSE_HOST, MWSE_PORT, MWSE_PUBLIC_DIR, MWSE_SCRIPT_DIR, // MWSE_SHUTDOWN_TIMEOUT (seconds), MWSE_TERM_OUTPUT (1/true) func Load() Config { return Config{ Host: env("MWSE_HOST", "0.0.0.0"), Port: envInt("MWSE_PORT", 7707), PublicDir: env("MWSE_PUBLIC_DIR", "./public"), ScriptDir: env("MWSE_SCRIPT_DIR", "./script"), SDKDir: env("MWSE_SDK_DIR", "./sdk"), ReadHeaderTimeout: 10 * time.Second, ShutdownTimeout: time.Duration(envInt("MWSE_SHUTDOWN_TIMEOUT", 10)) * time.Second, TermOutput: envBool("MWSE_TERM_OUTPUT", false), Conn: ConnConfig{ OutboundBuffer: envInt("MWSE_OUTBOUND_BUFFER", 1024), MaxMessageSize: int64(envInt("MWSE_MAX_MESSAGE_SIZE", 16<<20)), ReadBufferSize: envInt("MWSE_READ_BUFFER", 4096), WriteBufferSize: envInt("MWSE_WRITE_BUFFER", 4096), PingInterval: time.Duration(envInt("MWSE_PING_INTERVAL", 10)) * time.Second, PongWait: time.Duration(envInt("MWSE_PONG_WAIT", 60)) * time.Second, WriteWait: time.Duration(envInt("MWSE_WRITE_WAIT", 10)) * time.Second, }, } } // DefaultConnConfig returns the same connection tuning Load would produce from a // clean environment. Useful for tests and for ws.NewServer callers that do not // load the full Config. func DefaultConnConfig() ConnConfig { return ConnConfig{ OutboundBuffer: 1024, MaxMessageSize: 16 << 20, ReadBufferSize: 4096, WriteBufferSize: 4096, PingInterval: 10 * time.Second, PongWait: 60 * time.Second, WriteWait: 10 * time.Second, } } // Addr returns the host:port string for net/http. func (c Config) Addr() string { return net.JoinHostPort(c.Host, strconv.Itoa(c.Port)) } func env(key, def string) string { if v, ok := os.LookupEnv(key); ok && v != "" { return v } return def } func envInt(key string, def int) int { if v, ok := os.LookupEnv(key); ok { if n, err := strconv.Atoi(v); err == nil { return n } } return def } func envBool(key string, def bool) bool { if v, ok := os.LookupEnv(key); ok { if b, err := strconv.ParseBool(v); err == nil { return b } } return def }