2022-09-12 18:10:09 +03:00
package capture
import (
"bytes"
"fmt"
"io"
"net/http"
"net/http/httptest"
"testing"
"github.com/containous/alice"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestCapture ( t * testing . T ) {
wrapMiddleware := func ( next http . Handler ) ( http . Handler , error ) {
return http . HandlerFunc ( func ( rw http . ResponseWriter , req * http . Request ) {
capt , err := FromContext ( req . Context ( ) )
require . NoError ( t , err )
_ , err = fmt . Fprintf ( rw , "%d,%d,%d," , capt . RequestSize ( ) , capt . ResponseSize ( ) , capt . StatusCode ( ) )
require . NoError ( t , err )
next . ServeHTTP ( rw , req )
_ , err = fmt . Fprintf ( rw , ",%d,%d,%d" , capt . RequestSize ( ) , capt . ResponseSize ( ) , capt . StatusCode ( ) )
require . NoError ( t , err )
} ) , nil
}
handler := http . HandlerFunc ( func ( rw http . ResponseWriter , req * http . Request ) {
_ , err := rw . Write ( [ ] byte ( "foo" ) )
require . NoError ( t , err )
all , err := io . ReadAll ( req . Body )
require . NoError ( t , err )
assert . Equal ( t , "bar" , string ( all ) )
} )
chain := alice . New ( )
2022-10-27 17:08:06 +03:00
chain = chain . Append ( Wrap )
2022-09-12 18:10:09 +03:00
chain = chain . Append ( wrapMiddleware )
handlers , err := chain . Then ( handler )
require . NoError ( t , err )
request , err := http . NewRequest ( http . MethodGet , "/" , bytes . NewReader ( [ ] byte ( "bar" ) ) )
require . NoError ( t , err )
recorder := httptest . NewRecorder ( )
handlers . ServeHTTP ( recorder , request )
// 3 = len("bar")
// 9 = len("0,0,0,toto")
assert . Equal ( t , "0,0,0,foo,3,9,200" , recorder . Body . String ( ) )
}
// BenchmarkCapture with response writer and request reader
// $ go test -bench=. ./pkg/middlewares/capture/
// goos: linux
// goarch: amd64
2023-02-03 17:24:05 +03:00
// pkg: github.com/traefik/traefik/v3/pkg/middlewares/capture
2022-09-12 18:10:09 +03:00
// cpu: Intel(R) Core(TM) i7-10750H CPU @ 2.60GHz
// BenchmarkCapture/2k-12 280507 4015 ns/op 510.03 MB/s 5072 B/op 14 allocs/op
// BenchmarkCapture/20k-12 135726 8301 ns/op 2467.26 MB/s 41936 B/op 14 allocs/op
// BenchmarkCapture/100k-12 45494 26059 ns/op 3929.54 MB/s 213968 B/op 14 allocs/op
// BenchmarkCapture/2k_captured-12 263713 4356 ns/op 470.20 MB/s 5552 B/op 18 allocs/op
// BenchmarkCapture/20k_captured-12 132243 8790 ns/op 2329.98 MB/s 42416 B/op 18 allocs/op
// BenchmarkCapture/100k_captured-12 45650 26587 ns/op 3851.57 MB/s 214448 B/op 18 allocs/op
// BenchmarkCapture/2k_body-12 274135 7471 ns/op 274.12 MB/s 5624 B/op 20 allocs/op
// BenchmarkCapture/20k_body-12 130206 21149 ns/op 968.36 MB/s 42488 B/op 20 allocs/op
// BenchmarkCapture/100k_body-12 41600 51716 ns/op 1980.06 MB/s 214520 B/op 20 allocs/op
// PASS
func BenchmarkCapture ( b * testing . B ) {
testCases := [ ] struct {
name string
size int
capture bool
body bool
} {
{
name : "2k" ,
size : 2048 ,
} ,
{
name : "20k" ,
size : 20480 ,
} ,
{
name : "100k" ,
size : 102400 ,
} ,
{
name : "2k captured" ,
size : 2048 ,
capture : true ,
} ,
{
name : "20k captured" ,
size : 20480 ,
capture : true ,
} ,
{
name : "100k captured" ,
size : 102400 ,
capture : true ,
} ,
{
name : "2k body" ,
size : 2048 ,
body : true ,
} ,
{
name : "20k body" ,
size : 20480 ,
body : true ,
} ,
{
name : "100k body" ,
size : 102400 ,
body : true ,
} ,
}
for _ , test := range testCases {
b . Run ( test . name , func ( b * testing . B ) {
baseBody := generateBytes ( test . size )
next := http . HandlerFunc ( func ( rw http . ResponseWriter , r * http . Request ) {
n , err := rw . Write ( baseBody )
require . Equal ( b , test . size , n )
require . NoError ( b , err )
} )
var body io . Reader
if test . body {
body = bytes . NewReader ( baseBody )
}
req , err := http . NewRequest ( http . MethodGet , "http://foo/" , body )
require . NoError ( b , err )
chain := alice . New ( )
if test . capture || test . body {
2022-10-27 17:08:06 +03:00
chain = chain . Append ( Wrap )
2022-09-12 18:10:09 +03:00
}
handlers , err := chain . Then ( next )
require . NoError ( b , err )
b . ReportAllocs ( )
b . SetBytes ( int64 ( test . size ) )
b . ResetTimer ( )
2024-02-19 17:44:03 +03:00
for range b . N {
2022-09-12 18:10:09 +03:00
runBenchmark ( b , test . size , req , handlers )
}
} )
}
}
func runBenchmark ( b * testing . B , size int , req * http . Request , handler http . Handler ) {
b . Helper ( )
recorder := httptest . NewRecorder ( )
handler . ServeHTTP ( recorder , req )
if code := recorder . Code ; code != 200 {
b . Fatalf ( "Expected 200 but got %d" , code )
}
2023-11-17 03:50:06 +03:00
assert . Len ( b , recorder . Body . String ( ) , size )
2022-09-12 18:10:09 +03:00
}
func generateBytes ( length int ) [ ] byte {
var value [ ] byte
2024-02-19 17:44:03 +03:00
for i := range length {
2022-09-12 18:10:09 +03:00
value = append ( value , 0x61 + byte ( i % 26 ) )
}
return value
}
func TestRequestReader ( t * testing . T ) {
2023-06-05 11:24:06 +03:00
buff := bytes . NewBufferString ( "foo" )
2022-09-12 18:10:09 +03:00
rr := readCounter { source : io . NopCloser ( buff ) }
assert . Equal ( t , int64 ( 0 ) , rr . size )
n , err := rr . Read ( [ ] byte ( "bar" ) )
require . NoError ( t , err )
assert . Equal ( t , 3 , n )
err = rr . Close ( )
require . NoError ( t , err )
assert . Equal ( t , int64 ( 3 ) , rr . size )
}