]> git.ipfire.org Git - thirdparty/httpx.git/commitdiff
Decoder support
authorTom Christie <tom@tomchristie.com>
Fri, 5 Apr 2019 16:16:12 +0000 (17:16 +0100)
committerTom Christie <tom@tomchristie.com>
Fri, 5 Apr 2019 16:16:12 +0000 (17:16 +0100)
httpcore/api.py
httpcore/decoders.py [new file with mode: 0644]

index c270d5692dc76a0f0b63a0a9b5bdcd109d82a2fd..e8bb5d98efad2c7c6bc108be826b689a44ca1f19 100644 (file)
@@ -9,6 +9,7 @@ from .config import (
     SSLConfig,
     TimeoutConfig,
 )
+from .decoders import IdentityDecoder
 from .exceptions import ResponseClosed, StreamConsumed
 
 
@@ -86,6 +87,7 @@ class Response:
         self.on_close = on_close
         self.is_closed = False
         self.is_streamed = False
+        self.decoder = IdentityDecoder()
         if isinstance(body, bytes):
             self.is_closed = True
             self.body = body
@@ -93,6 +95,9 @@ class Response:
             self.body_aiter = body
 
     async def read(self) -> bytes:
+        """
+        Read and return the response content.
+        """
         if not hasattr(self, "body"):
             body = b""
             async for part in self.stream():
@@ -100,33 +105,33 @@ class Response:
             self.body = body
         return self.body
 
-    async def stream(self) -> typing.AsyncIterator[bytes]:
+    async def stream(self):
+        """
+        A byte-iterator over the decoded response content.
+        This will allow us to handle gzip, deflate, and brotli encoded responses.
+        """
         if hasattr(self, "body"):
             yield self.body
         else:
-            if self.is_streamed:
-                raise StreamConsumed()
-            if self.is_closed:
-                raise ResponseClosed()
-            self.is_streamed = True
-            async for part in self.body_aiter():
-                yield part
-            await self.close()
+            async for chunk in self.raw():
+                yield self.decoder.decode(chunk)
+            yield self.decoder.flush()
+
+    async def raw(self) -> typing.AsyncIterator[bytes]:
+        """
+        A byte-iterator over the raw response content.
+        """
+        if self.is_streamed:
+            raise StreamConsumed()
+        if self.is_closed:
+            raise ResponseClosed()
+        self.is_streamed = True
+        async for part in self.body_aiter():
+            yield part
+        await self.close()
 
     async def close(self) -> None:
         if not self.is_closed:
             self.is_closed = True
             if self.on_close is not None:
                 await self.on_close()
-
-    async def __aenter__(self) -> "Response":
-        return self
-
-    async def __aexit__(
-        self,
-        exc_type: typing.Type[BaseException] = None,
-        exc_value: BaseException = None,
-        traceback: TracebackType = None,
-    ) -> None:
-        if not self.is_closed:
-            await self.close()
diff --git a/httpcore/decoders.py b/httpcore/decoders.py
new file mode 100644 (file)
index 0000000..09b9336
--- /dev/null
@@ -0,0 +1,41 @@
+"""
+Handlers for Content-Encoding.
+"""
+
+
+class IdentityDecoder:
+    def decode(self, chunk: bytes) -> bytes:
+        return chunk
+
+    def flush(self) -> bytes:
+        return b''
+
+
+# class DeflateDecoder:
+#     pass
+#
+#
+# class GZipDecoder:
+#     pass
+#
+#
+# class BrotliDecoder:
+#     pass
+#
+#
+# class MultiDecoder:
+#     def __init__(self, children):
+#         self.children = children
+#
+#     def decode(self, chunk: bytes) -> bytes:
+#         data = chunk
+#         for child in children:
+#             data = child.decode(data)
+#         return data
+#
+#     def flush(self) -> bytes:
+#         data = b''
+#         for child in children:
+#             data = child.decode(data)
+#             data = child.flush()
+#         return data