mirror of
https://github.com/caddyserver/caddy.git
synced 2025-12-08 06:09:53 +00:00
376 lines
8 KiB
Go
376 lines
8 KiB
Go
//go:build linux && !nosystemd
|
|
|
|
package caddy
|
|
|
|
import (
|
|
"os"
|
|
"reflect"
|
|
"strconv"
|
|
"testing"
|
|
)
|
|
|
|
// TestGetSdListenFd tests the getSdListenFd function for systemd socket activation.
|
|
func TestGetSdListenFd(t *testing.T) {
|
|
// Save original environment
|
|
originalFdNames := os.Getenv("LISTEN_FDNAMES")
|
|
originalFds := os.Getenv("LISTEN_FDS")
|
|
originalPid := os.Getenv("LISTEN_PID")
|
|
|
|
// Restore environment after test
|
|
defer func() {
|
|
if originalFdNames != "" {
|
|
os.Setenv("LISTEN_FDNAMES", originalFdNames)
|
|
} else {
|
|
os.Unsetenv("LISTEN_FDNAMES")
|
|
}
|
|
if originalFds != "" {
|
|
os.Setenv("LISTEN_FDS", originalFds)
|
|
} else {
|
|
os.Unsetenv("LISTEN_FDS")
|
|
}
|
|
if originalPid != "" {
|
|
os.Setenv("LISTEN_PID", originalPid)
|
|
} else {
|
|
os.Unsetenv("LISTEN_PID")
|
|
}
|
|
}()
|
|
|
|
tests := []struct {
|
|
name string
|
|
fdNames string
|
|
fds string
|
|
socketName string
|
|
expectedFd uint
|
|
expectError bool
|
|
}{
|
|
{
|
|
name: "simple http socket",
|
|
fdNames: "http",
|
|
fds: "1",
|
|
socketName: "http",
|
|
expectedFd: 3,
|
|
},
|
|
{
|
|
name: "multiple different sockets - first",
|
|
fdNames: "http:https:dns",
|
|
fds: "3",
|
|
socketName: "http",
|
|
expectedFd: 3,
|
|
},
|
|
{
|
|
name: "multiple different sockets - second",
|
|
fdNames: "http:https:dns",
|
|
fds: "3",
|
|
socketName: "https",
|
|
expectedFd: 4,
|
|
},
|
|
{
|
|
name: "multiple different sockets - third",
|
|
fdNames: "http:https:dns",
|
|
fds: "3",
|
|
socketName: "dns",
|
|
expectedFd: 5,
|
|
},
|
|
{
|
|
name: "duplicate names - first occurrence (no index)",
|
|
fdNames: "web:web:api",
|
|
fds: "3",
|
|
socketName: "web",
|
|
expectedFd: 3,
|
|
},
|
|
{
|
|
name: "duplicate names - first occurrence (explicit index 0)",
|
|
fdNames: "web:web:api",
|
|
fds: "3",
|
|
socketName: "web:0",
|
|
expectedFd: 3,
|
|
},
|
|
{
|
|
name: "duplicate names - second occurrence (index 1)",
|
|
fdNames: "web:web:api",
|
|
fds: "3",
|
|
socketName: "web:1",
|
|
expectedFd: 4,
|
|
},
|
|
{
|
|
name: "complex duplicates - first api",
|
|
fdNames: "web:api:web:api:dns",
|
|
fds: "5",
|
|
socketName: "api:0",
|
|
expectedFd: 4,
|
|
},
|
|
{
|
|
name: "complex duplicates - second api",
|
|
fdNames: "web:api:web:api:dns",
|
|
fds: "5",
|
|
socketName: "api:1",
|
|
expectedFd: 6,
|
|
},
|
|
{
|
|
name: "complex duplicates - first web",
|
|
fdNames: "web:api:web:api:dns",
|
|
fds: "5",
|
|
socketName: "web:0",
|
|
expectedFd: 3,
|
|
},
|
|
{
|
|
name: "complex duplicates - second web",
|
|
fdNames: "web:api:web:api:dns",
|
|
fds: "5",
|
|
socketName: "web:1",
|
|
expectedFd: 5,
|
|
},
|
|
{
|
|
name: "socket not found",
|
|
fdNames: "http:https",
|
|
fds: "2",
|
|
socketName: "missing",
|
|
expectError: true,
|
|
},
|
|
{
|
|
name: "empty socket name",
|
|
fdNames: "http",
|
|
fds: "1",
|
|
socketName: "",
|
|
expectError: true,
|
|
},
|
|
{
|
|
name: "missing LISTEN_FDNAMES",
|
|
fdNames: "",
|
|
fds: "",
|
|
socketName: "http",
|
|
expectError: true,
|
|
},
|
|
{
|
|
name: "index out of range",
|
|
fdNames: "web:web",
|
|
fds: "2",
|
|
socketName: "web:2",
|
|
expectError: true,
|
|
},
|
|
{
|
|
name: "negative index",
|
|
fdNames: "web",
|
|
fds: "1",
|
|
socketName: "web:-1",
|
|
expectError: true,
|
|
},
|
|
{
|
|
name: "invalid index format",
|
|
fdNames: "web",
|
|
fds: "1",
|
|
socketName: "web:abc",
|
|
expectError: true,
|
|
},
|
|
{
|
|
name: "too many colons",
|
|
fdNames: "web",
|
|
fds: "1",
|
|
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")
|
|
}
|
|
|
|
if tc.fds != "" {
|
|
os.Setenv("LISTEN_FDS", tc.fds)
|
|
} else {
|
|
os.Unsetenv("LISTEN_FDS")
|
|
}
|
|
|
|
os.Setenv("LISTEN_PID", strconv.Itoa(os.Getpid()))
|
|
|
|
// Test the function
|
|
var (
|
|
listenFdsWithNames map[string][]uint
|
|
err error
|
|
fd uint
|
|
)
|
|
listenFdsWithNames, err = sdListenFdsWithNames()
|
|
if err == nil {
|
|
fd, err = getSdListenFd(listenFdsWithNames, 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)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestParseSystemdListenPlaceholder tests parsing of {systemd.listen.name} placeholders.
|
|
func TestParseSystemdListenPlaceholder(t *testing.T) {
|
|
// Save and restore environment
|
|
originalFdNames := os.Getenv("LISTEN_FDNAMES")
|
|
originalFds := os.Getenv("LISTEN_FDS")
|
|
originalPid := os.Getenv("LISTEN_PID")
|
|
|
|
defer func() {
|
|
if originalFdNames != "" {
|
|
os.Setenv("LISTEN_FDNAMES", originalFdNames)
|
|
} else {
|
|
os.Unsetenv("LISTEN_FDNAMES")
|
|
}
|
|
if originalFds != "" {
|
|
os.Setenv("LISTEN_FDS", originalFds)
|
|
} else {
|
|
os.Unsetenv("LISTEN_FDS")
|
|
}
|
|
if originalPid != "" {
|
|
os.Setenv("LISTEN_PID", originalPid)
|
|
} else {
|
|
os.Unsetenv("LISTEN_PID")
|
|
}
|
|
}()
|
|
|
|
// Set up test environment
|
|
os.Setenv("LISTEN_FDNAMES", "http:https:dns")
|
|
os.Setenv("LISTEN_FDS", "3")
|
|
os.Setenv("LISTEN_PID", strconv.Itoa(os.Getpid()))
|
|
|
|
tests := []struct {
|
|
input string
|
|
expectedAddr NetworkAddress
|
|
expectedFd uint
|
|
expectErr bool
|
|
}{
|
|
{
|
|
input: "fd/{systemd.listen.http}",
|
|
expectedAddr: NetworkAddress{
|
|
Network: "fd",
|
|
Host: "{systemd.listen.http}",
|
|
},
|
|
expectedFd: 3,
|
|
},
|
|
{
|
|
input: "fd/{systemd.listen.https}",
|
|
expectedAddr: NetworkAddress{
|
|
Network: "fd",
|
|
Host: "{systemd.listen.https}",
|
|
},
|
|
expectedFd: 4,
|
|
},
|
|
{
|
|
input: "fd/{systemd.listen.dns}",
|
|
expectedAddr: NetworkAddress{
|
|
Network: "fd",
|
|
Host: "{systemd.listen.dns}",
|
|
},
|
|
expectedFd: 5,
|
|
},
|
|
{
|
|
input: "fd/{systemd.listen.http:0}",
|
|
expectedAddr: NetworkAddress{
|
|
Network: "fd",
|
|
Host: "{systemd.listen.http:0}",
|
|
},
|
|
expectedFd: 3,
|
|
},
|
|
{
|
|
input: "fd/{systemd.listen.https:0}",
|
|
expectedAddr: NetworkAddress{
|
|
Network: "fd",
|
|
Host: "{systemd.listen.https:0}",
|
|
},
|
|
expectedFd: 4,
|
|
},
|
|
{
|
|
input: "fdgram/{systemd.listen.http}",
|
|
expectedAddr: NetworkAddress{
|
|
Network: "fdgram",
|
|
Host: "{systemd.listen.http}",
|
|
},
|
|
expectedFd: 3,
|
|
},
|
|
{
|
|
input: "fdgram/{systemd.listen.https}",
|
|
expectedAddr: NetworkAddress{
|
|
Network: "fdgram",
|
|
Host: "{systemd.listen.https}",
|
|
},
|
|
expectedFd: 4,
|
|
},
|
|
{
|
|
input: "fdgram/{systemd.listen.http:0}",
|
|
expectedAddr: NetworkAddress{
|
|
Network: "fdgram",
|
|
Host: "http:0",
|
|
},
|
|
expectedFd: 3,
|
|
},
|
|
{
|
|
input: "fd/{systemd.listen.nonexistent}",
|
|
expectErr: true,
|
|
},
|
|
{
|
|
input: "fdgram/{systemd.listen.nonexistent}",
|
|
expectErr: true,
|
|
},
|
|
{
|
|
input: "fd/{systemd.listen.http:99}",
|
|
expectErr: true,
|
|
},
|
|
{
|
|
input: "fd/{systemd.listen.invalid:abc}",
|
|
expectErr: true,
|
|
},
|
|
// Test that old fd/N syntax still works
|
|
{
|
|
input: "fd/7",
|
|
expectedAddr: NetworkAddress{
|
|
Network: "fd",
|
|
Host: "7",
|
|
},
|
|
expectedFd: 7,
|
|
},
|
|
{
|
|
input: "fdgram/8",
|
|
expectedAddr: NetworkAddress{
|
|
Network: "fdgram",
|
|
Host: "8",
|
|
},
|
|
expectedFd: 8,
|
|
},
|
|
}
|
|
|
|
for i, tc := range tests {
|
|
actualAddr, err := ParseNetworkAddress(tc.input)
|
|
if err == nil {
|
|
var fd uint
|
|
fdWide, err := strconv.ParseUint(actualAddr.Host, 0, strconv.IntSize)
|
|
if err == nil {
|
|
fd = uint(fdWide)
|
|
}
|
|
|
|
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.expectedAddr, actualAddr) {
|
|
t.Errorf("Test %d (%s): Expected %+v but got %+v", i, tc.input, tc.expectedAddr, actualAddr)
|
|
}
|
|
if !tc.expectErr && fd != tc.expectedFd {
|
|
t.Errorf("Expected FD %d but got %d", tc.expectedFd, fd)
|
|
}
|
|
}
|
|
}
|
|
}
|