diff --git a/fe/gzip.go b/fe/gzip.go deleted file mode 100644 index 9513dbb50d05a92cc5d8be72a9a4d3a03e4f7b4a..0000000000000000000000000000000000000000 --- a/fe/gzip.go +++ /dev/null @@ -1,205 +0,0 @@ -package fe - -import ( - "compress/gzip" - "io" - "net/http" - "strings" -) - -// Slightly modified by ale@incal.net, based on: -// https://github.com/PuerkitoBio/ghost - -// Thanks to Andrew Gerrand for inspiration: -// https://groups.google.com/d/msg/golang-nuts/eVnTcMwNVjM/4vYU8id9Q2UJ -// -// Also, node's Connect library implementation of the compress middleware: -// https://github.com/senchalabs/connect/blob/master/lib/middleware/compress.js -// -// And StackOverflow's explanation of Vary: Accept-Encoding header: -// http://stackoverflow.com/questions/7848796/what-does-varyaccept-encoding-mean - -// Internal gzipped writer that satisfies both the (body) writer in gzipped format, -// and maintains the rest of the ResponseWriter interface for header manipulation. -type gzipResponseWriter struct { - io.Writer - http.ResponseWriter - r *http.Request // Keep a hold of the Request, for the filter function - filtered bool // Has the request been run through the filter function? - dogzip bool // Should we do GZIP compression for this request? - filterFn func(http.ResponseWriter, *http.Request) bool -} - -// Make sure the filter function is applied. -func (w *gzipResponseWriter) applyFilter() { - if !w.filtered { - if w.dogzip = w.filterFn(w, w.r); w.dogzip { - setGzipHeaders(w.Header()) - } - w.filtered = true - } -} - -// Unambiguous Write() implementation (otherwise both ResponseWriter and Writer -// want to claim this method). -func (w *gzipResponseWriter) Write(b []byte) (int, error) { - w.applyFilter() - if w.dogzip { - // Write compressed - return w.Writer.Write(b) - } - // Write uncompressed - return w.ResponseWriter.Write(b) -} - -// Intercept the WriteHeader call to correctly set the GZIP headers. -func (w *gzipResponseWriter) WriteHeader(code int) { - w.applyFilter() - w.ResponseWriter.WriteHeader(code) -} - -// Implement WrapWriter interface -func (w *gzipResponseWriter) WrappedWriter() http.ResponseWriter { - return w.ResponseWriter -} - -var ( - defaultFilterTypes = [...]string{ - "text/", - "javascript", - "json", - } -) - -// Default filter to check if the response should be GZIPped. -// By default, all text (html, css, xml, ...), javascript and json -// content types are candidates for GZIP. -func defaultFilter(w http.ResponseWriter, r *http.Request) bool { - hdr := w.Header() - for _, tp := range defaultFilterTypes { - ok := headerMatch(hdr, "Content-Type", tp) - if ok { - return true - } - } - return false -} - -// GZIPHandlerFunc is the same as GZIPHandler, it is just a convenience -// signature that accepts a func(http.ResponseWriter, *http.Request) instead of -// a http.Handler interface. It saves the boilerplate http.HandlerFunc() cast. -func GZIPHandlerFunc(h http.HandlerFunc, filterFn func(http.ResponseWriter, *http.Request) bool) http.HandlerFunc { - return GZIPHandler(h, filterFn) -} - -// Gzip compression HTTP handler. If the client supports it, it compresses the response -// written by the wrapped handler. The filter function is called when the response is about -// to be written to determine if compression should be applied. If this argument is nil, -// the default filter will GZIP only content types containing /json|text|javascript/. -func GZIPHandler(h http.Handler, filterFn func(http.ResponseWriter, *http.Request) bool) http.HandlerFunc { - if filterFn == nil { - filterFn = defaultFilter - } - return func(w http.ResponseWriter, r *http.Request) { - if _, ok := getGzipWriter(w); ok { - // Self-awareness, gzip handler is already set up - h.ServeHTTP(w, r) - return - } - hdr := w.Header() - setVaryHeader(hdr) - - // Do nothing on a HEAD request - if r.Method == "HEAD" { - h.ServeHTTP(w, r) - return - } - if !acceptsGzip(r.Header) { - // No gzip support from the client, return uncompressed - h.ServeHTTP(w, r) - return - } - - // Prepare a gzip response container - gz := gzip.NewWriter(w) - gzw := &gzipResponseWriter{ - Writer: gz, - ResponseWriter: w, - r: r, - filterFn: filterFn, - } - h.ServeHTTP(gzw, r) - // Iff the handler completed successfully (no panic) and GZIP was indeed used, close the gzip writer, - // which seems to generate a Write to the underlying writer. - if gzw.dogzip { - gz.Close() - } - } -} - -// Add the vary by "accept-encoding" header if it is not already set. -func setVaryHeader(hdr http.Header) { - if !headerMatch(hdr, "Vary", "accept-encoding") { - hdr.Add("Vary", "Accept-Encoding") - } -} - -// Checks if the client accepts GZIP-encoded responses. -func acceptsGzip(hdr http.Header) bool { - ok := headerMatch(hdr, "Accept-Encoding", "gzip") - if !ok { - ok = headerEquals(hdr, "Accept-Encoding", "*") - } - return ok -} - -func setGzipHeaders(hdr http.Header) { - // The content-type will be explicitly set somewhere down the path of handlers - hdr.Set("Content-Encoding", "gzip") - hdr.Del("Content-Length") -} - -// Helper function to retrieve the gzip writer. -func getGzipWriter(w http.ResponseWriter) (*gzipResponseWriter, bool) { - gz, ok := GetResponseWriter(w, func(tst http.ResponseWriter) bool { - _, ok := tst.(*gzipResponseWriter) - return ok - }) - if ok { - return gz.(*gzipResponseWriter), true - } - return nil, false -} - -func headerMatch(hdr http.Header, name, s string) bool { - return strings.Contains(hdr.Get(name), s) -} - -func headerEquals(hdr http.Header, name, s string) bool { - return hdr.Get(name) == s -} - -// This interface can be implemented by an augmented ResponseWriter, so that -// it doesn't hide other augmented writers in the chain. -type WrapWriter interface { - http.ResponseWriter - WrappedWriter() http.ResponseWriter -} - -// Helper function to retrieve a specific ResponseWriter. -func GetResponseWriter(w http.ResponseWriter, - predicate func(http.ResponseWriter) bool) (http.ResponseWriter, bool) { - - for { - // Check if this writer is the one we're looking for - if w != nil && predicate(w) { - return w, true - } - // If it is a WrapWriter, move back the chain of wrapped writers - ww, ok := w.(WrapWriter) - if !ok { - return nil, false - } - w = ww.WrappedWriter() - } -}