Skip to content

Commit bbef4be

Browse files
committed
Refactor environment parsing
Systemd environment variables LISTEN* are unset by default and saved for future calls
1 parent bbd21d7 commit bbef4be

1 file changed

Lines changed: 55 additions & 27 deletions

File tree

anyhttp.go

Lines changed: 55 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"os"
1111
"strconv"
1212
"strings"
13+
"sync"
1314
"syscall"
1415
)
1516

@@ -39,6 +40,39 @@ func NewUnixSocketConfig(socketPath string) UnixSocketConfig {
3940
return usc
4041
}
4142

43+
type sysdEnvData struct {
44+
pid int
45+
fdNames []string
46+
fdNamesStr string
47+
numFds int
48+
}
49+
50+
var sysdEnvParser = struct {
51+
sysdOnce sync.Once
52+
data sysdEnvData
53+
err error
54+
}{}
55+
56+
func parse() (sysdEnvData, error) {
57+
p := &sysdEnvParser
58+
p.sysdOnce.Do(func() {
59+
p.data.pid, p.err = strconv.Atoi(os.Getenv("LISTEN_PID"))
60+
if p.err != nil {
61+
p.err = fmt.Errorf("invalid LISTEN_PID, err: %w", p.err)
62+
return
63+
}
64+
p.data.numFds, p.err = strconv.Atoi(os.Getenv("LISTEN_FDS"))
65+
if p.err != nil {
66+
p.err = fmt.Errorf("invalid LISTEN_FDS, err: %w", p.err)
67+
return
68+
}
69+
p.data.fdNamesStr = os.Getenv("LISTEN_FDNAMES")
70+
p.data.fdNames = strings.Split(p.data.fdNamesStr, ":")
71+
72+
})
73+
return p.data, p.err
74+
}
75+
4276
// SysdConfig has the configuration for the socket activated fd
4377
type SysdConfig struct {
4478
// Integer value starting at 0. Either index or name is required
@@ -54,7 +88,7 @@ type SysdConfig struct {
5488
// DefaultSysdConfig has the default values for SysdConfig
5589
var DefaultSysdConfig = SysdConfig{
5690
CheckPID: true,
57-
UnsetEnv: false,
91+
UnsetEnv: true,
5892
}
5993

6094
// NewSysDConfigWithFDIdx creates SysdConfig with defaults and fdIdx
@@ -112,53 +146,47 @@ func (s *SysdConfig) GetListener() (net.Listener, error) {
112146
defer UnsetSystemdListenVars()
113147
}
114148

115-
if s.CheckPID {
116-
pid, err := strconv.Atoi(os.Getenv("LISTEN_PID"))
117-
if err != nil {
118-
return nil, fmt.Errorf("invalid LISTEN_PID, err: %w", err)
119-
}
120-
if pid != os.Getpid() {
121-
return nil, fmt.Errorf("unexpected PID, current:%v, LISTEN_PID: %v", os.Getpid(), pid)
122-
}
123-
}
124-
125-
numFds, err := strconv.Atoi(os.Getenv("LISTEN_FDS"))
149+
envData, err := parse()
126150
if err != nil {
127-
return nil, fmt.Errorf("invalid LISTEN_FDS, err: %w", err)
151+
return nil, err
128152
}
129153

130-
fdNames := strings.Split(os.Getenv("LISTEN_FDNAMES"), ":")
154+
if s.CheckPID {
155+
if envData.pid != os.Getpid() {
156+
return nil, fmt.Errorf("unexpected PID, current:%v, LISTEN_PID: %v", os.Getpid(), envData.pid)
157+
}
158+
}
131159

132160
if s.FDIndex != nil {
133161
idx := *s.FDIndex
134-
if idx < 0 || idx >= numFds {
135-
return nil, fmt.Errorf("invalid fd index, expected between 0 and %v, got: %v", numFds, idx)
162+
if idx < 0 || idx >= envData.numFds {
163+
return nil, fmt.Errorf("invalid fd index, expected between 0 and %v, got: %v", envData.numFds, idx)
136164
}
137165
fd := StartFD + idx
138-
if idx < len(fdNames) {
139-
return makeFdListener(fd, fdNames[idx])
166+
if idx < len(envData.fdNames) {
167+
return makeFdListener(fd, envData.fdNames[idx])
140168
}
141169
return makeFdListener(fd, fmt.Sprintf("sysdfd_%d", fd))
142170
}
143171

144172
if s.FDName != nil {
145-
for idx, name := range fdNames {
173+
for idx, name := range envData.fdNames {
146174
if name == *s.FDName {
147175
fd := StartFD + idx
148176
return makeFdListener(fd, name)
149177
}
150178
}
151-
return nil, fmt.Errorf("fdName not found: %q, LISTEN_FDNAMES:%q", *s.FDName, os.Getenv("LISTEN_FDNAMES"))
179+
return nil, fmt.Errorf("fdName not found: %q, LISTEN_FDNAMES:%q", *s.FDName, envData.fdNamesStr)
152180
}
153181

154182
return nil, errors.New("neither FDIndex nor FDName set")
155183
}
156184

157185
// UnknownAddress Error is returned when address does not match any known syntax
158-
type UnknownAddress string
186+
type UnknownAddress struct{}
159187

160188
func (u UnknownAddress) Error() string {
161-
return fmt.Sprintf("unknown address: %q", string(u))
189+
return "unknown address"
162190
}
163191

164192
// GetListener gets a unix or systemd socket listener
@@ -182,15 +210,15 @@ func GetListener(addr string) (net.Listener, error) {
182210
return sysdc.GetListener()
183211
}
184212

185-
return nil, UnknownAddress(addr)
213+
return nil, UnknownAddress{}
186214
}
187215

188216
// ListenAndServe is the drop-in replacement for `http.ListenAndServe`.
189217
// Supports unix and systemd sockets in addition
190218
func ListenAndServe(addr string, h http.Handler) error {
191219

192220
listener, err := GetListener(addr)
193-
if _, ok := err.(UnknownAddress); err != nil && !ok {
221+
if _, isUnknown := err.(UnknownAddress); err != nil && !isUnknown {
194222
return err
195223
}
196224

@@ -211,7 +239,7 @@ func ListenAndServe(addr string, h http.Handler) error {
211239

212240
// UnsetSystemdListenVars unsets the LISTEN* environment variables so they are not passed to any child processes
213241
func UnsetSystemdListenVars() {
214-
os.Unsetenv("LISTEN_PID")
215-
os.Unsetenv("LISTEN_FDS")
216-
os.Unsetenv("LISTEN_FDNAMES")
242+
_ = os.Unsetenv("LISTEN_PID")
243+
_ = os.Unsetenv("LISTEN_FDS")
244+
_ = os.Unsetenv("LISTEN_FDNAMES")
217245
}

0 commit comments

Comments
 (0)