@@ -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.
1524type 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.
2535type 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
199217func (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
204223func (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
213236func (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