brotli = None
+import httpcore.exceptions
+
+
class Decoder:
def decode(self, data: bytes) -> bytes:
raise NotImplementedError() # pragma: nocover
self.decompressor = zlib.decompressobj(-zlib.MAX_WBITS)
def decode(self, data: bytes) -> bytes:
- return self.decompressor.decompress(data)
+ try:
+ return self.decompressor.decompress(data)
+ except zlib.error as exc:
+ raise httpcore.exceptions.DeflateDecodingError from exc
def flush(self) -> bytes:
- return self.decompressor.flush()
+ try:
+ return self.decompressor.flush()
+ except zlib.error as exc:
+ raise httpcore.exceptions.DeflateDecodingError from exc
class GZipDecoder(Decoder):
self.decompressor = zlib.decompressobj(zlib.MAX_WBITS | 16)
def decode(self, data: bytes) -> bytes:
- return self.decompressor.decompress(data)
+ try:
+ return self.decompressor.decompress(data)
+ except zlib.error as exc:
+ raise httpcore.exceptions.GzipDecodingError from exc
def flush(self) -> bytes:
- return self.decompressor.flush()
+ try:
+ return self.decompressor.flush()
+ except zlib.error as exc:
+ raise httpcore.exceptions.GzipDecodingError from exc
class BrotliDecoder(Decoder):
self.decompressor = brotli.Decompressor()
def decode(self, data: bytes) -> bytes:
- return self.decompressor.decompress(data)
+ try:
+ return self.decompressor.decompress(data)
+ except brotli.Error as exc:
+ raise httpcore.exceptions.BrotliDecodingError from exc
def flush(self) -> bytes:
- self.decompressor.finish()
- return b""
+ try:
+ self.decompressor.finish()
+ return b""
+ except brotli.Error as exc:
+ raise httpcore.exceptions.BrotliDecodingError from exc
class MultiDecoder(Decoder):
"""
- Handle the case where mutliple encodings have been applied.
+ Handle the case where mutiple encodings have been applied.
"""
def __init__(self, children: typing.Sequence[Decoder]) -> None:
Attempted to read or stream response content, but the request has been
closed without loading the body.
"""
+
+
+class DecodingError(Exception):
+ """
+ Decoding of the response failed.
+ """
+
+
+class DeflateDecodingError(DecodingError):
+ """
+ Decoding of the response using deflate failed.
+ """
+
+
+class GzipDecodingError(DecodingError):
+ """
+ Decoding of the response using gzip failed.
+ """
+
+
+class BrotliDecodingError(DecodingError):
+ """
+ Decoding of the response using brotli failed.
+ """
response = httpcore.Response(200, headers=headers, body=compress(body))
assert not hasattr(response, "body")
assert await response.read() == body
+
+
+@pytest.mark.parametrize(
+ 'header_value, expected_exception',
+ [
+ (b"deflate", httpcore.DeflateDecodingError),
+ (b"gzip", httpcore.GzipDecodingError),
+ (b"br", httpcore.BrotliDecodingError),
+ ])
+def test_decoding_errors(header_value, expected_exception):
+ headers = [(b"Content-Encoding", header_value)]
+ body = b"test 123"
+ compressed_body = brotli.compress(body)[3:]
+ with pytest.raises(expected_exception):
+ response = httpcore.Response(200, headers=headers, body=compressed_body)