@@ -8,10 +8,19 @@ import (
8
8
"time"
9
9
)
10
10
11
+ // DefaultReadHeaderTimeout is how long header processing waits for header to
12
+ // be read from the wire, if Listener.ReaderHeaderTimeout is not set.
13
+ // It's kept as a global variable so to make it easier to find and override,
14
+ // e.g. go build -ldflags -X "github.com/pires/go-proxyproto.DefaultReadHeaderTimeout=1s"
15
+ var DefaultReadHeaderTimeout = 200 * time .Millisecond
16
+
11
17
// Listener is used to wrap an underlying listener,
12
18
// whose connections may be using the HAProxy Proxy Protocol.
13
19
// If the connection is using the protocol, the RemoteAddr() will return
14
- // the correct client address.
20
+ // the correct client address. ReadHeaderTimeout will be applied to all
21
+ // connections in order to prevent blocking operations. If no ReadHeaderTimeout
22
+ // is set, a default of 200ms will be used. This can be disabled by setting the
23
+ // timeout to < 0.
15
24
type Listener struct {
16
25
Listener net.Listener
17
26
Policy PolicyFunc
@@ -21,7 +30,8 @@ type Listener struct {
21
30
22
31
// Conn is used to wrap and underlying connection which
23
32
// may be speaking the Proxy Protocol. If it is, the RemoteAddr() will
24
- // return the address of the client instead of the proxy address.
33
+ // return the address of the client instead of the proxy address. Each connection
34
+ // will have its own readHeaderTimeout and readDeadline set by the Accept() call.
25
35
type Conn struct {
26
36
bufReader * bufio.Reader
27
37
conn net.Conn
@@ -30,6 +40,8 @@ type Conn struct {
30
40
ProxyHeaderPolicy Policy
31
41
Validate Validator
32
42
readErr error
43
+ readHeaderTimeout time.Duration
44
+ readDeadline time.Time
33
45
}
34
46
35
47
// Validator receives a header and decides whether it is a valid one
@@ -53,10 +65,6 @@ func (p *Listener) Accept() (net.Conn, error) {
53
65
return nil , err
54
66
}
55
67
56
- if d := p .ReadHeaderTimeout ; d != 0 {
57
- conn .SetReadDeadline (time .Now ().Add (d ))
58
- }
59
-
60
68
proxyHeaderPolicy := USE
61
69
if p .Policy != nil {
62
70
proxyHeaderPolicy , err = p .Policy (conn .RemoteAddr ())
@@ -72,6 +80,15 @@ func (p *Listener) Accept() (net.Conn, error) {
72
80
WithPolicy (proxyHeaderPolicy ),
73
81
ValidateHeader (p .ValidateHeader ),
74
82
)
83
+
84
+ // If the ReadHeaderTimeout for the listener is unset, use the default timeout.
85
+ if p .ReadHeaderTimeout == 0 {
86
+ p .ReadHeaderTimeout = DefaultReadHeaderTimeout
87
+ }
88
+
89
+ // Set the readHeaderTimeout of the new conn to the value of the listener
90
+ newConn .readHeaderTimeout = p .ReadHeaderTimeout
91
+
75
92
return newConn , nil
76
93
}
77
94
@@ -110,6 +127,7 @@ func (p *Conn) Read(b []byte) (int, error) {
110
127
if p .readErr != nil {
111
128
return 0 , p .readErr
112
129
}
130
+
113
131
return p .bufReader .Read (b )
114
132
}
115
133
@@ -197,11 +215,16 @@ func (p *Conn) UDPConn() (conn *net.UDPConn, ok bool) {
197
215
198
216
// SetDeadline wraps original conn.SetDeadline
199
217
func (p * Conn ) SetDeadline (t time.Time ) error {
218
+ p .readDeadline = t
200
219
return p .conn .SetDeadline (t )
201
220
}
202
221
203
222
// SetReadDeadline wraps original conn.SetReadDeadline
204
223
func (p * Conn ) SetReadDeadline (t time.Time ) error {
224
+ // Set a local var that tells us the desired deadline. This is
225
+ // needed in order to reset the read deadline to the one that is
226
+ // desired by the user, rather than an empty deadline.
227
+ p .readDeadline = t
205
228
return p .conn .SetReadDeadline (t )
206
229
}
207
230
@@ -211,7 +234,28 @@ func (p *Conn) SetWriteDeadline(t time.Time) error {
211
234
}
212
235
213
236
func (p * Conn ) readHeader () error {
237
+ // If the connection's readHeaderTimeout is more than 0,
238
+ // push our deadline back to now plus the timeout. This should only
239
+ // run on the connection, as we don't want to override the previous
240
+ // read deadline the user may have used.
241
+ if p .readHeaderTimeout > 0 {
242
+ p .conn .SetReadDeadline (time .Now ().Add (p .readHeaderTimeout ))
243
+ }
244
+
214
245
header , err := Read (p .bufReader )
246
+
247
+ // If the connection's readHeaderTimeout is more than 0, undo the change to the
248
+ // deadline that we made above. Because we retain the readDeadline as part of our
249
+ // SetReadDeadline override, we know the user's desired deadline so we use that.
250
+ // Therefore, we check whether the error is a net.Timeout and if it is, we decide
251
+ // the proxy proto does not exist and set the error accordingly.
252
+ if p .readHeaderTimeout > 0 {
253
+ p .conn .SetReadDeadline (p .readDeadline )
254
+ if netErr , ok := err .(net.Error ); ok && netErr .Timeout () {
255
+ err = ErrNoProxyProtocol
256
+ }
257
+ }
258
+
215
259
// For the purpose of this wrapper shamefully stolen from armon/go-proxyproto
216
260
// let's act as if there was no error when PROXY protocol is not present.
217
261
if err == ErrNoProxyProtocol {
0 commit comments