Golang http client with proxy protocol support
Rajat Jindal
Mar 11th 2021, 09:30AM
The proxy protocol
is an internet protocol that is used to carry connection information from the source requesting the connection to the destination for which the connection was requested.
During one of the project, I needed to send traffic directly to the nginx
which was running in proxy protocol
mode. Now if I sent the request directly, it would not accept the request and throw following error
error connecting to the server, did you send request to the right port
after some research I came across the nice project go-proxyproto which provides a way to format proxy protocol header for consumption in http client
.
a simplified example in their repo is:
package main
import (
"io"
"log"
"net"
proxyproto "github.com/pires/go-proxyproto"
)
func chkErr(err error) {
if err != nil {
log.Fatalf("Error: %s", err.Error())
}
}
func main() {
// Dial some proxy listener e.g. https://github.com/mailgun/proxyproto
target, err := net.ResolveTCPAddr("tcp", "127.0.0.1:2319")
chkErr(err)
conn, err := net.DialTCP("tcp", nil, target)
chkErr(err)
defer conn.Close()
// Create a proxyprotocol header or use HeaderProxyFromAddrs() if you
// have two conn's
header := &proxyproto.Header{
Version: 1,
Command: proxyproto.PROXY,
TransportProtocol: proxyproto.TCPv4,
SourceAddr: &net.TCPAddr{
IP: net.ParseIP("10.1.1.1"),
Port: 1000,
},
DestinationAddr: &net.TCPAddr{
IP: net.ParseIP("20.2.2.2"),
Port: 2000,
},
}
// After the connection was created write the proxy headers first
_, err = header.WriteTo(conn)
chkErr(err)
// Then your data... e.g.:
_, err = io.WriteString(conn, "HELO")
chkErr(err)
}
Now, for my project, although I have control of http client object, I didn't wanted to go to level of using net.DialTCP
or net.ResolveTCPAddr
directly in my code.
so I ended up doing following
.
.
.
tr := &http.Transport{}
tr.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
conn, err := (&net.Dialer{}).Dial(network, addr)
if err != nil {
return nil, err
}
header := &proxyproto.Header{
Version: 1,
Command: proxyproto.PROXY,
TransportProtocol: proxyproto.TCPv4,
SourceAddr: conn.LocalAddr(),
DestinationAddr: conn.RemoteAddr(),
}
_, err = header.WriteTo(conn)
if err != nil {
return nil, err
}
return conn, nil
}
once I ran it, I was able to send traffic directly to nginx
successfully.
Conclusion
the nice thing about golang
is that you can do as deep as you want to go, and stay as highlevel as you want to. It is easy to try out different things without having to worry about anything else.