Files
smallwebwaf/internal/sww/rproxy.go
T
sneak 42643be662
check / check (push) Successful in 3m13s
Rename the program to sww and its deciding daemon to discriminator
The program is now sww, built from cmd/sww, with two modes: sww rproxy and
sww discriminator. servewaf becomes discriminator everywhere it names that
daemon: the package (internal/discriminator), its types, functions, files, the
discriminator_line log field, and its docs and tests.

smallwebwaf is left only where sneak's directive allows it: the documents'
product name, the git.eeqj.de/sneak/smallwebwaf URLs, and the Go module path
sneak.berlin/go/smallwebwaf. Everywhere else a name containing it takes the sww
form: the package (internal/sww), metric names (sww_*), the X-Sww-* headers,
the /_sww/ admin endpoints, the OS user, and the state and rules directories.
The SWWAF_ environment variables take the SWW_ form.

No logic changes. make check passes.

Model: deepseek-v4-pro
2026-11-07 04:47:34 +00:00

100 lines
2.4 KiB
Go

package sww
import (
"context"
"errors"
"log/slog"
"net"
"net/http"
"os"
"sneak.berlin/go/smallwebwaf/internal/config"
"sneak.berlin/go/smallwebwaf/internal/requestlog"
"sneak.berlin/go/smallwebwaf/internal/rproxy"
)
// RunRProxy is `sww rproxy`: it reads rproxy's settings, then serves
// requests until ctx is done. Then it gives the requests in progress
// shutdownTimeout to finish, and the reports being sent SWRP_TIMEOUT_MS. It
// returns the process's exit status, 1 when rproxy cannot start. Its process
// messages name the host's name as instance, and it writes those at info or
// more severe: rproxy has no setting for either.
func RunRProxy(ctx context.Context, params Params) int {
hostname, _ := os.Hostname() // "" when the host has no name to give
processLog := requestlog.NewProcessLogger(params.Stdout, hostname, slog.LevelInfo)
cfg, err := config.RProxyFromEnvironment(params.LookupEnv)
if err != nil {
processLog.Error("invalid setting", "error", err.Error())
return 1
}
server := rproxy.New(rproxy.Params{
Config: cfg,
RequestLog: params.Stdout,
ProcessLog: processLog,
})
listener, err := (&net.ListenConfig{}).Listen(ctx, "tcp", cfg.ListenAddr)
if err != nil {
processLog.Error("cannot listen on SWRP_LISTEN_ADDR", "error", err.Error())
return 1
}
processLog.Info("starting",
"version", params.Version,
"address", listener.Addr().String(),
"settings", cfg)
return serveRProxy(ctx, server, listener, processLog)
}
// serveRProxy serves requests on listener until ctx is done, and then stops as
// RunRProxy says.
func serveRProxy(
ctx context.Context, server *rproxy.Server, listener net.Listener,
processLog *slog.Logger,
) int {
served := make(chan error, 1)
go func() {
served <- server.Serve(listener)
}()
select {
case err := <-served:
processLog.Error("serving failed", "error", err.Error())
return 1
case <-ctx.Done():
}
processLog.Info("stopping")
shutdownCtx, cancel := context.WithTimeout(context.WithoutCancel(ctx),
shutdownTimeout)
defer cancel()
err := server.Shutdown(shutdownCtx)
if err != nil {
processLog.Warn("requests still in progress were cut off",
"error", err.Error())
_ = server.Close()
}
err = <-served
if !errors.Is(err, http.ErrServerClosed) {
processLog.Error("serving failed", "error", err.Error())
return 1
}
server.WaitForReports()
processLog.Info("stopped")
return 0
}