Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
Mirrored from https://github.com/Slazee/caddy.git
Synced Nov 11, 11:00 UTC
cmd: support caddy start on IPv6-only hosts (#7744)
Zen Dodd committed
May 21, 2026 at 00:17 UTC
6628c4a9de5588e43430b285f6f4de376aaafe70
2 files changed
+93
-1
cmd/commandfuncs.go
+17
-1
@@ -58,7 +58,7 @@ func cmdStart(fl Flags) (int, error) {
58
59
// open a listener to which the child process will connect when
60
// it is ready to confirm that it has successfully started
61
- ln, err := net.Listen("tcp", "127.0.0.1:0")
61
+ ln, err := listenTCPForPingback(net.Listen)
62
if err != nil {
63
return caddy.ExitCodeFailedStartup,
64
fmt.Errorf("opening listener for success confirmation: %v", err)
@@ -169,6 +169,22 @@ func cmdStart(fl Flags) (int, error) {
169
return caddy.ExitCodeSuccess, nil
170
}
171
172
+type tcpListenFunc func(network, address string) (net.Listener, error)
173
+
174
+func listenTCPForPingback(listen tcpListenFunc) (net.Listener, error) {
175
+ ln, ipv4Err := listen("tcp4", "127.0.0.1:0")
176
+ if ipv4Err == nil {
177
+ return ln, nil
178
+ }
179
+
180
+ ln, ipv6Err := listen("tcp6", "[::1]:0")
181
+ if ipv6Err == nil {
182
+ return ln, nil
183
+ }
184
+
185
+ return nil, fmt.Errorf("listen on 127.0.0.1:0: %v; listen on [::1]:0: %v", ipv4Err, ipv6Err)
186
+}
187
+
188
func cmdRun(fl Flags) (int, error) {
189
caddy.TrapSignals()
190
cmd/main_test.go
+76
@@ -1,6 +1,8 @@
1
package caddycmd
2
3
import (
4
+ "errors"
5
+ "net"
6
"reflect"
7
"strings"
8
"testing"
@@ -169,6 +171,80 @@ here"
171
}
172
}
173
174
+func TestListenTCPForPingbackUsesIPv4Loopback(t *testing.T) {
175
+ var calls []string
176
+ expected := &stubListener{addr: &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 1234}}
177
+
178
+ actual, err := listenTCPForPingback(func(network, address string) (net.Listener, error) {
179
+ calls = append(calls, network+" "+address)
180
+ return expected, nil
181
+ })
182
+ if err != nil {
183
+ t.Fatalf("listenTCPForPingback returned error: %v", err)
184
+ }
185
+ if actual != expected {
186
+ t.Fatalf("expected listener %p, got %p", expected, actual)
187
+ }
188
+
189
+ expectCalls := []string{"tcp4 127.0.0.1:0"}
190
+ if !reflect.DeepEqual(calls, expectCalls) {
191
+ t.Fatalf("expected calls %v, got %v", expectCalls, calls)
192
+ }
193
+}
194
+
195
+func TestListenTCPForPingbackFallsBackToIPv6Loopback(t *testing.T) {
196
+ var calls []string
197
+ expected := &stubListener{addr: &net.TCPAddr{IP: net.ParseIP("::1"), Port: 1234}}
198
+
199
+ actual, err := listenTCPForPingback(func(network, address string) (net.Listener, error) {
200
+ calls = append(calls, network+" "+address)
201
+ if len(calls) == 1 {
202
+ return nil, errors.New("ipv4 unavailable")
203
+ }
204
+ return expected, nil
205
+ })
206
+ if err != nil {
207
+ t.Fatalf("listenTCPForPingback returned error: %v", err)
208
+ }
209
+ if actual != expected {
210
+ t.Fatalf("expected listener %p, got %p", expected, actual)
211
+ }
212
+
213
+ expectCalls := []string{"tcp4 127.0.0.1:0", "tcp6 [::1]:0"}
214
+ if !reflect.DeepEqual(calls, expectCalls) {
215
+ t.Fatalf("expected calls %v, got %v", expectCalls, calls)
216
+ }
217
+}
218
+
219
+func TestListenTCPForPingbackReportsBothFailures(t *testing.T) {
220
+ _, err := listenTCPForPingback(func(network, address string) (net.Listener, error) {
221
+ return nil, errors.New(network + " failed")
222
+ })
223
+ if err == nil {
224
+ t.Fatal("expected error")
225
+ }
226
+ if !strings.Contains(err.Error(), "tcp4 failed") ||
227
+ !strings.Contains(err.Error(), "tcp6 failed") {
228
+ t.Fatalf("expected both listener errors, got: %v", err)
229
+ }
230
+}
231
+
232
+type stubListener struct {
233
+ addr net.Addr
234
+}
235
+
236
+func (sl *stubListener) Accept() (net.Conn, error) {
237
+ return nil, net.ErrClosed
238
+}
239
+
240
+func (sl *stubListener) Close() error {
241
+ return nil
242
+}
243
+
244
+func (sl *stubListener) Addr() net.Addr {
245
+ return sl.addr
246
+}
247
+
248
func Test_isCaddyfile(t *testing.T) {
249
type args struct {
250
configFile string