diff options
author | Marc Pervaz Boocha <mboocha@sudomsg.com> | 2025-08-07 22:51:34 +0530 |
---|---|---|
committer | Marc Pervaz Boocha <mboocha@sudomsg.com> | 2025-08-07 22:51:34 +0530 |
commit | 1326bb4103694d7ceac23b23329997ea2207a3f6 (patch) | |
tree | 72eb0065b597121c4e54518d303f5d15de40336d /http/server.go | |
parent | Fixed missing signals (diff) | |
download | kit-1326bb4103694d7ceac23b23329997ea2207a3f6.tar kit-1326bb4103694d7ceac23b23329997ea2207a3f6.tar.gz kit-1326bb4103694d7ceac23b23329997ea2207a3f6.tar.bz2 kit-1326bb4103694d7ceac23b23329997ea2207a3f6.tar.lz kit-1326bb4103694d7ceac23b23329997ea2207a3f6.tar.xz kit-1326bb4103694d7ceac23b23329997ea2207a3f6.tar.zst kit-1326bb4103694d7ceac23b23329997ea2207a3f6.zip |
Added File Mode to sockets and socket activation
Diffstat (limited to 'http/server.go')
-rw-r--r-- | http/server.go | 153 |
1 files changed, 89 insertions, 64 deletions
diff --git a/http/server.go b/http/server.go index dc74008..b74e39b 100644 --- a/http/server.go +++ b/http/server.go @@ -12,13 +12,12 @@ import ( "context" "errors" "fmt" - "log/slog" + "io/fs" "net" - "net/http" + "os" + "strings" "sync" - "time" - "go.sudomsg.com/kit/logging" "golang.org/x/sync/errgroup" ) @@ -42,13 +41,71 @@ func (ls Listeners) CloseAll() error { return nil } +type NetType int + +const ( + NetTCP NetType = iota + NetTCP4 + NetTCP6 + NetUnix + NetUnixPacket +) + +func (n NetType) String() string { + switch n { + case NetTCP: + return "tcp" + case NetTCP4: + return "tcp4" + case NetTCP6: + return "tcp6" + case NetUnix: + return "unix" + case NetUnixPacket: + return "unixpacket" + default: + return fmt.Sprintf("NetType(%d)", n) + } +} + +func (n *NetType) Set(s string) error { + switch strings.ToLower(s) { + case "tcp": + *n = NetTCP + case "tcp4": + *n = NetTCP4 + case "tcp6": + *n = NetTCP6 + case "unix": + *n = NetUnix + case "unixpacket": + *n = NetUnixPacket + default: + return fmt.Errorf("invalid NetType %q", s) + } + return nil +} + +func (n *NetType) UnmarshalText(b []byte) error { + return n.Set(string(b)) +} + +func (n NetType) MarshalText() ([]byte, error) { + return n.AppendText(nil) +} + +func (n NetType) AppendText(dst []byte) ([]byte, error) { + return append(dst, n.String()...), nil +} + // ServerConfig defines a single network listener configuration. // // Network is the network type, e.g., "tcp". // Address is the socket address, e.g., ":8080". type ServerConfig struct { - Network string `toml:"network"` - Address string `toml:"address"` + Network NetType + Address string + Mode fs.FileMode } // OpenConfigListeners opens network listeners as specified by the provided ServerConfig slice. @@ -62,16 +119,12 @@ func OpenConfigListners(ctx context.Context, config []ServerConfig) (Listeners, lns := make(Listeners, 0, len(config)) var mu sync.Mutex g, ctx := errgroup.WithContext(ctx) - var lc net.ListenConfig + for _, cfg := range config { g.Go(func() error { - network := cfg.Network - if network == "" { - network = "tcp" - } - ln, err := lc.Listen(ctx, network, cfg.Address) + ln, err := listenConfig(ctx, cfg) if err != nil { - return fmt.Errorf("failed to listen on %s %s: %w", network, cfg.Address, err) + return err } mu.Lock() @@ -87,60 +140,32 @@ func OpenConfigListners(ctx context.Context, config []ServerConfig) (Listeners, return lns, nil } -func OpenListeners(ctx context.Context, config []ServerConfig) (Listeners, error) { - return OpenConfigListners(ctx, config) -} - -// RunHTTPServers runs HTTP servers concurrently on all provided listeners. -// -// The provided handler is used for all servers. -// Servers respond to context cancellation by performing a graceful shutdown with a timeout of 10 seconds. -// -// Logging is performed using the slog.Logger extracted from context. -// Each server logs startup, shutdown, and errors. -// -// This function blocks until all servers have stopped or an error occurs. -func RunHTTPServers(ctx context.Context, lns Listeners, handler http.Handler) error { - g, ctx := errgroup.WithContext(ctx) - - for _, ln := range lns { - g.Go(func() error { - logger, ctx := logging.With(ctx, "address", ln.Addr()) - - srv := &http.Server{ - Addr: ln.Addr().String(), - Handler: handler, - BaseContext: func(l net.Listener) context.Context { - return ctx - }, - ErrorLog: slog.NewLogLogger(logger.Handler(), slog.LevelError), - } - - logger.Log(ctx, slog.LevelInfo, "HTTP server serving") - - if err := httpServeContext(ctx, srv, ln); err != nil && !errors.Is(err, http.ErrServerClosed) { - return fmt.Errorf("HTTP server Serve Error: %w", err) - } - return nil - }) +func listenConfig(ctx context.Context, cfg ServerConfig) (net.Listener, error) { + network := cfg.Network + var lc net.ListenConfig + ln, err := lc.Listen(ctx, network.String(), cfg.Address) + if err != nil { + return nil, fmt.Errorf("failed to listen on %s %s: %w", network, cfg.Address, err) + } + if _, ok := ln.(*net.UnixListener); cfg.Mode != 0 && ok { + if err := os.Chmod(cfg.Address, cfg.Mode); err != nil { + ln.Close() + return nil, fmt.Errorf("chmod failed on %s: %w", cfg.Address, err) + } } - return g.Wait() + return ln, nil } -func httpServeContext(ctx context.Context, srv *http.Server, ln net.Listener) error { - logger := logging.FromContext(ctx) - go func() { - <-ctx.Done() - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() +func OpenListeners(ctx context.Context, config []ServerConfig) (Listeners, error) { + lns, err := getSystemdListeners() + if err != nil { + return nil, fmt.Errorf("systemd socket activation failed: %w", err) + } + if lns != nil { + // Systemd is in charge; we don't honor ServerConfig here + return lns, nil + } - err := srv.Shutdown(ctx) - if err != nil { - logger.Log(ctx, slog.LevelWarn, "HTTP server Shutdown Error", slog.Any("error", err)) - } else { - logger.Log(ctx, slog.LevelInfo, "HTTP Server Shutdown Complete") - } - }() - return srv.Serve(ln) + return OpenConfigListners(ctx, config) } |