Wrap decoding errors with custom exceptions

This commit is contained in:
Yeray Diaz Diaz 2019-04-26 20:09:57 +01:00
parent 91a2a1b896
commit bdc218f8e6
4 changed files with 71 additions and 8 deletions

View File

@ -9,6 +9,9 @@ from .exceptions import (
ResponseClosed,
StreamConsumed,
Timeout,
DeflateDecodingError,
GzipDecodingError,
BrotliDecodingError,
)
from .http11 import HTTP11Connection
from .sync import SyncClient, SyncConnectionPool

View File

@ -12,6 +12,9 @@ except ImportError: # pragma: nocover
brotli = None
import httpcore.exceptions
class Decoder:
def decode(self, data: bytes) -> bytes:
raise NotImplementedError() # pragma: nocover
@ -39,10 +42,16 @@ class DeflateDecoder(Decoder):
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):
@ -56,10 +65,16 @@ 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):
@ -77,16 +92,22 @@ 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:

View File

@ -40,3 +40,27 @@ class ResponseClosed(Exception):
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.
"""

View File

@ -77,3 +77,18 @@ async def test_streaming():
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)