2017-07-06 14:28:13 +00:00
package integration
2015-09-27 13:59:51 +00:00
import (
2022-06-27 13:16:08 +00:00
"bufio"
2019-01-15 13:28:04 +00:00
"bytes"
2020-03-05 17:03:08 +00:00
"crypto/rand"
2019-01-15 13:28:04 +00:00
"encoding/json"
2017-05-17 13:22:44 +00:00
"fmt"
2021-03-04 19:08:03 +00:00
"io"
2021-11-25 10:10:06 +00:00
"net"
2015-09-27 13:59:51 +00:00
"net/http"
2018-08-24 14:20:03 +00:00
"net/http/httptest"
2017-09-26 08:22:03 +00:00
"os"
2022-12-05 10:30:05 +00:00
"regexp"
2017-05-17 13:22:44 +00:00
"strings"
2019-08-26 17:00:04 +00:00
"sync/atomic"
2017-09-26 08:22:03 +00:00
"syscall"
2024-01-09 16:00:07 +00:00
"testing"
2015-09-27 13:59:51 +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/assert"
"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"
"github.com/traefik/traefik/v3/pkg/config/dynamic"
2015-09-27 13:59:51 +00:00
)
2020-05-11 10:06:07 +00:00
// SimpleSuite tests suite.
2015-11-03 22:06:31 +00:00
type SimpleSuite struct { BaseSuite }
2024-01-09 16:00:07 +00:00
func TestSimpleSuite ( t * testing . T ) {
suite . Run ( t , new ( SimpleSuite ) )
}
func ( s * SimpleSuite ) SetupSuite ( ) {
s . BaseSuite . SetupSuite ( )
}
2016-04-19 17:23:08 +00:00
2024-01-09 16:00:07 +00:00
func ( s * SimpleSuite ) TearDownSuite ( ) {
s . BaseSuite . TearDownSuite ( )
}
2016-04-19 17:23:08 +00:00
2024-01-09 16:00:07 +00:00
func ( s * SimpleSuite ) TestInvalidConfigShouldFail ( ) {
_ , output := s . cmdTraefik ( withConfigFile ( "fixtures/invalid_configuration.toml" ) )
err := try . Do ( 500 * time . Millisecond , func ( ) error {
2022-03-22 10:04:08 +00:00
expected := "expected '.' or '=', but got '{' instead"
2017-07-10 12:58:31 +00:00
actual := output . String ( )
2017-05-17 13:22:44 +00:00
if ! strings . Contains ( actual , expected ) {
2018-07-03 08:02:03 +00:00
return fmt . Errorf ( "got %s, wanted %s" , actual , expected )
2017-05-17 13:22:44 +00:00
}
return nil
} )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2015-09-27 13:59:51 +00:00
}
2024-01-09 16:00:07 +00:00
func ( s * SimpleSuite ) TestSimpleDefaultConfig ( ) {
s . cmdTraefik ( withConfigFile ( "fixtures/simple_default.toml" ) )
2015-09-27 13:59:51 +00:00
2016-03-15 17:57:56 +00:00
// Expected a 404 as we did not configure anything
2024-01-09 16:00:07 +00:00
err := try . GetRequest ( "http://127.0.0.1:8000/" , 1 * time . Second , try . StatusCodeIs ( http . StatusNotFound ) )
require . NoError ( s . T ( ) , err )
2015-11-03 22:06:31 +00:00
}
2024-01-09 16:00:07 +00:00
func ( s * SimpleSuite ) TestWithWebConfig ( ) {
s . cmdTraefik ( withConfigFile ( "fixtures/simple_web.toml" ) )
2015-11-03 22:06:31 +00:00
2024-01-09 16:00:07 +00:00
err := try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 1 * time . Second , try . StatusCodeIs ( http . StatusOK ) )
require . NoError ( s . T ( ) , err )
2015-09-27 13:59:51 +00:00
}
2016-06-02 13:17:04 +00:00
2024-01-09 16:00:07 +00:00
func ( s * SimpleSuite ) TestPrintHelp ( ) {
_ , output := s . cmdTraefik ( "--help" )
2016-06-02 13:17:04 +00:00
2024-01-09 16:00:07 +00:00
err := try . Do ( 500 * time . Millisecond , func ( ) error {
2017-05-17 13:22:44 +00:00
expected := "Usage:"
notExpected := "panic:"
2017-07-10 12:58:31 +00:00
actual := output . String ( )
2017-05-17 13:22:44 +00:00
if strings . Contains ( actual , notExpected ) {
2018-07-03 08:02:03 +00:00
return fmt . Errorf ( "got %s" , actual )
2017-05-17 13:22:44 +00:00
}
if ! strings . Contains ( actual , expected ) {
2018-07-03 08:02:03 +00:00
return fmt . Errorf ( "got %s, wanted %s" , actual , expected )
2017-05-17 13:22:44 +00:00
}
return nil
} )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2016-06-02 13:17:04 +00:00
}
2017-09-26 08:22:03 +00:00
2024-01-09 16:00:07 +00:00
func ( s * SimpleSuite ) TestRequestAcceptGraceTimeout ( ) {
s . createComposeProject ( "reqacceptgrace" )
2017-09-26 08:22:03 +00:00
2024-01-09 16:00:07 +00:00
s . composeUp ( )
defer s . composeDown ( )
2021-11-25 10:10:06 +00:00
2024-01-09 16:00:07 +00:00
whoamiURL := "http://" + net . JoinHostPort ( s . getComposeServiceIP ( "whoami" ) , "80" )
2017-09-26 08:22:03 +00:00
2024-01-09 16:00:07 +00:00
file := s . adaptFile ( "fixtures/reqacceptgrace.toml" , struct {
2017-09-26 08:22:03 +00:00
Server string
2021-11-25 10:10:06 +00:00
} { whoamiURL } )
2024-01-09 16:00:07 +00:00
cmd , _ := s . cmdTraefik ( withConfigFile ( file ) )
2017-09-26 08:22:03 +00:00
// Wait for Traefik to turn ready.
2024-01-09 16:00:07 +00:00
err := try . GetRequest ( "http://127.0.0.1:8000/" , 2 * time . Second , try . StatusCodeIs ( http . StatusNotFound ) )
require . NoError ( s . T ( ) , err )
2017-09-26 08:22:03 +00:00
// Make sure exposed service is ready.
err = try . GetRequest ( "http://127.0.0.1:8000/service" , 3 * time . Second , try . StatusCodeIs ( http . StatusOK ) )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2017-09-26 08:22:03 +00:00
2018-03-22 17:18:03 +00:00
// Check that /ping endpoint is responding with 200.
err = try . GetRequest ( "http://127.0.0.1:8001/ping" , 3 * time . Second , try . StatusCodeIs ( http . StatusOK ) )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2018-03-22 17:18:03 +00:00
2017-09-26 08:22:03 +00:00
// Send SIGTERM to Traefik.
proc , err := os . FindProcess ( cmd . Process . Pid )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2017-09-26 08:22:03 +00:00
err = proc . Signal ( syscall . SIGTERM )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2017-09-26 08:22:03 +00:00
// Give Traefik time to process the SIGTERM and send a request half-way
// into the request accepting grace period, by which requests should
// still get served.
time . Sleep ( 5 * time . Second )
resp , err := http . Get ( "http://127.0.0.1:8000/service" )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2017-09-26 08:22:03 +00:00
defer resp . Body . Close ( )
2024-01-09 16:00:07 +00:00
assert . Equal ( s . T ( ) , http . StatusOK , resp . StatusCode )
2017-09-26 08:22:03 +00:00
2018-03-22 17:18:03 +00:00
// ping endpoint should now return a Service Unavailable.
resp , err = http . Get ( "http://127.0.0.1:8001/ping" )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2018-03-22 17:18:03 +00:00
defer resp . Body . Close ( )
2024-01-09 16:00:07 +00:00
assert . Equal ( s . T ( ) , http . StatusServiceUnavailable , resp . StatusCode )
2018-03-22 17:18:03 +00:00
2017-09-26 08:22:03 +00:00
// Expect Traefik to shut down gracefully once the request accepting grace
// period has elapsed.
waitErr := make ( chan error )
go func ( ) {
waitErr <- cmd . Wait ( )
} ( )
select {
case err := <- waitErr :
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2017-09-26 08:22:03 +00:00
case <- time . After ( 10 * time . Second ) :
// By now we are ~5 seconds out of the request accepting grace period
// (start + 5 seconds sleep prior to the mid-grace period request +
// 10 seconds timeout = 15 seconds > 10 seconds grace period).
// Something must have gone wrong if we still haven't terminated at
// this point.
2024-01-09 16:00:07 +00:00
s . T ( ) . Fatal ( "Traefik did not terminate in time" )
2017-09-26 08:22:03 +00:00
}
}
2017-11-09 15:12:04 +00:00
2024-01-09 16:00:07 +00:00
func ( s * SimpleSuite ) TestCustomPingTerminationStatusCode ( ) {
file := s . adaptFile ( "fixtures/custom_ping_termination_status_code.toml" , struct { } { } )
cmd , _ := s . cmdTraefik ( withConfigFile ( file ) )
2020-07-01 12:40:04 +00:00
// Wait for Traefik to turn ready.
2024-01-09 16:00:07 +00:00
err := try . GetRequest ( "http://127.0.0.1:8001/" , 2 * time . Second , try . StatusCodeIs ( http . StatusNotFound ) )
require . NoError ( s . T ( ) , err )
2020-07-01 12:40:04 +00:00
// Check that /ping endpoint is responding with 200.
err = try . GetRequest ( "http://127.0.0.1:8001/ping" , 3 * time . Second , try . StatusCodeIs ( http . StatusOK ) )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2020-07-01 12:40:04 +00:00
// Send SIGTERM to Traefik.
proc , err := os . FindProcess ( cmd . Process . Pid )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2020-07-01 12:40:04 +00:00
err = proc . Signal ( syscall . SIGTERM )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2020-07-01 12:40:04 +00:00
// ping endpoint should now return a Service Unavailable.
err = try . GetRequest ( "http://127.0.0.1:8001/ping" , 2 * time . Second , try . StatusCodeIs ( http . StatusNoContent ) )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2020-07-01 12:40:04 +00:00
}
2024-01-09 16:00:07 +00:00
func ( s * SimpleSuite ) TestStatsWithMultipleEntryPoint ( ) {
s . T ( ) . Skip ( "Stats is missing" )
s . createComposeProject ( "stats" )
2017-11-30 11:18:03 +00:00
2024-01-09 16:00:07 +00:00
s . composeUp ( )
defer s . composeDown ( )
2021-11-25 10:10:06 +00:00
2024-01-09 16:00:07 +00:00
whoami1URL := "http://" + net . JoinHostPort ( s . getComposeServiceIP ( "whoami1" ) , "80" )
whoami2URL := "http://" + net . JoinHostPort ( s . getComposeServiceIP ( "whoami2" ) , "80" )
2017-11-30 11:18:03 +00:00
2024-01-09 16:00:07 +00:00
file := s . adaptFile ( "fixtures/simple_stats.toml" , struct {
2017-11-30 11:18:03 +00:00
Server1 string
Server2 string
2021-11-25 10:10:06 +00:00
} { whoami1URL , whoami2URL } )
2024-01-09 16:00:07 +00:00
s . traefikCmd ( withConfigFile ( file ) )
2017-11-30 11:18:03 +00:00
2024-01-09 16:00:07 +00:00
err := try . GetRequest ( "http://127.0.0.1:8080/api" , 1 * time . Second , try . StatusCodeIs ( http . StatusOK ) )
require . NoError ( s . T ( ) , err )
2017-11-30 11:18:03 +00:00
2019-05-16 08:58:06 +00:00
err = try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 1 * time . Second , try . BodyContains ( "PathPrefix" ) )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2017-11-30 11:18:03 +00:00
err = try . GetRequest ( "http://127.0.0.1:8000/whoami" , 1 * time . Second , try . StatusCodeIs ( http . StatusOK ) )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2017-11-30 11:18:03 +00:00
err = try . GetRequest ( "http://127.0.0.1:8080/whoami" , 1 * time . Second , try . StatusCodeIs ( http . StatusOK ) )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2017-11-30 11:18:03 +00:00
err = try . GetRequest ( "http://127.0.0.1:8080/health" , 1 * time . Second , try . BodyContains ( ` "total_status_code_count": { "200":2} ` ) )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2017-11-30 11:18:03 +00:00
}
2024-01-09 16:00:07 +00:00
func ( s * SimpleSuite ) TestNoAuthOnPing ( ) {
s . T ( ) . Skip ( "Waiting for new api handler implementation" )
2021-11-25 10:10:06 +00:00
2024-01-09 16:00:07 +00:00
s . createComposeProject ( "base" )
2017-11-09 15:12:04 +00:00
2024-01-09 16:00:07 +00:00
s . composeUp ( )
defer s . composeDown ( )
2017-11-09 15:12:04 +00:00
2024-01-09 16:00:07 +00:00
file := s . adaptFile ( "./fixtures/simple_auth.toml" , struct { } { } )
s . traefikCmd ( withConfigFile ( file ) )
2017-11-09 15:12:04 +00:00
2024-01-09 16:00:07 +00:00
err := try . GetRequest ( "http://127.0.0.1:8001/api/rawdata" , 2 * time . Second , try . StatusCodeIs ( http . StatusUnauthorized ) )
require . NoError ( s . T ( ) , err )
2017-11-09 15:12:04 +00:00
err = try . GetRequest ( "http://127.0.0.1:8001/ping" , 1 * time . Second , try . StatusCodeIs ( http . StatusOK ) )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2017-11-09 15:12:04 +00:00
}
2024-01-09 16:00:07 +00:00
func ( s * SimpleSuite ) TestDefaultEntryPointHTTP ( ) {
s . createComposeProject ( "base" )
2021-11-25 10:10:06 +00:00
2024-01-09 16:00:07 +00:00
s . composeUp ( )
defer s . composeDown ( )
2017-11-30 15:10:02 +00:00
2024-01-09 16:00:07 +00:00
s . traefikCmd ( "--entryPoints.http.Address=:8000" , "--log.level=DEBUG" , "--providers.docker" , "--api.insecure" )
2017-11-30 15:10:02 +00:00
2024-01-09 16:00:07 +00:00
err := try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 1 * time . Second , try . BodyContains ( "PathPrefix" ) )
require . NoError ( s . T ( ) , err )
2017-11-30 15:10:02 +00:00
err = try . GetRequest ( "http://127.0.0.1:8000/whoami" , 1 * time . Second , try . StatusCodeIs ( http . StatusOK ) )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2017-11-30 15:10:02 +00:00
}
2024-01-09 16:00:07 +00:00
func ( s * SimpleSuite ) TestWithNonExistingEntryPoint ( ) {
s . createComposeProject ( "base" )
2017-11-30 15:10:02 +00:00
2024-01-09 16:00:07 +00:00
s . composeUp ( )
defer s . composeDown ( )
2017-11-30 15:10:02 +00:00
2024-01-09 16:00:07 +00:00
s . traefikCmd ( "--entryPoints.http.Address=:8000" , "--log.level=DEBUG" , "--providers.docker" , "--api.insecure" )
2017-11-30 15:10:02 +00:00
2024-01-09 16:00:07 +00:00
err := try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 1 * time . Second , try . BodyContains ( "PathPrefix" ) )
require . NoError ( s . T ( ) , err )
2017-11-30 15:10:02 +00:00
err = try . GetRequest ( "http://127.0.0.1:8000/whoami" , 1 * time . Second , try . StatusCodeIs ( http . StatusOK ) )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2017-11-30 15:10:02 +00:00
}
2018-01-19 13:30:04 +00:00
2024-01-09 16:00:07 +00:00
func ( s * SimpleSuite ) TestMetricsPrometheusDefaultEntryPoint ( ) {
s . createComposeProject ( "base" )
2021-11-25 10:10:06 +00:00
2024-01-09 16:00:07 +00:00
s . composeUp ( )
defer s . composeDown ( )
2018-01-19 13:30:04 +00:00
2024-01-09 16:00:07 +00:00
s . traefikCmd ( "--entryPoints.http.Address=:8000" , "--api.insecure" , "--metrics.prometheus.buckets=0.1,0.3,1.2,5.0" , "--providers.docker" , "--metrics.prometheus.addrouterslabels=true" , "--log.level=DEBUG" )
2018-01-19 13:30:04 +00:00
2024-01-09 16:00:07 +00:00
err := try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 1 * time . Second , try . BodyContains ( "PathPrefix(`/whoami`)" ) )
require . NoError ( s . T ( ) , err )
2018-01-19 13:30:04 +00:00
err = try . GetRequest ( "http://127.0.0.1:8000/whoami" , 1 * time . Second , try . StatusCodeIs ( http . StatusOK ) )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2018-01-19 13:30:04 +00:00
err = try . GetRequest ( "http://127.0.0.1:8080/metrics" , 1 * time . Second , try . StatusCodeIs ( http . StatusOK ) )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2021-04-30 08:22:04 +00:00
err = try . GetRequest ( "http://127.0.0.1:8080/metrics" , 1 * time . Second , try . BodyContains ( "_router_" ) )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2021-04-30 08:22:04 +00:00
err = try . GetRequest ( "http://127.0.0.1:8080/metrics" , 1 * time . Second , try . BodyContains ( "_entrypoint_" ) )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2021-04-30 08:22:04 +00:00
err = try . GetRequest ( "http://127.0.0.1:8080/metrics" , 1 * time . Second , try . BodyContains ( "_service_" ) )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2024-01-30 15:28:05 +00:00
// No metrics for internals.
err = try . GetRequest ( "http://127.0.0.1:8080/metrics" , 1 * time . Second , try . BodyNotContains ( "router=\"api@internal\"" , "service=\"api@internal\"" ) )
require . NoError ( s . T ( ) , err )
2021-04-30 08:22:04 +00:00
}
2024-01-09 16:00:07 +00:00
func ( s * SimpleSuite ) TestMetricsPrometheusTwoRoutersOneService ( ) {
s . createComposeProject ( "base" )
2021-04-30 08:22:04 +00:00
2024-01-09 16:00:07 +00:00
s . composeUp ( )
defer s . composeDown ( )
2021-04-30 08:22:04 +00:00
2024-01-09 16:00:07 +00:00
s . traefikCmd ( "--entryPoints.http.Address=:8000" , "--api.insecure" , "--metrics.prometheus.buckets=0.1,0.3,1.2,5.0" , "--providers.docker" , "--metrics.prometheus.addentrypointslabels=false" , "--metrics.prometheus.addrouterslabels=true" , "--log.level=DEBUG" )
2021-04-30 08:22:04 +00:00
2024-01-09 16:00:07 +00:00
err := try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 1 * time . Second , try . BodyContains ( "PathPrefix" ) )
require . NoError ( s . T ( ) , err )
2021-04-30 08:22:04 +00:00
err = try . GetRequest ( "http://127.0.0.1:8000/whoami" , 1 * time . Second , try . StatusCodeIs ( http . StatusOK ) )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2021-04-30 08:22:04 +00:00
err = try . GetRequest ( "http://127.0.0.1:8000/whoami2" , 1 * time . Second , try . StatusCodeIs ( http . StatusOK ) )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2021-04-30 08:22:04 +00:00
2021-09-15 15:26:06 +00:00
// adding a loop to test if metrics are not deleted
2024-02-19 14:44:03 +00:00
for range 10 {
2021-09-15 15:26:06 +00:00
request , err := http . NewRequest ( http . MethodGet , "http://127.0.0.1:8080/metrics" , nil )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2021-04-30 08:22:04 +00:00
2021-09-15 15:26:06 +00:00
response , err := http . DefaultClient . Do ( request )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
assert . Equal ( s . T ( ) , http . StatusOK , response . StatusCode )
2021-04-30 08:22:04 +00:00
2021-09-15 15:26:06 +00:00
body , err := io . ReadAll ( response . Body )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2021-04-30 08:22:04 +00:00
2021-09-15 15:26:06 +00:00
// Reqs count of 1 for both routers
2024-01-09 16:00:07 +00:00
assert . Contains ( s . T ( ) , string ( body ) , "traefik_router_requests_total{code=\"200\",method=\"GET\",protocol=\"http\",router=\"router1@docker\",service=\"whoami1@docker\"} 1" )
assert . Contains ( s . T ( ) , string ( body ) , "traefik_router_requests_total{code=\"200\",method=\"GET\",protocol=\"http\",router=\"router2@docker\",service=\"whoami1@docker\"} 1" )
2021-09-15 15:26:06 +00:00
// Reqs count of 2 for service behind both routers
2024-01-09 16:00:07 +00:00
assert . Contains ( s . T ( ) , string ( body ) , "traefik_service_requests_total{code=\"200\",method=\"GET\",protocol=\"http\",service=\"whoami1@docker\"} 2" )
2021-09-15 15:26:06 +00:00
}
2018-01-19 13:30:04 +00:00
}
2018-02-16 10:04:04 +00:00
2022-10-27 14:08:06 +00:00
// TestMetricsWithBufferingMiddleware checks that the buffering middleware
// (which introduces its own response writer in the chain), does not interfere with
// the capture middleware on which the metrics mechanism relies.
2024-01-09 16:00:07 +00:00
func ( s * SimpleSuite ) TestMetricsWithBufferingMiddleware ( ) {
s . createComposeProject ( "base" )
2022-10-27 14:08:06 +00:00
2024-01-09 16:00:07 +00:00
s . composeUp ( )
defer s . composeDown ( )
2022-10-27 14:08:06 +00:00
server := httptest . NewUnstartedServer ( http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
w . WriteHeader ( http . StatusOK )
_ , _ = w . Write ( [ ] byte ( "MORE THAN TEN BYTES IN RESPONSE" ) )
} ) )
server . Start ( )
defer server . Close ( )
2024-01-09 16:00:07 +00:00
file := s . adaptFile ( "fixtures/simple_metrics_with_buffer_middleware.toml" , struct { IP string } { IP : strings . TrimPrefix ( server . URL , "http://" ) } )
2022-10-27 14:08:06 +00:00
2024-01-09 16:00:07 +00:00
s . traefikCmd ( withConfigFile ( file ) )
2022-10-27 14:08:06 +00:00
2024-01-09 16:00:07 +00:00
err := try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 1 * time . Second , try . BodyContains ( "PathPrefix(`/without`)" ) )
require . NoError ( s . T ( ) , err )
2022-10-27 14:08:06 +00:00
err = try . GetRequest ( "http://127.0.0.1:8001/without" , 1 * time . Second , try . StatusCodeIs ( http . StatusOK ) )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2022-10-27 14:08:06 +00:00
req , err := http . NewRequest ( http . MethodGet , "http://127.0.0.1:8002/with-req" , strings . NewReader ( "MORE THAN TEN BYTES IN REQUEST" ) )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2022-10-27 14:08:06 +00:00
// The request should fail because the body is too large.
err = try . Request ( req , 1 * time . Second , try . StatusCodeIs ( http . StatusRequestEntityTooLarge ) )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2022-10-27 14:08:06 +00:00
// The request should fail because the response exceeds the configured limit.
err = try . GetRequest ( "http://127.0.0.1:8003/with-resp" , 1 * time . Second , try . StatusCodeIs ( http . StatusInternalServerError ) )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2022-10-27 14:08:06 +00:00
request , err := http . NewRequest ( http . MethodGet , "http://127.0.0.1:8080/metrics" , nil )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2022-10-27 14:08:06 +00:00
response , err := http . DefaultClient . Do ( request )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
assert . Equal ( s . T ( ) , http . StatusOK , response . StatusCode )
2022-10-27 14:08:06 +00:00
body , err := io . ReadAll ( response . Body )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2022-10-27 14:08:06 +00:00
// For allowed requests and responses, the entrypoint and service metrics have the same status code.
2024-01-09 16:00:07 +00:00
assert . Contains ( s . T ( ) , string ( body ) , "traefik_entrypoint_requests_total{code=\"200\",entrypoint=\"webA\",method=\"GET\",protocol=\"http\"} 1" )
assert . Contains ( s . T ( ) , string ( body ) , "traefik_entrypoint_requests_bytes_total{code=\"200\",entrypoint=\"webA\",method=\"GET\",protocol=\"http\"} 0" )
assert . Contains ( s . T ( ) , string ( body ) , "traefik_entrypoint_responses_bytes_total{code=\"200\",entrypoint=\"webA\",method=\"GET\",protocol=\"http\"} 31" )
2022-10-27 14:08:06 +00:00
2024-01-09 16:00:07 +00:00
assert . Contains ( s . T ( ) , string ( body ) , "traefik_service_requests_total{code=\"200\",method=\"GET\",protocol=\"http\",service=\"service-without@file\"} 1" )
assert . Contains ( s . T ( ) , string ( body ) , "traefik_service_requests_bytes_total{code=\"200\",method=\"GET\",protocol=\"http\",service=\"service-without@file\"} 0" )
assert . Contains ( s . T ( ) , string ( body ) , "traefik_service_responses_bytes_total{code=\"200\",method=\"GET\",protocol=\"http\",service=\"service-without@file\"} 31" )
2022-10-27 14:08:06 +00:00
// For forbidden requests, the entrypoints have metrics, the services don't.
2024-01-09 16:00:07 +00:00
assert . Contains ( s . T ( ) , string ( body ) , "traefik_entrypoint_requests_total{code=\"413\",entrypoint=\"webB\",method=\"GET\",protocol=\"http\"} 1" )
assert . Contains ( s . T ( ) , string ( body ) , "traefik_entrypoint_requests_bytes_total{code=\"413\",entrypoint=\"webB\",method=\"GET\",protocol=\"http\"} 0" )
assert . Contains ( s . T ( ) , string ( body ) , "traefik_entrypoint_responses_bytes_total{code=\"413\",entrypoint=\"webB\",method=\"GET\",protocol=\"http\"} 24" )
2022-10-27 14:08:06 +00:00
// For disallowed responses, the entrypoint and service metrics don't have the same status code.
2024-01-09 16:00:07 +00:00
assert . Contains ( s . T ( ) , string ( body ) , "traefik_entrypoint_requests_bytes_total{code=\"500\",entrypoint=\"webC\",method=\"GET\",protocol=\"http\"} 0" )
assert . Contains ( s . T ( ) , string ( body ) , "traefik_entrypoint_requests_total{code=\"500\",entrypoint=\"webC\",method=\"GET\",protocol=\"http\"} 1" )
assert . Contains ( s . T ( ) , string ( body ) , "traefik_entrypoint_responses_bytes_total{code=\"500\",entrypoint=\"webC\",method=\"GET\",protocol=\"http\"} 21" )
2022-10-27 14:08:06 +00:00
2024-01-09 16:00:07 +00:00
assert . Contains ( s . T ( ) , string ( body ) , "traefik_service_requests_bytes_total{code=\"200\",method=\"GET\",protocol=\"http\",service=\"service-resp@file\"} 0" )
assert . Contains ( s . T ( ) , string ( body ) , "traefik_service_requests_total{code=\"200\",method=\"GET\",protocol=\"http\",service=\"service-resp@file\"} 1" )
assert . Contains ( s . T ( ) , string ( body ) , "traefik_service_responses_bytes_total{code=\"200\",method=\"GET\",protocol=\"http\",service=\"service-resp@file\"} 31" )
2022-10-27 14:08:06 +00:00
}
2024-01-09 16:00:07 +00:00
func ( s * SimpleSuite ) TestMultipleProviderSameBackendName ( ) {
s . createComposeProject ( "base" )
2021-11-25 10:10:06 +00:00
2024-01-09 16:00:07 +00:00
s . composeUp ( )
defer s . composeDown ( )
2018-08-24 14:20:03 +00:00
2024-01-09 16:00:07 +00:00
whoami1IP := s . getComposeServiceIP ( "whoami1" )
whoami2IP := s . getComposeServiceIP ( "whoami2" )
file := s . adaptFile ( "fixtures/multiple_provider.toml" , struct { IP string } { IP : whoami2IP } )
2018-02-16 10:04:04 +00:00
2024-01-09 16:00:07 +00:00
s . traefikCmd ( withConfigFile ( file ) )
2018-02-16 10:04:04 +00:00
2024-01-09 16:00:07 +00:00
err := try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 1 * time . Second , try . BodyContains ( "PathPrefix" ) )
require . NoError ( s . T ( ) , err )
2018-02-16 10:04:04 +00:00
2021-11-25 10:10:06 +00:00
err = try . GetRequest ( "http://127.0.0.1:8000/whoami" , 1 * time . Second , try . BodyContains ( whoami1IP ) )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2018-02-16 10:04:04 +00:00
2021-11-25 10:10:06 +00:00
err = try . GetRequest ( "http://127.0.0.1:8000/file" , 1 * time . Second , try . BodyContains ( whoami2IP ) )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2018-02-16 10:04:04 +00:00
}
2018-08-24 14:20:03 +00:00
2024-01-09 16:00:07 +00:00
func ( s * SimpleSuite ) TestIPStrategyAllowlist ( ) {
s . createComposeProject ( "allowlist" )
2021-11-25 10:10:06 +00:00
2024-01-09 16:00:07 +00:00
s . composeUp ( )
defer s . composeDown ( )
2018-08-24 14:20:03 +00:00
2024-01-09 16:00:07 +00:00
s . traefikCmd ( withConfigFile ( "fixtures/simple_allowlist.toml" ) )
2018-08-24 14:20:03 +00:00
2024-01-09 16:00:07 +00:00
err := try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 2 * time . Second , try . BodyContains ( "override" ) )
require . NoError ( s . T ( ) , err )
2019-01-18 14:18:04 +00:00
2022-10-26 15:16:05 +00:00
err = try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 2 * time . Second , try . BodyContains ( "override.remoteaddr.allowlist.docker.local" ) )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2018-08-24 14:20:03 +00:00
testCases := [ ] struct {
desc string
xForwardedFor string
host string
expectedStatusCode int
} {
{
desc : "override remote addr reject" ,
xForwardedFor : "8.8.8.8,8.8.8.8" ,
2022-10-26 15:16:05 +00:00
host : "override.remoteaddr.allowlist.docker.local" ,
2018-08-24 14:20:03 +00:00
expectedStatusCode : 403 ,
} ,
{
desc : "override depth accept" ,
xForwardedFor : "8.8.8.8,10.0.0.1,127.0.0.1" ,
2022-10-26 15:16:05 +00:00
host : "override.depth.allowlist.docker.local" ,
2018-08-24 14:20:03 +00:00
expectedStatusCode : 200 ,
} ,
{
desc : "override depth reject" ,
xForwardedFor : "10.0.0.1,8.8.8.8,127.0.0.1" ,
2022-10-26 15:16:05 +00:00
host : "override.depth.allowlist.docker.local" ,
2018-08-24 14:20:03 +00:00
expectedStatusCode : 403 ,
} ,
{
desc : "override excludedIPs reject" ,
xForwardedFor : "10.0.0.3,10.0.0.1,10.0.0.2" ,
2022-10-26 15:16:05 +00:00
host : "override.excludedips.allowlist.docker.local" ,
2018-08-24 14:20:03 +00:00
expectedStatusCode : 403 ,
} ,
{
desc : "override excludedIPs accept" ,
xForwardedFor : "8.8.8.8,10.0.0.1,10.0.0.2" ,
2022-10-26 15:16:05 +00:00
host : "override.excludedips.allowlist.docker.local" ,
2018-08-24 14:20:03 +00:00
expectedStatusCode : 200 ,
} ,
}
for _ , test := range testCases {
req := httptest . NewRequest ( http . MethodGet , "http://127.0.0.1:8000" , nil )
req . Header . Set ( "X-Forwarded-For" , test . xForwardedFor )
req . Host = test . host
req . RequestURI = ""
err = try . Request ( req , 1 * time . Second , try . StatusCodeIs ( test . expectedStatusCode ) )
2024-01-09 16:00:07 +00:00
require . NoErrorf ( s . T ( ) , err , "Error during %s: %v" , test . desc , err )
2018-08-24 14:20:03 +00:00
}
}
2018-10-30 11:34:00 +00:00
2024-01-11 09:40:06 +00:00
func ( s * SimpleSuite ) TestIPStrategyWhitelist ( ) {
s . createComposeProject ( "whitelist" )
s . composeUp ( )
defer s . composeDown ( )
s . traefikCmd ( withConfigFile ( "fixtures/simple_whitelist.toml" ) )
err := try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 2 * time . Second , try . BodyContains ( "override" ) )
require . NoError ( s . T ( ) , err )
err = try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 2 * time . Second , try . BodyContains ( "override.remoteaddr.whitelist.docker.local" ) )
require . NoError ( s . T ( ) , err )
testCases := [ ] struct {
desc string
xForwardedFor string
host string
expectedStatusCode int
} {
{
desc : "override remote addr reject" ,
xForwardedFor : "8.8.8.8,8.8.8.8" ,
host : "override.remoteaddr.whitelist.docker.local" ,
expectedStatusCode : 403 ,
} ,
{
desc : "override depth accept" ,
xForwardedFor : "8.8.8.8,10.0.0.1,127.0.0.1" ,
host : "override.depth.whitelist.docker.local" ,
expectedStatusCode : 200 ,
} ,
{
desc : "override depth reject" ,
xForwardedFor : "10.0.0.1,8.8.8.8,127.0.0.1" ,
host : "override.depth.whitelist.docker.local" ,
expectedStatusCode : 403 ,
} ,
{
desc : "override excludedIPs reject" ,
xForwardedFor : "10.0.0.3,10.0.0.1,10.0.0.2" ,
host : "override.excludedips.whitelist.docker.local" ,
expectedStatusCode : 403 ,
} ,
{
desc : "override excludedIPs accept" ,
xForwardedFor : "8.8.8.8,10.0.0.1,10.0.0.2" ,
host : "override.excludedips.whitelist.docker.local" ,
expectedStatusCode : 200 ,
} ,
}
for _ , test := range testCases {
req := httptest . NewRequest ( http . MethodGet , "http://127.0.0.1:8000" , nil )
req . Header . Set ( "X-Forwarded-For" , test . xForwardedFor )
req . Host = test . host
req . RequestURI = ""
err = try . Request ( req , 1 * time . Second , try . StatusCodeIs ( test . expectedStatusCode ) )
require . NoErrorf ( s . T ( ) , err , "Error during %s: %v" , test . desc , err )
}
}
2024-01-09 16:00:07 +00:00
func ( s * SimpleSuite ) TestXForwardedHeaders ( ) {
2024-01-10 09:47:44 +00:00
s . createComposeProject ( "allowlist" )
2019-04-02 14:56:05 +00:00
2024-01-09 16:00:07 +00:00
s . composeUp ( )
defer s . composeDown ( )
2019-04-02 14:56:05 +00:00
2024-01-10 09:47:44 +00:00
s . traefikCmd ( withConfigFile ( "fixtures/simple_allowlist.toml" ) )
2019-04-02 14:56:05 +00:00
2024-01-09 16:00:07 +00:00
err := try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 2 * time . Second ,
2022-10-26 15:16:05 +00:00
try . BodyContains ( "override.remoteaddr.allowlist.docker.local" ) )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2019-04-02 14:56:05 +00:00
req , err := http . NewRequest ( http . MethodGet , "http://127.0.0.1:8000" , nil )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2019-04-02 14:56:05 +00:00
2022-10-26 15:16:05 +00:00
req . Host = "override.depth.allowlist.docker.local"
2019-04-02 14:56:05 +00:00
req . Header . Set ( "X-Forwarded-For" , "8.8.8.8,10.0.0.1,127.0.0.1" )
err = try . Request ( req , 1 * time . Second ,
try . StatusCodeIs ( http . StatusOK ) ,
try . BodyContains ( "X-Forwarded-Proto" , "X-Forwarded-For" , "X-Forwarded-Host" ,
"X-Forwarded-Host" , "X-Forwarded-Port" , "X-Forwarded-Server" , "X-Real-Ip" ) )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2019-04-02 14:56:05 +00:00
}
2024-01-09 16:00:07 +00:00
func ( s * SimpleSuite ) TestMultiProvider ( ) {
s . createComposeProject ( "base" )
2019-01-15 13:28:04 +00:00
2024-01-09 16:00:07 +00:00
s . composeUp ( )
defer s . composeDown ( )
2019-01-15 13:28:04 +00:00
2024-01-09 16:00:07 +00:00
whoamiURL := "http://" + net . JoinHostPort ( s . getComposeServiceIP ( "whoami1" ) , "80" )
2021-11-25 10:10:06 +00:00
2024-01-09 16:00:07 +00:00
file := s . adaptFile ( "fixtures/multiprovider.toml" , struct { Server string } { Server : whoamiURL } )
2019-01-15 13:28:04 +00:00
2024-01-09 16:00:07 +00:00
s . traefikCmd ( withConfigFile ( file ) )
2019-01-15 13:28:04 +00:00
2024-01-09 16:00:07 +00:00
err := try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 1000 * time . Millisecond , try . BodyContains ( "service" ) )
require . NoError ( s . T ( ) , err )
2019-01-15 13:28:04 +00:00
2019-07-12 23:24:03 +00:00
config := dynamic . Configuration {
HTTP : & dynamic . HTTPConfiguration {
Routers : map [ string ] * dynamic . Router {
"router1" : {
EntryPoints : [ ] string { "web" } ,
Middlewares : [ ] string { "customheader@file" } ,
Service : "service@file" ,
Rule : "PathPrefix(`/`)" ,
} ,
2019-01-15 13:28:04 +00:00
} ,
} ,
}
2019-07-15 08:22:03 +00:00
jsonContent , err := json . Marshal ( config )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2019-01-15 13:28:04 +00:00
2019-07-15 08:22:03 +00:00
request , err := http . NewRequest ( http . MethodPut , "http://127.0.0.1:8080/api/providers/rest" , bytes . NewReader ( jsonContent ) )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2019-01-15 13:28:04 +00:00
response , err := http . DefaultClient . Do ( request )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
assert . Equal ( s . T ( ) , http . StatusOK , response . StatusCode )
2019-01-15 13:28:04 +00:00
2019-05-16 08:58:06 +00:00
err = try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 1000 * time . Millisecond , try . BodyContains ( "PathPrefix(`/`)" ) )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2019-01-15 13:28:04 +00:00
err = try . GetRequest ( "http://127.0.0.1:8000/" , 1 * time . Second , try . StatusCodeIs ( http . StatusOK ) , try . BodyContains ( "CustomValue" ) )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2019-01-15 13:28:04 +00:00
}
2019-05-06 15:16:03 +00:00
2024-01-09 16:00:07 +00:00
func ( s * SimpleSuite ) TestSimpleConfigurationHostRequestTrailingPeriod ( ) {
s . createComposeProject ( "base" )
2019-05-06 15:16:03 +00:00
2024-01-09 16:00:07 +00:00
s . composeUp ( )
defer s . composeDown ( )
2021-11-25 10:10:06 +00:00
2024-01-09 16:00:07 +00:00
whoamiURL := "http://" + net . JoinHostPort ( s . getComposeServiceIP ( "whoami1" ) , "80" )
2019-05-06 15:16:03 +00:00
2024-01-09 16:00:07 +00:00
file := s . adaptFile ( "fixtures/file/simple-hosts.toml" , struct { Server string } { Server : whoamiURL } )
2019-05-06 15:16:03 +00:00
2024-01-09 16:00:07 +00:00
s . traefikCmd ( withConfigFile ( file ) )
2019-05-06 15:16:03 +00:00
testCases := [ ] struct {
desc string
requestHost string
} {
{
desc : "Request host without trailing period, rule without trailing period" ,
requestHost : "test.localhost" ,
} ,
{
desc : "Request host with trailing period, rule without trailing period" ,
requestHost : "test.localhost." ,
} ,
{
desc : "Request host without trailing period, rule with trailing period" ,
requestHost : "test.foo.localhost" ,
} ,
{
desc : "Request host with trailing period, rule with trailing period" ,
requestHost : "test.foo.localhost." ,
} ,
}
for _ , test := range testCases {
req , err := http . NewRequest ( http . MethodGet , "http://127.0.0.1:8000" , nil )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2019-05-06 15:16:03 +00:00
req . Host = test . requestHost
err = try . Request ( req , 1 * time . Second , try . StatusCodeIs ( http . StatusOK ) )
2024-01-09 16:00:07 +00:00
require . NoErrorf ( s . T ( ) , err , "Error while testing %s: %v" , test . desc , err )
2019-05-06 15:16:03 +00:00
}
}
2019-07-15 15:04:04 +00:00
2024-01-23 10:34:05 +00:00
func ( s * SimpleSuite ) TestWithDefaultRuleSyntax ( ) {
file := s . adaptFile ( "fixtures/with_default_rule_syntax.toml" , struct { } { } )
s . traefikCmd ( withConfigFile ( file ) )
err := try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 1 * time . Second , try . BodyContains ( "PathPrefix" ) )
require . NoError ( s . T ( ) , err )
// router1 has no error
err = try . GetRequest ( "http://127.0.0.1:8080/api/http/routers/router1@file" , 1 * time . Second , try . BodyContains ( ` "status":"enabled" ` ) )
require . NoError ( s . T ( ) , err )
err = try . GetRequest ( "http://127.0.0.1:8000/notfound" , 1 * time . Second , try . StatusCodeIs ( http . StatusNotFound ) )
require . NoError ( s . T ( ) , err )
err = try . GetRequest ( "http://127.0.0.1:8000/foo" , 1 * time . Second , try . StatusCodeIs ( http . StatusServiceUnavailable ) )
require . NoError ( s . T ( ) , err )
err = try . GetRequest ( "http://127.0.0.1:8000/bar" , 1 * time . Second , try . StatusCodeIs ( http . StatusServiceUnavailable ) )
require . NoError ( s . T ( ) , err )
// router2 has an error because it uses the wrong rule syntax (v3 instead of v2)
err = try . GetRequest ( "http://127.0.0.1:8080/api/http/routers/router2@file" , 1 * time . Second , try . BodyContains ( "error while parsing rule QueryRegexp(`foo`, `bar`): unsupported function: QueryRegexp" ) )
require . NoError ( s . T ( ) , err )
// router3 has an error because it uses the wrong rule syntax (v2 instead of v3)
err = try . GetRequest ( "http://127.0.0.1:8080/api/http/routers/router3@file" , 1 * time . Second , try . BodyContains ( "error while adding rule PathPrefix: unexpected number of parameters; got 2, expected one of [1]" ) )
require . NoError ( s . T ( ) , err )
}
func ( s * SimpleSuite ) TestWithoutDefaultRuleSyntax ( ) {
file := s . adaptFile ( "fixtures/without_default_rule_syntax.toml" , struct { } { } )
s . traefikCmd ( withConfigFile ( file ) )
err := try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 1 * time . Second , try . BodyContains ( "PathPrefix" ) )
require . NoError ( s . T ( ) , err )
// router1 has no error
err = try . GetRequest ( "http://127.0.0.1:8080/api/http/routers/router1@file" , 1 * time . Second , try . BodyContains ( ` "status":"enabled" ` ) )
require . NoError ( s . T ( ) , err )
err = try . GetRequest ( "http://127.0.0.1:8000/notfound" , 1 * time . Second , try . StatusCodeIs ( http . StatusNotFound ) )
require . NoError ( s . T ( ) , err )
err = try . GetRequest ( "http://127.0.0.1:8000/foo" , 1 * time . Second , try . StatusCodeIs ( http . StatusServiceUnavailable ) )
require . NoError ( s . T ( ) , err )
err = try . GetRequest ( "http://127.0.0.1:8000/bar" , 1 * time . Second , try . StatusCodeIs ( http . StatusServiceUnavailable ) )
require . NoError ( s . T ( ) , err )
// router2 has an error because it uses the wrong rule syntax (v3 instead of v2)
err = try . GetRequest ( "http://127.0.0.1:8080/api/http/routers/router2@file" , 1 * time . Second , try . BodyContains ( "error while adding rule PathPrefix: unexpected number of parameters; got 2, expected one of [1]" ) )
require . NoError ( s . T ( ) , err )
// router2 has an error because it uses the wrong rule syntax (v2 instead of v3)
err = try . GetRequest ( "http://127.0.0.1:8080/api/http/routers/router3@file" , 1 * time . Second , try . BodyContains ( "error while parsing rule QueryRegexp(`foo`, `bar`): unsupported function: QueryRegexp" ) )
require . NoError ( s . T ( ) , err )
}
2024-01-09 16:00:07 +00:00
func ( s * SimpleSuite ) TestRouterConfigErrors ( ) {
file := s . adaptFile ( "fixtures/router_errors.toml" , struct { } { } )
2019-07-15 15:04:04 +00:00
2024-01-09 16:00:07 +00:00
s . traefikCmd ( withConfigFile ( file ) )
2019-07-15 15:04:04 +00:00
// All errors
2024-01-09 16:00:07 +00:00
err := try . GetRequest ( "http://127.0.0.1:8080/api/http/routers" , 1000 * time . Millisecond , try . BodyContains ( ` ["middleware \"unknown@file\" does not exist","found different TLS options for routers on the same host snitest.net, so using the default TLS options instead"] ` ) )
require . NoError ( s . T ( ) , err )
2019-07-15 15:04:04 +00:00
2019-08-29 10:38:04 +00:00
// router3 has an error because it uses an unknown entrypoint
err = try . GetRequest ( "http://127.0.0.1:8080/api/http/routers/router3@file" , 1000 * time . Millisecond , try . BodyContains ( ` entryPoint \"unknown-entrypoint\" doesn't exist ` , "no valid entryPoint for this router" ) )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2019-08-29 10:38:04 +00:00
2019-07-15 15:04:04 +00:00
// router4 is enabled, but in warning state because its tls options conf was messed up
err = try . GetRequest ( "http://127.0.0.1:8080/api/http/routers/router4@file" , 1000 * time . Millisecond , try . BodyContains ( ` "status":"warning" ` ) )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2019-07-15 15:04:04 +00:00
// router5 is disabled because its middleware conf is broken
err = try . GetRequest ( "http://127.0.0.1:8080/api/http/routers/router5@file" , 1000 * time . Millisecond , try . BodyContains ( ) )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2019-07-15 15:04:04 +00:00
}
2024-01-09 16:00:07 +00:00
func ( s * SimpleSuite ) TestServiceConfigErrors ( ) {
file := s . adaptFile ( "fixtures/service_errors.toml" , struct { } { } )
2019-07-15 15:04:04 +00:00
2024-01-09 16:00:07 +00:00
s . traefikCmd ( withConfigFile ( file ) )
2019-07-15 15:04:04 +00:00
2024-01-09 16:00:07 +00:00
err := try . GetRequest ( "http://127.0.0.1:8080/api/http/services" , 1000 * time . Millisecond , try . BodyContains ( ` ["the service \"service1@file\" does not have any type defined"] ` ) )
require . NoError ( s . T ( ) , err )
2019-07-15 15:04:04 +00:00
err = try . GetRequest ( "http://127.0.0.1:8080/api/http/services/service1@file" , 1000 * time . Millisecond , try . BodyContains ( ` "status":"disabled" ` ) )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2019-07-15 15:04:04 +00:00
err = try . GetRequest ( "http://127.0.0.1:8080/api/http/services/service2@file" , 1000 * time . Millisecond , try . BodyContains ( ` "status":"enabled" ` ) )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2019-07-15 15:04:04 +00:00
}
2019-08-26 08:30:05 +00:00
2024-01-09 16:00:07 +00:00
func ( s * SimpleSuite ) TestTCPRouterConfigErrors ( ) {
file := s . adaptFile ( "fixtures/router_errors.toml" , struct { } { } )
2020-02-11 00:26:04 +00:00
2024-01-09 16:00:07 +00:00
s . traefikCmd ( withConfigFile ( file ) )
2020-02-11 00:26:04 +00:00
// router3 has an error because it uses an unknown entrypoint
2024-01-09 16:00:07 +00:00
err := try . GetRequest ( "http://127.0.0.1:8080/api/tcp/routers/router3@file" , 1000 * time . Millisecond , try . BodyContains ( ` entryPoint \"unknown-entrypoint\" doesn't exist ` , "no valid entryPoint for this router" ) )
require . NoError ( s . T ( ) , err )
2020-02-11 00:26:04 +00:00
// router4 has an unsupported Rule
2022-03-17 17:02:08 +00:00
err = try . GetRequest ( "http://127.0.0.1:8080/api/tcp/routers/router4@file" , 1000 * time . Millisecond , try . BodyContains ( "invalid rule: \\\"Host(`mydomain.com`)\\\"" ) )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2020-02-11 00:26:04 +00:00
}
2024-01-09 16:00:07 +00:00
func ( s * SimpleSuite ) TestTCPServiceConfigErrors ( ) {
file := s . adaptFile ( "fixtures/tcp/service_errors.toml" , struct { } { } )
2020-02-11 00:26:04 +00:00
2024-01-09 16:00:07 +00:00
s . traefikCmd ( withConfigFile ( file ) )
2020-02-11 00:26:04 +00:00
2024-01-09 16:00:07 +00:00
err := try . GetRequest ( "http://127.0.0.1:8080/api/tcp/services" , 1000 * time . Millisecond , try . BodyContains ( ` ["the service \"service1@file\" does not have any type defined"] ` ) )
require . NoError ( s . T ( ) , err )
2020-02-11 00:26:04 +00:00
err = try . GetRequest ( "http://127.0.0.1:8080/api/tcp/services/service1@file" , 1000 * time . Millisecond , try . BodyContains ( ` "status":"disabled" ` ) )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2020-02-11 00:26:04 +00:00
err = try . GetRequest ( "http://127.0.0.1:8080/api/tcp/services/service2@file" , 1000 * time . Millisecond , try . BodyContains ( ` "status":"enabled" ` ) )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2020-02-11 00:26:04 +00:00
}
2024-01-09 16:00:07 +00:00
func ( s * SimpleSuite ) TestUDPRouterConfigErrors ( ) {
file := s . adaptFile ( "fixtures/router_errors.toml" , struct { } { } )
2020-02-11 00:26:04 +00:00
2024-01-09 16:00:07 +00:00
s . traefikCmd ( withConfigFile ( file ) )
2020-02-11 00:26:04 +00:00
2024-01-09 16:00:07 +00:00
err := try . GetRequest ( "http://127.0.0.1:8080/api/udp/routers/router3@file" , 1000 * time . Millisecond , try . BodyContains ( ` entryPoint \"unknown-entrypoint\" doesn't exist ` , "no valid entryPoint for this router" ) )
require . NoError ( s . T ( ) , err )
2020-02-11 00:26:04 +00:00
}
2024-01-09 16:00:07 +00:00
func ( s * SimpleSuite ) TestUDPServiceConfigErrors ( ) {
file := s . adaptFile ( "fixtures/udp/service_errors.toml" , struct { } { } )
2020-02-11 00:26:04 +00:00
2024-01-09 16:00:07 +00:00
s . traefikCmd ( withConfigFile ( file ) )
2020-02-11 00:26:04 +00:00
2024-01-10 09:47:44 +00:00
err := try . GetRequest ( "http://127.0.0.1:8080/api/udp/services" , 1000 * time . Millisecond , try . BodyContains ( ` ["the UDP service \"service1@file\" does not have any type defined"] ` ) )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2020-02-11 00:26:04 +00:00
err = try . GetRequest ( "http://127.0.0.1:8080/api/udp/services/service1@file" , 1000 * time . Millisecond , try . BodyContains ( ` "status":"disabled" ` ) )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2020-02-11 00:26:04 +00:00
err = try . GetRequest ( "http://127.0.0.1:8080/api/udp/services/service2@file" , 1000 * time . Millisecond , try . BodyContains ( ` "status":"enabled" ` ) )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2020-02-11 00:26:04 +00:00
}
2024-01-26 00:44:05 +00:00
func ( s * SimpleSuite ) TestWRRServer ( ) {
s . createComposeProject ( "base" )
s . composeUp ( )
defer s . composeDown ( )
whoami1IP := s . getComposeServiceIP ( "whoami1" )
whoami2IP := s . getComposeServiceIP ( "whoami2" )
file := s . adaptFile ( "fixtures/wrr_server.toml" , struct {
Server1 string
Server2 string
} { Server1 : "http://" + whoami1IP , Server2 : "http://" + whoami2IP } )
s . traefikCmd ( withConfigFile ( file ) )
err := try . GetRequest ( "http://127.0.0.1:8080/api/http/services" , 1000 * time . Millisecond , try . BodyContains ( "service1" ) )
require . NoError ( s . T ( ) , err )
repartition := map [ string ] int { }
2024-06-04 21:58:38 +00:00
for range 4 {
2024-01-26 00:44:05 +00:00
req , err := http . NewRequest ( http . MethodGet , "http://127.0.0.1:8000/whoami" , nil )
require . NoError ( s . T ( ) , err )
response , err := http . DefaultClient . Do ( req )
require . NoError ( s . T ( ) , err )
assert . Equal ( s . T ( ) , http . StatusOK , response . StatusCode )
body , err := io . ReadAll ( response . Body )
require . NoError ( s . T ( ) , err )
if strings . Contains ( string ( body ) , whoami1IP ) {
repartition [ whoami1IP ] ++
}
if strings . Contains ( string ( body ) , whoami2IP ) {
repartition [ whoami2IP ] ++
}
}
assert . Equal ( s . T ( ) , 3 , repartition [ whoami1IP ] )
assert . Equal ( s . T ( ) , 1 , repartition [ whoami2IP ] )
}
2024-01-09 16:00:07 +00:00
func ( s * SimpleSuite ) TestWRR ( ) {
s . createComposeProject ( "base" )
2019-08-26 08:30:05 +00:00
2024-01-09 16:00:07 +00:00
s . composeUp ( )
defer s . composeDown ( )
2021-11-25 10:10:06 +00:00
2024-01-09 16:00:07 +00:00
whoami1IP := s . getComposeServiceIP ( "whoami1" )
whoami2IP := s . getComposeServiceIP ( "whoami2" )
2019-08-26 08:30:05 +00:00
2024-01-09 16:00:07 +00:00
file := s . adaptFile ( "fixtures/wrr.toml" , struct {
2019-08-26 08:30:05 +00:00
Server1 string
Server2 string
2021-11-25 10:10:06 +00:00
} { Server1 : "http://" + whoami1IP , Server2 : "http://" + whoami2IP } )
2019-08-26 08:30:05 +00:00
2024-01-09 16:00:07 +00:00
s . traefikCmd ( withConfigFile ( file ) )
2019-08-26 08:30:05 +00:00
2024-01-09 16:00:07 +00:00
err := try . GetRequest ( "http://127.0.0.1:8080/api/http/services" , 1000 * time . Millisecond , try . BodyContains ( "service1" , "service2" ) )
require . NoError ( s . T ( ) , err )
2019-08-26 08:30:05 +00:00
repartition := map [ string ] int { }
2024-02-19 14:44:03 +00:00
for range 4 {
2019-08-26 08:30:05 +00:00
req , err := http . NewRequest ( http . MethodGet , "http://127.0.0.1:8000/whoami" , nil )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2019-08-26 08:30:05 +00:00
response , err := http . DefaultClient . Do ( req )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
assert . Equal ( s . T ( ) , http . StatusOK , response . StatusCode )
2019-08-26 08:30:05 +00:00
2021-03-04 19:08:03 +00:00
body , err := io . ReadAll ( response . Body )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2019-08-26 08:30:05 +00:00
2021-11-25 10:10:06 +00:00
if strings . Contains ( string ( body ) , whoami1IP ) {
repartition [ whoami1IP ] ++
2019-08-26 08:30:05 +00:00
}
2021-11-25 10:10:06 +00:00
if strings . Contains ( string ( body ) , whoami2IP ) {
repartition [ whoami2IP ] ++
2019-08-26 08:30:05 +00:00
}
}
2024-01-09 16:00:07 +00:00
assert . Equal ( s . T ( ) , 3 , repartition [ whoami1IP ] )
assert . Equal ( s . T ( ) , 1 , repartition [ whoami2IP ] )
2019-08-26 08:30:05 +00:00
}
2024-01-09 16:00:07 +00:00
func ( s * SimpleSuite ) TestWRRSticky ( ) {
s . createComposeProject ( "base" )
2019-08-26 08:30:05 +00:00
2024-01-09 16:00:07 +00:00
s . composeUp ( )
defer s . composeDown ( )
2021-11-25 10:10:06 +00:00
2024-01-09 16:00:07 +00:00
whoami1IP := s . getComposeServiceIP ( "whoami1" )
whoami2IP := s . getComposeServiceIP ( "whoami2" )
2019-08-26 08:30:05 +00:00
2024-01-09 16:00:07 +00:00
file := s . adaptFile ( "fixtures/wrr_sticky.toml" , struct {
2019-08-26 08:30:05 +00:00
Server1 string
Server2 string
2021-11-25 10:10:06 +00:00
} { Server1 : "http://" + whoami1IP , Server2 : "http://" + whoami2IP } )
2019-08-26 08:30:05 +00:00
2024-01-09 16:00:07 +00:00
s . traefikCmd ( withConfigFile ( file ) )
2019-08-26 08:30:05 +00:00
2024-01-09 16:00:07 +00:00
err := try . GetRequest ( "http://127.0.0.1:8080/api/http/services" , 1000 * time . Millisecond , try . BodyContains ( "service1" , "service2" ) )
require . NoError ( s . T ( ) , err )
2019-08-26 08:30:05 +00:00
repartition := map [ string ] int { }
req , err := http . NewRequest ( http . MethodGet , "http://127.0.0.1:8000/whoami" , nil )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2019-08-26 08:30:05 +00:00
2024-02-19 14:44:03 +00:00
for range 4 {
2019-08-26 08:30:05 +00:00
response , err := http . DefaultClient . Do ( req )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
assert . Equal ( s . T ( ) , http . StatusOK , response . StatusCode )
2019-08-26 08:30:05 +00:00
for _ , cookie := range response . Cookies ( ) {
req . AddCookie ( cookie )
}
2021-03-04 19:08:03 +00:00
body , err := io . ReadAll ( response . Body )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2019-08-26 08:30:05 +00:00
2021-11-25 10:10:06 +00:00
if strings . Contains ( string ( body ) , whoami1IP ) {
repartition [ whoami1IP ] ++
2019-08-26 08:30:05 +00:00
}
2021-11-25 10:10:06 +00:00
if strings . Contains ( string ( body ) , whoami2IP ) {
repartition [ whoami2IP ] ++
2019-08-26 08:30:05 +00:00
}
}
2024-01-09 16:00:07 +00:00
assert . Equal ( s . T ( ) , 4 , repartition [ whoami1IP ] )
assert . Equal ( s . T ( ) , 0 , repartition [ whoami2IP ] )
2019-08-26 08:30:05 +00:00
}
2019-08-26 17:00:04 +00:00
2024-01-09 16:00:07 +00:00
func ( s * SimpleSuite ) TestMirror ( ) {
2019-08-26 17:00:04 +00:00
var count , countMirror1 , countMirror2 int32
main := httptest . NewServer ( http . HandlerFunc ( func ( rw http . ResponseWriter , req * http . Request ) {
atomic . AddInt32 ( & count , 1 )
} ) )
mirror1 := httptest . NewServer ( http . HandlerFunc ( func ( rw http . ResponseWriter , req * http . Request ) {
atomic . AddInt32 ( & countMirror1 , 1 )
} ) )
mirror2 := httptest . NewServer ( http . HandlerFunc ( func ( rw http . ResponseWriter , req * http . Request ) {
atomic . AddInt32 ( & countMirror2 , 1 )
} ) )
mainServer := main . URL
mirror1Server := mirror1 . URL
mirror2Server := mirror2 . URL
2024-01-09 16:00:07 +00:00
file := s . adaptFile ( "fixtures/mirror.toml" , struct {
2019-08-26 17:00:04 +00:00
MainServer string
Mirror1Server string
Mirror2Server string
} { MainServer : mainServer , Mirror1Server : mirror1Server , Mirror2Server : mirror2Server } )
2024-01-09 16:00:07 +00:00
s . traefikCmd ( withConfigFile ( file ) )
2019-08-26 17:00:04 +00:00
2024-01-09 16:00:07 +00:00
err := try . GetRequest ( "http://127.0.0.1:8080/api/http/services" , 1000 * time . Millisecond , try . BodyContains ( "mirror1" , "mirror2" , "service1" ) )
require . NoError ( s . T ( ) , err )
2019-08-26 17:00:04 +00:00
req , err := http . NewRequest ( http . MethodGet , "http://127.0.0.1:8000/whoami" , nil )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2024-02-19 14:44:03 +00:00
for range 10 {
2019-08-26 17:00:04 +00:00
response , err := http . DefaultClient . Do ( req )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
assert . Equal ( s . T ( ) , http . StatusOK , response . StatusCode )
2019-08-26 17:00:04 +00:00
}
countTotal := atomic . LoadInt32 ( & count )
val1 := atomic . LoadInt32 ( & countMirror1 )
val2 := atomic . LoadInt32 ( & countMirror2 )
2024-01-09 16:00:07 +00:00
assert . Equal ( s . T ( ) , int32 ( 10 ) , countTotal )
assert . Equal ( s . T ( ) , int32 ( 1 ) , val1 )
assert . Equal ( s . T ( ) , int32 ( 5 ) , val2 )
2019-08-26 17:00:04 +00:00
}
2024-01-09 16:00:07 +00:00
func ( s * SimpleSuite ) TestMirrorWithBody ( ) {
2020-03-05 17:03:08 +00:00
var count , countMirror1 , countMirror2 int32
body20 := make ( [ ] byte , 20 )
_ , err := rand . Read ( body20 )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2020-03-05 17:03:08 +00:00
body5 := make ( [ ] byte , 5 )
_ , err = rand . Read ( body5 )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2020-03-05 17:03:08 +00:00
verifyBody := func ( req * http . Request ) {
2021-03-04 19:08:03 +00:00
b , _ := io . ReadAll ( req . Body )
2020-03-05 17:03:08 +00:00
switch req . Header . Get ( "Size" ) {
case "20" :
2024-01-09 16:00:07 +00:00
require . Equal ( s . T ( ) , body20 , b )
2020-03-05 17:03:08 +00:00
case "5" :
2024-01-09 16:00:07 +00:00
require . Equal ( s . T ( ) , body5 , b )
2020-03-05 17:03:08 +00:00
default :
2024-01-09 16:00:07 +00:00
s . T ( ) . Fatal ( "Size header not present" )
2020-03-05 17:03:08 +00:00
}
}
main := httptest . NewServer ( http . HandlerFunc ( func ( rw http . ResponseWriter , req * http . Request ) {
verifyBody ( req )
atomic . AddInt32 ( & count , 1 )
} ) )
mirror1 := httptest . NewServer ( http . HandlerFunc ( func ( rw http . ResponseWriter , req * http . Request ) {
verifyBody ( req )
atomic . AddInt32 ( & countMirror1 , 1 )
} ) )
mirror2 := httptest . NewServer ( http . HandlerFunc ( func ( rw http . ResponseWriter , req * http . Request ) {
verifyBody ( req )
atomic . AddInt32 ( & countMirror2 , 1 )
} ) )
mainServer := main . URL
mirror1Server := mirror1 . URL
mirror2Server := mirror2 . URL
2024-01-09 16:00:07 +00:00
file := s . adaptFile ( "fixtures/mirror.toml" , struct {
2020-03-05 17:03:08 +00:00
MainServer string
Mirror1Server string
Mirror2Server string
} { MainServer : mainServer , Mirror1Server : mirror1Server , Mirror2Server : mirror2Server } )
2024-01-09 16:00:07 +00:00
s . traefikCmd ( withConfigFile ( file ) )
2020-03-05 17:03:08 +00:00
err = try . GetRequest ( "http://127.0.0.1:8080/api/http/services" , 1000 * time . Millisecond , try . BodyContains ( "mirror1" , "mirror2" , "service1" ) )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2020-03-05 17:03:08 +00:00
req , err := http . NewRequest ( http . MethodGet , "http://127.0.0.1:8000/whoami" , bytes . NewBuffer ( body20 ) )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2020-03-05 17:03:08 +00:00
req . Header . Set ( "Size" , "20" )
2024-02-19 14:44:03 +00:00
for range 10 {
2020-03-05 17:03:08 +00:00
response , err := http . DefaultClient . Do ( req )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
assert . Equal ( s . T ( ) , http . StatusOK , response . StatusCode )
2020-03-05 17:03:08 +00:00
}
countTotal := atomic . LoadInt32 ( & count )
val1 := atomic . LoadInt32 ( & countMirror1 )
val2 := atomic . LoadInt32 ( & countMirror2 )
2024-01-09 16:00:07 +00:00
assert . Equal ( s . T ( ) , int32 ( 10 ) , countTotal )
assert . Equal ( s . T ( ) , int32 ( 1 ) , val1 )
assert . Equal ( s . T ( ) , int32 ( 5 ) , val2 )
2020-03-05 17:03:08 +00:00
atomic . StoreInt32 ( & count , 0 )
atomic . StoreInt32 ( & countMirror1 , 0 )
atomic . StoreInt32 ( & countMirror2 , 0 )
req , err = http . NewRequest ( http . MethodGet , "http://127.0.0.1:8000/whoamiWithMaxBody" , bytes . NewBuffer ( body5 ) )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2020-10-09 07:32:03 +00:00
req . Header . Set ( "Size" , "5" )
2024-02-19 14:44:03 +00:00
for range 10 {
2020-03-05 17:03:08 +00:00
response , err := http . DefaultClient . Do ( req )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
assert . Equal ( s . T ( ) , http . StatusOK , response . StatusCode )
2020-03-05 17:03:08 +00:00
}
countTotal = atomic . LoadInt32 ( & count )
val1 = atomic . LoadInt32 ( & countMirror1 )
val2 = atomic . LoadInt32 ( & countMirror2 )
2024-01-09 16:00:07 +00:00
assert . Equal ( s . T ( ) , int32 ( 10 ) , countTotal )
assert . Equal ( s . T ( ) , int32 ( 1 ) , val1 )
assert . Equal ( s . T ( ) , int32 ( 5 ) , val2 )
2020-03-05 17:03:08 +00:00
atomic . StoreInt32 ( & count , 0 )
atomic . StoreInt32 ( & countMirror1 , 0 )
atomic . StoreInt32 ( & countMirror2 , 0 )
req , err = http . NewRequest ( http . MethodGet , "http://127.0.0.1:8000/whoamiWithMaxBody" , bytes . NewBuffer ( body20 ) )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2020-10-09 07:32:03 +00:00
req . Header . Set ( "Size" , "20" )
2024-02-19 14:44:03 +00:00
for range 10 {
2020-03-05 17:03:08 +00:00
response , err := http . DefaultClient . Do ( req )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
assert . Equal ( s . T ( ) , http . StatusOK , response . StatusCode )
2020-03-05 17:03:08 +00:00
}
countTotal = atomic . LoadInt32 ( & count )
val1 = atomic . LoadInt32 ( & countMirror1 )
val2 = atomic . LoadInt32 ( & countMirror2 )
2024-01-09 16:00:07 +00:00
assert . Equal ( s . T ( ) , int32 ( 10 ) , countTotal )
assert . Equal ( s . T ( ) , int32 ( 0 ) , val1 )
assert . Equal ( s . T ( ) , int32 ( 0 ) , val2 )
2020-03-05 17:03:08 +00:00
}
2024-01-09 16:00:07 +00:00
func ( s * SimpleSuite ) TestMirrorCanceled ( ) {
2019-08-26 17:00:04 +00:00
var count , countMirror1 , countMirror2 int32
main := httptest . NewServer ( http . HandlerFunc ( func ( rw http . ResponseWriter , req * http . Request ) {
atomic . AddInt32 ( & count , 1 )
2019-11-26 20:38:03 +00:00
time . Sleep ( 2 * time . Second )
2019-08-26 17:00:04 +00:00
} ) )
mirror1 := httptest . NewServer ( http . HandlerFunc ( func ( rw http . ResponseWriter , req * http . Request ) {
atomic . AddInt32 ( & countMirror1 , 1 )
} ) )
mirror2 := httptest . NewServer ( http . HandlerFunc ( func ( rw http . ResponseWriter , req * http . Request ) {
atomic . AddInt32 ( & countMirror2 , 1 )
} ) )
mainServer := main . URL
mirror1Server := mirror1 . URL
mirror2Server := mirror2 . URL
2024-01-09 16:00:07 +00:00
file := s . adaptFile ( "fixtures/mirror.toml" , struct {
2019-08-26 17:00:04 +00:00
MainServer string
Mirror1Server string
Mirror2Server string
} { MainServer : mainServer , Mirror1Server : mirror1Server , Mirror2Server : mirror2Server } )
2024-01-09 16:00:07 +00:00
s . traefikCmd ( withConfigFile ( file ) )
2019-08-26 17:00:04 +00:00
2024-01-09 16:00:07 +00:00
err := try . GetRequest ( "http://127.0.0.1:8080/api/http/services" , 1000 * time . Millisecond , try . BodyContains ( "mirror1" , "mirror2" , "service1" ) )
require . NoError ( s . T ( ) , err )
2019-08-26 17:00:04 +00:00
2024-02-19 14:44:03 +00:00
for range 5 {
2019-08-26 17:00:04 +00:00
req , err := http . NewRequest ( http . MethodGet , "http://127.0.0.1:8000/whoami" , nil )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2019-08-26 17:00:04 +00:00
2019-09-10 15:52:04 +00:00
client := & http . Client {
Timeout : time . Second ,
}
_ , _ = client . Do ( req )
2019-08-26 17:00:04 +00:00
}
countTotal := atomic . LoadInt32 ( & count )
val1 := atomic . LoadInt32 ( & countMirror1 )
val2 := atomic . LoadInt32 ( & countMirror2 )
2024-01-09 16:00:07 +00:00
assert . Equal ( s . T ( ) , int32 ( 5 ) , countTotal )
assert . Equal ( s . T ( ) , int32 ( 0 ) , val1 )
assert . Equal ( s . T ( ) , int32 ( 0 ) , val2 )
2019-08-26 17:00:04 +00:00
}
2019-09-06 13:08:04 +00:00
2024-01-09 16:00:07 +00:00
func ( s * SimpleSuite ) TestSecureAPI ( ) {
s . createComposeProject ( "base" )
2021-11-25 10:10:06 +00:00
2024-01-09 16:00:07 +00:00
s . composeUp ( )
defer s . composeDown ( )
2019-09-06 13:08:04 +00:00
2024-01-09 16:00:07 +00:00
file := s . adaptFile ( "./fixtures/simple_secure_api.toml" , struct { } { } )
2019-09-06 13:08:04 +00:00
2024-01-09 16:00:07 +00:00
s . traefikCmd ( withConfigFile ( file ) )
2019-09-06 13:08:04 +00:00
2024-01-09 16:00:07 +00:00
err := try . GetRequest ( "http://127.0.0.1:8000/secure/api/rawdata" , 1 * time . Second , try . StatusCodeIs ( http . StatusOK ) )
require . NoError ( s . T ( ) , err )
2019-09-06 13:08:04 +00:00
err = try . GetRequest ( "http://127.0.0.1:8000/api/rawdata" , 1 * time . Second , try . StatusCodeIs ( http . StatusNotFound ) )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2019-09-06 13:08:04 +00:00
err = try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 1 * time . Second , try . StatusCodeIs ( http . StatusNotFound ) )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2019-09-06 13:08:04 +00:00
}
2020-01-21 17:06:03 +00:00
2024-01-09 16:00:07 +00:00
func ( s * SimpleSuite ) TestContentTypeDisableAutoDetect ( ) {
2020-01-21 17:06:03 +00:00
srv1 := httptest . NewServer ( http . HandlerFunc ( func ( rw http . ResponseWriter , req * http . Request ) {
rw . Header ( ) [ "Content-Type" ] = nil
2022-11-29 10:48:05 +00:00
path := strings . TrimPrefix ( req . URL . Path , "/autodetect" )
switch path [ : 4 ] {
2020-01-21 17:06:03 +00:00
case "/css" :
2022-11-29 10:48:05 +00:00
if strings . Contains ( req . URL . Path , "/ct" ) {
2020-01-21 17:06:03 +00:00
rw . Header ( ) . Set ( "Content-Type" , "text/css" )
}
rw . WriteHeader ( http . StatusOK )
_ , err := rw . Write ( [ ] byte ( ".testcss { }" ) )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2020-01-21 17:06:03 +00:00
case "/pdf" :
2022-11-29 10:48:05 +00:00
if strings . Contains ( req . URL . Path , "/ct" ) {
2020-01-21 17:06:03 +00:00
rw . Header ( ) . Set ( "Content-Type" , "application/pdf" )
}
rw . WriteHeader ( http . StatusOK )
2021-03-04 19:08:03 +00:00
data , err := os . ReadFile ( "fixtures/test.pdf" )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2020-01-21 17:06:03 +00:00
2020-10-09 07:32:03 +00:00
_ , err = rw . Write ( data )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2020-01-21 17:06:03 +00:00
}
} ) )
defer srv1 . Close ( )
2024-01-09 16:00:07 +00:00
file := s . adaptFile ( "fixtures/simple_contenttype.toml" , struct {
2020-01-21 17:06:03 +00:00
Server string
} {
Server : srv1 . URL ,
} )
2024-01-09 16:00:07 +00:00
s . traefikCmd ( withConfigFile ( file ) , "--log.level=DEBUG" )
2020-01-21 17:06: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" , 10 * time . Second , try . BodyContains ( "127.0.0.1" ) )
require . NoError ( s . T ( ) , err )
2020-01-21 17:06:03 +00:00
2022-11-29 10:48:05 +00:00
err = try . GetRequest ( "http://127.0.0.1:8000/css/ct" , time . Second , try . HasHeaderValue ( "Content-Type" , "text/css" , false ) )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2020-01-21 17:06:03 +00:00
2022-11-29 10:48:05 +00:00
err = try . GetRequest ( "http://127.0.0.1:8000/pdf/ct" , time . Second , try . HasHeaderValue ( "Content-Type" , "application/pdf" , false ) )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2020-01-21 17:06:03 +00:00
2022-11-29 10:48:05 +00:00
err = try . GetRequest ( "http://127.0.0.1:8000/css/noct" , time . Second , func ( res * http . Response ) error {
2020-01-21 17:06:03 +00:00
if ct , ok := res . Header [ "Content-Type" ] ; ok {
return fmt . Errorf ( "should have no content type and %s is present" , ct )
}
return nil
} )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2020-01-21 17:06:03 +00:00
2022-11-29 10:48:05 +00:00
err = try . GetRequest ( "http://127.0.0.1:8000/pdf/noct" , time . Second , func ( res * http . Response ) error {
2020-01-21 17:06:03 +00:00
if ct , ok := res . Header [ "Content-Type" ] ; ok {
return fmt . Errorf ( "should have no content type and %s is present" , ct )
}
return nil
} )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2022-11-29 10:48:05 +00:00
err = try . GetRequest ( "http://127.0.0.1:8000/autodetect/css/ct" , time . Second , try . HasHeaderValue ( "Content-Type" , "text/css" , false ) )
2024-01-10 09:47:44 +00:00
require . NoError ( s . T ( ) , err )
2022-11-29 10:48:05 +00:00
err = try . GetRequest ( "http://127.0.0.1:8000/autodetect/pdf/ct" , time . Second , try . HasHeaderValue ( "Content-Type" , "application/pdf" , false ) )
2024-01-10 09:47:44 +00:00
require . NoError ( s . T ( ) , err )
2022-11-29 10:48:05 +00:00
err = try . GetRequest ( "http://127.0.0.1:8000/autodetect/css/noct" , time . Second , try . HasHeaderValue ( "Content-Type" , "text/plain; charset=utf-8" , false ) )
2024-01-10 09:47:44 +00:00
require . NoError ( s . T ( ) , err )
2022-11-29 10:48:05 +00:00
err = try . GetRequest ( "http://127.0.0.1:8000/autodetect/pdf/noct" , time . Second , try . HasHeaderValue ( "Content-Type" , "application/pdf" , false ) )
2024-01-10 09:47:44 +00:00
require . NoError ( s . T ( ) , err )
2020-01-21 17:06:03 +00:00
}
2022-06-27 13:16:08 +00:00
2024-01-09 16:00:07 +00:00
func ( s * SimpleSuite ) TestMuxer ( ) {
s . createComposeProject ( "base" )
2022-06-27 13:16:08 +00:00
2024-01-09 16:00:07 +00:00
s . composeUp ( )
defer s . composeDown ( )
2022-06-27 13:16:08 +00:00
2024-01-09 16:00:07 +00:00
whoami1URL := "http://" + net . JoinHostPort ( s . getComposeServiceIP ( "whoami1" ) , "80" )
2022-06-27 13:16:08 +00:00
2024-01-09 16:00:07 +00:00
file := s . adaptFile ( "fixtures/simple_muxer.toml" , struct {
2022-06-27 13:16:08 +00:00
Server1 string
} { whoami1URL } )
2024-01-09 16:00:07 +00:00
s . traefikCmd ( withConfigFile ( file ) )
2022-06-27 13:16:08 +00:00
2024-01-09 16:00:07 +00:00
err := try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 1 * time . Second , try . BodyContains ( "!Host" ) )
require . NoError ( s . T ( ) , err )
2022-06-27 13:16:08 +00:00
testCases := [ ] struct {
desc string
request string
target string
body string
expected int
} {
{
desc : "!Host with absolute-form URL with empty host and host header, no match" ,
request : "GET http://@/ HTTP/1.1\r\nHost: test.localhost\r\n\r\n" ,
target : "127.0.0.1:8000" ,
expected : http . StatusNotFound ,
} ,
{
desc : "!Host with absolute-form URL with empty host and host header, match" ,
request : "GET http://@/ HTTP/1.1\r\nHost: toto.localhost\r\n\r\n" ,
target : "127.0.0.1:8000" ,
expected : http . StatusOK ,
} ,
{
desc : "!Host with absolute-form URL and host header, no match" ,
request : "GET http://test.localhost/ HTTP/1.1\r\nHost: toto.localhost\r\n\r\n" ,
target : "127.0.0.1:8000" ,
expected : http . StatusNotFound ,
} ,
{
desc : "!Host with absolute-form URL and host header, match" ,
request : "GET http://toto.localhost/ HTTP/1.1\r\nHost: test.localhost\r\n\r\n" ,
target : "127.0.0.1:8000" ,
expected : http . StatusOK ,
} ,
{
desc : "!HostRegexp with absolute-form URL with empty host and host header, no match" ,
request : "GET http://@/ HTTP/1.1\r\nHost: test.localhost\r\n\r\n" ,
target : "127.0.0.1:8001" ,
expected : http . StatusNotFound ,
} ,
{
desc : "!HostRegexp with absolute-form URL with empty host and host header, match" ,
request : "GET http://@/ HTTP/1.1\r\nHost: toto.localhost\r\n\r\n" ,
target : "127.0.0.1:8001" ,
expected : http . StatusOK ,
} ,
{
desc : "!HostRegexp with absolute-form URL and host header, no match" ,
request : "GET http://test.localhost/ HTTP/1.1\r\nHost: toto.localhost\r\n\r\n" ,
target : "127.0.0.1:8001" ,
expected : http . StatusNotFound ,
} ,
{
desc : "!HostRegexp with absolute-form URL and host header, match" ,
request : "GET http://toto.localhost/ HTTP/1.1\r\nHost: test.localhost\r\n\r\n" ,
target : "127.0.0.1:8001" ,
expected : http . StatusOK ,
} ,
{
2022-11-28 14:48:05 +00:00
desc : "!Query with semicolon and empty query param value, no match" ,
2022-06-27 13:16:08 +00:00
request : "GET /?foo=; HTTP/1.1\r\nHost: other.localhost\r\n\r\n" ,
target : "127.0.0.1:8002" ,
expected : http . StatusNotFound ,
} ,
{
desc : "!Query with semicolon, no match" ,
request : "GET /?foo=titi;bar=toto HTTP/1.1\r\nHost: other.localhost\r\n\r\n" ,
target : "127.0.0.1:8002" ,
expected : http . StatusNotFound ,
} ,
{
desc : "!Query with semicolon, match" ,
request : "GET /?bar=toto;boo=titi HTTP/1.1\r\nHost: other.localhost\r\n\r\n" ,
target : "127.0.0.1:8002" ,
expected : http . StatusOK ,
body : "bar=toto&boo=titi" ,
} ,
}
for _ , test := range testCases {
conn , err := net . Dial ( "tcp" , test . target )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2022-06-27 13:16:08 +00:00
_ , err = conn . Write ( [ ] byte ( test . request ) )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2022-06-27 13:16:08 +00:00
resp , err := http . ReadResponse ( bufio . NewReader ( conn ) , nil )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2022-06-27 13:16:08 +00:00
2024-01-09 16:00:07 +00:00
assert . Equal ( s . T ( ) , test . expected , resp . StatusCode , test . desc )
2022-06-27 13:16:08 +00:00
if test . body != "" {
body , err := io . ReadAll ( resp . Body )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
assert . Contains ( s . T ( ) , string ( body ) , test . body )
2022-06-27 13:16:08 +00:00
}
}
}
2022-12-05 10:30:05 +00:00
2024-01-09 16:00:07 +00:00
func ( s * SimpleSuite ) TestDebugLog ( ) {
s . createComposeProject ( "base" )
2022-12-05 10:30:05 +00:00
2024-01-09 16:00:07 +00:00
s . composeUp ( )
defer s . composeDown ( )
2022-12-05 10:30:05 +00:00
2024-01-09 16:00:07 +00:00
file := s . adaptFile ( "fixtures/simple_debug_log.toml" , struct { } { } )
2022-12-05 10:30:05 +00:00
2024-01-09 16:00:07 +00:00
_ , output := s . cmdTraefik ( withConfigFile ( file ) )
2022-12-05 10:30:05 +00:00
2024-01-09 16:00:07 +00:00
err := try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 1 * time . Second , try . BodyContains ( "PathPrefix(`/whoami`)" ) )
require . NoError ( s . T ( ) , err )
2022-12-05 10:30:05 +00:00
req , err := http . NewRequest ( http . MethodGet , "http://localhost:8000/whoami" , http . NoBody )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2022-12-05 10:30:05 +00:00
req . Header . Set ( "Autorization" , "Bearer ThisIsABearerToken" )
response , err := http . DefaultClient . Do ( req )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
assert . Equal ( s . T ( ) , http . StatusOK , response . StatusCode )
2022-12-05 10:30:05 +00:00
if regexp . MustCompile ( "ThisIsABearerToken" ) . MatchReader ( output ) {
2024-01-10 09:47:44 +00:00
log . Info ( ) . Msgf ( "Traefik Logs: %s" , output . String ( ) )
log . Info ( ) . Msg ( "Found Authorization Header in Traefik DEBUG logs" )
2024-01-09 16:00:07 +00:00
s . T ( ) . Fail ( )
2022-12-05 10:30:05 +00:00
}
}
2023-06-15 16:20:06 +00:00
2024-01-09 16:00:07 +00:00
func ( s * SimpleSuite ) TestEncodeSemicolons ( ) {
s . createComposeProject ( "base" )
2023-06-15 16:20:06 +00:00
2024-01-09 16:00:07 +00:00
s . composeUp ( )
defer s . composeDown ( )
2023-06-15 16:20:06 +00:00
2024-01-09 16:00:07 +00:00
whoami1URL := "http://" + net . JoinHostPort ( s . getComposeServiceIP ( "whoami1" ) , "80" )
2023-06-15 16:20:06 +00:00
2024-01-09 16:00:07 +00:00
file := s . adaptFile ( "fixtures/simple_encode_semicolons.toml" , struct {
2023-06-15 16:20:06 +00:00
Server1 string
} { whoami1URL } )
2024-01-09 16:00:07 +00:00
s . traefikCmd ( withConfigFile ( file ) )
2023-06-15 16:20:06 +00:00
2024-01-09 16:00:07 +00:00
err := try . GetRequest ( "http://127.0.0.1:8080/api/rawdata" , 1 * time . Second , try . BodyContains ( "Host(`other.localhost`)" ) )
require . NoError ( s . T ( ) , err )
2023-06-15 16:20:06 +00:00
testCases := [ ] struct {
desc string
request string
target string
body string
expected int
} {
{
desc : "Transforming semicolons" ,
request : "GET /?bar=toto;boo=titi HTTP/1.1\r\nHost: other.localhost\r\n\r\n" ,
target : "127.0.0.1:8000" ,
expected : http . StatusOK ,
body : "bar=toto&boo=titi" ,
} ,
{
desc : "Encoding semicolons" ,
request : "GET /?bar=toto&boo=titi;aaaa HTTP/1.1\r\nHost: other.localhost\r\n\r\n" ,
target : "127.0.0.1:8001" ,
expected : http . StatusOK ,
body : "bar=toto&boo=titi%3Baaaa" ,
} ,
}
for _ , test := range testCases {
conn , err := net . Dial ( "tcp" , test . target )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2023-06-15 16:20:06 +00:00
_ , err = conn . Write ( [ ] byte ( test . request ) )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2023-06-15 16:20:06 +00:00
resp , err := http . ReadResponse ( bufio . NewReader ( conn ) , nil )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2023-06-15 16:20:06 +00:00
if resp . StatusCode != test . expected {
2024-01-10 09:47:44 +00:00
log . Info ( ) . Msgf ( "%s failed with %d instead of %d" , test . desc , resp . StatusCode , test . expected )
2023-06-15 16:20:06 +00:00
}
if test . body != "" {
body , err := io . ReadAll ( resp . Body )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
assert . Contains ( s . T ( ) , string ( body ) , test . body )
2023-06-15 16:20:06 +00:00
}
}
}
2023-11-16 15:54:07 +00:00
2024-01-09 16:00:07 +00:00
func ( s * SimpleSuite ) TestDenyFragment ( ) {
s . createComposeProject ( "base" )
2023-11-16 15:54:07 +00:00
2024-01-09 16:00:07 +00:00
s . composeUp ( )
defer s . composeDown ( )
2023-11-16 15:54:07 +00:00
2024-01-09 16:00:07 +00:00
s . traefikCmd ( withConfigFile ( "fixtures/simple_default.toml" ) )
2023-11-16 15:54:07 +00:00
// Expected a 404 as we did not configure anything
2024-01-09 16:00:07 +00:00
err := try . GetRequest ( "http://127.0.0.1:8000/" , 1 * time . Second , try . StatusCodeIs ( http . StatusNotFound ) )
require . NoError ( s . T ( ) , err )
2023-11-16 15:54:07 +00:00
conn , err := net . Dial ( "tcp" , "127.0.0.1:8000" )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2023-11-16 15:54:07 +00:00
_ , err = conn . Write ( [ ] byte ( "GET /#/?bar=toto;boo=titi HTTP/1.1\nHost: other.localhost\n\n" ) )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
2023-11-16 15:54:07 +00:00
resp , err := http . ReadResponse ( bufio . NewReader ( conn ) , nil )
2024-01-09 16:00:07 +00:00
require . NoError ( s . T ( ) , err )
assert . Equal ( s . T ( ) , http . StatusBadRequest , resp . StatusCode )
2023-11-16 15:54:07 +00:00
}