diff --git a/handler.go b/handler.go index f84ec83..9dc8f54 100644 --- a/handler.go +++ b/handler.go @@ -115,14 +115,14 @@ func (h *Server) HTTPHandlerFunc(handler interface{}) (http.HandlerFunc, error) md["Method"] = r.Method md["URL"] = r.URL.String() md["Proto"] = r.Proto - md["ContentLength"] = fmt.Sprintf("%d", r.ContentLength) - md["TransferEncoding"] = strings.Join(r.TransferEncoding, ",") + md["Content-Length"] = fmt.Sprintf("%d", r.ContentLength) + md["Transfer-Encoding"] = strings.Join(r.TransferEncoding, ",") md["Host"] = r.Host md["RequestURI"] = r.RequestURI if r.TLS != nil { md["TLS"] = "true" - md["TLS_ALPN"] = r.TLS.NegotiatedProtocol - md["TLS_ServerName"] = r.TLS.ServerName + md["TLS-ALPN"] = r.TLS.NegotiatedProtocol + md["TLS-ServerName"] = r.TLS.ServerName } ctx = metadata.NewIncomingContext(ctx, md) diff --git a/handler/meter/meter.go b/handler/meter/meter.go index f92830e..7fc3978 100644 --- a/handler/meter/meter.go +++ b/handler/meter/meter.go @@ -2,13 +2,35 @@ 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 +78,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, "http/meter write failed", 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 +}