github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/handlers/compress.go (about) 1 // Copyright 2013 The Gorilla Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package handlers 6 7 import ( 8 "compress/flate" 9 "compress/gzip" 10 "io" 11 "strings" 12 13 http "github.com/hxx258456/ccgo/gmhttp" 14 "github.com/hxx258456/ccgo/httpsnoop" 15 ) 16 17 const acceptEncoding string = "Accept-Encoding" 18 19 type compressResponseWriter struct { 20 compressor io.Writer 21 w http.ResponseWriter 22 } 23 24 func (cw *compressResponseWriter) WriteHeader(c int) { 25 cw.w.Header().Del("Content-Length") 26 cw.w.WriteHeader(c) 27 } 28 29 func (cw *compressResponseWriter) Write(b []byte) (int, error) { 30 h := cw.w.Header() 31 if h.Get("Content-Type") == "" { 32 h.Set("Content-Type", http.DetectContentType(b)) 33 } 34 h.Del("Content-Length") 35 36 return cw.compressor.Write(b) 37 } 38 39 func (cw *compressResponseWriter) ReadFrom(r io.Reader) (int64, error) { 40 return io.Copy(cw.compressor, r) 41 } 42 43 type flusher interface { 44 Flush() error 45 } 46 47 func (w *compressResponseWriter) Flush() { 48 // Flush compressed data if compressor supports it. 49 if f, ok := w.compressor.(flusher); ok { 50 f.Flush() 51 } 52 // Flush HTTP response. 53 if f, ok := w.w.(http.Flusher); ok { 54 f.Flush() 55 } 56 } 57 58 // CompressHandler gzip compresses HTTP responses for clients that support it 59 // via the 'Accept-Encoding' header. 60 // 61 // Compressing TLS traffic may leak the page contents to an attacker if the 62 // page contains user input: http://security.stackexchange.com/a/102015/12208 63 func CompressHandler(h http.Handler) http.Handler { 64 return CompressHandlerLevel(h, gzip.DefaultCompression) 65 } 66 67 // CompressHandlerLevel gzip compresses HTTP responses with specified compression level 68 // for clients that support it via the 'Accept-Encoding' header. 69 // 70 // The compression level should be gzip.DefaultCompression, gzip.NoCompression, 71 // or any integer value between gzip.BestSpeed and gzip.BestCompression inclusive. 72 // gzip.DefaultCompression is used in case of invalid compression level. 73 func CompressHandlerLevel(h http.Handler, level int) http.Handler { 74 if level < gzip.DefaultCompression || level > gzip.BestCompression { 75 level = gzip.DefaultCompression 76 } 77 78 const ( 79 gzipEncoding = "gzip" 80 flateEncoding = "deflate" 81 ) 82 83 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 84 // detect what encoding to use 85 var encoding string 86 for _, curEnc := range strings.Split(r.Header.Get(acceptEncoding), ",") { 87 curEnc = strings.TrimSpace(curEnc) 88 if curEnc == gzipEncoding || curEnc == flateEncoding { 89 encoding = curEnc 90 break 91 } 92 } 93 94 // always add Accept-Encoding to Vary to prevent intermediate caches corruption 95 w.Header().Add("Vary", acceptEncoding) 96 97 // if we weren't able to identify an encoding we're familiar with, pass on the 98 // request to the handler and return 99 if encoding == "" { 100 h.ServeHTTP(w, r) 101 return 102 } 103 104 if r.Header.Get("Upgrade") != "" { 105 h.ServeHTTP(w, r) 106 return 107 } 108 109 // wrap the ResponseWriter with the writer for the chosen encoding 110 var encWriter io.WriteCloser 111 if encoding == gzipEncoding { 112 encWriter, _ = gzip.NewWriterLevel(w, level) 113 } else if encoding == flateEncoding { 114 encWriter, _ = flate.NewWriter(w, level) 115 } 116 defer encWriter.Close() 117 118 w.Header().Set("Content-Encoding", encoding) 119 r.Header.Del(acceptEncoding) 120 121 cw := &compressResponseWriter{ 122 w: w, 123 compressor: encWriter, 124 } 125 126 w = httpsnoop.Wrap(w, httpsnoop.Hooks{ 127 Write: func(httpsnoop.WriteFunc) httpsnoop.WriteFunc { 128 return cw.Write 129 }, 130 WriteHeader: func(httpsnoop.WriteHeaderFunc) httpsnoop.WriteHeaderFunc { 131 return cw.WriteHeader 132 }, 133 Flush: func(httpsnoop.FlushFunc) httpsnoop.FlushFunc { 134 return cw.Flush 135 }, 136 ReadFrom: func(rff httpsnoop.ReadFromFunc) httpsnoop.ReadFromFunc { 137 return cw.ReadFrom 138 }, 139 }) 140 141 h.ServeHTTP(w, r) 142 }) 143 }