From 95dcdd6025ecb607d515b5101e66fb966ca8ada3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=B8=D1=80=D0=B8=D0=BB=D0=BB=20=D0=93=D0=BE=D1=80?= =?UTF-8?q?=D0=B1=D1=83=D0=BD=D0=BE=D0=B2?= Date: Mon, 11 Mar 2024 13:21:11 +0300 Subject: [PATCH] =?UTF-8?q?gzip=20for=20v3=20#153=20(#183)=20Co-authored-b?= =?UTF-8?q?y:=20=D0=9A=D0=B8=D1=80=D0=B8=D0=BB=D0=BB=20=D0=93=D0=BE=D1=80?= =?UTF-8?q?=D0=B1=D1=83=D0=BD=D0=BE=D0=B2=20=20Co-committed-by:=20=D0=9A=D0=B8=D1=80=D0=B8=D0=BB=D0=BB?= =?UTF-8?q?=20=D0=93=D0=BE=D1=80=D0=B1=D1=83=D0=BD=D0=BE=D0=B2=20?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- handler/meter/meter.go | 63 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 60 insertions(+), 3 deletions(-) diff --git a/handler/meter/meter.go b/handler/meter/meter.go index f92830e..76974ce 100644 --- a/handler/meter/meter.go +++ b/handler/meter/meter.go @@ -2,13 +2,36 @@ package meter // import "go.unistack.org/micro-server-http/v3/handler/meter" import ( "bytes" + "compress/gzip" "context" + "io" + "strings" + "sync" codecpb "go.unistack.org/micro-proto/v3/codec" "go.unistack.org/micro/v3/errors" + "go.unistack.org/micro/v3/logger" + "go.unistack.org/micro/v3/metadata" "go.unistack.org/micro/v3/meter" ) +const ( + contentEncodingHeader = "Content-Encoding" + acceptEncodingHeader = "Accept-Encoding" +) + +var gzipPool = sync.Pool{ + New: func() interface{} { + return gzip.NewWriter(nil) + }, +} + +var bufPool = sync.Pool{ + New: func() interface{} { + return bytes.NewBuffer(nil) + }, +} + // guard to fail early var _ MeterServiceServer = &Handler{} @@ -56,12 +79,46 @@ func NewHandler(opts ...Option) *Handler { } func (h *Handler) Metrics(ctx context.Context, req *codecpb.Frame, rsp *codecpb.Frame) error { - buf := bytes.NewBuffer(nil) - if err := h.opts.Meter.Write(buf, h.opts.MeterOptions...); err != nil { - return errors.InternalServerError(h.opts.Name, "%v", err) + log, ok := logger.FromContext(ctx) + if !ok { + log = logger.DefaultLogger() + } + + buf := bufPool.Get().(*bytes.Buffer) + defer bufPool.Put(buf) + buf.Reset() + + w := io.Writer(buf) + + if md, ok := metadata.FromIncomingContext(ctx); gzipAccepted(md) && ok { + md.Set(contentEncodingHeader, "gzip") + gz := gzipPool.Get().(*gzip.Writer) + defer gzipPool.Put(gz) + + gz.Reset(w) + defer gz.Close() + + w = gz + } + + if err := h.opts.Meter.Write(w, h.opts.MeterOptions...); err != nil { + log.Error(ctx, errors.InternalServerError(h.opts.Name, "%v", err)) + return nil } rsp.Data = buf.Bytes() return nil } + +// gzipAccepted returns whether the client will accept gzip-encoded content. +func gzipAccepted(md metadata.Metadata) bool { + a, ok := md.Get(acceptEncodingHeader) + if !ok { + return false + } + if strings.Contains(a, "gzip") { + return true + } + return false +}