package smallwebwaf 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 `smallwebwaf 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 }