2019-06-28 01:36:04 +03:00
package integration
import (
"math"
"net"
"net/http"
"net/http/httptest"
2024-01-09 19:00:07 +03:00
"testing"
2019-06-28 01:36:04 +03:00
"time"
2024-01-10 12:47:44 +03:00
"github.com/rs/zerolog/log"
2024-01-09 19:00:07 +03:00
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
2023-02-03 17:24:05 +03:00
"github.com/traefik/traefik/v3/integration/try"
2019-06-28 01:36:04 +03:00
)
type KeepAliveSuite struct {
BaseSuite
}
2024-01-09 19:00:07 +03:00
func TestKeepAliveSuite ( t * testing . T ) {
suite . Run ( t , new ( KeepAliveSuite ) )
}
2019-06-28 01:36:04 +03:00
type KeepAliveConfig struct {
KeepAliveServer string
IdleConnTimeout string
}
type connStateChangeEvent struct {
key string
state http . ConnState
}
2024-01-09 19:00:07 +03:00
func ( s * KeepAliveSuite ) TestShouldRespectConfiguredBackendHttpKeepAliveTime ( ) {
2019-06-28 01:36:04 +03:00
idleTimeout := time . Duration ( 75 ) * time . Millisecond
connStateChanges := make ( chan connStateChangeEvent )
noMoreRequests := make ( chan bool , 1 )
completed := make ( chan bool , 1 )
// keep track of HTTP connections and their status changes and measure their idle period
go func ( ) {
connCount := 0
idlePeriodStartMap := make ( map [ string ] time . Time )
idlePeriodLengthMap := make ( map [ string ] time . Duration )
maxWaitDuration := 5 * time . Second
maxWaitTimeExceeded := time . After ( maxWaitDuration )
moreRequestsExpected := true
// Ensure that all idle HTTP connections are closed before verification phase
for moreRequestsExpected || len ( idlePeriodLengthMap ) < connCount {
select {
case event := <- connStateChanges :
switch event . state {
case http . StateNew :
connCount ++
case http . StateIdle :
idlePeriodStartMap [ event . key ] = time . Now ( )
case http . StateClosed :
idlePeriodLengthMap [ event . key ] = time . Since ( idlePeriodStartMap [ event . key ] )
}
case <- noMoreRequests :
moreRequestsExpected = false
case <- maxWaitTimeExceeded :
2024-01-10 12:47:44 +03:00
log . Info ( ) . Msgf ( "timeout waiting for all connections to close, waited for %v, configured idle timeout was %v" , maxWaitDuration , idleTimeout )
2024-01-09 19:00:07 +03:00
s . T ( ) . Fail ( )
2019-06-28 01:36:04 +03:00
close ( completed )
return
}
}
2024-01-09 19:00:07 +03:00
require . Equal ( s . T ( ) , 1 , connCount )
2019-06-28 01:36:04 +03:00
for _ , idlePeriod := range idlePeriodLengthMap {
// Our method of measuring the actual idle period is not precise, so allow some sub-ms deviation
2024-01-09 19:00:07 +03:00
require . LessOrEqual ( s . T ( ) , math . Round ( idlePeriod . Seconds ( ) ) , idleTimeout . Seconds ( ) )
2019-06-28 01:36:04 +03:00
}
close ( completed )
} ( )
server := httptest . NewUnstartedServer ( http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
2022-08-09 18:36:08 +03:00
w . WriteHeader ( http . StatusOK )
2019-06-28 01:36:04 +03:00
} ) )
server . Config . ConnState = func ( conn net . Conn , state http . ConnState ) {
connStateChanges <- connStateChangeEvent { key : conn . RemoteAddr ( ) . String ( ) , state : state }
}
server . Start ( )
defer server . Close ( )
config := KeepAliveConfig { KeepAliveServer : server . URL , IdleConnTimeout : idleTimeout . String ( ) }
2024-01-09 19:00:07 +03:00
file := s . adaptFile ( "fixtures/timeout/keepalive.toml" , config )
2019-06-28 01:36:04 +03:00
2024-01-09 19:00:07 +03:00
s . traefikCmd ( withConfigFile ( file ) )
2019-06-28 01:36:04 +03:00
2020-09-11 16:40:03 +03:00
// Wait for Traefik
2024-01-09 19:00:07 +03:00
err := try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , time . Duration ( 1 ) * time . Second , try . StatusCodeIs ( 200 ) , try . BodyContains ( "PathPrefix(`/keepalive`)" ) )
require . NoError ( s . T ( ) , err )
2020-09-11 16:40:03 +03:00
2019-06-28 01:36:04 +03:00
err = try . GetRequest ( "http://127.0.0.1:8000/keepalive" , time . Duration ( 1 ) * time . Second , try . StatusCodeIs ( 200 ) )
2024-01-09 19:00:07 +03:00
require . NoError ( s . T ( ) , err )
2019-06-28 01:36:04 +03:00
close ( noMoreRequests )
<- completed
}