func ContentTypes(types []string) option {
	return func(c *config) {
		c.contentTypes = []parsedContentType{}
		for _, v := range types {
			mediaType, params, err := mime.ParseMediaType(v)
			if err == nil {
				c.contentTypes = append(c.contentTypes, parsedContentType{mediaType, params})
			}
		}
	}
}
ContentTypes specifies a list of content types to compare
the Content-Type header to before compressing. If none
match, the response will be returned as-is.

Content types are compared in a case-insensitive, whitespace-ignored
manner.

A MIME type without any other directive will match a content type
that has the same MIME type, regardless of that content type's other
directives. I.e., "text/html" will match both "text/html" and
"text/html; charset=utf-8".

A MIME type with any other directive will only match a content type
that has the same MIME type and other directives. I.e.,
"text/html; charset=utf-8" will only match "text/html; charset=utf-8".

By default, responses are gzipped regardless of
Content-Type.
package gziphandler  
import (
	"bufio"
	"compress/gzip"
	"fmt"
	"io"
	"mime"
	"net"
	"net/http"
	"strconv"
	"strings"
	"sync"
)
const (
	vary            = "Vary"
	acceptEncoding  = "Accept-Encoding"
	contentEncoding = "Content-Encoding"
	contentType     = "Content-Type"
	contentLength   = "Content-Length"
)
type codings map[string]float64
const (
	DefaultQValue = 1.0
	DefaultMinSize = 1400
)
var gzipWriterPools [gzip.BestCompression - gzip.BestSpeed + 2]*sync.Pool
func init() {
	for i := gzip.BestSpeed; i <= gzip.BestCompression; i++ {
		addLevelPool(i)
	}
	addLevelPool(gzip.DefaultCompression)
}
func poolIndex(level int) int {
	if level == gzip.DefaultCompression {
		return gzip.BestCompression - gzip.BestSpeed + 1
	}
	return level - gzip.BestSpeed
}
func addLevelPool(level int) {
	gzipWriterPools[poolIndex(level)] = &sync.Pool{
		New: func() interface{} {
			w, _ := gzip.NewWriterLevel(nil, level)
			return w
		},
	}
}
type GzipResponseWriter struct {
	http.ResponseWriter
	index int  
	gw    *gzip.Writer
	code int  
	minSize int     
	buf     []byte  
	ignore  bool    
	contentTypes []parsedContentType  
}
type GzipResponseWriterWithCloseNotify struct {
	*GzipResponseWriter
}
func (w GzipResponseWriterWithCloseNotify) CloseNotify() <-chan bool {
	return w.ResponseWriter.(http.CloseNotifier).CloseNotify()
}
func (w *GzipResponseWriter) Write(b []byte) (int, error) {
	if w.gw != nil {
		return w.gw.Write(b)
	}
	if w.ignore {
		return w.ResponseWriter.Write(b)
	}
	w.buf = append(w.buf, b...)
	var (
		cl, _ = strconv.Atoi(w.Header().Get(contentLength))
		ct    = w.Header().Get(contentType)
		ce    = w.Header().Get(contentEncoding)
	)
	if ce == "" && (cl == 0 || cl >= w.minSize) && (ct == "" || handleContentType(w.contentTypes, ct)) {
		if len(w.buf) < w.minSize && cl == 0 {
			return len(b), nil
		}
		if cl >= w.minSize || len(w.buf) >= w.minSize {
			if ct == "" {
				ct = http.DetectContentType(w.buf)
				w.Header().Set(contentType, ct)
			}
			if handleContentType(w.contentTypes, ct) {
				if err := w.startGzip(); err != nil {
					return 0, err
				}
				return len(b), nil
			}
		}
	}
	if err := w.startPlain(); err != nil {
		return 0, err
	}
	return len(b), nil
}
func (w *GzipResponseWriter) startGzip() error {
	w.Header().Set(contentEncoding, "gzip")
	w.Header().Del(contentLength)
	if w.code != 0 {
		w.ResponseWriter.WriteHeader(w.code)
		w.code = 0
	}
	if len(w.buf) > 0 {
		w.init()
		n, err := w.gw.Write(w.buf)
		if err == nil && n < len(w.buf) {
			err = io.ErrShortWrite
		}
		return err
	}
	return nil
}
func (w *GzipResponseWriter) startPlain() error {
	if w.code != 0 {
		w.ResponseWriter.WriteHeader(w.code)
		w.code = 0
	}
	w.ignore = true
	if w.buf == nil {
		return nil
	}
	n, err := w.ResponseWriter.Write(w.buf)
	w.buf = nil
	if err == nil && n < len(w.buf) {
		err = io.ErrShortWrite
	}
	return err
}
func (w *GzipResponseWriter) WriteHeader(code int) {
	if w.code == 0 {
		w.code = code
	}
}
func (w *GzipResponseWriter) init() {
	gzw := gzipWriterPools[w.index].Get().(*gzip.Writer)
	gzw.Reset(w.ResponseWriter)
	w.gw = gzw
}
func (w *GzipResponseWriter) Close() error {
	if w.ignore {
		return nil
	}
	if w.gw == nil {
		err := w.startPlain()
		if err != nil {
			err = fmt.Errorf("gziphandler: write to regular responseWriter at close gets error: %q", err.Error())
		}
		return err
	}
	err := w.gw.Close()
	gzipWriterPools[w.index].Put(w.gw)
	w.gw = nil
	return err
}
func (w *GzipResponseWriter) Flush() {
	if w.gw == nil && !w.ignore {
		return
	}
	if w.gw != nil {
		w.gw.Flush()
	}
	if fw, ok := w.ResponseWriter.(http.Flusher); ok {
		fw.Flush()
	}
}
func (w *GzipResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
	if hj, ok := w.ResponseWriter.(http.Hijacker); ok {
		return hj.Hijack()
	}
	return nil, nil, fmt.Errorf("http.Hijacker interface is not supported")
}
var _ http.Hijacker = &GzipResponseWriter{}
func MustNewGzipLevelHandler(level int) func(http.Handler) http.Handler {
	wrap, err := NewGzipLevelHandler(level)
	if err != nil {
		panic(err)
	}
	return wrap
}
func NewGzipLevelHandler(level int) (func(http.Handler) http.Handler, error) {
	return NewGzipLevelAndMinSize(level, DefaultMinSize)
}
func NewGzipLevelAndMinSize(level, minSize int) (func(http.Handler) http.Handler, error) {
	return GzipHandlerWithOpts(CompressionLevel(level), MinSize(minSize))
}
func GzipHandlerWithOpts(opts ...option) (func(http.Handler) http.Handler, error) {
	c := &config{
		level:   gzip.DefaultCompression,
		minSize: DefaultMinSize,
	}
	for _, o := range opts {
		o(c)
	}
	if err := c.validate(); err != nil {
		return nil, err
	}
	return func(h http.Handler) http.Handler {
		index := poolIndex(c.level)
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			w.Header().Add(vary, acceptEncoding)
			if acceptsGzip(r) {
				gw := &GzipResponseWriter{
					ResponseWriter: w,
					index:          index,
					minSize:        c.minSize,
					contentTypes:   c.contentTypes,
				}
				defer gw.Close()
				if _, ok := w.(http.CloseNotifier); ok {
					gwcn := GzipResponseWriterWithCloseNotify{gw}
					h.ServeHTTP(gwcn, r)
				} else {
					h.ServeHTTP(gw, r)
				}
			} else {
				h.ServeHTTP(w, r)
			}
		})
	}, nil
}
type parsedContentType struct {
	mediaType string
	params    map[string]string
}
func (pct parsedContentType) equals(mediaType string, params map[string]string) bool {
	if pct.mediaType != mediaType {
		return false
	}
	if len(pct.params) == 0 {
		return true
	}
	if len(pct.params) != len(params) {
		return false
	}
	for k, v := range pct.params {
		if w, ok := params[k]; !ok || v != w {
			return false
		}
	}
	return true
}
type config struct {
	minSize      int
	level        int
	contentTypes []parsedContentType
}
func (c *config) validate() error {
	if c.level != gzip.DefaultCompression && (c.level < gzip.BestSpeed || c.level > gzip.BestCompression) {
		return fmt.Errorf("invalid compression level requested: %d", c.level)
	}
	if c.minSize < 0 {
		return fmt.Errorf("minimum size must be more than zero")
	}
	return nil
}
type option func(c *config)
func MinSize(size int) option {
	return func(c *config) {
		c.minSize = size
	}
}
func CompressionLevel(level int) option {
	return func(c *config) {
		c.level = level
	}
}