6
6
7
7
from .util import CaseInsensitiveDict
8
8
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
+
9
23
10
24
def replace_headers (request , replacements ):
11
25
"""Replace headers in request according to replacements.
@@ -136,15 +150,16 @@ def remove_post_data_parameters(request, post_data_parameters_to_remove):
136
150
137
151
def decode_response (response ):
138
152
"""
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):
140
155
1. decompress the response body
141
156
2. delete the content-encoding header
142
157
3. update content-length header to decompressed length
143
158
"""
144
159
145
- def is_compressed (headers ):
160
+ def is_decompressable (headers ):
146
161
encoding = headers .get ("content-encoding" , [])
147
- return encoding and encoding [0 ] in ( "gzip" , "deflate" )
162
+ return encoding and encoding [0 ] in AVAILABLE_DECOMPRESSORS
148
163
149
164
def decompress_body (body , encoding ):
150
165
"""Returns decompressed body according to encoding using zlib.
@@ -157,17 +172,23 @@ def decompress_body(body, encoding):
157
172
return zlib .decompress (body , zlib .MAX_WBITS | 16 )
158
173
except zlib .error :
159
174
return body # assumes that the data was already decompressed
160
- else : # encoding == 'deflate'
175
+ elif encoding == 'deflate' :
161
176
try :
162
177
return zlib .decompress (body )
163
178
except zlib .error :
164
179
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
+
165
186
166
187
# Deepcopy here in case `headers` contain objects that could
167
188
# be mutated by a shallow copy and corrupt the real response.
168
189
response = copy .deepcopy (response )
169
190
headers = CaseInsensitiveDict (response ["headers" ])
170
- if is_compressed (headers ):
191
+ if is_decompressable (headers ):
171
192
encoding = headers ["content-encoding" ][0 ]
172
193
headers ["content-encoding" ].remove (encoding )
173
194
if not headers ["content-encoding" ]:
0 commit comments