This repository has been archived by the owner on Jan 31, 2024. It is now read-only.
forked from pires/go-proxyproto
-
Notifications
You must be signed in to change notification settings - Fork 4
/
protocol.go
136 lines (117 loc) · 3.53 KB
/
protocol.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
package proxyproto
import (
"bufio"
"net"
"sync"
"time"
)
// Listener is used to wrap an underlying listener,
// whose connections may be using the HAProxy Proxy Protocol.
// If the connection is using the protocol, the RemoteAddr() will return
// the correct client address.
//
// Optionally define ProxyHeaderTimeout to set a maximum time to
// receive the Proxy Protocol Header. Zero means no timeout.
type Listener struct {
Listener net.Listener
ProxyHeaderTimeout time.Duration
}
// Conn is used to wrap and underlying connection which
// may be speaking the Proxy Protocol. If it is, the RemoteAddr() will
// return the address of the client instead of the proxy address.
type Conn struct {
bufReader *bufio.Reader
conn net.Conn
header *Header
once sync.Once
proxyHeaderTimeout time.Duration
}
// Accept waits for and returns the next connection to the listener.
func (p *Listener) Accept() (net.Conn, error) {
// Get the underlying connection
conn, err := p.Listener.Accept()
if err != nil {
return nil, err
}
return NewConn(conn, p.ProxyHeaderTimeout), nil
}
// Close closes the underlying listener.
func (p *Listener) Close() error {
return p.Listener.Close()
}
// Addr returns the underlying listener's network address.
func (p *Listener) Addr() net.Addr {
return p.Listener.Addr()
}
// NewConn is used to wrap a net.Conn that may be speaking
// the proxy protocol into a proxyproto.Conn
func NewConn(conn net.Conn, timeout time.Duration) *Conn {
pConn := &Conn{
bufReader: bufio.NewReader(conn),
conn: conn,
proxyHeaderTimeout: timeout,
}
return pConn
}
// Read is check for the proxy protocol header when doing
// the initial scan. If there is an error parsing the header,
// it is returned and the socket is closed.
func (p *Conn) Read(b []byte) (int, error) {
var err error
p.once.Do(func() {
err = p.readHeader()
})
if err != nil {
return 0, err
}
return p.bufReader.Read(b)
}
// Write wraps original conn.Write
func (p *Conn) Write(b []byte) (int, error) {
return p.conn.Write(b)
}
// Close wraps original conn.Close
func (p *Conn) Close() error {
return p.conn.Close()
}
// LocalAddr returns the address of the server if the proxy
// protocol is being used, otherwise just returns the address of
// the socket server.
func (p *Conn) LocalAddr() net.Addr {
p.once.Do(func() { p.readHeader() })
if p.header == nil || p.header.Command.IsLocal() {
return p.conn.LocalAddr()
}
return p.header.LocalAddr()
}
// RemoteAddr returns the address of the client if the proxy
// protocol is being used, otherwise just returns the address of
// the socket peer.
func (p *Conn) RemoteAddr() net.Addr {
p.once.Do(func() { p.readHeader() })
if p.header == nil || p.header.Command.IsLocal() {
return p.conn.RemoteAddr()
}
return p.header.RemoteAddr()
}
// SetDeadline wraps original conn.SetDeadline
func (p *Conn) SetDeadline(t time.Time) error {
return p.conn.SetDeadline(t)
}
// SetReadDeadline wraps original conn.SetReadDeadline
func (p *Conn) SetReadDeadline(t time.Time) error {
return p.conn.SetReadDeadline(t)
}
// SetWriteDeadline wraps original conn.SetWriteDeadline
func (p *Conn) SetWriteDeadline(t time.Time) error {
return p.conn.SetWriteDeadline(t)
}
func (p *Conn) readHeader() (err error) {
p.header, err = Read(p.bufReader)
// For the purpose of this wrapper shamefully stolen from armon/go-proxyproto
// let's act as if there was no error when PROXY protocol is not present.
if err == ErrNoProxyProtocol {
err = nil
}
return
}