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