Skip to content

Commit 2e44d7a

Browse files
authored
Merge pull request #76 from pires/bugfix/reset_conn_read_deadline_after_successful_header
Reset read deadline after parsing header
2 parents a55009f + 880d578 commit 2e44d7a

File tree

4 files changed

+348
-37
lines changed

4 files changed

+348
-37
lines changed

.github/workflows/test.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ jobs:
1010
runs-on: ubuntu-latest
1111
strategy:
1212
matrix:
13-
go: [ '1.15', '1.14' ]
13+
go: [ '1.15' ]
1414
steps:
1515
- uses: actions/checkout@v2
1616
- name: Set up Go

protocol.go

+50-6
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,19 @@ import (
88
"time"
99
)
1010

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+
1117
// Listener is used to wrap an underlying listener,
1218
// whose connections may be using the HAProxy Proxy Protocol.
1319
// 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.
1524
type Listener struct {
1625
Listener net.Listener
1726
Policy PolicyFunc
@@ -21,7 +30,8 @@ type Listener struct {
2130

2231
// Conn is used to wrap and underlying connection which
2332
// 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.
2535
type Conn struct {
2636
bufReader *bufio.Reader
2737
conn net.Conn
@@ -30,6 +40,8 @@ type Conn struct {
3040
ProxyHeaderPolicy Policy
3141
Validate Validator
3242
readErr error
43+
readHeaderTimeout time.Duration
44+
readDeadline time.Time
3345
}
3446

3547
// Validator receives a header and decides whether it is a valid one
@@ -53,10 +65,6 @@ func (p *Listener) Accept() (net.Conn, error) {
5365
return nil, err
5466
}
5567

56-
if d := p.ReadHeaderTimeout; d != 0 {
57-
conn.SetReadDeadline(time.Now().Add(d))
58-
}
59-
6068
proxyHeaderPolicy := USE
6169
if p.Policy != nil {
6270
proxyHeaderPolicy, err = p.Policy(conn.RemoteAddr())
@@ -72,6 +80,15 @@ func (p *Listener) Accept() (net.Conn, error) {
7280
WithPolicy(proxyHeaderPolicy),
7381
ValidateHeader(p.ValidateHeader),
7482
)
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+
7592
return newConn, nil
7693
}
7794

@@ -110,6 +127,7 @@ func (p *Conn) Read(b []byte) (int, error) {
110127
if p.readErr != nil {
111128
return 0, p.readErr
112129
}
130+
113131
return p.bufReader.Read(b)
114132
}
115133

@@ -197,11 +215,16 @@ func (p *Conn) UDPConn() (conn *net.UDPConn, ok bool) {
197215

198216
// SetDeadline wraps original conn.SetDeadline
199217
func (p *Conn) SetDeadline(t time.Time) error {
218+
p.readDeadline = t
200219
return p.conn.SetDeadline(t)
201220
}
202221

203222
// SetReadDeadline wraps original conn.SetReadDeadline
204223
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
205228
return p.conn.SetReadDeadline(t)
206229
}
207230

@@ -211,7 +234,28 @@ func (p *Conn) SetWriteDeadline(t time.Time) error {
211234
}
212235

213236
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+
214245
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+
215259
// For the purpose of this wrapper shamefully stolen from armon/go-proxyproto
216260
// let's act as if there was no error when PROXY protocol is not present.
217261
if err == ErrNoProxyProtocol {

0 commit comments

Comments
 (0)