aboutsummaryrefslogtreecommitdiffstats
path: root/http/server.go
diff options
context:
space:
mode:
authorMarc Pervaz Boocha <mboocha@sudomsg.com>2025-08-07 22:51:34 +0530
committerMarc Pervaz Boocha <mboocha@sudomsg.com>2025-08-07 22:51:34 +0530
commit1326bb4103694d7ceac23b23329997ea2207a3f6 (patch)
tree72eb0065b597121c4e54518d303f5d15de40336d /http/server.go
parentFixed missing signals (diff)
downloadkit-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.go153
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)
}