Last active
August 14, 2020 16:59
-
-
Save martinusso/257d75c19b81a42727c4d0eb747392b8 to your computer and use it in GitHub Desktop.
gzip http golang
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
func main() { | |
req, err := http.NewRequest(method, url, body) | |
if err != nil { | |
return nil, err | |
} | |
req.Header.Add("Accept-Encoding", "gzip") | |
httpClient := &http.Client{Timeout: 15 * time.Second} | |
res, err := httpClient.Do(req) | |
if err != nil { | |
return nil, err | |
} | |
defer res.Body.Close() | |
// Check that the server actually sent compressed data | |
var reader io.ReadCloser | |
switch res.Header.Get("Content-Encoding") { | |
case "gzip": | |
reader, err = gzip.NewReader(res.Body) | |
defer reader.Close() | |
default: | |
reader = res.Body | |
} | |
return ioutil.ReadAll(reader) | |
// res.StatusCode | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
ewares | |
import ( | |
"compress/gzip" | |
"io/ioutil" | |
"net/http" | |
"strings" | |
"sync" | |
"github.com/codegangsta/negroni" | |
) | |
// These compression constants are copied from the compress/gzip package. | |
const ( | |
encodingGzip = "gzip" | |
headerAcceptEncoding = "Accept-Encoding" | |
headerContentEncoding = "Content-Encoding" | |
headerContentLength = "Content-Length" | |
headerContentType = "Content-Type" | |
headerVary = "Vary" | |
headerSecWebSocketKey = "Sec-WebSocket-Key" | |
BestCompression = gzip.BestCompression | |
BestSpeed = gzip.BestSpeed | |
DefaultCompression = gzip.DefaultCompression | |
NoCompression = gzip.NoCompression | |
) | |
// gzipResponseWriter is the ResponseWriter that negroni.ResponseWriter is | |
// wrapped in. | |
type gzipResponseWriter struct { | |
w *gzip.Writer | |
negroni.ResponseWriter | |
wroteHeader bool | |
} | |
// Check whether underlying response is already pre-encoded and disable | |
// gzipWriter before the body gets written, otherwise encoding headers | |
func (grw *gzipResponseWriter) WriteHeader(code int) { | |
headers := grw.ResponseWriter.Header() | |
if headers.Get(headerContentEncoding) == "" { | |
headers.Set(headerContentEncoding, encodingGzip) | |
headers.Add(headerVary, headerAcceptEncoding) | |
} else { | |
grw.w.Reset(ioutil.Discard) | |
grw.w = nil | |
} | |
// Avoid sending Content-Length header before compression. The length would | |
// be invalid, and some browsers like Safari will report | |
// "The network connection was lost." errors | |
grw.Header().Del(headerContentLength) | |
grw.ResponseWriter.WriteHeader(code) | |
grw.wroteHeader = true | |
} | |
// Write writes bytes to the gzip.Writer. It will also set the Content-Type | |
// header using the net/http library content type detection if the Content-Type | |
// header was not set yet. | |
func (grw *gzipResponseWriter) Write(b []byte) (int, error) { | |
if !grw.wroteHeader { | |
grw.WriteHeader(http.StatusOK) | |
} | |
if grw.w == nil { | |
return grw.ResponseWriter.Write(b) | |
} | |
if len(grw.Header().Get(headerContentType)) == 0 { | |
grw.Header().Set(headerContentType, http.DetectContentType(b)) | |
} | |
return grw.w.Write(b) | |
} | |
type gzipResponseWriterCloseNotifier struct { | |
*gzipResponseWriter | |
} | |
func (rw *gzipResponseWriterCloseNotifier) CloseNotify() <-chan bool { | |
return rw.ResponseWriter.(http.CloseNotifier).CloseNotify() | |
} | |
func newGzipResponseWriter(rw negroni.ResponseWriter, w *gzip.Writer) negroni.ResponseWriter { | |
wr := &gzipResponseWriter{w: w, ResponseWriter: rw} | |
if _, ok := rw.(http.CloseNotifier); ok { | |
return &gzipResponseWriterCloseNotifier{gzipResponseWriter: wr} | |
} | |
return wr | |
} | |
// handler struct contains the ServeHTTP method | |
type handler struct { | |
pool sync.Pool | |
} | |
// Gzip returns a handler which will handle the Gzip compression in ServeHTTP. | |
// Valid values for level are identical to those in the compress/gzip package. | |
func Gzip(level int) *handler { | |
h := &handler{} | |
h.pool.New = func() interface{} { | |
gz, err := gzip.NewWriterLevel(ioutil.Discard, level) | |
if err != nil { | |
panic(err) | |
} | |
return gz | |
} | |
return h | |
} | |
// ServeHTTP wraps the http.ResponseWriter with a gzip.Writer. | |
func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { | |
// Skip compression if the client doesn't accept gzip encoding. | |
if !strings.Contains(r.Header.Get(headerAcceptEncoding), encodingGzip) { | |
next(w, r) | |
return | |
} | |
// Skip compression if client attempt WebSocket connection | |
if len(r.Header.Get(headerSecWebSocketKey)) > 0 { | |
next(w, r) | |
return | |
} | |
// Retrieve gzip writer from the pool. Reset it to use the ResponseWriter. | |
// This allows us to re-use an already allocated buffer rather than | |
// allocating a new buffer for every request. | |
// We defer g.pool.Put here so that the gz writer is returned to the | |
// pool if any thing after here fails for some reason (functions in | |
// next could potentially panic, etc) | |
gz := h.pool.Get().(*gzip.Writer) | |
defer h.pool.Put(gz) | |
gz.Reset(w) | |
// Wrap the original http.ResponseWriter with negroni.ResponseWriter | |
// and create the gzipResponseWriter. | |
nrw := negroni.NewResponseWriter(w) | |
grw := newGzipResponseWriter(nrw, gz) | |
// Call the next handler supplying the gzipResponseWriter instead of | |
// the original. | |
next(grw, r) | |
gz.Close() | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment