2019-06-27 22:36:04 +00:00
|
|
|
package integration
|
|
|
|
|
|
|
|
import (
|
|
|
|
"math"
|
|
|
|
"net"
|
|
|
|
"net/http"
|
|
|
|
"net/http/httptest"
|
2024-01-09 16:00:07 +00:00
|
|
|
"testing"
|
2019-06-27 22:36:04 +00:00
|
|
|
"time"
|
|
|
|
|
2024-01-10 09:47:44 +00:00
|
|
|
"github.com/rs/zerolog/log"
|
2024-01-09 16:00:07 +00:00
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/stretchr/testify/suite"
|
2023-02-03 14:24:05 +00:00
|
|
|
"github.com/traefik/traefik/v3/integration/try"
|
2019-06-27 22:36:04 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type KeepAliveSuite struct {
|
|
|
|
BaseSuite
|
|
|
|
}
|
|
|
|
|
2024-01-09 16:00:07 +00:00
|
|
|
func TestKeepAliveSuite(t *testing.T) {
|
|
|
|
suite.Run(t, new(KeepAliveSuite))
|
|
|
|
}
|
|
|
|
|
2019-06-27 22:36:04 +00:00
|
|
|
type KeepAliveConfig struct {
|
|
|
|
KeepAliveServer string
|
|
|
|
IdleConnTimeout string
|
|
|
|
}
|
|
|
|
|
|
|
|
type connStateChangeEvent struct {
|
|
|
|
key string
|
|
|
|
state http.ConnState
|
|
|
|
}
|
|
|
|
|
2024-01-09 16:00:07 +00:00
|
|
|
func (s *KeepAliveSuite) TestShouldRespectConfiguredBackendHttpKeepAliveTime() {
|
2019-06-27 22:36:04 +00: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 09:47:44 +00:00
|
|
|
log.Info().Msgf("timeout waiting for all connections to close, waited for %v, configured idle timeout was %v", maxWaitDuration, idleTimeout)
|
2024-01-09 16:00:07 +00:00
|
|
|
s.T().Fail()
|
2019-06-27 22:36:04 +00:00
|
|
|
close(completed)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-09 16:00:07 +00:00
|
|
|
require.Equal(s.T(), 1, connCount)
|
2019-06-27 22:36:04 +00: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 16:00:07 +00:00
|
|
|
require.LessOrEqual(s.T(), math.Round(idlePeriod.Seconds()), idleTimeout.Seconds())
|
2019-06-27 22:36:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
close(completed)
|
|
|
|
}()
|
|
|
|
|
|
|
|
server := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
2022-08-09 15:36:08 +00:00
|
|
|
w.WriteHeader(http.StatusOK)
|
2019-06-27 22:36:04 +00: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 16:00:07 +00:00
|
|
|
file := s.adaptFile("fixtures/timeout/keepalive.toml", config)
|
2019-06-27 22:36:04 +00:00
|
|
|
|
2024-01-09 16:00:07 +00:00
|
|
|
s.traefikCmd(withConfigFile(file))
|
2019-06-27 22:36:04 +00:00
|
|
|
|
2020-09-11 13:40:03 +00:00
|
|
|
// Wait for Traefik
|
2024-01-09 16:00:07 +00: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 13:40:03 +00:00
|
|
|
|
2019-06-27 22:36:04 +00:00
|
|
|
err = try.GetRequest("http://127.0.0.1:8000/keepalive", time.Duration(1)*time.Second, try.StatusCodeIs(200))
|
2024-01-09 16:00:07 +00:00
|
|
|
require.NoError(s.T(), err)
|
2019-06-27 22:36:04 +00:00
|
|
|
|
|
|
|
close(noMoreRequests)
|
|
|
|
<-completed
|
|
|
|
}
|