@@ -33,7 +33,6 @@ import (
33
33
"os"
34
34
"reflect"
35
35
"strconv"
36
- "strings"
37
36
"testing"
38
37
"time"
39
38
@@ -42,6 +41,7 @@ import (
42
41
kerrors "k8s.io/apimachinery/pkg/api/errors"
43
42
"k8s.io/apimachinery/pkg/runtime"
44
43
"k8s.io/apimachinery/pkg/runtime/schema"
44
+ rand2 "k8s.io/apimachinery/pkg/util/rand"
45
45
"k8s.io/apimachinery/pkg/util/uuid"
46
46
"k8s.io/apiserver/pkg/features"
47
47
utilfeature "k8s.io/apiserver/pkg/util/feature"
@@ -378,29 +378,94 @@ func TestDeferredResponseWriter_Write(t *testing.T) {
378
378
largeChunk := bytes .Repeat ([]byte ("b" ), defaultGzipThresholdBytes + 1 )
379
379
380
380
tests := []struct {
381
- name string
382
- chunks [][]byte
383
- expectGzip bool
381
+ name string
382
+ chunks [][]byte
383
+ expectGzip bool
384
+ expectHeaders http.Header
384
385
}{
386
+ {
387
+ name : "no writes" ,
388
+ chunks : nil ,
389
+ expectGzip : false ,
390
+ expectHeaders : http.Header {},
391
+ },
392
+ {
393
+ name : "one empty write" ,
394
+ chunks : [][]byte {{}},
395
+ expectGzip : false ,
396
+ expectHeaders : http.Header {
397
+ "Content-Type" : []string {"text/plain" },
398
+ },
399
+ },
400
+ {
401
+ name : "one single byte write" ,
402
+ chunks : [][]byte {{'{' }},
403
+ expectGzip : false ,
404
+ expectHeaders : http.Header {
405
+ "Content-Type" : []string {"text/plain" },
406
+ },
407
+ },
385
408
{
386
409
name : "one small chunk write" ,
387
410
chunks : [][]byte {smallChunk },
388
411
expectGzip : false ,
412
+ expectHeaders : http.Header {
413
+ "Content-Type" : []string {"text/plain" },
414
+ },
389
415
},
390
416
{
391
417
name : "two small chunk writes" ,
392
418
chunks : [][]byte {smallChunk , smallChunk },
393
419
expectGzip : false ,
420
+ expectHeaders : http.Header {
421
+ "Content-Type" : []string {"text/plain" },
422
+ },
423
+ },
424
+ {
425
+ name : "one single byte and one small chunk write" ,
426
+ chunks : [][]byte {{'{' }, smallChunk },
427
+ expectGzip : false ,
428
+ expectHeaders : http.Header {
429
+ "Content-Type" : []string {"text/plain" },
430
+ },
431
+ },
432
+ {
433
+ name : "two single bytes and one small chunk write" ,
434
+ chunks : [][]byte {{'{' }, {'{' }, smallChunk },
435
+ expectGzip : true ,
436
+ expectHeaders : http.Header {
437
+ "Content-Type" : []string {"text/plain" },
438
+ "Content-Encoding" : []string {"gzip" },
439
+ "Vary" : []string {"Accept-Encoding" },
440
+ },
394
441
},
395
442
{
396
443
name : "one large chunk writes" ,
397
444
chunks : [][]byte {largeChunk },
398
445
expectGzip : true ,
446
+ expectHeaders : http.Header {
447
+ "Content-Type" : []string {"text/plain" },
448
+ "Content-Encoding" : []string {"gzip" },
449
+ "Vary" : []string {"Accept-Encoding" },
450
+ },
399
451
},
400
452
{
401
453
name : "two large chunk writes" ,
402
454
chunks : [][]byte {largeChunk , largeChunk },
403
455
expectGzip : true ,
456
+ expectHeaders : http.Header {
457
+ "Content-Type" : []string {"text/plain" },
458
+ "Content-Encoding" : []string {"gzip" },
459
+ "Vary" : []string {"Accept-Encoding" },
460
+ },
461
+ },
462
+ {
463
+ name : "one small chunk and one large chunk write" ,
464
+ chunks : [][]byte {smallChunk , largeChunk },
465
+ expectGzip : false ,
466
+ expectHeaders : http.Header {
467
+ "Content-Type" : []string {"text/plain" },
468
+ },
404
469
},
405
470
}
406
471
@@ -441,23 +506,16 @@ func TestDeferredResponseWriter_Write(t *testing.T) {
441
506
if res .StatusCode != http .StatusOK {
442
507
t .Fatalf ("status code is not writtend properly, expected: 200, got: %d" , res .StatusCode )
443
508
}
444
- contentEncoding := res .Header .Get ("Content-Encoding" )
445
- varyHeader := res .Header .Get ("Vary" )
509
+ if ! reflect .DeepEqual (res .Header , tt .expectHeaders ) {
510
+ t .Fatal (cmp .Diff (tt .expectHeaders , res .Header ))
511
+ }
446
512
447
513
resBytes , err := io .ReadAll (res .Body )
448
514
if err != nil {
449
515
t .Fatalf ("unexpected error occurred while reading response body: %v" , err )
450
516
}
451
517
452
518
if tt .expectGzip {
453
- if contentEncoding != "gzip" {
454
- t .Fatalf ("content-encoding is not set properly, expected: gzip, got: %s" , contentEncoding )
455
- }
456
-
457
- if ! strings .Contains (varyHeader , "Accept-Encoding" ) {
458
- t .Errorf ("vary header doesn't have Accept-Encoding" )
459
- }
460
-
461
519
gr , err := gzip .NewReader (bytes .NewReader (resBytes ))
462
520
if err != nil {
463
521
t .Fatalf ("failed to create gzip reader: %v" , err )
@@ -471,22 +529,101 @@ func TestDeferredResponseWriter_Write(t *testing.T) {
471
529
if ! bytes .Equal (fullPayload , decompressed ) {
472
530
t .Errorf ("payload mismatch, expected: %s, got: %s" , fullPayload , decompressed )
473
531
}
474
-
475
532
} else {
476
- if contentEncoding != "" {
477
- t .Errorf ("content-encoding is set unexpectedly" )
478
- }
479
-
480
- if strings .Contains (varyHeader , "Accept-Encoding" ) {
481
- t .Errorf ("accept encoding is set unexpectedly" )
482
- }
483
-
484
533
if ! bytes .Equal (fullPayload , resBytes ) {
485
534
t .Errorf ("payload mismatch, expected: %s, got: %s" , fullPayload , resBytes )
486
535
}
487
-
488
536
}
537
+ })
538
+ }
539
+ }
540
+
541
+ func benchmarkChunkingGzip (b * testing.B , count int , chunk []byte ) {
542
+ mockResponseWriter := httptest .NewRecorder ()
543
+ mockResponseWriter .Body = nil
544
+
545
+ drw := & deferredResponseWriter {
546
+ mediaType : "text/plain" ,
547
+ statusCode : 200 ,
548
+ contentEncoding : "gzip" ,
549
+ hw : mockResponseWriter ,
550
+ ctx : context .Background (),
551
+ }
552
+ b .ResetTimer ()
553
+ for i := 0 ; i < count ; i ++ {
554
+ n , err := drw .Write (chunk )
555
+ if err != nil {
556
+ b .Fatalf ("unexpected error while writing chunk: %v" , err )
557
+ }
558
+ if n != len (chunk ) {
559
+ b .Errorf ("write is not complete, expected: %d bytes, written: %d bytes" , len (chunk ), n )
560
+ }
561
+ }
562
+ err := drw .Close ()
563
+ if err != nil {
564
+ b .Fatalf ("unexpected error when closing deferredResponseWriter: %v" , err )
565
+ }
566
+ res := mockResponseWriter .Result ()
567
+ if res .StatusCode != http .StatusOK {
568
+ b .Fatalf ("status code is not writtend properly, expected: 200, got: %d" , res .StatusCode )
569
+ }
570
+ }
571
+
572
+ func BenchmarkChunkingGzip (b * testing.B ) {
573
+ tests := []struct {
574
+ count int
575
+ size int
576
+ }{
577
+ {
578
+ count : 100 ,
579
+ size : 1_000 ,
580
+ },
581
+ {
582
+ count : 100 ,
583
+ size : 100_000 ,
584
+ },
585
+ {
586
+ count : 1_000 ,
587
+ size : 100_000 ,
588
+ },
589
+ {
590
+ count : 1_000 ,
591
+ size : 1_000_000 ,
592
+ },
593
+ {
594
+ count : 10_000 ,
595
+ size : 100_000 ,
596
+ },
597
+ {
598
+ count : 100_000 ,
599
+ size : 10_000 ,
600
+ },
601
+ {
602
+ count : 1 ,
603
+ size : 100_000 ,
604
+ },
605
+ {
606
+ count : 1 ,
607
+ size : 1_000_000 ,
608
+ },
609
+ {
610
+ count : 1 ,
611
+ size : 10_000_000 ,
612
+ },
613
+ {
614
+ count : 1 ,
615
+ size : 100_000_000 ,
616
+ },
617
+ {
618
+ count : 1 ,
619
+ size : 1_000_000_000 ,
620
+ },
621
+ }
489
622
623
+ for _ , t := range tests {
624
+ b .Run (fmt .Sprintf ("Count=%d/Size=%d" , t .count , t .size ), func (b * testing.B ) {
625
+ chunk := []byte (rand2 .String (t .size ))
626
+ benchmarkChunkingGzip (b , t .count , chunk )
490
627
})
491
628
}
492
629
}
0 commit comments