mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-30 22:52:25 -05:00 
			
		
		
		
	[feature] Gin enable gzip encoding (#405)
* add gin gzip dependency * add gzip middleware to router * go mod tidy
This commit is contained in:
		
					parent
					
						
							
								23034ec145
							
						
					
				
			
			
				commit
				
					
						a089a98ea9
					
				
			
		
					 11 changed files with 442 additions and 5 deletions
				
			
		
							
								
								
									
										21
									
								
								vendor/github.com/gin-contrib/gzip/LICENSE
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								vendor/github.com/gin-contrib/gzip/LICENSE
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,21 @@ | |||
| MIT License | ||||
| 
 | ||||
| Copyright (c) 2017 Gin-Gonic | ||||
| 
 | ||||
| Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
| of this software and associated documentation files (the "Software"), to deal | ||||
| in the Software without restriction, including without limitation the rights | ||||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
| copies of the Software, and to permit persons to whom the Software is | ||||
| furnished to do so, subject to the following conditions: | ||||
| 
 | ||||
| The above copyright notice and this permission notice shall be included in all | ||||
| copies or substantial portions of the Software. | ||||
| 
 | ||||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||||
| SOFTWARE. | ||||
							
								
								
									
										135
									
								
								vendor/github.com/gin-contrib/gzip/README.md
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										135
									
								
								vendor/github.com/gin-contrib/gzip/README.md
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,135 @@ | |||
| # GZIP gin's middleware | ||||
| 
 | ||||
| [](https://github.com/gin-contrib/gzip/actions/workflows/go.yml) | ||||
| [](https://codecov.io/gh/gin-contrib/gzip) | ||||
| [](https://goreportcard.com/report/github.com/gin-contrib/gzip) | ||||
| [](https://godoc.org/github.com/gin-contrib/gzip) | ||||
| [](https://gitter.im/gin-gonic/gin) | ||||
| 
 | ||||
| Gin middleware to enable `GZIP` support. | ||||
| 
 | ||||
| ## Usage | ||||
| 
 | ||||
| Download and install it: | ||||
| 
 | ||||
| ```sh | ||||
| go get github.com/gin-contrib/gzip | ||||
| ``` | ||||
| 
 | ||||
| Import it in your code: | ||||
| 
 | ||||
| ```go | ||||
| import "github.com/gin-contrib/gzip" | ||||
| ``` | ||||
| 
 | ||||
| Canonical example: | ||||
| 
 | ||||
| ```go | ||||
| package main | ||||
| 
 | ||||
| import ( | ||||
|   "fmt" | ||||
|   "net/http" | ||||
|   "time" | ||||
| 
 | ||||
|   "github.com/gin-contrib/gzip" | ||||
|   "github.com/gin-gonic/gin" | ||||
| ) | ||||
| 
 | ||||
| func main() { | ||||
|   r := gin.Default() | ||||
|   r.Use(gzip.Gzip(gzip.DefaultCompression)) | ||||
|   r.GET("/ping", func(c *gin.Context) { | ||||
|     c.String(http.StatusOK, "pong "+fmt.Sprint(time.Now().Unix())) | ||||
|   }) | ||||
| 
 | ||||
|   // Listen and Server in 0.0.0.0:8080 | ||||
|   if err := r.Run(":8080"); err != nil { | ||||
|     log.Fatal(err) | ||||
|   } | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| Customized Excluded Extensions | ||||
| 
 | ||||
| ```go | ||||
| package main | ||||
| 
 | ||||
| import ( | ||||
|   "fmt" | ||||
|   "net/http" | ||||
|   "time" | ||||
| 
 | ||||
|   "github.com/gin-contrib/gzip" | ||||
|   "github.com/gin-gonic/gin" | ||||
| ) | ||||
| 
 | ||||
| func main() { | ||||
|   r := gin.Default() | ||||
|   r.Use(gzip.Gzip(gzip.DefaultCompression, gzip.WithExcludedExtensions([]string{".pdf", ".mp4"}))) | ||||
|   r.GET("/ping", func(c *gin.Context) { | ||||
|     c.String(http.StatusOK, "pong "+fmt.Sprint(time.Now().Unix())) | ||||
|   }) | ||||
| 
 | ||||
|   // Listen and Server in 0.0.0.0:8080 | ||||
|   if err := r.Run(":8080"); err != nil { | ||||
|     log.Fatal(err) | ||||
|   } | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| Customized Excluded Paths | ||||
| 
 | ||||
| ```go | ||||
| package main | ||||
| 
 | ||||
| import ( | ||||
|   "fmt" | ||||
|   "net/http" | ||||
|   "time" | ||||
| 
 | ||||
|   "github.com/gin-contrib/gzip" | ||||
|   "github.com/gin-gonic/gin" | ||||
| ) | ||||
| 
 | ||||
| func main() { | ||||
|   r := gin.Default() | ||||
|   r.Use(gzip.Gzip(gzip.DefaultCompression, gzip.WithExcludedPaths([]string{"/api/"}))) | ||||
|   r.GET("/ping", func(c *gin.Context) { | ||||
|     c.String(http.StatusOK, "pong "+fmt.Sprint(time.Now().Unix())) | ||||
|   }) | ||||
| 
 | ||||
|   // Listen and Server in 0.0.0.0:8080 | ||||
|   if err := r.Run(":8080"); err != nil { | ||||
|     log.Fatal(err) | ||||
|   } | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| Customized Excluded Paths | ||||
| 
 | ||||
| ```go | ||||
| package main | ||||
| 
 | ||||
| import ( | ||||
|   "fmt" | ||||
|   "net/http" | ||||
|   "time" | ||||
| 
 | ||||
|   "github.com/gin-contrib/gzip" | ||||
|   "github.com/gin-gonic/gin" | ||||
| ) | ||||
| 
 | ||||
| func main() { | ||||
|   r := gin.Default() | ||||
|   r.Use(gzip.Gzip(gzip.DefaultCompression, gzip.WithExcludedPathsRegexs([]string{".*"}))) | ||||
|   r.GET("/ping", func(c *gin.Context) { | ||||
|     c.String(http.StatusOK, "pong "+fmt.Sprint(time.Now().Unix())) | ||||
|   }) | ||||
| 
 | ||||
|   // Listen and Server in 0.0.0.0:8080 | ||||
|   if err := r.Run(":8080"); err != nil { | ||||
|     log.Fatal(err) | ||||
|   } | ||||
| } | ||||
| ``` | ||||
							
								
								
									
										39
									
								
								vendor/github.com/gin-contrib/gzip/gzip.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								vendor/github.com/gin-contrib/gzip/gzip.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,39 @@ | |||
| package gzip | ||||
| 
 | ||||
| import ( | ||||
| 	"compress/gzip" | ||||
| 
 | ||||
| 	"github.com/gin-gonic/gin" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	BestCompression    = gzip.BestCompression | ||||
| 	BestSpeed          = gzip.BestSpeed | ||||
| 	DefaultCompression = gzip.DefaultCompression | ||||
| 	NoCompression      = gzip.NoCompression | ||||
| ) | ||||
| 
 | ||||
| func Gzip(level int, options ...Option) gin.HandlerFunc { | ||||
| 	return newGzipHandler(level, options...).Handle | ||||
| } | ||||
| 
 | ||||
| type gzipWriter struct { | ||||
| 	gin.ResponseWriter | ||||
| 	writer *gzip.Writer | ||||
| } | ||||
| 
 | ||||
| func (g *gzipWriter) WriteString(s string) (int, error) { | ||||
| 	g.Header().Del("Content-Length") | ||||
| 	return g.writer.Write([]byte(s)) | ||||
| } | ||||
| 
 | ||||
| func (g *gzipWriter) Write(data []byte) (int, error) { | ||||
| 	g.Header().Del("Content-Length") | ||||
| 	return g.writer.Write(data) | ||||
| } | ||||
| 
 | ||||
| // Fix: https://github.com/mholt/caddy/issues/38 | ||||
| func (g *gzipWriter) WriteHeader(code int) { | ||||
| 	g.Header().Del("Content-Length") | ||||
| 	g.ResponseWriter.WriteHeader(code) | ||||
| } | ||||
							
								
								
									
										84
									
								
								vendor/github.com/gin-contrib/gzip/handler.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								vendor/github.com/gin-contrib/gzip/handler.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,84 @@ | |||
| package gzip | ||||
| 
 | ||||
| import ( | ||||
| 	"compress/gzip" | ||||
| 	"fmt" | ||||
| 	"io/ioutil" | ||||
| 	"net/http" | ||||
| 	"path/filepath" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
| 
 | ||||
| 	"github.com/gin-gonic/gin" | ||||
| ) | ||||
| 
 | ||||
| type gzipHandler struct { | ||||
| 	*Options | ||||
| 	gzPool sync.Pool | ||||
| } | ||||
| 
 | ||||
| func newGzipHandler(level int, options ...Option) *gzipHandler { | ||||
| 	handler := &gzipHandler{ | ||||
| 		Options: DefaultOptions, | ||||
| 		gzPool: sync.Pool{ | ||||
| 			New: func() interface{} { | ||||
| 				gz, err := gzip.NewWriterLevel(ioutil.Discard, level) | ||||
| 				if err != nil { | ||||
| 					panic(err) | ||||
| 				} | ||||
| 				return gz | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
| 	for _, setter := range options { | ||||
| 		setter(handler.Options) | ||||
| 	} | ||||
| 	return handler | ||||
| } | ||||
| 
 | ||||
| func (g *gzipHandler) Handle(c *gin.Context) { | ||||
| 	if fn := g.DecompressFn; fn != nil && c.Request.Header.Get("Content-Encoding") == "gzip" { | ||||
| 		fn(c) | ||||
| 	} | ||||
| 
 | ||||
| 	if !g.shouldCompress(c.Request) { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	gz := g.gzPool.Get().(*gzip.Writer) | ||||
| 	defer g.gzPool.Put(gz) | ||||
| 	defer gz.Reset(ioutil.Discard) | ||||
| 	gz.Reset(c.Writer) | ||||
| 
 | ||||
| 	c.Header("Content-Encoding", "gzip") | ||||
| 	c.Header("Vary", "Accept-Encoding") | ||||
| 	c.Writer = &gzipWriter{c.Writer, gz} | ||||
| 	defer func() { | ||||
| 		gz.Close() | ||||
| 		c.Header("Content-Length", fmt.Sprint(c.Writer.Size())) | ||||
| 	}() | ||||
| 	c.Next() | ||||
| } | ||||
| 
 | ||||
| func (g *gzipHandler) shouldCompress(req *http.Request) bool { | ||||
| 	if !strings.Contains(req.Header.Get("Accept-Encoding"), "gzip") || | ||||
| 		strings.Contains(req.Header.Get("Connection"), "Upgrade") || | ||||
| 		strings.Contains(req.Header.Get("Accept"), "text/event-stream") { | ||||
| 
 | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| 	extension := filepath.Ext(req.URL.Path) | ||||
| 	if g.ExcludedExtensions.Contains(extension) { | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| 	if g.ExcludedPaths.Contains(req.URL.Path) { | ||||
| 		return false | ||||
| 	} | ||||
| 	if g.ExcludedPathesRegexs.Contains(req.URL.Path) { | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| 	return true | ||||
| } | ||||
							
								
								
									
										116
									
								
								vendor/github.com/gin-contrib/gzip/options.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								vendor/github.com/gin-contrib/gzip/options.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,116 @@ | |||
| package gzip | ||||
| 
 | ||||
| import ( | ||||
| 	"compress/gzip" | ||||
| 	"net/http" | ||||
| 	"regexp" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	"github.com/gin-gonic/gin" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	DefaultExcludedExtentions = NewExcludedExtensions([]string{ | ||||
| 		".png", ".gif", ".jpeg", ".jpg", | ||||
| 	}) | ||||
| 	DefaultOptions = &Options{ | ||||
| 		ExcludedExtensions: DefaultExcludedExtentions, | ||||
| 	} | ||||
| ) | ||||
| 
 | ||||
| type Options struct { | ||||
| 	ExcludedExtensions   ExcludedExtensions | ||||
| 	ExcludedPaths        ExcludedPaths | ||||
| 	ExcludedPathesRegexs ExcludedPathesRegexs | ||||
| 	DecompressFn         func(c *gin.Context) | ||||
| } | ||||
| 
 | ||||
| type Option func(*Options) | ||||
| 
 | ||||
| func WithExcludedExtensions(args []string) Option { | ||||
| 	return func(o *Options) { | ||||
| 		o.ExcludedExtensions = NewExcludedExtensions(args) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func WithExcludedPaths(args []string) Option { | ||||
| 	return func(o *Options) { | ||||
| 		o.ExcludedPaths = NewExcludedPaths(args) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func WithExcludedPathsRegexs(args []string) Option { | ||||
| 	return func(o *Options) { | ||||
| 		o.ExcludedPathesRegexs = NewExcludedPathesRegexs(args) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func WithDecompressFn(decompressFn func(c *gin.Context)) Option { | ||||
| 	return func(o *Options) { | ||||
| 		o.DecompressFn = decompressFn | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Using map for better lookup performance | ||||
| type ExcludedExtensions map[string]bool | ||||
| 
 | ||||
| func NewExcludedExtensions(extensions []string) ExcludedExtensions { | ||||
| 	res := make(ExcludedExtensions) | ||||
| 	for _, e := range extensions { | ||||
| 		res[e] = true | ||||
| 	} | ||||
| 	return res | ||||
| } | ||||
| 
 | ||||
| func (e ExcludedExtensions) Contains(target string) bool { | ||||
| 	_, ok := e[target] | ||||
| 	return ok | ||||
| } | ||||
| 
 | ||||
| type ExcludedPaths []string | ||||
| 
 | ||||
| func NewExcludedPaths(paths []string) ExcludedPaths { | ||||
| 	return ExcludedPaths(paths) | ||||
| } | ||||
| 
 | ||||
| func (e ExcludedPaths) Contains(requestURI string) bool { | ||||
| 	for _, path := range e { | ||||
| 		if strings.HasPrefix(requestURI, path) { | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
| 
 | ||||
| type ExcludedPathesRegexs []*regexp.Regexp | ||||
| 
 | ||||
| func NewExcludedPathesRegexs(regexs []string) ExcludedPathesRegexs { | ||||
| 	result := make([]*regexp.Regexp, len(regexs)) | ||||
| 	for i, reg := range regexs { | ||||
| 		result[i] = regexp.MustCompile(reg) | ||||
| 	} | ||||
| 	return result | ||||
| } | ||||
| 
 | ||||
| func (e ExcludedPathesRegexs) Contains(requestURI string) bool { | ||||
| 	for _, reg := range e { | ||||
| 		if reg.MatchString(requestURI) { | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
| 
 | ||||
| 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 | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue