diff options
author | 2025-01-27 11:06:46 +0000 | |
---|---|---|
committer | 2025-01-27 11:06:46 +0000 | |
commit | 5c96702cb5d9461b35c232858a3c91ab699dec7d (patch) | |
tree | d11da1c2140aadb19c5888c545af81ab3d9f6081 /vendor/github.com/gin-contrib | |
parent | [chore] Allow suppressing trusted-proxies warning by disabling rate limiting ... (diff) | |
download | gotosocial-5c96702cb5d9461b35c232858a3c91ab699dec7d.tar.xz |
[chore]: Bump github.com/gin-contrib/gzip from 1.1.0 to 1.2.2 (#3693)
Bumps [github.com/gin-contrib/gzip](https://github.com/gin-contrib/gzip) from 1.1.0 to 1.2.2.
- [Release notes](https://github.com/gin-contrib/gzip/releases)
- [Changelog](https://github.com/gin-contrib/gzip/blob/master/.goreleaser.yaml)
- [Commits](https://github.com/gin-contrib/gzip/compare/v1.1.0...v1.2.2)
---
updated-dependencies:
- dependency-name: github.com/gin-contrib/gzip
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Diffstat (limited to 'vendor/github.com/gin-contrib')
-rw-r--r-- | vendor/github.com/gin-contrib/gzip/README.md | 41 | ||||
-rw-r--r-- | vendor/github.com/gin-contrib/gzip/gzip.go | 28 | ||||
-rw-r--r-- | vendor/github.com/gin-contrib/gzip/handler.go | 90 | ||||
-rw-r--r-- | vendor/github.com/gin-contrib/gzip/options.go | 208 | ||||
-rw-r--r-- | vendor/github.com/gin-contrib/sse/.golangci.yml | 3 | ||||
-rw-r--r-- | vendor/github.com/gin-contrib/sse/.goreleaser.yaml | 29 | ||||
-rw-r--r-- | vendor/github.com/gin-contrib/sse/.travis.yml | 26 | ||||
-rw-r--r-- | vendor/github.com/gin-contrib/sse/README.md | 44 | ||||
-rw-r--r-- | vendor/github.com/gin-contrib/sse/sse-encoder.go | 10 |
9 files changed, 371 insertions, 108 deletions
diff --git a/vendor/github.com/gin-contrib/gzip/README.md b/vendor/github.com/gin-contrib/gzip/README.md index 86469858f..bb651977c 100644 --- a/vendor/github.com/gin-contrib/gzip/README.md +++ b/vendor/github.com/gin-contrib/gzip/README.md @@ -49,7 +49,7 @@ func main() { } ``` -Customized Excluded Extensions +### Customized Excluded Extensions ```go package main @@ -77,7 +77,7 @@ func main() { } ``` -Customized Excluded Paths +### Customized Excluded Paths ```go package main @@ -105,7 +105,7 @@ func main() { } ``` -Customized Excluded Paths +### Customized Excluded Paths with Regex ```go package main @@ -132,3 +132,38 @@ func main() { } } ``` + +### Server Push + +```go +package main + +import ( + "fmt" + "log" + "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("/stream", func(c *gin.Context) { + c.Header("Content-Type", "text/event-stream") + c.Header("Connection", "keep-alive") + for i := 0; i < 10; i++ { + fmt.Fprintf(c.Writer, "id: %d\ndata: tick %d\n\n", i, time.Now().Unix()) + c.Writer.Flush() + time.Sleep(1 * time.Second) + } + }) + + // Listen and Server in 0.0.0.0:8080 + if err := r.Run(":8080"); err != nil { + log.Fatal(err) + } +} +``` diff --git a/vendor/github.com/gin-contrib/gzip/gzip.go b/vendor/github.com/gin-contrib/gzip/gzip.go index 529c62df6..931945a88 100644 --- a/vendor/github.com/gin-contrib/gzip/gzip.go +++ b/vendor/github.com/gin-contrib/gzip/gzip.go @@ -1,7 +1,11 @@ package gzip import ( + "bufio" "compress/gzip" + "errors" + "net" + "net/http" "github.com/gin-gonic/gin" ) @@ -11,6 +15,7 @@ const ( BestSpeed = gzip.BestSpeed DefaultCompression = gzip.DefaultCompression NoCompression = gzip.NoCompression + HuffmanOnly = gzip.HuffmanOnly ) func Gzip(level int, options ...Option) gin.HandlerFunc { @@ -32,8 +37,31 @@ func (g *gzipWriter) Write(data []byte) (int, error) { return g.writer.Write(data) } +func (g *gzipWriter) Flush() { + _ = g.writer.Flush() + g.ResponseWriter.Flush() +} + // Fix: https://github.com/mholt/caddy/issues/38 func (g *gzipWriter) WriteHeader(code int) { g.Header().Del("Content-Length") g.ResponseWriter.WriteHeader(code) } + +// Ensure gzipWriter implements the http.Hijacker interface. +// This will cause a compile-time error if gzipWriter does not implement all methods of the http.Hijacker interface. +var _ http.Hijacker = (*gzipWriter)(nil) + +// Hijack allows the caller to take over the connection from the HTTP server. +// After a call to Hijack, the HTTP server library will not do anything else with the connection. +// It becomes the caller's responsibility to manage and close the connection. +// +// It returns the underlying net.Conn, a buffered reader/writer for the connection, and an error +// if the ResponseWriter does not support the Hijacker interface. +func (g *gzipWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { + hijacker, ok := g.ResponseWriter.(http.Hijacker) + if !ok { + return nil, nil, errors.New("the ResponseWriter doesn't support the Hijacker interface") + } + return hijacker.Hijack() +} diff --git a/vendor/github.com/gin-contrib/gzip/handler.go b/vendor/github.com/gin-contrib/gzip/handler.go index ee9eb9f66..412c8386b 100644 --- a/vendor/github.com/gin-contrib/gzip/handler.go +++ b/vendor/github.com/gin-contrib/gzip/handler.go @@ -2,84 +2,114 @@ package gzip import ( "compress/gzip" - "fmt" "io" "net/http" "path/filepath" + "strconv" "strings" "sync" "github.com/gin-gonic/gin" ) +const ( + headerAcceptEncoding = "Accept-Encoding" + headerContentEncoding = "Content-Encoding" + headerVary = "Vary" +) + type gzipHandler struct { - *Options + *config gzPool sync.Pool } -func newGzipHandler(level int, options ...Option) *gzipHandler { +func isCompressionLevelValid(level int) bool { + return level == gzip.DefaultCompression || + level == gzip.NoCompression || + (level >= gzip.BestSpeed && level <= gzip.BestCompression) +} + +func newGzipHandler(level int, opts ...Option) *gzipHandler { + cfg := &config{ + excludedExtensions: DefaultExcludedExtentions, + } + + // Apply each option to the config + for _, o := range opts { + o.apply(cfg) + } + + if !isCompressionLevelValid(level) { + // For web content, level 4 seems to be a sweet spot. + level = 4 + } + handler := &gzipHandler{ - Options: DefaultOptions, + config: cfg, gzPool: sync.Pool{ New: func() interface{} { - gz, err := gzip.NewWriterLevel(io.Discard, level) - if err != nil { - panic(err) - } + gz, _ := gzip.NewWriterLevel(io.Discard, level) return gz }, }, } - for _, setter := range options { - setter(handler.Options) - } return handler } +// Handle is a middleware function for handling gzip compression in HTTP requests and responses. +// It first checks if the request has a "Content-Encoding" header set to "gzip" and if a decompression +// function is provided, it will call the decompression function. If the handler is set to decompress only, +// or if the custom compression decision function indicates not to compress, it will return early. +// Otherwise, it retrieves a gzip.Writer from the pool, sets the necessary response headers for gzip encoding, +// and wraps the response writer with a gzipWriter. After the request is processed, it ensures the gzip.Writer +// is properly closed and the "Content-Length" header is set based on the response size. func (g *gzipHandler) Handle(c *gin.Context) { - if fn := g.DecompressFn; fn != nil && c.Request.Header.Get("Content-Encoding") == "gzip" { + if fn := g.decompressFn; fn != nil && strings.Contains(c.Request.Header.Get("Content-Encoding"), "gzip") { fn(c) } - if !g.shouldCompress(c.Request) { + if g.decompressOnly || + (g.customShouldCompressFn != nil && !g.customShouldCompressFn(c)) || + (g.customShouldCompressFn == nil && !g.shouldCompress(c.Request)) { return } gz := g.gzPool.Get().(*gzip.Writer) - defer g.gzPool.Put(gz) - defer gz.Reset(io.Discard) gz.Reset(c.Writer) - c.Header("Content-Encoding", "gzip") - c.Header("Vary", "Accept-Encoding") + c.Header(headerContentEncoding, "gzip") + c.Writer.Header().Add(headerVary, headerAcceptEncoding) + // check ETag Header + originalEtag := c.GetHeader("ETag") + if originalEtag != "" && !strings.HasPrefix(originalEtag, "W/") { + c.Header("ETag", "W/"+originalEtag) + } c.Writer = &gzipWriter{c.Writer, gz} defer func() { if c.Writer.Size() < 0 { // do not write gzip footer when nothing is written to the response body gz.Reset(io.Discard) } - gz.Close() - c.Header("Content-Length", fmt.Sprint(c.Writer.Size())) + _ = gz.Close() + if c.Writer.Size() > -1 { + c.Header("Content-Length", strconv.Itoa(c.Writer.Size())) + } + g.gzPool.Put(gz) }() 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") { + if !strings.Contains(req.Header.Get(headerAcceptEncoding), "gzip") || + strings.Contains(req.Header.Get("Connection"), "Upgrade") { return false } + // Check if the request path is excluded from compression 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) { + if g.excludedExtensions.Contains(extension) || + g.excludedPaths.Contains(req.URL.Path) || + g.excludedPathesRegexs.Contains(req.URL.Path) { return false } diff --git a/vendor/github.com/gin-contrib/gzip/options.go b/vendor/github.com/gin-contrib/gzip/options.go index c0953e08a..67607f51b 100644 --- a/vendor/github.com/gin-contrib/gzip/options.go +++ b/vendor/github.com/gin-contrib/gzip/options.go @@ -2,6 +2,8 @@ package gzip import ( "compress/gzip" + "errors" + "io" "net/http" "regexp" "strings" @@ -10,58 +12,134 @@ import ( ) var ( + // 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. DefaultExcludedExtentions = NewExcludedExtensions([]string{ ".png", ".gif", ".jpeg", ".jpg", }) - DefaultOptions = &Options{ - ExcludedExtensions: DefaultExcludedExtentions, - } + // ErrUnsupportedContentEncoding is an error that indicates the content encoding + // is not supported by the application. + ErrUnsupportedContentEncoding = errors.New("unsupported content encoding") ) -type Options struct { - ExcludedExtensions ExcludedExtensions - ExcludedPaths ExcludedPaths - ExcludedPathesRegexs ExcludedPathesRegexs - DecompressFn func(c *gin.Context) +// 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 Option func(*Options) +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 func(o *Options) { - o.ExcludedExtensions = NewExcludedExtensions(args) - } + 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 func(o *Options) { - o.ExcludedPaths = NewExcludedPaths(args) - } + 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 func(o *Options) { - o.ExcludedPathesRegexs = NewExcludedPathesRegexs(args) - } + 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 func(o *Options) { - o.DecompressFn = decompressFn - } + 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) + 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 @@ -69,10 +147,22 @@ func (e ExcludedExtensions) Contains(target string) bool { 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) { @@ -84,14 +174,26 @@ func (e ExcludedPaths) Contains(requestURI string) bool { 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([]*regexp.Regexp, len(regexs)) + 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) { @@ -101,16 +203,68 @@ func (e ExcludedPathesRegexs) Contains(requestURI string) bool { 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) + + contentEncodingField := strings.Split(strings.ToLower(c.GetHeader("Content-Encoding")), ",") + if len(contentEncodingField) == 0 { // nothing to decompress + c.Next() + return } + + toClose := make([]io.Closer, 0, len(contentEncodingField)) + defer func() { + for i := len(toClose); i > 0; i-- { + toClose[i-1].Close() + } + }() + + // parses multiply gzips like + // Content-Encoding: gzip, gzip, gzip + // allowed by RFC + for i := 0; i < len(contentEncodingField); i++ { + trimmedValue := strings.TrimSpace(contentEncodingField[i]) + + if trimmedValue == "" { + continue + } + + if trimmedValue != "gzip" { + // According to RFC 7231, Section 3.1.2.2: + // https://www.rfc-editor.org/rfc/rfc7231#section-3.1.2.2 + // An origin server MAY respond with a status code of 415 (Unsupported + // Media Type) if a representation in the request message has a content + // coding that is not acceptable. + _ = c.AbortWithError(http.StatusUnsupportedMediaType, ErrUnsupportedContentEncoding) + } + + r, err := gzip.NewReader(c.Request.Body) + if err != nil { + _ = c.AbortWithError(http.StatusBadRequest, err) + + return + } + + toClose = append(toClose, c.Request.Body) + + c.Request.Body = r + } + c.Request.Header.Del("Content-Encoding") c.Request.Header.Del("Content-Length") - c.Request.Body = r + + c.Next() } diff --git a/vendor/github.com/gin-contrib/sse/.golangci.yml b/vendor/github.com/gin-contrib/sse/.golangci.yml new file mode 100644 index 000000000..4c44c5fae --- /dev/null +++ b/vendor/github.com/gin-contrib/sse/.golangci.yml @@ -0,0 +1,3 @@ +linters: + disable: + - errcheck diff --git a/vendor/github.com/gin-contrib/sse/.goreleaser.yaml b/vendor/github.com/gin-contrib/sse/.goreleaser.yaml new file mode 100644 index 000000000..4c910add4 --- /dev/null +++ b/vendor/github.com/gin-contrib/sse/.goreleaser.yaml @@ -0,0 +1,29 @@ +builds: + - # If true, skip the build. + # Useful for library projects. + # Default is false + skip: true + +changelog: + use: github + groups: + - title: Features + regexp: "^.*feat[(\\w)]*:+.*$" + order: 0 + - title: "Bug fixes" + regexp: "^.*fix[(\\w)]*:+.*$" + order: 1 + - title: "Enhancements" + regexp: "^.*chore[(\\w)]*:+.*$" + order: 2 + - title: "Refactor" + regexp: "^.*refactor[(\\w)]*:+.*$" + order: 3 + - title: "Build process updates" + regexp: ^.*?(build|ci)(\(.+\))??!?:.+$ + order: 4 + - title: "Documentation updates" + regexp: ^.*?docs?(\(.+\))??!?:.+$ + order: 4 + - title: Others + order: 999 diff --git a/vendor/github.com/gin-contrib/sse/.travis.yml b/vendor/github.com/gin-contrib/sse/.travis.yml deleted file mode 100644 index d0e8fcf99..000000000 --- a/vendor/github.com/gin-contrib/sse/.travis.yml +++ /dev/null @@ -1,26 +0,0 @@ -language: go -sudo: false -go: - - 1.8.x - - 1.9.x - - 1.10.x - - 1.11.x - - 1.12.x - - master - -git: - depth: 10 - -matrix: - fast_finish: true - include: - - go: 1.11.x - env: GO111MODULE=on - - go: 1.12.x - env: GO111MODULE=on - -script: - - go test -v -covermode=count -coverprofile=coverage.out - -after_success: - - bash <(curl -s https://codecov.io/bash) diff --git a/vendor/github.com/gin-contrib/sse/README.md b/vendor/github.com/gin-contrib/sse/README.md index c9c49cf94..cfe2c820b 100644 --- a/vendor/github.com/gin-contrib/sse/README.md +++ b/vendor/github.com/gin-contrib/sse/README.md @@ -1,7 +1,7 @@ # Server-Sent Events -[](https://godoc.org/github.com/gin-contrib/sse) -[](https://travis-ci.org/gin-contrib/sse) +[](https://pkg.go.dev/github.com/gin-contrib/sse) +[](https://github.com/gin-contrib/sse/actions/workflows/go.yml) [](https://codecov.io/gh/gin-contrib/sse) [](https://goreportcard.com/report/github.com/gin-contrib/sse) @@ -16,32 +16,33 @@ Server-sent events (SSE) is a technology where a browser receives automatic upda import "github.com/gin-contrib/sse" func httpHandler(w http.ResponseWriter, req *http.Request) { - // data can be a primitive like a string, an integer or a float - sse.Encode(w, sse.Event{ - Event: "message", - Data: "some data\nmore data", - }) - - // also a complex type, like a map, a struct or a slice - sse.Encode(w, sse.Event{ - Id: "124", - Event: "message", - Data: map[string]interface{}{ - "user": "manu", - "date": time.Now().Unix(), - "content": "hi!", - }, - }) + // data can be a primitive like a string, an integer or a float + sse.Encode(w, sse.Event{ + Event: "message", + Data: "some data\nmore data", + }) + + // also a complex type, like a map, a struct or a slice + sse.Encode(w, sse.Event{ + Id: "124", + Event: "message", + Data: map[string]interface{}{ + "user": "manu", + "date": time.Now().Unix(), + "content": "hi!", + }, + }) } ``` -``` + +```sh event: message data: some data\\nmore data id: 124 event: message data: {"content":"hi!","date":1431540810,"user":"manu"} - + ``` ## Content-Type @@ -49,7 +50,8 @@ data: {"content":"hi!","date":1431540810,"user":"manu"} ```go fmt.Println(sse.ContentType) ``` -``` + +```sh text/event-stream ``` diff --git a/vendor/github.com/gin-contrib/sse/sse-encoder.go b/vendor/github.com/gin-contrib/sse/sse-encoder.go index f9c808750..0d26c82f0 100644 --- a/vendor/github.com/gin-contrib/sse/sse-encoder.go +++ b/vendor/github.com/gin-contrib/sse/sse-encoder.go @@ -18,7 +18,7 @@ import ( // W3C Working Draft 29 October 2009 // http://www.w3.org/TR/2009/WD-eventsource-20091029/ -const ContentType = "text/event-stream" +const ContentType = "text/event-stream;charset=utf-8" var contentType = []string{ContentType} var noCache = []string{"no-cache"} @@ -72,6 +72,14 @@ func writeRetry(w stringWriter, retry uint) { func writeData(w stringWriter, data interface{}) error { w.WriteString("data:") + + bData, ok := data.([]byte) + if ok { + dataReplacer.WriteString(w, string(bData)) + w.WriteString("\n\n") + return nil + } + switch kindOfData(data) { case reflect.Struct, reflect.Slice, reflect.Map: err := json.NewEncoder(w).Encode(data) |