2019-03-14 09:30:04 +01:00
package server
import (
"bufio"
"context"
2020-01-06 16:56:05 +01:00
"errors"
"io"
2019-03-14 09:30:04 +01:00
"net"
"net/http"
2020-01-06 16:56:05 +01:00
"strings"
2019-03-14 09:30:04 +01:00
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
2020-08-17 18:04:03 +02:00
ptypes "github.com/traefik/paerser/types"
2020-09-16 15:46:04 +02:00
"github.com/traefik/traefik/v2/pkg/config/static"
2022-03-17 11:02:08 -06:00
tcprouter "github.com/traefik/traefik/v2/pkg/server/router/tcp"
2020-09-16 15:46:04 +02:00
"github.com/traefik/traefik/v2/pkg/tcp"
2019-03-14 09:30:04 +01:00
)
2020-01-06 16:56:05 +01:00
func TestShutdownHijacked ( t * testing . T ) {
2022-03-17 11:02:08 -06:00
router := & tcprouter . Router { }
router . SetHTTPHandler ( http . HandlerFunc ( func ( rw http . ResponseWriter , req * http . Request ) {
2020-01-06 16:56:05 +01:00
conn , _ , err := rw . ( http . Hijacker ) . Hijack ( )
require . NoError ( t , err )
2019-03-14 09:30:04 +01:00
2020-01-06 16:56:05 +01:00
resp := http . Response { StatusCode : http . StatusOK }
err = resp . Write ( conn )
require . NoError ( t , err )
} ) )
2020-12-29 10:54:03 +01:00
2020-01-06 16:56:05 +01:00
testShutdown ( t , router )
}
2019-03-14 09:30:04 +01:00
2020-01-06 16:56:05 +01:00
func TestShutdownHTTP ( t * testing . T ) {
2022-03-17 11:02:08 -06:00
router := & tcprouter . Router { }
router . SetHTTPHandler ( http . HandlerFunc ( func ( rw http . ResponseWriter , req * http . Request ) {
2019-03-14 09:30:04 +01:00
rw . WriteHeader ( http . StatusOK )
2020-01-06 16:56:05 +01:00
time . Sleep ( time . Second )
2019-03-14 09:30:04 +01:00
} ) )
2020-12-29 10:54:03 +01:00
2020-01-06 16:56:05 +01:00
testShutdown ( t , router )
}
2019-03-14 09:30:04 +01:00
2020-01-06 16:56:05 +01:00
func TestShutdownTCP ( t * testing . T ) {
2022-03-17 11:02:08 -06:00
router , err := tcprouter . NewRouter ( )
require . NoError ( t , err )
err = router . AddRoute ( "HostSNI(`*`)" , 0 , tcp . HandlerFunc ( func ( conn tcp . WriteCloser ) {
2022-12-07 10:56:05 +01:00
_ , err := http . ReadRequest ( bufio . NewReader ( conn ) )
if err != nil {
return
2020-01-06 16:56:05 +01:00
}
2022-12-07 10:56:05 +01:00
resp := http . Response { StatusCode : http . StatusOK }
_ = resp . Write ( conn )
2020-01-06 16:56:05 +01:00
} ) )
2022-03-17 11:02:08 -06:00
require . NoError ( t , err )
2020-01-06 16:56:05 +01:00
testShutdown ( t , router )
}
2022-03-17 11:02:08 -06:00
func testShutdown ( t * testing . T , router * tcprouter . Router ) {
2020-12-29 10:54:03 +01:00
t . Helper ( )
2020-01-06 16:56:05 +01:00
epConfig := & static . EntryPointsTransport { }
epConfig . SetDefaults ( )
epConfig . LifeCycle . RequestAcceptGraceTimeout = 0
2020-08-17 18:04:03 +02:00
epConfig . LifeCycle . GraceTimeOut = ptypes . Duration ( 5 * time . Second )
2021-03-08 09:58:04 +01:00
epConfig . RespondingTimeouts . ReadTimeout = ptypes . Duration ( 5 * time . Second )
epConfig . RespondingTimeouts . WriteTimeout = ptypes . Duration ( 5 * time . Second )
2020-01-06 16:56:05 +01:00
entryPoint , err := NewTCPEntryPoint ( context . Background ( ) , & static . EntryPoint {
// We explicitly use an IPV4 address because on Alpine, with an IPV6 address
// there seems to be shenanigans related to properly cleaning up file descriptors
Address : "127.0.0.1:0" ,
Transport : epConfig ,
ForwardedHeaders : & static . ForwardedHeaders { } ,
2022-04-04 11:46:07 +02:00
HTTP2 : & static . HTTP2Config { } ,
2022-02-14 17:18:08 +01:00
} , nil )
2019-03-14 09:30:04 +01:00
require . NoError ( t , err )
2020-01-06 16:56:05 +01:00
conn , err := startEntrypoint ( entryPoint , router )
require . NoError ( t , err )
2022-12-07 10:56:05 +01:00
t . Cleanup ( func ( ) { _ = conn . Close ( ) } )
2019-03-14 09:30:04 +01:00
2020-01-06 16:56:05 +01:00
epAddr := entryPoint . listener . Addr ( ) . String ( )
request , err := http . NewRequest ( http . MethodHead , "http://127.0.0.1:8082" , nil )
2019-03-14 09:30:04 +01:00
require . NoError ( t , err )
2020-03-18 14:50:06 +01:00
time . Sleep ( 100 * time . Millisecond )
2020-01-06 16:56:05 +01:00
2022-12-07 10:56:05 +01:00
// We need to do a write on conn before the shutdown to make it "exist".
2020-01-06 16:56:05 +01:00
// Because the connection indeed exists as far as TCP is concerned,
2021-06-25 21:08:11 +02:00
// but since we only pass it along to the HTTP server after at least one byte is peeked,
// the HTTP server (and hence its shutdown) does not know about the connection until that first byte peeked.
2019-03-14 09:30:04 +01:00
err = request . Write ( conn )
require . NoError ( t , err )
2022-12-07 10:56:05 +01:00
reader := bufio . NewReaderSize ( conn , 1 )
2021-03-08 09:58:04 +01:00
// Wait for first byte in response.
_ , err = reader . Peek ( 1 )
require . NoError ( t , err )
2020-01-06 16:56:05 +01:00
go entryPoint . Shutdown ( context . Background ( ) )
// Make sure that new connections are not permitted anymore.
// Note that this should be true not only after Shutdown has returned,
// but technically also as early as the Shutdown has closed the listener,
// i.e. during the shutdown and before the gracetime is over.
var testOk bool
for i := 0 ; i < 10 ; i ++ {
loopConn , err := net . Dial ( "tcp" , epAddr )
if err == nil {
loopConn . Close ( )
2020-03-18 14:50:06 +01:00
time . Sleep ( 100 * time . Millisecond )
2020-01-06 16:56:05 +01:00
continue
}
if ! strings . HasSuffix ( err . Error ( ) , "connection refused" ) && ! strings . HasSuffix ( err . Error ( ) , "reset by peer" ) {
t . Fatalf ( ` unexpected error: got %v, wanted "connection refused" or "reset by peer" ` , err )
}
testOk = true
break
}
if ! testOk {
t . Fatal ( "entry point never closed" )
}
// And make sure that the connection we had opened before shutting things down is still operational
2021-03-08 09:58:04 +01:00
resp , err := http . ReadResponse ( reader , request )
2019-03-14 09:30:04 +01:00
require . NoError ( t , err )
2020-01-06 16:56:05 +01:00
assert . Equal ( t , http . StatusOK , resp . StatusCode )
2019-03-14 09:30:04 +01:00
}
2022-03-17 11:02:08 -06:00
func startEntrypoint ( entryPoint * TCPEntryPoint , router * tcprouter . Router ) ( net . Conn , error ) {
2020-02-11 01:26:04 +01:00
go entryPoint . Start ( context . Background ( ) )
2020-01-06 16:56:05 +01:00
entryPoint . SwitchRouter ( router )
for i := 0 ; i < 10 ; i ++ {
2021-03-08 09:58:04 +01:00
conn , err := net . Dial ( "tcp" , entryPoint . listener . Addr ( ) . String ( ) )
2020-01-06 16:56:05 +01:00
if err != nil {
2020-03-18 14:50:06 +01:00
time . Sleep ( 100 * time . Millisecond )
2020-01-06 16:56:05 +01:00
continue
}
2021-03-08 09:58:04 +01:00
return conn , err
2020-01-06 16:56:05 +01:00
}
2021-03-08 09:58:04 +01:00
return nil , errors . New ( "entry point never started" )
2020-01-06 16:56:05 +01:00
}
func TestReadTimeoutWithoutFirstByte ( t * testing . T ) {
epConfig := & static . EntryPointsTransport { }
epConfig . SetDefaults ( )
2020-08-17 18:04:03 +02:00
epConfig . RespondingTimeouts . ReadTimeout = ptypes . Duration ( 2 * time . Second )
2020-01-06 16:56:05 +01:00
2019-03-14 09:30:04 +01:00
entryPoint , err := NewTCPEntryPoint ( context . Background ( ) , & static . EntryPoint {
2020-01-06 16:56:05 +01:00
Address : ":0" ,
Transport : epConfig ,
2019-03-14 09:30:04 +01:00
ForwardedHeaders : & static . ForwardedHeaders { } ,
2022-04-04 11:46:07 +02:00
HTTP2 : & static . HTTP2Config { } ,
2022-02-14 17:18:08 +01:00
} , nil )
2019-03-14 09:30:04 +01:00
require . NoError ( t , err )
2022-03-17 11:02:08 -06:00
router := & tcprouter . Router { }
router . SetHTTPHandler ( http . HandlerFunc ( func ( rw http . ResponseWriter , req * http . Request ) {
2020-01-06 16:56:05 +01:00
rw . WriteHeader ( http . StatusOK )
2019-03-14 09:30:04 +01:00
} ) )
2019-09-26 11:00:06 +02:00
2020-01-06 16:56:05 +01:00
conn , err := startEntrypoint ( entryPoint , router )
2019-03-14 09:30:04 +01:00
require . NoError ( t , err )
2020-01-06 16:56:05 +01:00
errChan := make ( chan error )
2019-03-14 09:30:04 +01:00
2020-01-06 16:56:05 +01:00
go func ( ) {
b := make ( [ ] byte , 2048 )
_ , err := conn . Read ( b )
errChan <- err
} ( )
2019-03-14 09:30:04 +01:00
2020-01-06 16:56:05 +01:00
select {
case err := <- errChan :
require . Equal ( t , io . EOF , err )
2020-03-18 14:50:06 +01:00
case <- time . Tick ( 5 * time . Second ) :
2020-01-06 16:56:05 +01:00
t . Error ( "Timeout while read" )
}
2019-03-14 09:30:04 +01:00
}
2020-01-06 16:56:05 +01:00
func TestReadTimeoutWithFirstByte ( t * testing . T ) {
epConfig := & static . EntryPointsTransport { }
epConfig . SetDefaults ( )
2020-08-17 18:04:03 +02:00
epConfig . RespondingTimeouts . ReadTimeout = ptypes . Duration ( 2 * time . Second )
2020-01-06 16:56:05 +01:00
2019-03-14 09:30:04 +01:00
entryPoint , err := NewTCPEntryPoint ( context . Background ( ) , & static . EntryPoint {
2020-01-06 16:56:05 +01:00
Address : ":0" ,
Transport : epConfig ,
2019-03-14 09:30:04 +01:00
ForwardedHeaders : & static . ForwardedHeaders { } ,
2022-04-04 11:46:07 +02:00
HTTP2 : & static . HTTP2Config { } ,
2022-02-14 17:18:08 +01:00
} , nil )
2019-03-14 09:30:04 +01:00
require . NoError ( t , err )
2022-03-17 11:02:08 -06:00
router := & tcprouter . Router { }
router . SetHTTPHandler ( http . HandlerFunc ( func ( rw http . ResponseWriter , req * http . Request ) {
2020-01-06 16:56:05 +01:00
rw . WriteHeader ( http . StatusOK )
2019-03-14 09:30:04 +01:00
} ) )
2020-01-06 16:56:05 +01:00
conn , err := startEntrypoint ( entryPoint , router )
2019-03-14 09:30:04 +01:00
require . NoError ( t , err )
2020-01-06 16:56:05 +01:00
_ , err = conn . Write ( [ ] byte ( "GET /some HTTP/1.1\r\n" ) )
2019-03-14 09:30:04 +01:00
require . NoError ( t , err )
2020-01-06 16:56:05 +01:00
errChan := make ( chan error )
2019-03-14 09:30:04 +01:00
2020-01-06 16:56:05 +01:00
go func ( ) {
b := make ( [ ] byte , 2048 )
_ , err := conn . Read ( b )
errChan <- err
} ( )
select {
case err := <- errChan :
require . Equal ( t , io . EOF , err )
2020-03-18 14:50:06 +01:00
case <- time . Tick ( 5 * time . Second ) :
2020-01-06 16:56:05 +01:00
t . Error ( "Timeout while read" )
}
2019-03-14 09:30:04 +01:00
}