compress.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. package restful
  2. // Copyright 2013 Ernest Micklei. All rights reserved.
  3. // Use of this source code is governed by a license
  4. // that can be found in the LICENSE file.
  5. import (
  6. "bufio"
  7. "compress/gzip"
  8. "compress/zlib"
  9. "errors"
  10. "io"
  11. "net"
  12. "net/http"
  13. "strings"
  14. )
  15. // OBSOLETE : use restful.DefaultContainer.EnableContentEncoding(true) to change this setting.
  16. var EnableContentEncoding = false
  17. // CompressingResponseWriter is a http.ResponseWriter that can perform content encoding (gzip and zlib)
  18. type CompressingResponseWriter struct {
  19. writer http.ResponseWriter
  20. compressor io.WriteCloser
  21. encoding string
  22. }
  23. // Header is part of http.ResponseWriter interface
  24. func (c *CompressingResponseWriter) Header() http.Header {
  25. return c.writer.Header()
  26. }
  27. // WriteHeader is part of http.ResponseWriter interface
  28. func (c *CompressingResponseWriter) WriteHeader(status int) {
  29. c.writer.WriteHeader(status)
  30. }
  31. // Write is part of http.ResponseWriter interface
  32. // It is passed through the compressor
  33. func (c *CompressingResponseWriter) Write(bytes []byte) (int, error) {
  34. if c.isCompressorClosed() {
  35. return -1, errors.New("Compressing error: tried to write data using closed compressor")
  36. }
  37. return c.compressor.Write(bytes)
  38. }
  39. // CloseNotify is part of http.CloseNotifier interface
  40. func (c *CompressingResponseWriter) CloseNotify() <-chan bool {
  41. return c.writer.(http.CloseNotifier).CloseNotify()
  42. }
  43. // Flush is part of http.Flusher interface. Noop if the underlying writer doesn't support it.
  44. func (c *CompressingResponseWriter) Flush() {
  45. flusher, ok := c.writer.(http.Flusher)
  46. if !ok {
  47. // writer doesn't support http.Flusher interface
  48. return
  49. }
  50. flusher.Flush()
  51. }
  52. // Close the underlying compressor
  53. func (c *CompressingResponseWriter) Close() error {
  54. if c.isCompressorClosed() {
  55. return errors.New("Compressing error: tried to close already closed compressor")
  56. }
  57. c.compressor.Close()
  58. if ENCODING_GZIP == c.encoding {
  59. currentCompressorProvider.ReleaseGzipWriter(c.compressor.(*gzip.Writer))
  60. }
  61. if ENCODING_DEFLATE == c.encoding {
  62. currentCompressorProvider.ReleaseZlibWriter(c.compressor.(*zlib.Writer))
  63. }
  64. // gc hint needed?
  65. c.compressor = nil
  66. return nil
  67. }
  68. func (c *CompressingResponseWriter) isCompressorClosed() bool {
  69. return nil == c.compressor
  70. }
  71. // Hijack implements the Hijacker interface
  72. // This is especially useful when combining Container.EnabledContentEncoding
  73. // in combination with websockets (for instance gorilla/websocket)
  74. func (c *CompressingResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
  75. hijacker, ok := c.writer.(http.Hijacker)
  76. if !ok {
  77. return nil, nil, errors.New("ResponseWriter doesn't support Hijacker interface")
  78. }
  79. return hijacker.Hijack()
  80. }
  81. // WantsCompressedResponse reads the Accept-Encoding header to see if and which encoding is requested.
  82. // It also inspects the httpWriter whether its content-encoding is already set (non-empty).
  83. func wantsCompressedResponse(httpRequest *http.Request, httpWriter http.ResponseWriter) (bool, string) {
  84. if contentEncoding := httpWriter.Header().Get(HEADER_ContentEncoding); contentEncoding != "" {
  85. return false, ""
  86. }
  87. header := httpRequest.Header.Get(HEADER_AcceptEncoding)
  88. gi := strings.Index(header, ENCODING_GZIP)
  89. zi := strings.Index(header, ENCODING_DEFLATE)
  90. // use in order of appearance
  91. if gi == -1 {
  92. return zi != -1, ENCODING_DEFLATE
  93. } else if zi == -1 {
  94. return gi != -1, ENCODING_GZIP
  95. } else {
  96. if gi < zi {
  97. return true, ENCODING_GZIP
  98. }
  99. return true, ENCODING_DEFLATE
  100. }
  101. }
  102. // NewCompressingResponseWriter create a CompressingResponseWriter for a known encoding = {gzip,deflate}
  103. func NewCompressingResponseWriter(httpWriter http.ResponseWriter, encoding string) (*CompressingResponseWriter, error) {
  104. httpWriter.Header().Set(HEADER_ContentEncoding, encoding)
  105. c := new(CompressingResponseWriter)
  106. c.writer = httpWriter
  107. var err error
  108. if ENCODING_GZIP == encoding {
  109. w := currentCompressorProvider.AcquireGzipWriter()
  110. w.Reset(httpWriter)
  111. c.compressor = w
  112. c.encoding = ENCODING_GZIP
  113. } else if ENCODING_DEFLATE == encoding {
  114. w := currentCompressorProvider.AcquireZlibWriter()
  115. w.Reset(httpWriter)
  116. c.compressor = w
  117. c.encoding = ENCODING_DEFLATE
  118. } else {
  119. return nil, errors.New("Unknown encoding:" + encoding)
  120. }
  121. return c, err
  122. }