1
0
Fork 0

Support systemd socket-activation

Co-authored-by: Michael <michael.matur@gmail.com>
This commit is contained in:
Julien Salleyron 2024-06-25 16:30:04 +02:00 committed by GitHub
parent 9e0800f938
commit b7de043991
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 91 additions and 20 deletions

View file

@ -48,8 +48,15 @@ const (
var (
clientConnectionStates = map[string]*connState{}
clientConnectionStatesMu = sync.RWMutex{}
socketActivationListeners map[string]net.Listener
)
func init() {
// Populates pre-defined socketActivationListeners by socket activation.
populateSocketActivationListeners()
}
type connState struct {
State string
KeepAliveState string
@ -96,6 +103,7 @@ func NewTCPEntryPoints(entryPointsConfig static.EntryPoints, hostResolverConfig
return clientConnectionStates
}))
}
serverEntryPointsTCP := make(TCPEntryPoints)
for entryPointName, config := range entryPointsConfig {
protocol, err := config.GetProtocol()
@ -113,7 +121,7 @@ func NewTCPEntryPoints(entryPointsConfig static.EntryPoints, hostResolverConfig
OpenConnectionsGauge().
With("entrypoint", entryPointName, "protocol", "TCP")
serverEntryPointsTCP[entryPointName], err = NewTCPEntryPoint(ctx, config, hostResolverConfig, openConnectionsGauge)
serverEntryPointsTCP[entryPointName], err = NewTCPEntryPoint(ctx, entryPointName, config, hostResolverConfig, openConnectionsGauge)
if err != nil {
return nil, fmt.Errorf("error while building entryPoint %s: %w", entryPointName, err)
}
@ -169,10 +177,10 @@ type TCPEntryPoint struct {
}
// NewTCPEntryPoint creates a new TCPEntryPoint.
func NewTCPEntryPoint(ctx context.Context, configuration *static.EntryPoint, hostResolverConfig *types.HostResolverConfig, openConnectionsGauge gokitmetrics.Gauge) (*TCPEntryPoint, error) {
func NewTCPEntryPoint(ctx context.Context, name string, config *static.EntryPoint, hostResolverConfig *types.HostResolverConfig, openConnectionsGauge gokitmetrics.Gauge) (*TCPEntryPoint, error) {
tracker := newConnectionTracker(openConnectionsGauge)
listener, err := buildListener(ctx, configuration)
listener, err := buildListener(ctx, name, config)
if err != nil {
return nil, fmt.Errorf("error preparing server: %w", err)
}
@ -181,19 +189,19 @@ func NewTCPEntryPoint(ctx context.Context, configuration *static.EntryPoint, hos
reqDecorator := requestdecorator.New(hostResolverConfig)
httpServer, err := createHTTPServer(ctx, listener, configuration, true, reqDecorator)
httpServer, err := createHTTPServer(ctx, listener, config, true, reqDecorator)
if err != nil {
return nil, fmt.Errorf("error preparing http server: %w", err)
}
rt.SetHTTPForwarder(httpServer.Forwarder)
httpsServer, err := createHTTPServer(ctx, listener, configuration, false, reqDecorator)
httpsServer, err := createHTTPServer(ctx, listener, config, false, reqDecorator)
if err != nil {
return nil, fmt.Errorf("error preparing https server: %w", err)
}
h3Server, err := newHTTP3Server(ctx, configuration, httpsServer)
h3Server, err := newHTTP3Server(ctx, config, httpsServer)
if err != nil {
return nil, fmt.Errorf("error preparing http3 server: %w", err)
}
@ -206,7 +214,7 @@ func NewTCPEntryPoint(ctx context.Context, configuration *static.EntryPoint, hos
return &TCPEntryPoint{
listener: listener,
switcher: tcpSwitcher,
transportConfiguration: configuration.Transport,
transportConfiguration: config.Transport,
tracker: tracker,
httpServer: httpServer,
httpsServer: httpsServer,
@ -460,17 +468,29 @@ func buildProxyProtocolListener(ctx context.Context, entryPoint *static.EntryPoi
return proxyListener, nil
}
func buildListener(ctx context.Context, entryPoint *static.EntryPoint) (net.Listener, error) {
listenConfig := newListenConfig(entryPoint)
listener, err := listenConfig.Listen(ctx, "tcp", entryPoint.GetAddress())
if err != nil {
return nil, fmt.Errorf("error opening listener: %w", err)
func buildListener(ctx context.Context, name string, config *static.EntryPoint) (net.Listener, error) {
var listener net.Listener
var err error
// if we have predefined listener from socket activation
if ln, ok := socketActivationListeners[name]; ok {
listener = ln
} else {
if len(socketActivationListeners) > 0 {
log.Warn().Str("name", name).Msg("Unable to find socket activation listener for entryPoint")
}
listenConfig := newListenConfig(config)
listener, err = listenConfig.Listen(ctx, "tcp", config.GetAddress())
if err != nil {
return nil, fmt.Errorf("error opening listener: %w", err)
}
}
listener = tcpKeepAliveListener{listener.(*net.TCPListener)}
if entryPoint.ProxyProtocol != nil {
listener, err = buildProxyProtocolListener(ctx, entryPoint, listener)
if config.ProxyProtocol != nil {
listener, err = buildProxyProtocolListener(ctx, config, listener)
if err != nil {
return nil, fmt.Errorf("error creating proxy protocol listener: %w", err)
}

View file

@ -85,7 +85,7 @@ func TestHTTP3AdvertisedPort(t *testing.T) {
epConfig := &static.EntryPointsTransport{}
epConfig.SetDefaults()
entryPoint, err := NewTCPEntryPoint(context.Background(), &static.EntryPoint{
entryPoint, err := NewTCPEntryPoint(context.Background(), "", &static.EntryPoint{
Address: "127.0.0.1:8090",
Transport: epConfig,
ForwardedHeaders: &static.ForwardedHeaders{},

View file

@ -72,7 +72,7 @@ func testShutdown(t *testing.T, router *tcprouter.Router) {
epConfig.RespondingTimeouts.ReadTimeout = ptypes.Duration(5 * time.Second)
epConfig.RespondingTimeouts.WriteTimeout = ptypes.Duration(5 * time.Second)
entryPoint, err := NewTCPEntryPoint(context.Background(), &static.EntryPoint{
entryPoint, err := NewTCPEntryPoint(context.Background(), "", &static.EntryPoint{
// We explicitly use an IPV4 address because on Alpine, with an IPV6 address
// there seems to be shenanigans related to properly cleaning up file descriptors
Address: "127.0.0.1:0",
@ -159,7 +159,7 @@ func TestReadTimeoutWithoutFirstByte(t *testing.T) {
epConfig.SetDefaults()
epConfig.RespondingTimeouts.ReadTimeout = ptypes.Duration(2 * time.Second)
entryPoint, err := NewTCPEntryPoint(context.Background(), &static.EntryPoint{
entryPoint, err := NewTCPEntryPoint(context.Background(), "", &static.EntryPoint{
Address: ":0",
Transport: epConfig,
ForwardedHeaders: &static.ForwardedHeaders{},
@ -196,7 +196,7 @@ func TestReadTimeoutWithFirstByte(t *testing.T) {
epConfig.SetDefaults()
epConfig.RespondingTimeouts.ReadTimeout = ptypes.Duration(2 * time.Second)
entryPoint, err := NewTCPEntryPoint(context.Background(), &static.EntryPoint{
entryPoint, err := NewTCPEntryPoint(context.Background(), "", &static.EntryPoint{
Address: ":0",
Transport: epConfig,
ForwardedHeaders: &static.ForwardedHeaders{},
@ -236,7 +236,7 @@ func TestKeepAliveMaxRequests(t *testing.T) {
epConfig.SetDefaults()
epConfig.KeepAliveMaxRequests = 3
entryPoint, err := NewTCPEntryPoint(context.Background(), &static.EntryPoint{
entryPoint, err := NewTCPEntryPoint(context.Background(), "", &static.EntryPoint{
Address: ":0",
Transport: epConfig,
ForwardedHeaders: &static.ForwardedHeaders{},
@ -282,7 +282,7 @@ func TestKeepAliveMaxTime(t *testing.T) {
epConfig.SetDefaults()
epConfig.KeepAliveMaxTime = ptypes.Duration(time.Millisecond)
entryPoint, err := NewTCPEntryPoint(context.Background(), &static.EntryPoint{
entryPoint, err := NewTCPEntryPoint(context.Background(), "", &static.EntryPoint{
Address: ":0",
Transport: epConfig,
ForwardedHeaders: &static.ForwardedHeaders{},

View file

@ -0,0 +1,24 @@
//go:build !windows
package server
import (
"net"
"github.com/coreos/go-systemd/activation"
"github.com/rs/zerolog/log"
)
func populateSocketActivationListeners() {
listenersWithName, _ := activation.ListenersWithNames()
socketActivationListeners = make(map[string]net.Listener)
for name, lns := range listenersWithName {
if len(lns) != 1 {
log.Error().Str("listenersName", name).Msg("Socket activation listeners must have one and only one listener per name")
continue
}
socketActivationListeners[name] = lns[0]
}
}

View file

@ -0,0 +1,5 @@
//go:build windows
package server
func populateSocketActivationListeners() {}