mirror of
https://github.com/caddyserver/caddy.git
synced 2026-03-17 14:34:03 +00:00
Revert "listeners: Add support for named socket activation (#7243)"
This reverts commit 156ce99d3a.
This commit is contained in:
82
listeners.go
82
listeners.go
@@ -38,10 +38,6 @@ import (
|
|||||||
"github.com/caddyserver/caddy/v2/internal"
|
"github.com/caddyserver/caddy/v2/internal"
|
||||||
)
|
)
|
||||||
|
|
||||||
// listenFdsStart is the first file descriptor number for systemd socket activation.
|
|
||||||
// File descriptors 0, 1, 2 are reserved for stdin, stdout, stderr.
|
|
||||||
const listenFdsStart = 3
|
|
||||||
|
|
||||||
// NetworkAddress represents one or more network addresses.
|
// NetworkAddress represents one or more network addresses.
|
||||||
// It contains the individual components for a parsed network
|
// It contains the individual components for a parsed network
|
||||||
// address of the form accepted by ParseNetworkAddress().
|
// address of the form accepted by ParseNetworkAddress().
|
||||||
@@ -309,64 +305,6 @@ func IsFdNetwork(netw string) bool {
|
|||||||
return strings.HasPrefix(netw, "fd")
|
return strings.HasPrefix(netw, "fd")
|
||||||
}
|
}
|
||||||
|
|
||||||
// getFdByName returns the file descriptor number for the given
|
|
||||||
// socket name from systemd's LISTEN_FDNAMES environment variable.
|
|
||||||
// Socket names are provided by systemd via socket activation.
|
|
||||||
//
|
|
||||||
// The name can optionally include an index to handle multiple sockets
|
|
||||||
// with the same name: "web:0" for first, "web:1" for second, etc.
|
|
||||||
// If no index is specified, defaults to index 0 (first occurrence).
|
|
||||||
func getFdByName(nameWithIndex string) (int, error) {
|
|
||||||
if nameWithIndex == "" {
|
|
||||||
return 0, fmt.Errorf("socket name cannot be empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
fdNamesStr := os.Getenv("LISTEN_FDNAMES")
|
|
||||||
if fdNamesStr == "" {
|
|
||||||
return 0, fmt.Errorf("LISTEN_FDNAMES environment variable not set")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse name and optional index
|
|
||||||
parts := strings.Split(nameWithIndex, ":")
|
|
||||||
if len(parts) > 2 {
|
|
||||||
return 0, fmt.Errorf("invalid socket name format '%s': too many colons", nameWithIndex)
|
|
||||||
}
|
|
||||||
|
|
||||||
name := parts[0]
|
|
||||||
targetIndex := 0
|
|
||||||
|
|
||||||
if len(parts) > 1 {
|
|
||||||
var err error
|
|
||||||
targetIndex, err = strconv.Atoi(parts[1])
|
|
||||||
if err != nil {
|
|
||||||
return 0, fmt.Errorf("invalid socket index '%s': %v", parts[1], err)
|
|
||||||
}
|
|
||||||
if targetIndex < 0 {
|
|
||||||
return 0, fmt.Errorf("socket index cannot be negative: %d", targetIndex)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse the socket names
|
|
||||||
names := strings.Split(fdNamesStr, ":")
|
|
||||||
|
|
||||||
// Find the Nth occurrence of the requested name
|
|
||||||
matchCount := 0
|
|
||||||
for i, fdName := range names {
|
|
||||||
if fdName == name {
|
|
||||||
if matchCount == targetIndex {
|
|
||||||
return listenFdsStart + i, nil
|
|
||||||
}
|
|
||||||
matchCount++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if matchCount == 0 {
|
|
||||||
return 0, fmt.Errorf("socket name '%s' not found in LISTEN_FDNAMES", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0, fmt.Errorf("socket name '%s' found %d times, but index %d requested", name, matchCount, targetIndex)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseNetworkAddress parses addr into its individual
|
// ParseNetworkAddress parses addr into its individual
|
||||||
// components. The input string is expected to be of
|
// components. The input string is expected to be of
|
||||||
// the form "network/host:port-range" where any part is
|
// the form "network/host:port-range" where any part is
|
||||||
@@ -398,27 +336,9 @@ func ParseNetworkAddressWithDefaults(addr, defaultNetwork string, defaultPort ui
|
|||||||
}, err
|
}, err
|
||||||
}
|
}
|
||||||
if IsFdNetwork(network) {
|
if IsFdNetwork(network) {
|
||||||
fdAddr := host
|
|
||||||
|
|
||||||
// Handle named socket activation (fdname/name, fdgramname/name)
|
|
||||||
if strings.HasPrefix(network, "fdname") || strings.HasPrefix(network, "fdgramname") {
|
|
||||||
fdNum, err := getFdByName(host)
|
|
||||||
if err != nil {
|
|
||||||
return NetworkAddress{}, fmt.Errorf("named socket activation: %v", err)
|
|
||||||
}
|
|
||||||
fdAddr = strconv.Itoa(fdNum)
|
|
||||||
|
|
||||||
// Normalize network to standard fd/fdgram
|
|
||||||
if strings.HasPrefix(network, "fdname") {
|
|
||||||
network = "fd"
|
|
||||||
} else {
|
|
||||||
network = "fdgram"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NetworkAddress{
|
return NetworkAddress{
|
||||||
Network: network,
|
Network: network,
|
||||||
Host: fdAddr,
|
Host: host,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
var start, end uint64
|
var start, end uint64
|
||||||
|
|||||||
@@ -15,7 +15,6 @@
|
|||||||
package caddy
|
package caddy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@@ -653,286 +652,3 @@ func TestSplitUnixSocketPermissionsBits(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestGetFdByName tests the getFdByName function for systemd socket activation.
|
|
||||||
func TestGetFdByName(t *testing.T) {
|
|
||||||
// Save original environment
|
|
||||||
originalFdNames := os.Getenv("LISTEN_FDNAMES")
|
|
||||||
|
|
||||||
// Restore environment after test
|
|
||||||
defer func() {
|
|
||||||
if originalFdNames != "" {
|
|
||||||
os.Setenv("LISTEN_FDNAMES", originalFdNames)
|
|
||||||
} else {
|
|
||||||
os.Unsetenv("LISTEN_FDNAMES")
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
fdNames string
|
|
||||||
socketName string
|
|
||||||
expectedFd int
|
|
||||||
expectError bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "simple http socket",
|
|
||||||
fdNames: "http",
|
|
||||||
socketName: "http",
|
|
||||||
expectedFd: 3,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "multiple different sockets - first",
|
|
||||||
fdNames: "http:https:dns",
|
|
||||||
socketName: "http",
|
|
||||||
expectedFd: 3,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "multiple different sockets - second",
|
|
||||||
fdNames: "http:https:dns",
|
|
||||||
socketName: "https",
|
|
||||||
expectedFd: 4,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "multiple different sockets - third",
|
|
||||||
fdNames: "http:https:dns",
|
|
||||||
socketName: "dns",
|
|
||||||
expectedFd: 5,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "duplicate names - first occurrence (no index)",
|
|
||||||
fdNames: "web:web:api",
|
|
||||||
socketName: "web",
|
|
||||||
expectedFd: 3,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "duplicate names - first occurrence (explicit index 0)",
|
|
||||||
fdNames: "web:web:api",
|
|
||||||
socketName: "web:0",
|
|
||||||
expectedFd: 3,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "duplicate names - second occurrence (index 1)",
|
|
||||||
fdNames: "web:web:api",
|
|
||||||
socketName: "web:1",
|
|
||||||
expectedFd: 4,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "complex duplicates - first api",
|
|
||||||
fdNames: "web:api:web:api:dns",
|
|
||||||
socketName: "api:0",
|
|
||||||
expectedFd: 4,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "complex duplicates - second api",
|
|
||||||
fdNames: "web:api:web:api:dns",
|
|
||||||
socketName: "api:1",
|
|
||||||
expectedFd: 6,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "complex duplicates - first web",
|
|
||||||
fdNames: "web:api:web:api:dns",
|
|
||||||
socketName: "web:0",
|
|
||||||
expectedFd: 3,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "complex duplicates - second web",
|
|
||||||
fdNames: "web:api:web:api:dns",
|
|
||||||
socketName: "web:1",
|
|
||||||
expectedFd: 5,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "socket not found",
|
|
||||||
fdNames: "http:https",
|
|
||||||
socketName: "missing",
|
|
||||||
expectError: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "empty socket name",
|
|
||||||
fdNames: "http",
|
|
||||||
socketName: "",
|
|
||||||
expectError: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "missing LISTEN_FDNAMES",
|
|
||||||
fdNames: "",
|
|
||||||
socketName: "http",
|
|
||||||
expectError: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "index out of range",
|
|
||||||
fdNames: "web:web",
|
|
||||||
socketName: "web:2",
|
|
||||||
expectError: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "negative index",
|
|
||||||
fdNames: "web",
|
|
||||||
socketName: "web:-1",
|
|
||||||
expectError: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "invalid index format",
|
|
||||||
fdNames: "web",
|
|
||||||
socketName: "web:abc",
|
|
||||||
expectError: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "too many colons",
|
|
||||||
fdNames: "web",
|
|
||||||
socketName: "web:0:extra",
|
|
||||||
expectError: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range tests {
|
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
|
||||||
// Set up environment
|
|
||||||
if tc.fdNames != "" {
|
|
||||||
os.Setenv("LISTEN_FDNAMES", tc.fdNames)
|
|
||||||
} else {
|
|
||||||
os.Unsetenv("LISTEN_FDNAMES")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test the function
|
|
||||||
fd, err := getFdByName(tc.socketName)
|
|
||||||
|
|
||||||
if tc.expectError {
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("Expected error but got none")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Expected no error but got: %v", err)
|
|
||||||
}
|
|
||||||
if fd != tc.expectedFd {
|
|
||||||
t.Errorf("Expected FD %d but got %d", tc.expectedFd, fd)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestParseNetworkAddressFdName tests parsing of fdname and fdgramname addresses.
|
|
||||||
func TestParseNetworkAddressFdName(t *testing.T) {
|
|
||||||
// Save and restore environment
|
|
||||||
originalFdNames := os.Getenv("LISTEN_FDNAMES")
|
|
||||||
defer func() {
|
|
||||||
if originalFdNames != "" {
|
|
||||||
os.Setenv("LISTEN_FDNAMES", originalFdNames)
|
|
||||||
} else {
|
|
||||||
os.Unsetenv("LISTEN_FDNAMES")
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Set up test environment
|
|
||||||
os.Setenv("LISTEN_FDNAMES", "http:https:dns")
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
input string
|
|
||||||
expectAddr NetworkAddress
|
|
||||||
expectErr bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
input: "fdname/http",
|
|
||||||
expectAddr: NetworkAddress{
|
|
||||||
Network: "fd",
|
|
||||||
Host: "3",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: "fdname/https",
|
|
||||||
expectAddr: NetworkAddress{
|
|
||||||
Network: "fd",
|
|
||||||
Host: "4",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: "fdname/dns",
|
|
||||||
expectAddr: NetworkAddress{
|
|
||||||
Network: "fd",
|
|
||||||
Host: "5",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: "fdname/http:0",
|
|
||||||
expectAddr: NetworkAddress{
|
|
||||||
Network: "fd",
|
|
||||||
Host: "3",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: "fdname/https:0",
|
|
||||||
expectAddr: NetworkAddress{
|
|
||||||
Network: "fd",
|
|
||||||
Host: "4",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: "fdgramname/http",
|
|
||||||
expectAddr: NetworkAddress{
|
|
||||||
Network: "fdgram",
|
|
||||||
Host: "3",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: "fdgramname/https",
|
|
||||||
expectAddr: NetworkAddress{
|
|
||||||
Network: "fdgram",
|
|
||||||
Host: "4",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: "fdgramname/http:0",
|
|
||||||
expectAddr: NetworkAddress{
|
|
||||||
Network: "fdgram",
|
|
||||||
Host: "3",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: "fdname/nonexistent",
|
|
||||||
expectErr: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: "fdgramname/nonexistent",
|
|
||||||
expectErr: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: "fdname/http:99",
|
|
||||||
expectErr: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: "fdname/invalid:abc",
|
|
||||||
expectErr: true,
|
|
||||||
},
|
|
||||||
// Test that old fd/N syntax still works
|
|
||||||
{
|
|
||||||
input: "fd/7",
|
|
||||||
expectAddr: NetworkAddress{
|
|
||||||
Network: "fd",
|
|
||||||
Host: "7",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
input: "fdgram/8",
|
|
||||||
expectAddr: NetworkAddress{
|
|
||||||
Network: "fdgram",
|
|
||||||
Host: "8",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, tc := range tests {
|
|
||||||
actualAddr, err := ParseNetworkAddress(tc.input)
|
|
||||||
|
|
||||||
if tc.expectErr && err == nil {
|
|
||||||
t.Errorf("Test %d (%s): Expected error but got none", i, tc.input)
|
|
||||||
}
|
|
||||||
if !tc.expectErr && err != nil {
|
|
||||||
t.Errorf("Test %d (%s): Expected no error but got: %v", i, tc.input, err)
|
|
||||||
}
|
|
||||||
if !tc.expectErr && !reflect.DeepEqual(tc.expectAddr, actualAddr) {
|
|
||||||
t.Errorf("Test %d (%s): Expected %+v but got %+v", i, tc.input, tc.expectAddr, actualAddr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user