@marcel91200 / caddy Mirror · read-only / commits / 1426c97d
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 13, 11:45 UTC

core: Reuse unix sockets (UDS) and don't try to serve HTTP/3 over UDS (#5063)

* core: Reuse unix sockets * Don't serve HTTP/3 over unix sockets This requires upstream support, if even useful * Don't use unix build tag... yet * Fix build tag * Allow ErrNotExist when unlinking socket

Matt Holt committed Sep 21, 2022 at 12:55 UTC 1426c97da57fc84d8b584a960c43fbb58df68b80
4 files changed +134 -36
listen.go
+18 -1
@@ -1,4 +1,21 @@
1 -//go:build !linux
1 +// Copyright 2015 Matthew Holt and The Caddy Authors
2 +//
3 +// Licensed under the Apache License, Version 2.0 (the "License");
4 +// you may not use this file except in compliance with the License.
5 +// You may obtain a copy of the License at
6 +//
7 +// http://www.apache.org/licenses/LICENSE-2.0
8 +//
9 +// Unless required by applicable law or agreed to in writing, software
10 +// distributed under the License is distributed on an "AS IS" BASIS,
11 +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 +// See the License for the specific language governing permissions and
13 +// limitations under the License.
14 +
15 +// TODO: Go 1.19 introduced the "unix" build tag. We have to support Go 1.18 until Go 1.20 is released.
16 +// When Go 1.19 is our minimum, change this build tag to simply "!unix".
17 +// (see similar change needed in listen_unix.go)
18 +//go:build !(aix || android || darwin || dragonfly || freebsd || hurd || illumos || ios || linux || netbsd || openbsd || solaris)
19
20 package caddy
21
listen_linux.go deleted
-34
@@ -1,34 +0,0 @@
1 -package caddy
2 -
3 -import (
4 - "context"
5 - "net"
6 - "syscall"
7 - "time"
8 -
9 - "go.uber.org/zap"
10 - "golang.org/x/sys/unix"
11 -)
12 -
13 -// ListenTimeout is the same as Listen, but with a configurable keep-alive timeout duration.
14 -func ListenTimeout(network, addr string, keepalivePeriod time.Duration) (net.Listener, error) {
15 - // check to see if plugin provides listener
16 - if ln, err := getListenerFromPlugin(network, addr); err != nil || ln != nil {
17 - return ln, err
18 - }
19 -
20 - config := &net.ListenConfig{Control: reusePort, KeepAlive: keepalivePeriod}
21 - return config.Listen(context.Background(), network, addr)
22 -}
23 -
24 -func reusePort(network, address string, conn syscall.RawConn) error {
25 - return conn.Control(func(descriptor uintptr) {
26 - if err := unix.SetsockoptInt(int(descriptor), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1); err != nil {
27 - Log().Error("setting SO_REUSEPORT",
28 - zap.String("network", network),
29 - zap.String("address", address),
30 - zap.Uintptr("descriptor", descriptor),
31 - zap.Error(err))
32 - }
33 - })
34 -}
listen_unix.go new
+115
@@ -0,0 +1,115 @@
1 +// Copyright 2015 Matthew Holt and The Caddy Authors
2 +//
3 +// Licensed under the Apache License, Version 2.0 (the "License");
4 +// you may not use this file except in compliance with the License.
5 +// You may obtain a copy of the License at
6 +//
7 +// http://www.apache.org/licenses/LICENSE-2.0
8 +//
9 +// Unless required by applicable law or agreed to in writing, software
10 +// distributed under the License is distributed on an "AS IS" BASIS,
11 +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 +// See the License for the specific language governing permissions and
13 +// limitations under the License.
14 +
15 +// TODO: Go 1.19 introduced the "unix" build tag. We have to support Go 1.18 until Go 1.20 is released.
16 +// When Go 1.19 is our minimum, remove this build tag, since "_unix" in the filename will do this.
17 +// (see also change needed in listen.go)
18 +//go:build aix || android || darwin || dragonfly || freebsd || hurd || illumos || ios || linux || netbsd || openbsd || solaris
19 +
20 +package caddy
21 +
22 +import (
23 + "context"
24 + "errors"
25 + "io/fs"
26 + "net"
27 + "sync"
28 + "syscall"
29 + "time"
30 +
31 + "go.uber.org/zap"
32 + "golang.org/x/sys/unix"
33 +)
34 +
35 +// ListenTimeout is the same as Listen, but with a configurable keep-alive timeout duration.
36 +func ListenTimeout(network, addr string, keepalivePeriod time.Duration) (net.Listener, error) {
37 + // check to see if plugin provides listener
38 + if ln, err := getListenerFromPlugin(network, addr); err != nil || ln != nil {
39 + return ln, err
40 + }
41 +
42 + socketKey := listenerKey(network, addr)
43 + if isUnixNetwork(network) {
44 + unixSocketsMu.Lock()
45 + defer unixSocketsMu.Unlock()
46 +
47 + socket, exists := unixSockets[socketKey]
48 + if exists {
49 + // make copy of file descriptor
50 + socketFile, err := socket.File() // dup() deep down
51 + if err != nil {
52 + return nil, err
53 + }
54 +
55 + // use copy to make new listener
56 + ln, err := net.FileListener(socketFile)
57 + if err != nil {
58 + return nil, err
59 + }
60 +
61 + // the old socket fd will likely be closed soon, so replace it in the map
62 + unixSockets[socketKey] = ln.(*net.UnixListener)
63 +
64 + return ln.(*net.UnixListener), nil
65 + }
66 +
67 + // from what I can tell after some quick research, it's quite common for programs to
68 + // leave their socket file behind after they close, so the typical pattern is to
69 + // unlink it before you bind to it -- this is often crucial if the last program using
70 + // it was killed forcefully without a chance to clean up the socket, but there is a
71 + // race, as the comment in net.UnixListener.close() explains... oh well?
72 + if err := syscall.Unlink(addr); err != nil && !errors.Is(err, fs.ErrNotExist) {
73 + return nil, err
74 + }
75 + }
76 +
77 + config := &net.ListenConfig{Control: reusePort, KeepAlive: keepalivePeriod}
78 +
79 + ln, err := config.Listen(context.Background(), network, addr)
80 + if err != nil {
81 + return nil, err
82 + }
83 +
84 + if uln, ok := ln.(*net.UnixListener); ok {
85 + // TODO: ideally, we should unlink the socket once we know we're done using it
86 + // (i.e. either on exit or a new config that doesn't use this socket; in UsagePool
87 + // terms, when the reference count reaches 0), but given that we unlink existing
88 + // socket before we create the new one anyway (see above), we don't necessarily
89 + // need to clean up after ourselves; still, doing so would probably be more tidy
90 + uln.SetUnlinkOnClose(false)
91 + unixSockets[socketKey] = uln
92 + }
93 +
94 + return ln, nil
95 +}
96 +
97 +// reusePort sets SO_REUSEPORT. Ineffective for unix sockets.
98 +func reusePort(network, address string, conn syscall.RawConn) error {
99 + return conn.Control(func(descriptor uintptr) {
100 + if err := unix.SetsockoptInt(int(descriptor), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1); err != nil {
101 + Log().Error("setting SO_REUSEPORT",
102 + zap.String("network", network),
103 + zap.String("address", address),
104 + zap.Uintptr("descriptor", descriptor),
105 + zap.Error(err))
106 + }
107 + })
108 +}
109 +
110 +// unixSockets keeps track of the currently-active unix sockets
111 +// so we can transfer their FDs gracefully during reloads.
112 +var (
113 + unixSockets = make(map[string]*net.UnixListener)
114 + unixSocketsMu sync.Mutex
115 +)
modules/caddyhttp/app.go
+1 -1
@@ -409,7 +409,7 @@ func (app *App) Start() error {
409 ln = tls.NewListener(ln, tlsCfg)
410
411 // enable HTTP/3 if configured
412 - if srv.protocol("h3") {
412 + if srv.protocol("h3") && !listenAddr.IsUnixNetwork() {
413 app.logger.Info("enabling HTTP/3 listener", zap.String("addr", hostport))
414 if err := srv.serveHTTP3(hostport, tlsCfg); err != nil {
415 return err