@@ -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,138 @@ func TestCustomShouldCompressFn(t *testing.T) {
377389 assert .Equal (t , testResponse , w .Body .String ())
378390}
379391
392+ func TestMinLengthShortResponse (t * testing.T ) {
393+ req , _ := http .NewRequestWithContext (context .Background (), "GET" , "/" , nil )
394+ req .Header .Add (headerAcceptEncoding , "gzip" )
395+
396+ router := gin .New ()
397+ router .Use (Gzip (DefaultCompression , WithMinLength (2048 )))
398+ router .GET ("/" , func (c * gin.Context ) {
399+ c .String (200 , testResponse )
400+ })
401+
402+ w := httptest .NewRecorder ()
403+ router .ServeHTTP (w , req )
404+
405+ assert .Equal (t , 200 , w .Code )
406+ assert .Equal (t , "" , w .Header ().Get (headerContentEncoding ))
407+ assert .Equal (t , "19" , w .Header ().Get ("Content-Length" ))
408+ assert .Equal (t , testResponse , w .Body .String ())
409+ }
410+
411+ func TestMinLengthLongResponse (t * testing.T ) {
412+ req , _ := http .NewRequestWithContext (context .Background (), "GET" , "/" , nil )
413+ req .Header .Add (headerAcceptEncoding , "gzip" )
414+
415+ router := gin .New ()
416+ router .Use (Gzip (DefaultCompression , WithMinLength (2048 )))
417+ router .GET ("/" , func (c * gin.Context ) {
418+ c .String (200 , strings .Repeat ("a" , 2048 ))
419+ })
420+
421+ w := httptest .NewRecorder ()
422+ router .ServeHTTP (w , req )
423+
424+ assert .Equal (t , 200 , w .Code )
425+ assert .Equal (t , "gzip" , w .Header ().Get (headerContentEncoding ))
426+ assert .NotEqual (t , "2048" , w .Header ().Get ("Content-Length" ))
427+ assert .Less (t , w .Body .Len (), 2048 )
428+ }
429+
430+ func TestMinLengthMultiWriteResponse (t * testing.T ) {
431+ req , _ := http .NewRequestWithContext (context .Background (), "GET" , "/" , nil )
432+ req .Header .Add (headerAcceptEncoding , "gzip" )
433+
434+ router := gin .New ()
435+ router .Use (Gzip (DefaultCompression , WithMinLength (2048 )))
436+ router .GET ("/" , func (c * gin.Context ) {
437+ c .String (200 , strings .Repeat ("a" , 1024 ))
438+ c .String (200 , strings .Repeat ("b" , 1024 ))
439+ })
440+
441+ w := httptest .NewRecorder ()
442+ router .ServeHTTP (w , req )
443+
444+ assert .Equal (t , 200 , w .Code )
445+ assert .Equal (t , "gzip" , w .Header ().Get (headerContentEncoding ))
446+ assert .NotEqual (t , "2048" , w .Header ().Get ("Content-Length" ))
447+ assert .Less (t , w .Body .Len (), 2048 )
448+ }
449+
450+ // Note this test intentionally triggers gzipping even when the actual response doesn't meet min length. This is because
451+ // we use the Content-Length header as the primary determinant of compression to avoid the cost of buffering.
452+ func TestMinLengthUsesContentLengthHeaderInsteadOfBuffering (t * testing.T ) {
453+ req , _ := http .NewRequestWithContext (context .Background (), "GET" , "/" , nil )
454+ req .Header .Add (headerAcceptEncoding , "gzip" )
455+
456+ router := gin .New ()
457+ router .Use (Gzip (DefaultCompression , WithMinLength (2048 )))
458+ router .GET ("/" , func (c * gin.Context ) {
459+ c .Header ("Content-Length" , "2048" )
460+ c .String (200 , testResponse )
461+ })
462+
463+ w := httptest .NewRecorder ()
464+ router .ServeHTTP (w , req )
465+
466+ assert .Equal (t , 200 , w .Code )
467+ assert .Equal (t , "gzip" , w .Header ().Get (headerContentEncoding ))
468+ assert .NotEmpty (t , w .Header ().Get ("Content-Length" ))
469+ assert .NotEqual (t , "19" , w .Header ().Get ("Content-Length" ))
470+ }
471+
472+ // Note this test intentionally does not trigger gzipping even when the actual response meets min length. This is
473+ // because we use the Content-Length header as the primary determinant of compression to avoid the cost of buffering.
474+ func TestMinLengthMultiWriteResponseUsesContentLengthHeaderInsteadOfBuffering (t * testing.T ) {
475+ req , _ := http .NewRequestWithContext (context .Background (), "GET" , "/" , nil )
476+ req .Header .Add (headerAcceptEncoding , "gzip" )
477+
478+ router := gin .New ()
479+ router .Use (Gzip (DefaultCompression , WithMinLength (1024 )))
480+ router .GET ("/" , func (c * gin.Context ) {
481+ c .Header ("Content-Length" , "999" )
482+ c .String (200 , strings .Repeat ("a" , 1024 ))
483+ c .String (200 , strings .Repeat ("b" , 1024 ))
484+ })
485+
486+ w := httptest .NewRecorder ()
487+ router .ServeHTTP (w , req )
488+
489+ assert .Equal (t , 200 , w .Code )
490+ assert .NotEqual (t , "gzip" , w .Header ().Get (headerContentEncoding )) // no gzip due to Content-Length header
491+ assert .Equal (t , "2048" , w .Header ().Get ("Content-Length" ))
492+ }
493+
494+ func TestMinLengthWithInvalidContentLengthHeader (t * testing.T ) {
495+ req , _ := http .NewRequestWithContext (context .Background (), "GET" , "/" , nil )
496+ req .Header .Add (headerAcceptEncoding , "gzip" )
497+
498+ router := gin .New ()
499+ router .Use (Gzip (DefaultCompression , WithMinLength (2048 )))
500+ router .GET ("/" , func (c * gin.Context ) {
501+ c .Header ("Content-Length" , "xyz" )
502+ c .String (200 , testResponse )
503+ })
504+
505+ w := httptest .NewRecorder ()
506+ router .ServeHTTP (w , req )
507+
508+ assert .Equal (t , 200 , w .Code )
509+ assert .Equal (t , "" , w .Header ().Get (headerContentEncoding ))
510+ assert .Equal (t , "19" , w .Header ().Get ("Content-Length" ))
511+ }
512+
513+ func TestFlush (t * testing.T ) {
514+ testC , _ := gin .CreateTestContext (httptest .NewRecorder ())
515+ gz := gzipWriter {
516+ ResponseWriter : testC .Writer ,
517+ writer : gzip .NewWriter (testC .Writer ),
518+ }
519+ _ , _ = gz .WriteString ("test" )
520+ gz .Flush ()
521+ assert .True (t , gz .Written ())
522+ }
523+
380524type hijackableResponse struct {
381525 Hijacked bool
382526 header http.Header
0 commit comments