Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 
 

144 linhas
3.7 KiB

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