gzipper.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. # (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org)
  2. # Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
  3. # (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org)
  4. # Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
  5. """
  6. WSGI middleware
  7. Gzip-encodes the response.
  8. """
  9. import gzip
  10. from paste.response import header_value, remove_header
  11. from paste.httpheaders import CONTENT_LENGTH
  12. try:
  13. from cStringIO import StringIO
  14. except ImportError:
  15. from StringIO import StringIO
  16. class GzipOutput(object):
  17. pass
  18. class middleware(object):
  19. def __init__(self, application, compress_level=6):
  20. self.application = application
  21. self.compress_level = int(compress_level)
  22. def __call__(self, environ, start_response):
  23. if 'gzip' not in environ.get('HTTP_ACCEPT_ENCODING', ''):
  24. # nothing for us to do, so this middleware will
  25. # be a no-op:
  26. return self.application(environ, start_response)
  27. response = GzipResponse(start_response, self.compress_level)
  28. app_iter = self.application(environ,
  29. response.gzip_start_response)
  30. if app_iter:
  31. response.finish_response(app_iter)
  32. return response.write()
  33. class GzipResponse(object):
  34. def __init__(self, start_response, compress_level):
  35. self.start_response = start_response
  36. self.compress_level = compress_level
  37. self.buffer = StringIO()
  38. self.compressible = False
  39. self.content_length = None
  40. def gzip_start_response(self, status, headers, exc_info=None):
  41. self.headers = headers
  42. ct = header_value(headers,'content-type')
  43. ce = header_value(headers,'content-encoding')
  44. self.compressible = False
  45. if ct and (ct.startswith('text/') or ct.startswith('application/')) \
  46. and 'zip' not in ct:
  47. self.compressible = True
  48. if ce:
  49. self.compressible = False
  50. if self.compressible:
  51. headers.append(('content-encoding', 'gzip'))
  52. remove_header(headers, 'content-length')
  53. self.headers = headers
  54. self.status = status
  55. return self.buffer.write
  56. def write(self):
  57. out = self.buffer
  58. out.seek(0)
  59. s = out.getvalue()
  60. out.close()
  61. return [s]
  62. def finish_response(self, app_iter):
  63. if self.compressible:
  64. output = gzip.GzipFile(mode='wb', compresslevel=self.compress_level,
  65. fileobj=self.buffer)
  66. else:
  67. output = self.buffer
  68. try:
  69. for s in app_iter:
  70. output.write(s)
  71. if self.compressible:
  72. output.close()
  73. finally:
  74. if hasattr(app_iter, 'close'):
  75. app_iter.close()
  76. content_length = self.buffer.tell()
  77. CONTENT_LENGTH.update(self.headers, content_length)
  78. self.start_response(self.status, self.headers)
  79. def filter_factory(application, **conf):
  80. import warnings
  81. warnings.warn(
  82. 'This function is deprecated; use make_gzip_middleware instead',
  83. DeprecationWarning, 2)
  84. def filter(application):
  85. return middleware(application)
  86. return filter
  87. def make_gzip_middleware(app, global_conf, compress_level=6):
  88. """
  89. Wrap the middleware, so that it applies gzipping to a response
  90. when it is supported by the browser and the content is of
  91. type ``text/*`` or ``application/*``
  92. """
  93. compress_level = int(compress_level)
  94. return middleware(app, compress_level=compress_level)