forked from zenazn/goji
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathnet.go
198 lines (168 loc) · 4.4 KB
/
net.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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
package graceful
import (
"io"
"net"
"sync"
"time"
)
type listener struct {
net.Listener
}
type gracefulConn interface {
gracefulShutdown()
}
// WrapListener wraps an arbitrary net.Listener for use with graceful shutdowns.
// All net.Conn's Accept()ed by this listener will be auto-wrapped as if
// WrapConn() were called on them.
func WrapListener(l net.Listener) net.Listener {
return listener{l}
}
func (l listener) Accept() (net.Conn, error) {
conn, err := l.Listener.Accept()
if err != nil {
return nil, err
}
return WrapConn(conn), nil
}
/*
WrapConn wraps an arbitrary connection for use with graceful shutdowns. The
graceful shutdown process will ensure that this connection is closed before
terminating the process.
In order to use this function, you must call SetReadDeadline() before the call
to Read() you might make to read a new request off the wire. The connection is
eligible for abrupt closing at any point between when the call to
SetReadDeadline() returns and when the call to Read returns with new data. It
does not matter what deadline is given to SetReadDeadline()--the default HTTP
server provided by this package sets a deadline far into the future when a
deadline is not provided, for instance.
Unfortunately, this means that it's difficult to use SetReadDeadline() in a
great many perfectly reasonable circumstances, such as to extend a deadline
after more data has been read, without the connection being eligible for
"graceful" termination at an undesirable time. Since this package was written
explicitly to target net/http, which does not as of this writing do any of this,
fixing the semantics here does not seem especially urgent.
As an optimization for net/http over TCP, if the input connection supports the
ReadFrom() function, the returned connection will as well. This allows the net
package to use sendfile(2) on certain platforms in certain circumstances.
*/
func WrapConn(c net.Conn) net.Conn {
wg.Add(1)
nc := conn{
Conn: c,
closing: make(chan struct{}),
}
if _, ok := c.(io.ReaderFrom); ok {
c = &sendfile{nc}
} else {
c = &nc
}
go c.(gracefulConn).gracefulShutdown()
return c
}
type connstate int
/*
State diagram. (Waiting) is the starting state.
(Waiting) -----Read()-----> Working ---+
| ^ / | ^ Read()
| \ / | +----+
kill SetReadDeadline() kill
| | +-----+
V V V Read()
Dead <-SetReadDeadline()-- Dying ----+
^
|
+--Close()--- [from any state]
*/
const (
// Waiting for more data, and eligible for killing
csWaiting connstate = iota
// In the middle of a connection
csWorking
// Kill has been requested, but waiting on request to finish up
csDying
// Connection is gone forever. Also used when a connection gets hijacked
csDead
)
type conn struct {
net.Conn
m sync.Mutex
state connstate
closing chan struct{}
}
type sendfile struct{ conn }
func (c *conn) gracefulShutdown() {
select {
case <-kill:
case <-c.closing:
return
}
c.m.Lock()
defer c.m.Unlock()
switch c.state {
case csWaiting:
c.unlockedClose(true)
case csWorking:
c.state = csDying
}
}
func (c *conn) unlockedClose(closeConn bool) {
if closeConn {
c.Conn.Close()
}
close(c.closing)
wg.Done()
c.state = csDead
}
// We do some hijinks to support hijacking. The semantics here is that any
// connection that gets hijacked is dead to us: we return the raw net.Conn and
// stop tracking the connection entirely.
type hijackConn interface {
hijack() net.Conn
}
func (c *conn) hijack() net.Conn {
c.m.Lock()
defer c.m.Unlock()
if c.state != csDead {
close(c.closing)
wg.Done()
c.state = csDead
}
return c.Conn
}
func (c *conn) Read(b []byte) (n int, err error) {
defer func() {
c.m.Lock()
defer c.m.Unlock()
if c.state == csWaiting {
c.state = csWorking
}
}()
return c.Conn.Read(b)
}
func (c *conn) Close() error {
defer func() {
c.m.Lock()
defer c.m.Unlock()
if c.state != csDead {
c.unlockedClose(false)
}
}()
return c.Conn.Close()
}
func (c *conn) SetReadDeadline(t time.Time) error {
defer func() {
c.m.Lock()
defer c.m.Unlock()
switch c.state {
case csDying:
c.unlockedClose(false)
case csWorking:
c.state = csWaiting
}
}()
return c.Conn.SetReadDeadline(t)
}
func (s *sendfile) ReadFrom(r io.Reader) (int64, error) {
// conn.Conn.KHAAAAAAAANNNNNN
return s.conn.Conn.(io.ReaderFrom).ReadFrom(r)
}