forked from gwatts/manners
-
Notifications
You must be signed in to change notification settings - Fork 9
/
listener.go
162 lines (143 loc) · 3.8 KB
/
listener.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
package manners
import (
"crypto/tls"
"errors"
"fmt"
"net"
"os"
"sync"
"time"
proxyproto "github.com/armon/go-proxyproto"
)
// NewListener wraps an existing listener for use with
// GracefulServer.
//
// Note that you generally don't need to use this directly as
// GracefulServer will automatically wrap any non-graceful listeners
// supplied to it.
func NewListener(l net.Listener) *GracefulListener {
return &GracefulListener{
listener: l,
mutex: &sync.RWMutex{},
open: true,
}
}
// A GracefulListener differs from a standard net.Listener in one way: if
// Accept() is called after it is gracefully closed, it returns a
// listenerAlreadyClosed error. The GracefulServer will ignore this error.
type GracefulListener struct {
listener net.Listener
open bool
mutex *sync.RWMutex
}
func (l *GracefulListener) isClosed() bool {
l.mutex.RLock()
defer l.mutex.RUnlock()
return !l.open
}
func (l *GracefulListener) Addr() net.Addr {
return l.listener.Addr()
}
// Accept implements the Accept method in the Listener interface.
func (l *GracefulListener) Accept() (net.Conn, error) {
conn, err := l.listener.Accept()
if err != nil {
if l.isClosed() {
err = fmt.Errorf("listener already closed: err=(%s)", err)
}
return nil, err
}
return conn, nil
}
// Close tells the wrapped listener to stop listening. It is idempotent.
func (l *GracefulListener) Close() error {
l.mutex.Lock()
defer l.mutex.Unlock()
if !l.open {
return nil
}
l.open = false
return l.listener.Close()
}
func (l *GracefulListener) GetFile() (*os.File, error) {
return getListenerFile(l.listener)
}
func (l *GracefulListener) Clone() (net.Listener, error) {
l.mutex.Lock()
defer l.mutex.Unlock()
if !l.open {
return nil, errors.New("listener is already closed")
}
file, err := l.GetFile()
if err != nil {
return nil, err
}
defer file.Close()
fl, err := net.FileListener(file)
if nil != err {
return nil, err
}
return fl, nil
}
// A listener implements a network listener (net.Listener) for TLS connections.
// direct lift from crypto/tls.go
type TLSListener struct {
net.Listener
config *tls.Config
}
// Accept waits for and returns the next incoming TLS connection.
// The returned connection c is a *tls.Conn.
func (l *TLSListener) Accept() (c net.Conn, err error) {
c, err = l.Listener.Accept()
if err != nil {
return
}
c = tls.Server(c, l.config)
return
}
// NewListener creates a Listener which accepts connections from an inner
// Listener and wraps each connection with Server.
// The configuration config must be non-nil and must have
// at least one certificate.
func NewTLSListener(inner net.Listener, config *tls.Config) net.Listener {
l := new(TLSListener)
l.Listener = inner
l.config = config
return l
}
// TCPKeepAliveListener sets TCP keep-alive timeouts on accepted
// connections. It's used by ListenAndServe and ListenAndServeTLS so
// dead TCP connections (e.g. closing laptop mid-download) eventually
// go away.
//
// direct lift from net/http/server.go
type TCPKeepAliveListener struct {
*net.TCPListener
}
func (ln TCPKeepAliveListener) Accept() (c net.Conn, err error) {
tc, err := ln.AcceptTCP()
if err != nil {
return
}
tc.SetKeepAlive(true)
tc.SetKeepAlivePeriod(3 * time.Minute)
return tc, nil
}
func getListenerFile(listener net.Listener) (*os.File, error) {
// TODO(pquerna): ideally we had a consistent interface for this, but proxyproto doesn't help us here.
switch t := listener.(type) {
case *net.TCPListener:
return t.File()
case *net.UnixListener:
return t.File()
case TCPKeepAliveListener:
return t.TCPListener.File()
case *TCPKeepAliveListener:
return t.TCPListener.File()
case *proxyproto.Listener:
return getListenerFile(t.Listener)
case *TLSListener:
return getListenerFile(t.Listener)
}
return nil, fmt.Errorf("Unsupported listener: %T", listener)
}