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
Merged in deadlines (pull request #1)
Cleanly fake-close listeners * WIP debugging listener deadlines * Fix listener deadlines
Matt Holt committed
Apr 2, 2019 at 20:58 UTC
f976aa744385b097239a7323af4dec11f83bc949
3 files changed
+65
-8
caddy.go
+1
-1
@@ -34,7 +34,7 @@ func Start(cfg Config) error {
34
35
currentCfgMu.Lock()
36
if currentCfg != nil {
37
- for _, r := range cfg.runners {
37
+ for _, r := range currentCfg.runners {
38
err := r.Cancel()
39
if err != nil {
40
log.Println(err)
listeners.go
+61
-7
@@ -5,6 +5,7 @@ import (
5
"net"
6
"sync"
7
"sync/atomic"
8
+ "time"
9
)
10
11
// Listen returns a listener suitable for use in a Caddy module.
@@ -41,16 +42,58 @@ type fakeCloseListener struct {
42
43
// Accept accepts connections until Close() is called.
44
func (fcl *fakeCloseListener) Accept() (net.Conn, error) {
45
+ // if the listener is already "closed", return error
46
if atomic.LoadInt32(&fcl.closed) == 1 {
45
- return nil, ErrSwappingServers
47
+ return nil, fcl.fakeClosedErr()
48
}
47
- return fcl.Listener.Accept()
49
+
50
+ // wrap underlying accept
51
+ conn, err := fcl.Listener.Accept()
52
+ if err == nil {
53
+ return conn, nil
54
+ }
55
+
56
+ if atomic.LoadInt32(&fcl.closed) == 1 {
57
+ // clear the deadline
58
+ switch ln := fcl.Listener.(type) {
59
+ case *net.TCPListener:
60
+ ln.SetDeadline(time.Time{})
61
+ case *net.UnixListener:
62
+ ln.SetDeadline(time.Time{})
63
+ }
64
+
65
+ // if we cancelled the Accept() by setting a deadline
66
+ // on the listener, we need to make sure any callers of
67
+ // Accept() think the listener was actually closed;
68
+ // if we return the timeout error instead, callers might
69
+ // simply retry, leaking goroutines for longer
70
+ if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
71
+ return nil, fcl.fakeClosedErr()
72
+ }
73
+ }
74
+
75
+ return nil, err
76
}
77
78
// Close stops accepting new connections, but does not
79
// actually close the underlying listener.
80
func (fcl *fakeCloseListener) Close() error {
53
- atomic.StoreInt32(&fcl.closed, 1)
81
+ if atomic.CompareAndSwapInt32(&fcl.closed, 0, 1) {
82
+ // unfortunately, there is no way to cancel any
83
+ // currently-blocking calls to Accept() that are
84
+ // awaiting connections since we're not actually
85
+ // closing the listener; so we cheat by setting
86
+ // a deadline in the past, which forces it to
87
+ // time out; note that this only works for
88
+ // certain types of listeners...
89
+ switch ln := fcl.Listener.(type) {
90
+ case *net.TCPListener:
91
+ ln.SetDeadline(time.Now().Add(-1 * time.Minute))
92
+ case *net.UnixListener:
93
+ ln.SetDeadline(time.Now().Add(-1 * time.Minute))
94
+ }
95
+ }
96
+
97
return nil
98
}
99
@@ -59,10 +102,21 @@ func (fcl *fakeCloseListener) CloseUnderlying() error {
102
return fcl.Listener.Close()
103
}
104
62
-// ErrSwappingServers is returned by fakeCloseListener when
63
-// Close() is called, indicating that it is pretending to
64
-// be closed so that the server using it can terminate.
65
-var ErrSwappingServers = fmt.Errorf("listener 'closed' 😉")
105
+func (fcl *fakeCloseListener) fakeClosedErr() error {
106
+ return &net.OpError{
107
+ Op: "accept",
108
+ Net: fcl.Listener.Addr().Network(),
109
+ Addr: fcl.Listener.Addr(),
110
+ Err: ErrFakeClosed,
111
+ }
112
+}
113
+
114
+// ErrFakeClosed is the underlying error value returned by
115
+// fakeCloseListener.Accept() after Close() has been called,
116
+// indicating that it is pretending to be closed so that the
117
+// server using it can terminate, while the underlying
118
+// socket is actually left open.
119
+var ErrFakeClosed = fmt.Errorf("listener 'closed' 😉")
120
121
var (
122
listeners = make(map[string]net.Listener)
modules/caddyhttp/responsewriter.go
+3
@@ -7,6 +7,9 @@ import (
7
"net/http"
8
)
9
10
+// TODO: Is this type really required? Wouldn't embedding the
11
+// default ResponseWriter always work too, when wrapping it?
12
+
13
// ResponseWriterWrapper wraps an underlying ResponseWriter and
14
// promotes its Pusher/Flusher/CloseNotifier/Hijacker methods
15
// as well. To use this type, embed a pointer to it within your