@@ -13,6 +13,7 @@ import (
1313 "net/http/httputil"
1414 "net/url"
1515 "strconv"
16+ "strings"
1617 "testing"
1718
1819 "github.com/gin-gonic/gin"
@@ -136,6 +137,17 @@ func TestGzipPNG(t *testing.T) {
136137 assert .Equal (t , w .Body .String (), "this is a PNG!" )
137138}
138139
140+ func TestWriteString (t * testing.T ) {
141+ testC , _ := gin .CreateTestContext (httptest .NewRecorder ())
142+ gz := gzipWriter {
143+ ResponseWriter : testC .Writer ,
144+ writer : gzip .NewWriter (testC .Writer ),
145+ }
146+ n , err := gz .WriteString ("test" )
147+ assert .NoError (t , err )
148+ assert .Equal (t , 4 , n )
149+ }
150+
139151func TestExcludedPathsAndExtensions (t * testing.T ) {
140152 tests := []struct {
141153 path string
@@ -377,6 +389,149 @@ func TestCustomShouldCompressFn(t *testing.T) {
377389 assert .Equal (t , testResponse , w .Body .String ())
378390}
379391
392+ func TestMinLengthInvalidValue (t * testing.T ) {
393+ defer func () {
394+ if r := recover (); r == nil {
395+ t .Errorf ("Invalid minLength should cause panic" )
396+ }
397+ }()
398+
399+ router := gin .New ()
400+ router .Use (Gzip (DefaultCompression , WithMinLength (- 1 )))
401+ }
402+
403+ func TestMinLengthShortResponse (t * testing.T ) {
404+ req , _ := http .NewRequestWithContext (context .Background (), "GET" , "/" , nil )
405+ req .Header .Add (headerAcceptEncoding , "gzip" )
406+
407+ router := gin .New ()
408+ router .Use (Gzip (DefaultCompression , WithMinLength (2048 )))
409+ router .GET ("/" , func (c * gin.Context ) {
410+ c .String (200 , testResponse )
411+ })
412+
413+ w := httptest .NewRecorder ()
414+ router .ServeHTTP (w , req )
415+
416+ assert .Equal (t , 200 , w .Code )
417+ assert .Equal (t , "" , w .Header ().Get (headerContentEncoding ))
418+ assert .Equal (t , "19" , w .Header ().Get ("Content-Length" ))
419+ assert .Equal (t , testResponse , w .Body .String ())
420+ }
421+
422+ func TestMinLengthLongResponse (t * testing.T ) {
423+ req , _ := http .NewRequestWithContext (context .Background (), "GET" , "/" , nil )
424+ req .Header .Add (headerAcceptEncoding , "gzip" )
425+
426+ router := gin .New ()
427+ router .Use (Gzip (DefaultCompression , WithMinLength (2048 )))
428+ router .GET ("/" , func (c * gin.Context ) {
429+ c .String (200 , strings .Repeat ("a" , 2048 ))
430+ })
431+
432+ w := httptest .NewRecorder ()
433+ router .ServeHTTP (w , req )
434+
435+ assert .Equal (t , 200 , w .Code )
436+ assert .Equal (t , "gzip" , w .Header ().Get (headerContentEncoding ))
437+ assert .NotEqual (t , "2048" , w .Header ().Get ("Content-Length" ))
438+ assert .Less (t , w .Body .Len (), 2048 )
439+ }
440+
441+ func TestMinLengthMultiWriteResponse (t * testing.T ) {
442+ req , _ := http .NewRequestWithContext (context .Background (), "GET" , "/" , nil )
443+ req .Header .Add (headerAcceptEncoding , "gzip" )
444+
445+ router := gin .New ()
446+ router .Use (Gzip (DefaultCompression , WithMinLength (2048 )))
447+ router .GET ("/" , func (c * gin.Context ) {
448+ c .String (200 , strings .Repeat ("a" , 1024 ))
449+ c .String (200 , strings .Repeat ("b" , 1024 ))
450+ })
451+
452+ w := httptest .NewRecorder ()
453+ router .ServeHTTP (w , req )
454+
455+ assert .Equal (t , 200 , w .Code )
456+ assert .Equal (t , "gzip" , w .Header ().Get (headerContentEncoding ))
457+ assert .NotEqual (t , "2048" , w .Header ().Get ("Content-Length" ))
458+ assert .Less (t , w .Body .Len (), 2048 )
459+ }
460+
461+ // Note this test intentionally triggers gzipping even when the actual response doesn't meet min length. This is because
462+ // we use the Content-Length header as the primary determinant of compression to avoid the cost of buffering.
463+ func TestMinLengthUsesContentLengthHeaderInsteadOfBuffering (t * testing.T ) {
464+ req , _ := http .NewRequestWithContext (context .Background (), "GET" , "/" , nil )
465+ req .Header .Add (headerAcceptEncoding , "gzip" )
466+
467+ router := gin .New ()
468+ router .Use (Gzip (DefaultCompression , WithMinLength (2048 )))
469+ router .GET ("/" , func (c * gin.Context ) {
470+ c .Header ("Content-Length" , "2048" )
471+ c .String (200 , testResponse )
472+ })
473+
474+ w := httptest .NewRecorder ()
475+ router .ServeHTTP (w , req )
476+
477+ assert .Equal (t , 200 , w .Code )
478+ assert .Equal (t , "gzip" , w .Header ().Get (headerContentEncoding ))
479+ assert .NotEmpty (t , w .Header ().Get ("Content-Length" ))
480+ assert .NotEqual (t , "19" , w .Header ().Get ("Content-Length" ))
481+ }
482+
483+ // Note this test intentionally does not trigger gzipping even when the actual response meets min length. This is
484+ // because we use the Content-Length header as the primary determinant of compression to avoid the cost of buffering.
485+ func TestMinLengthMultiWriteResponseUsesContentLengthHeaderInsteadOfBuffering (t * testing.T ) {
486+ req , _ := http .NewRequestWithContext (context .Background (), "GET" , "/" , nil )
487+ req .Header .Add (headerAcceptEncoding , "gzip" )
488+
489+ router := gin .New ()
490+ router .Use (Gzip (DefaultCompression , WithMinLength (1024 )))
491+ router .GET ("/" , func (c * gin.Context ) {
492+ c .Header ("Content-Length" , "999" )
493+ c .String (200 , strings .Repeat ("a" , 1024 ))
494+ c .String (200 , strings .Repeat ("b" , 1024 ))
495+ })
496+
497+ w := httptest .NewRecorder ()
498+ router .ServeHTTP (w , req )
499+
500+ assert .Equal (t , 200 , w .Code )
501+ assert .NotEqual (t , "gzip" , w .Header ().Get (headerContentEncoding )) // no gzip since Content-Length doesn't meet min length 1024
502+ assert .Equal (t , "2048" , w .Header ().Get ("Content-Length" ))
503+ }
504+
505+ func TestMinLengthWithInvalidContentLengthHeader (t * testing.T ) {
506+ req , _ := http .NewRequestWithContext (context .Background (), "GET" , "/" , nil )
507+ req .Header .Add (headerAcceptEncoding , "gzip" )
508+
509+ router := gin .New ()
510+ router .Use (Gzip (DefaultCompression , WithMinLength (2048 )))
511+ router .GET ("/" , func (c * gin.Context ) {
512+ c .Header ("Content-Length" , "xyz" )
513+ c .String (200 , testResponse )
514+ })
515+
516+ w := httptest .NewRecorder ()
517+ router .ServeHTTP (w , req )
518+
519+ assert .Equal (t , 200 , w .Code )
520+ assert .Equal (t , "" , w .Header ().Get (headerContentEncoding ))
521+ assert .Equal (t , "19" , w .Header ().Get ("Content-Length" ))
522+ }
523+
524+ func TestFlush (t * testing.T ) {
525+ testC , _ := gin .CreateTestContext (httptest .NewRecorder ())
526+ gz := gzipWriter {
527+ ResponseWriter : testC .Writer ,
528+ writer : gzip .NewWriter (testC .Writer ),
529+ }
530+ _ , _ = gz .WriteString ("test" )
531+ gz .Flush ()
532+ assert .True (t , gz .Written ())
533+ }
534+
380535type hijackableResponse struct {
381536 Hijacked bool
382537 header http.Header
0 commit comments