2019-09-13 17:46:04 +02:00
package tcp
import (
"bytes"
2020-11-17 13:04:04 +01:00
"errors"
2019-09-13 17:46:04 +02:00
"fmt"
"io"
"net"
2020-11-13 08:48:04 -03:00
"sync"
2019-09-13 17:46:04 +02:00
"testing"
"time"
2020-11-17 13:04:04 +01:00
"github.com/pires/go-proxyproto"
"github.com/stretchr/testify/assert"
2019-09-13 17:46:04 +02:00
"github.com/stretchr/testify/require"
2020-11-17 13:04:04 +01:00
"github.com/traefik/traefik/v2/pkg/config/dynamic"
2019-09-13 17:46:04 +02:00
)
func fakeRedis ( t * testing . T , listener net . Listener ) {
2020-12-29 10:54:03 +01:00
t . Helper ( )
2019-09-13 17:46:04 +02:00
for {
conn , err := listener . Accept ( )
fmt . Println ( "Accept on server" )
require . NoError ( t , err )
2020-11-17 13:04:04 +01:00
2019-09-13 17:46:04 +02:00
for {
withErr := false
buf := make ( [ ] byte , 64 )
if _ , err := conn . Read ( buf ) ; err != nil {
withErr = true
}
if string ( buf [ : 4 ] ) == "ping" {
2019-11-27 01:38:03 +05:00
time . Sleep ( 1 * time . Millisecond )
2019-09-13 17:46:04 +02:00
if _ , err := conn . Write ( [ ] byte ( "PONG" ) ) ; err != nil {
2020-11-17 13:04:04 +01:00
_ = conn . Close ( )
2019-09-13 17:46:04 +02:00
return
}
}
2020-11-17 13:04:04 +01:00
2019-09-13 17:46:04 +02:00
if withErr {
2020-11-17 13:04:04 +01:00
_ = conn . Close ( )
2019-09-13 17:46:04 +02:00
return
}
}
}
}
func TestCloseWrite ( t * testing . T ) {
backendListener , err := net . Listen ( "tcp" , ":0" )
require . NoError ( t , err )
go fakeRedis ( t , backendListener )
_ , port , err := net . SplitHostPort ( backendListener . Addr ( ) . String ( ) )
require . NoError ( t , err )
2020-11-17 13:04:04 +01:00
proxy , err := NewProxy ( ":" + port , 10 * time . Millisecond , nil )
2019-09-13 17:46:04 +02:00
require . NoError ( t , err )
proxyListener , err := net . Listen ( "tcp" , ":0" )
require . NoError ( t , err )
go func ( ) {
for {
conn , err := proxyListener . Accept ( )
require . NoError ( t , err )
proxy . ServeTCP ( conn . ( * net . TCPConn ) )
}
} ( )
_ , port , err = net . SplitHostPort ( proxyListener . Addr ( ) . String ( ) )
require . NoError ( t , err )
conn , err := net . Dial ( "tcp" , ":" + port )
require . NoError ( t , err )
_ , err = conn . Write ( [ ] byte ( "ping\n" ) )
require . NoError ( t , err )
err = conn . ( * net . TCPConn ) . CloseWrite ( )
require . NoError ( t , err )
var buf [ ] byte
buffer := bytes . NewBuffer ( buf )
n , err := io . Copy ( buffer , conn )
require . NoError ( t , err )
require . Equal ( t , int64 ( 4 ) , n )
require . Equal ( t , "PONG" , buffer . String ( ) )
}
2020-11-17 13:04:04 +01:00
func TestProxyProtocol ( t * testing . T ) {
testCases := [ ] struct {
desc string
version int
} {
{
desc : "PROXY protocol v1" ,
version : 1 ,
} ,
{
desc : "PROXY protocol v2" ,
version : 2 ,
} ,
}
for _ , test := range testCases {
test := test
t . Run ( test . desc , func ( t * testing . T ) {
backendListener , err := net . Listen ( "tcp" , ":0" )
require . NoError ( t , err )
var version int
proxyBackendListener := proxyproto . Listener {
Listener : backendListener ,
ValidateHeader : func ( h * proxyproto . Header ) error {
version = int ( h . Version )
return nil
} ,
Policy : func ( upstream net . Addr ) ( proxyproto . Policy , error ) {
switch test . version {
case 1 , 2 :
return proxyproto . USE , nil
default :
return proxyproto . REQUIRE , errors . New ( "unsupported version" )
}
} ,
}
defer proxyBackendListener . Close ( )
go fakeRedis ( t , & proxyBackendListener )
_ , port , err := net . SplitHostPort ( proxyBackendListener . Addr ( ) . String ( ) )
require . NoError ( t , err )
proxy , err := NewProxy ( ":" + port , 10 * time . Millisecond , & dynamic . ProxyProtocol { Version : test . version } )
require . NoError ( t , err )
proxyListener , err := net . Listen ( "tcp" , ":0" )
require . NoError ( t , err )
go func ( ) {
for {
conn , err := proxyListener . Accept ( )
require . NoError ( t , err )
proxy . ServeTCP ( conn . ( * net . TCPConn ) )
}
} ( )
_ , port , err = net . SplitHostPort ( proxyListener . Addr ( ) . String ( ) )
require . NoError ( t , err )
conn , err := net . Dial ( "tcp" , ":" + port )
require . NoError ( t , err )
_ , err = conn . Write ( [ ] byte ( "ping\n" ) )
require . NoError ( t , err )
err = conn . ( * net . TCPConn ) . CloseWrite ( )
require . NoError ( t , err )
var buf [ ] byte
buffer := bytes . NewBuffer ( buf )
n , err := io . Copy ( buffer , conn )
require . NoError ( t , err )
assert . Equal ( t , int64 ( 4 ) , n )
assert . Equal ( t , "PONG" , buffer . String ( ) )
assert . Equal ( t , test . version , version )
} )
}
}
2020-11-20 11:30:07 +01:00
2020-11-13 08:48:04 -03:00
func TestLookupAddress ( t * testing . T ) {
testCases := [ ] struct {
desc string
address string
expectSame assert . ComparisonAssertionFunc
} {
{
desc : "IP doesn't need refresh" ,
address : "8.8.4.4:53" ,
expectSame : assert . Same ,
} ,
{
desc : "Hostname needs refresh" ,
address : "dns.google:53" ,
expectSame : assert . NotSame ,
} ,
}
for _ , test := range testCases {
test := test
t . Run ( test . desc , func ( t * testing . T ) {
t . Parallel ( )
2020-11-20 11:30:07 +01:00
proxy , err := NewProxy ( test . address , 10 * time . Millisecond , nil )
2020-11-13 08:48:04 -03:00
require . NoError ( t , err )
require . NotNil ( t , proxy . target )
proxyListener , err := net . Listen ( "tcp" , ":0" )
require . NoError ( t , err )
var wg sync . WaitGroup
go func ( wg * sync . WaitGroup ) {
for {
conn , err := proxyListener . Accept ( )
require . NoError ( t , err )
proxy . ServeTCP ( conn . ( * net . TCPConn ) )
wg . Done ( )
}
} ( & wg )
var lastTarget * net . TCPAddr
for i := 0 ; i < 3 ; i ++ {
wg . Add ( 1 )
conn , err := net . Dial ( "tcp" , proxyListener . Addr ( ) . String ( ) )
require . NoError ( t , err )
_ , err = conn . Write ( [ ] byte ( "ping\n" ) )
require . NoError ( t , err )
err = conn . Close ( )
require . NoError ( t , err )
wg . Wait ( )
assert . NotNil ( t , proxy . target )
if lastTarget != nil {
test . expectSame ( t , lastTarget , proxy . target )
}
lastTarget = proxy . target
}
} )
}
}