-
Notifications
You must be signed in to change notification settings - Fork 91
/
Copy pathoptions.go
221 lines (197 loc) · 7.36 KB
/
options.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
package gzip
import (
"compress/gzip"
"net/http"
"regexp"
"strings"
"github.com/gin-gonic/gin"
)
// DefaultExcludedExtentions is a predefined list of file extensions that should be excluded from gzip compression.
// These extensions typically represent image files that are already compressed
// and do not benefit from additional compression.
var DefaultExcludedExtentions = NewExcludedExtensions([]string{
".png", ".gif", ".jpeg", ".jpg",
})
// Option is an interface that defines a method to apply a configuration
// to a given config instance. Implementations of this interface can be
// used to modify the configuration settings of the logger.
type Option interface {
apply(*config)
}
// Ensures that optionFunc implements the Option interface at compile time.
// If optionFunc does not implement Option, a compile-time error will occur.
var _ Option = (*optionFunc)(nil)
type optionFunc func(*config)
func (o optionFunc) apply(c *config) {
o(c)
}
type config struct {
excludedExtensions ExcludedExtensions
excludedPaths ExcludedPaths
excludedPathesRegexs ExcludedPathesRegexs
decompressFn func(c *gin.Context)
decompressOnly bool
customShouldCompressFn func(c *gin.Context) bool
}
// WithExcludedExtensions returns an Option that sets the ExcludedExtensions field of the Options struct.
// Parameters:
// - args: []string - A slice of file extensions to exclude from gzip compression.
func WithExcludedExtensions(args []string) Option {
return optionFunc(func(o *config) {
o.excludedExtensions = NewExcludedExtensions(args)
})
}
// WithExcludedPaths returns an Option that sets the ExcludedPaths field of the Options struct.
// Parameters:
// - args: []string - A slice of paths to exclude from gzip compression.
func WithExcludedPaths(args []string) Option {
return optionFunc(func(o *config) {
o.excludedPaths = NewExcludedPaths(args)
})
}
// WithExcludedPathsRegexs returns an Option that sets the ExcludedPathesRegexs field of the Options struct.
// Parameters:
// - args: []string - A slice of regex patterns to exclude paths from gzip compression.
func WithExcludedPathsRegexs(args []string) Option {
return optionFunc(func(o *config) {
o.excludedPathesRegexs = NewExcludedPathesRegexs(args)
})
}
// WithDecompressFn returns an Option that sets the DecompressFn field of the Options struct.
// Parameters:
// - decompressFn: func(c *gin.Context) - A function to handle decompression of incoming requests.
func WithDecompressFn(decompressFn func(c *gin.Context)) Option {
return optionFunc(func(o *config) {
o.decompressFn = decompressFn
})
}
// WithDecompressOnly is an option that configures the gzip middleware to only
// decompress incoming requests without compressing the responses. When this
// option is enabled, the middleware will set the DecompressOnly field of the
// Options struct to true.
func WithDecompressOnly() Option {
return optionFunc(func(o *config) {
o.decompressOnly = true
})
}
// WithCustomShouldCompressFn returns an Option that sets the CustomShouldCompressFn field of the Options struct.
// Parameters:
// - fn: func(c *gin.Context) bool - A function to determine if a request should be compressed.
// The function should return true if the request should be compressed, false otherwise.
// If the function returns false, the middleware will not compress the response.
// If the function is nil, the middleware will use the default logic to determine
// if the response should be compressed.
//
// Returns:
// - Option - An option that sets the CustomShouldCompressFn field of the Options struct.
//
// Example:
//
// router.Use(gzip.Gzip(gzip.DefaultCompression, gzip.WithCustomShouldCompressFn(func(c *gin.Context) bool {
// return c.Request.URL.Path != "/no-compress"
// })))
func WithCustomShouldCompressFn(fn func(c *gin.Context) bool) Option {
return optionFunc(func(o *config) {
o.customShouldCompressFn = fn
})
}
// Using map for better lookup performance
type ExcludedExtensions map[string]struct{}
// NewExcludedExtensions creates a new ExcludedExtensions map from a slice of file extensions.
// Parameters:
// - extensions: []string - A slice of file extensions to exclude from gzip compression.
//
// Returns:
// - ExcludedExtensions - A map of excluded file extensions.
func NewExcludedExtensions(extensions []string) ExcludedExtensions {
res := make(ExcludedExtensions, len(extensions))
for _, e := range extensions {
res[e] = struct{}{}
}
return res
}
// Contains checks if a given file extension is in the ExcludedExtensions map.
// Parameters:
// - target: string - The file extension to check.
//
// Returns:
// - bool - True if the extension is excluded, false otherwise.
func (e ExcludedExtensions) Contains(target string) bool {
_, ok := e[target]
return ok
}
type ExcludedPaths []string
// NewExcludedPaths creates a new ExcludedPaths slice from a slice of paths.
// Parameters:
// - paths: []string - A slice of paths to exclude from gzip compression.
//
// Returns:
// - ExcludedPaths - A slice of excluded paths.
func NewExcludedPaths(paths []string) ExcludedPaths {
return ExcludedPaths(paths)
}
// Contains checks if a given request URI starts with any of the excluded paths.
// Parameters:
// - requestURI: string - The request URI to check.
//
// Returns:
// - bool - True if the URI starts with an excluded path, false otherwise.
func (e ExcludedPaths) Contains(requestURI string) bool {
for _, path := range e {
if strings.HasPrefix(requestURI, path) {
return true
}
}
return false
}
type ExcludedPathesRegexs []*regexp.Regexp
// NewExcludedPathesRegexs creates a new ExcludedPathesRegexs slice from a slice of regex patterns.
// Parameters:
// - regexs: []string - A slice of regex patterns to exclude paths from gzip compression.
//
// Returns:
// - ExcludedPathesRegexs - A slice of excluded path regex patterns.
func NewExcludedPathesRegexs(regexs []string) ExcludedPathesRegexs {
result := make(ExcludedPathesRegexs, len(regexs))
for i, reg := range regexs {
result[i] = regexp.MustCompile(reg)
}
return result
}
// Contains checks if a given request URI matches any of the excluded path regex patterns.
// Parameters:
// - requestURI: string - The request URI to check.
//
// Returns:
// - bool - True if the URI matches an excluded path regex pattern, false otherwise.
func (e ExcludedPathesRegexs) Contains(requestURI string) bool {
for _, reg := range e {
if reg.MatchString(requestURI) {
return true
}
}
return false
}
// DefaultDecompressHandle is a middleware function for the Gin framework that
// decompresses the request body if it is gzip encoded. It checks if the request
// body is nil and returns immediately if it is. Otherwise, it attempts to create
// a new gzip reader from the request body. If an error occurs during this process,
// it aborts the request with a 400 Bad Request status and the error. If successful,
// it removes the "Content-Encoding" and "Content-Length" headers from the request
// and replaces the request body with the decompressed reader.
//
// Parameters:
// - c: *gin.Context - The Gin context for the current request.
func DefaultDecompressHandle(c *gin.Context) {
if c.Request.Body == nil {
return
}
r, err := gzip.NewReader(c.Request.Body)
if err != nil {
_ = c.AbortWithError(http.StatusBadRequest, err)
return
}
c.Request.Header.Del("Content-Encoding")
c.Request.Header.Del("Content-Length")
c.Request.Body = r
}