@marcel91200 / caddy Mirror · read-only / commits / f976aa74
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 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