66
77from .util import CaseInsensitiveDict
88
9+ try :
10+ # This supports both brotli & brotlipy packages
11+ import brotli
12+ except ImportError :
13+ try :
14+ import brotlicffi as brotli
15+ except ImportError :
16+ brotli = None
17+
18+
19+ AVAILABLE_DECOMPRESSORS = {"gzip" , "deflate" }
20+ if brotli is not None :
21+ AVAILABLE_DECOMPRESSORS .add ("br" )
22+
923
1024def replace_headers (request , replacements ):
1125 """Replace headers in request according to replacements.
@@ -136,15 +150,16 @@ def remove_post_data_parameters(request, post_data_parameters_to_remove):
136150
137151def decode_response (response ):
138152 """
139- If the response is compressed with gzip or deflate:
153+ If the response is compressed with any supported compression (gzip,
154+ deflate, br if available):
140155 1. decompress the response body
141156 2. delete the content-encoding header
142157 3. update content-length header to decompressed length
143158 """
144159
145- def is_compressed (headers ):
160+ def is_decompressable (headers ):
146161 encoding = headers .get ("content-encoding" , [])
147- return encoding and encoding [0 ] in ( "gzip" , "deflate" )
162+ return encoding and encoding [0 ] in AVAILABLE_DECOMPRESSORS
148163
149164 def decompress_body (body , encoding ):
150165 """Returns decompressed body according to encoding using zlib.
@@ -157,17 +172,23 @@ def decompress_body(body, encoding):
157172 return zlib .decompress (body , zlib .MAX_WBITS | 16 )
158173 except zlib .error :
159174 return body # assumes that the data was already decompressed
160- else : # encoding == 'deflate'
175+ elif encoding == 'deflate' :
161176 try :
162177 return zlib .decompress (body )
163178 except zlib .error :
164179 return body # assumes that the data was already decompressed
180+ else : # encoding == 'br'
181+ try :
182+ return brotli .decompress (body )
183+ except brotli .error :
184+ return body # assumes that the data was already decompressed
185+
165186
166187 # Deepcopy here in case `headers` contain objects that could
167188 # be mutated by a shallow copy and corrupt the real response.
168189 response = copy .deepcopy (response )
169190 headers = CaseInsensitiveDict (response ["headers" ])
170- if is_compressed (headers ):
191+ if is_decompressable (headers ):
171192 encoding = headers ["content-encoding" ][0 ]
172193 headers ["content-encoding" ].remove (encoding )
173194 if not headers ["content-encoding" ]:
0 commit comments