Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS
Mirrored from https://github.com/Slazee/caddy.git
Synced Nov 14, 05:15 UTC
Close listeners which are no longer used
Matthew Holt committed
Apr 2, 2019 at 15:31 UTC
59a5d0db28e36b9732bc9ad2b73bcde9188e207c
2 files changed
+40
-12
caddy.go
+17
@@ -6,6 +6,7 @@ import (
6
"log"
7
"strings"
8
"sync"
9
+ "sync/atomic"
10
"time"
11
)
12
@@ -24,6 +25,7 @@ func Start(cfg Config) error {
25
cfg.runners[modName] = val.(Runner)
26
}
27
28
+ // start the new runners
29
for name, r := range cfg.runners {
30
err := r.Run()
31
if err != nil {
@@ -32,6 +34,7 @@ func Start(cfg Config) error {
34
}
35
}
36
37
+ // shut down down the old ones
38
currentCfgMu.Lock()
39
if currentCfg != nil {
40
for _, r := range currentCfg.runners {
@@ -44,6 +47,20 @@ func Start(cfg Config) error {
47
currentCfg = &cfg
48
currentCfgMu.Unlock()
49
50
+ // shut down listeners that are no longer being used
51
+ listenersMu.Lock()
52
+ for key, info := range listeners {
53
+ if atomic.LoadInt32(&info.usage) == 0 {
54
+ err := info.ln.Close()
55
+ if err != nil {
56
+ log.Printf("[ERROR] closing listener %s: %v", info.ln.Addr(), err)
57
+ continue
58
+ }
59
+ delete(listeners, key)
60
+ }
61
+ }
62
+ listenersMu.Unlock()
63
+
64
return nil
65
}
66
listeners.go
+23
-12
@@ -15,9 +15,10 @@ func Listen(network, addr string) (net.Listener, error) {
15
listenersMu.Lock()
16
defer listenersMu.Unlock()
17
18
- // if listener already exists, return it
19
- if ln, ok := listeners[lnKey]; ok {
20
- return &fakeCloseListener{Listener: ln}, nil
18
+ // if listener already exists, increment usage counter, then return listener
19
+ if lnInfo, ok := listeners[lnKey]; ok {
20
+ atomic.AddInt32(&lnInfo.usage, 1)
21
+ return &fakeCloseListener{usage: &lnInfo.usage, Listener: lnInfo.ln}, nil
22
}
23
24
// or, create new one and save it
@@ -25,9 +26,12 @@ func Listen(network, addr string) (net.Listener, error) {
26
if err != nil {
27
return nil, err
28
}
28
- listeners[lnKey] = ln
29
30
- return &fakeCloseListener{Listener: ln}, nil
30
+ // make sure to start its usage counter at 1
31
+ lnInfo := &listenerUsage{usage: 1, ln: ln}
32
+ listeners[lnKey] = lnInfo
33
+
34
+ return &fakeCloseListener{usage: &lnInfo.usage, Listener: ln}, nil
35
}
36
37
// fakeCloseListener's Close() method is a no-op. This allows
@@ -36,7 +40,8 @@ func Listen(network, addr string) (net.Listener, error) {
40
// listener remains running. Listeners should be re-wrapped in
41
// a new fakeCloseListener each time the listener is reused.
42
type fakeCloseListener struct {
39
- closed int32
43
+ closed int32 // accessed atomically
44
+ usage *int32 // accessed atomically
45
net.Listener
46
}
47
@@ -92,16 +97,15 @@ func (fcl *fakeCloseListener) Close() error {
97
case *net.UnixListener:
98
ln.SetDeadline(time.Now().Add(-1 * time.Minute))
99
}
100
+
101
+ // since we're no longer using this listener,
102
+ // decrement the usage counter
103
+ atomic.AddInt32(fcl.usage, -1)
104
}
105
106
return nil
107
}
108
100
-// CloseUnderlying actually closes the underlying listener.
101
-func (fcl *fakeCloseListener) CloseUnderlying() error {
102
- return fcl.Listener.Close()
103
-}
104
-
109
func (fcl *fakeCloseListener) fakeClosedErr() error {
110
return &net.OpError{
111
Op: "accept",
@@ -118,7 +122,14 @@ func (fcl *fakeCloseListener) fakeClosedErr() error {
122
// socket is actually left open.
123
var ErrFakeClosed = fmt.Errorf("listener 'closed' 😉")
124
125
+// listenerUsage pairs a net.Listener with a
126
+// count of how many servers are using it.
127
+type listenerUsage struct {
128
+ usage int32 // accessed atomically
129
+ ln net.Listener
130
+}
131
+
132
var (
122
- listeners = make(map[string]net.Listener)
133
+ listeners = make(map[string]*listenerUsage)
134
listenersMu sync.Mutex
135
)