summaryrefslogtreecommitdiff
path: root/vendor/github.com/gin-contrib
diff options
context:
space:
mode:
authorLibravatar dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>2025-01-27 11:06:46 +0000
committerLibravatar GitHub <noreply@github.com>2025-01-27 11:06:46 +0000
commit5c96702cb5d9461b35c232858a3c91ab699dec7d (patch)
treed11da1c2140aadb19c5888c545af81ab3d9f6081 /vendor/github.com/gin-contrib
parent[chore] Allow suppressing trusted-proxies warning by disabling rate limiting ... (diff)
downloadgotosocial-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.md41
-rw-r--r--vendor/github.com/gin-contrib/gzip/gzip.go28
-rw-r--r--vendor/github.com/gin-contrib/gzip/handler.go90
-rw-r--r--vendor/github.com/gin-contrib/gzip/options.go208
-rw-r--r--vendor/github.com/gin-contrib/sse/.golangci.yml3
-rw-r--r--vendor/github.com/gin-contrib/sse/.goreleaser.yaml29
-rw-r--r--vendor/github.com/gin-contrib/sse/.travis.yml26
-rw-r--r--vendor/github.com/gin-contrib/sse/README.md44
-rw-r--r--vendor/github.com/gin-contrib/sse/sse-encoder.go10
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
-[![GoDoc](https://godoc.org/github.com/gin-contrib/sse?status.svg)](https://godoc.org/github.com/gin-contrib/sse)
-[![Build Status](https://travis-ci.org/gin-contrib/sse.svg)](https://travis-ci.org/gin-contrib/sse)
+[![Go Reference](https://pkg.go.dev/badge/github.com/gin-contrib/sse.svg)](https://pkg.go.dev/github.com/gin-contrib/sse)
+[![Run Tests](https://github.com/gin-contrib/sse/actions/workflows/go.yml/badge.svg)](https://github.com/gin-contrib/sse/actions/workflows/go.yml)
[![codecov](https://codecov.io/gh/gin-contrib/sse/branch/master/graph/badge.svg)](https://codecov.io/gh/gin-contrib/sse)
[![Go Report Card](https://goreportcard.com/badge/github.com/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)