httpencoder - golang net/http middleware for decode requests and encode responses based on Accept-Encoding and Content-Encoding headers
According to RFCs there is no 'Accept-Encoding' header at server side response. It means you cannot tell clients (browsers, include headless browsers like curl/python's request) that your server accept any encodings. But some of the backends (for example apache's mod_deflate) support decoding request body, thats why the same feature exists in this package.
There is a little overhead to compare to if strings.Contains(request.Header.Get("Accept-Encoding"), "myencoding")
:
$ go test -bench=. -benchmem -benchtime=10000000x
warning: GOPATH set to GOROOT (/home/user/go) has no effect
goos: linux
goarch: amd64
pkg: github.com/alexdyukov/httpencoder
cpu: AMD Ryzen 7 8845H w/ Radeon 780M Graphics
BenchmarkRaw-16 10000000 200.7 ns/op 720 B/op 5 allocs/op
BenchmarkIfedEncode-16 10000000 489.1 ns/op 1456 B/op 9 allocs/op
BenchmarkWrappedEncodeDecode-16 10000000 1083 ns/op 1569 B/op 15 allocs/op
BenchmarkWrappedDecode-16 10000000 391.4 ns/op 752 B/op 7 allocs/op
BenchmarkWrappedEncode-16 10000000 917.2 ns/op 1537 B/op 13 allocs/op
PASS
ok github.com/alexdyukov/httpencoder 30.844s
Gzip encoder/decoder:
type gzipper struct{}
func (gzipper) Encode(ctx context.Context, to io.Writer, from []byte) (err error) {
gzipWriter := gzip.NewWriter(to)
if _, err := gzipWriter.Write(from); err != nil {
reqID := ctx.Value(contextValueKey)
slog.Info("failed to gzip response", "request_id", reqID, "error", err.Error())
return fmt.Errorf("Internal server error occur. Your request id %v. Try again later or feel free to contact us to get detailed info", reqID)
}
if err := gzipWriter.Flush(); err != nil {
reqID := ctx.Value(contextValueKey)
slog.Info("failed to flush gzipped response", "request_id", reqID, "error", err.Error())
return fmt.Errorf("Internal server error occur. Your request id %v. Try again later or feel free to contact us to get detailed info", reqID)
}
return nil
}
func (gzipper) Decode(ctx context.Context, to io.Writer, from []byte) (err error) {
gzipReader, err := gzip.NewReader(bytes.NewReader(from))
if err != nil {
reqID := ctx.Value(contextValueKey)
slog.Info("failed to initialize gzip reader", "request_id", reqID, "error", err.Error())
return fmt.Errorf("Internal server error occur. Your request id %v. Try again later or feel free to contact us to get detailed info", reqID)
}
_, err = io.Copy(to, gzipReader)
if err != nil && !errors.Is(err, io.ErrUnexpectedEOF) {
reqID := ctx.Value(contextValueKey)
slog.Info("failed to read from gzip reader", "request_id", reqID, "error", err.Error())
return fmt.Errorf("Internal server error occur. Your request id %v. Try again later or feel free to contact us to get detailed info", reqID)
}
return nil
}
MIT licensed. See the included LICENSE file for details.