Skip to content

Commit 53b6bc1

Browse files
authored
feat: introduce custom compression decision function in handler (#96)
* feat: introduce custom compression decision function in handler - Add detailed comments explaining the `Handle` function in `handler.go` - Modify the condition in `Handle` to include a custom compression decision function - Consolidate exclusion checks in `shouldCompress` into a single condition - Add a new field `customShouldCompressFn` to the `config` struct in `options.go` - Introduce `WithCustomShouldCompressFn` option for setting a custom compression decision function Signed-off-by: appleboy <[email protected]> * test: improve test coverage and optimize API performance - Add a test for custom compression function in gzip middleware Signed-off-by: appleboy <[email protected]> --------- Signed-off-by: appleboy <[email protected]>
1 parent 9bb40fc commit 53b6bc1

File tree

3 files changed

+67
-14
lines changed

3 files changed

+67
-14
lines changed

gzip_test.go

+25
Original file line numberDiff line numberDiff line change
@@ -319,3 +319,28 @@ func TestGzipWithDecompressOnly(t *testing.T) {
319319
assert.Equal(t, w.Header().Get("Content-Encoding"), "")
320320
assert.Equal(t, w.Body.String(), testResponse)
321321
}
322+
323+
func TestCustomShouldCompressFn(t *testing.T) {
324+
req, _ := http.NewRequestWithContext(context.Background(), "GET", "/", nil)
325+
req.Header.Add("Accept-Encoding", "gzip")
326+
327+
router := gin.New()
328+
router.Use(Gzip(
329+
DefaultCompression,
330+
WithCustomShouldCompressFn(func(_ *gin.Context) bool {
331+
return false
332+
}),
333+
))
334+
router.GET("/", func(c *gin.Context) {
335+
c.Header("Content-Length", strconv.Itoa(len(testResponse)))
336+
c.String(200, testResponse)
337+
})
338+
339+
w := httptest.NewRecorder()
340+
router.ServeHTTP(w, req)
341+
342+
assert.Equal(t, 200, w.Code)
343+
assert.Equal(t, "", w.Header().Get("Content-Encoding"))
344+
assert.Equal(t, "19", w.Header().Get("Content-Length"))
345+
assert.Equal(t, testResponse, w.Body.String())
346+
}

handler.go

+14-9
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,21 @@ func newGzipHandler(level int, opts ...Option) *gzipHandler {
4242
return handler
4343
}
4444

45+
// Handle is a middleware function for handling gzip compression in HTTP requests and responses.
46+
// It first checks if the request has a "Content-Encoding" header set to "gzip" and if a decompression
47+
// function is provided, it will call the decompression function. If the handler is set to decompress only,
48+
// or if the custom compression decision function indicates not to compress, it will return early.
49+
// Otherwise, it retrieves a gzip.Writer from the pool, sets the necessary response headers for gzip encoding,
50+
// and wraps the response writer with a gzipWriter. After the request is processed, it ensures the gzip.Writer
51+
// is properly closed and the "Content-Length" header is set based on the response size.
4552
func (g *gzipHandler) Handle(c *gin.Context) {
4653
if fn := g.decompressFn; fn != nil && c.Request.Header.Get("Content-Encoding") == "gzip" {
4754
fn(c)
4855
}
4956

50-
if g.decompressOnly || !g.shouldCompress(c.Request) {
57+
if g.decompressOnly ||
58+
(g.customShouldCompressFn != nil && !g.customShouldCompressFn(c)) ||
59+
(g.customShouldCompressFn == nil && !g.shouldCompress(c.Request)) {
5160
return
5261
}
5362

@@ -76,15 +85,11 @@ func (g *gzipHandler) shouldCompress(req *http.Request) bool {
7685
return false
7786
}
7887

88+
// Check if the request path is excluded from compression
7989
extension := filepath.Ext(req.URL.Path)
80-
if g.excludedExtensions.Contains(extension) {
81-
return false
82-
}
83-
84-
if g.excludedPaths.Contains(req.URL.Path) {
85-
return false
86-
}
87-
if g.excludedPathesRegexs.Contains(req.URL.Path) {
90+
if g.excludedExtensions.Contains(extension) ||
91+
g.excludedPaths.Contains(req.URL.Path) ||
92+
g.excludedPathesRegexs.Contains(req.URL.Path) {
8893
return false
8994
}
9095

options.go

+28-5
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,12 @@ func (o optionFunc) apply(c *config) {
3434
}
3535

3636
type config struct {
37-
excludedExtensions ExcludedExtensions
38-
excludedPaths ExcludedPaths
39-
excludedPathesRegexs ExcludedPathesRegexs
40-
decompressFn func(c *gin.Context)
41-
decompressOnly bool
37+
excludedExtensions ExcludedExtensions
38+
excludedPaths ExcludedPaths
39+
excludedPathesRegexs ExcludedPathesRegexs
40+
decompressFn func(c *gin.Context)
41+
decompressOnly bool
42+
customShouldCompressFn func(c *gin.Context) bool
4243
}
4344

4445
// WithExcludedExtensions returns an Option that sets the ExcludedExtensions field of the Options struct.
@@ -87,6 +88,28 @@ func WithDecompressOnly() Option {
8788
})
8889
}
8990

91+
// WithCustomShouldCompressFn returns an Option that sets the CustomShouldCompressFn field of the Options struct.
92+
// Parameters:
93+
// - fn: func(c *gin.Context) bool - A function to determine if a request should be compressed.
94+
// The function should return true if the request should be compressed, false otherwise.
95+
// If the function returns false, the middleware will not compress the response.
96+
// If the function is nil, the middleware will use the default logic to determine
97+
// if the response should be compressed.
98+
//
99+
// Returns:
100+
// - Option - An option that sets the CustomShouldCompressFn field of the Options struct.
101+
//
102+
// Example:
103+
//
104+
// router.Use(gzip.Gzip(gzip.DefaultCompression, gzip.WithCustomShouldCompressFn(func(c *gin.Context) bool {
105+
// return c.Request.URL.Path != "/no-compress"
106+
// })))
107+
func WithCustomShouldCompressFn(fn func(c *gin.Context) bool) Option {
108+
return optionFunc(func(o *config) {
109+
o.customShouldCompressFn = fn
110+
})
111+
}
112+
90113
// Using map for better lookup performance
91114
type ExcludedExtensions map[string]struct{}
92115

0 commit comments

Comments
 (0)