We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
DecodingError with zlib-compressed responses
Using the following minimal WSGI app:

```python
import zlib

def app(environ, start_response):
    start_response("200 OK", [("Content-Encoding", "deflate")])
    return [zlib.compress(b"hello world")]
```

This works fine in a web browser.

Requests is quite happy:

```
>>> import requests
>>> requests.get("http://localhost:9999")
<Response [200]>
>>> requests.get("http://localhost:9999").text
'hello world'
```

httpx says:

```
>>> httpx.get('http://localhost:9999')
Traceback (most recent call last):
  File "/Users/jamie/code/httpx/httpx/decoders.py", line 52, in decode
    return self.decompressor.decompress(data)
zlib.error: Error -3 while decompressing data: invalid stored block lengths

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/jamie/code/httpx/httpx/api.py", line 170, in get
    trust_env=trust_env,
  File "/Users/jamie/code/httpx/httpx/api.py", line 96, in request
    allow_redirects=allow_redirects,
  File "/Users/jamie/code/httpx/httpx/client.py", line 568, in request
    request, auth=auth, allow_redirects=allow_redirects, timeout=timeout,
  File "/Users/jamie/code/httpx/httpx/client.py", line 593, in send
    response.read()
  File "/Users/jamie/code/httpx/httpx/models.py", line 900, in read
    self._content = b"".join([part for part in self.iter_bytes()])
  File "/Users/jamie/code/httpx/httpx/models.py", line 900, in <listcomp>
    self._content = b"".join([part for part in self.iter_bytes()])
  File "/Users/jamie/code/httpx/httpx/models.py", line 912, in iter_bytes
    yield self.decoder.decode(chunk)
  File "/Users/jamie/code/httpx/httpx/decoders.py", line 54, in decode
    raise DecodingError from exc
httpx.exceptions.DecodingError
```

The reason this is an issue and not a PR is because I don't understand the code in `decoders.py` - it very explicitly says `self.decompressor = zlib.decompressobj(-zlib.MAX_WBITS)`, which according to [the Python docs](https://docs.python.org/3/library/zlib.html#zlib.compressobj) means "Uses the absolute value of wbits as the window size logarithm, while producing a raw output stream with no header or trailing checksum." I don't know enough about zlib to understand this, but it looks very deliberate so I thought I'd better ask.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `httpx/decoders.py`
Content:
```
1 """
2 Handlers for Content-Encoding.
3 
4 See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding
5 """
6 import codecs
7 import typing
8 import zlib
9 
10 import chardet
11 
12 from .exceptions import DecodingError
13 
14 try:
15     import brotli
16 except ImportError:  # pragma: nocover
17     brotli = None
18 
19 
20 class Decoder:
21     def decode(self, data: bytes) -> bytes:
22         raise NotImplementedError()  # pragma: nocover
23 
24     def flush(self) -> bytes:
25         raise NotImplementedError()  # pragma: nocover
26 
27 
28 class IdentityDecoder(Decoder):
29     """
30     Handle unencoded data.
31     """
32 
33     def decode(self, data: bytes) -> bytes:
34         return data
35 
36     def flush(self) -> bytes:
37         return b""
38 
39 
40 class DeflateDecoder(Decoder):
41     """
42     Handle 'deflate' decoding.
43 
44     See: https://stackoverflow.com/questions/1838699
45     """
46 
47     def __init__(self) -> None:
48         self.decompressor = zlib.decompressobj(-zlib.MAX_WBITS)
49 
50     def decode(self, data: bytes) -> bytes:
51         try:
52             return self.decompressor.decompress(data)
53         except zlib.error as exc:
54             raise DecodingError from exc
55 
56     def flush(self) -> bytes:
57         try:
58             return self.decompressor.flush()
59         except zlib.error as exc:  # pragma: nocover
60             raise DecodingError from exc
61 
62 
63 class GZipDecoder(Decoder):
64     """
65     Handle 'gzip' decoding.
66 
67     See: https://stackoverflow.com/questions/1838699
68     """
69 
70     def __init__(self) -> None:
71         self.decompressor = zlib.decompressobj(zlib.MAX_WBITS | 16)
72 
73     def decode(self, data: bytes) -> bytes:
74         try:
75             return self.decompressor.decompress(data)
76         except zlib.error as exc:
77             raise DecodingError from exc
78 
79     def flush(self) -> bytes:
80         try:
81             return self.decompressor.flush()
82         except zlib.error as exc:  # pragma: nocover
83             raise DecodingError from exc
84 
85 
86 class BrotliDecoder(Decoder):
87     """
88     Handle 'brotli' decoding.
89 
90     Requires `pip install brotlipy`. See: https://brotlipy.readthedocs.io/
91         or   `pip install brotli`. See https://github.com/google/brotli
92     Supports both 'brotlipy' and 'Brotli' packages since they share an import
93     name. The top branches are for 'brotlipy' and bottom branches for 'Brotli'
94     """
95 
96     def __init__(self) -> None:
97         assert (
98             brotli is not None
99         ), "The 'brotlipy' or 'brotli' library must be installed to use 'BrotliDecoder'"
100         self.decompressor = brotli.Decompressor()
101         self.seen_data = False
102 
103     def decode(self, data: bytes) -> bytes:
104         if not data:
105             return b""
106         self.seen_data = True
107         try:
108             if hasattr(self.decompressor, "decompress"):
109                 return self.decompressor.decompress(data)
110             return self.decompressor.process(data)  # pragma: nocover
111         except brotli.error as exc:
112             raise DecodingError from exc
113 
114     def flush(self) -> bytes:
115         if not self.seen_data:
116             return b""
117         try:
118             if hasattr(self.decompressor, "finish"):
119                 self.decompressor.finish()
120             return b""
121         except brotli.error as exc:  # pragma: nocover
122             raise DecodingError from exc
123 
124 
125 class MultiDecoder(Decoder):
126     """
127     Handle the case where multiple encodings have been applied.
128     """
129 
130     def __init__(self, children: typing.Sequence[Decoder]) -> None:
131         """
132         'children' should be a sequence of decoders in the order in which
133         each was applied.
134         """
135         # Note that we reverse the order for decoding.
136         self.children = list(reversed(children))
137 
138     def decode(self, data: bytes) -> bytes:
139         for child in self.children:
140             data = child.decode(data)
141         return data
142 
143     def flush(self) -> bytes:
144         data = b""
145         for child in self.children:
146             data = child.decode(data) + child.flush()
147         return data
148 
149 
150 class TextDecoder:
151     """
152     Handles incrementally decoding bytes into text
153     """
154 
155     def __init__(self, encoding: typing.Optional[str] = None):
156         self.decoder: typing.Optional[codecs.IncrementalDecoder] = (
157             None if encoding is None else codecs.getincrementaldecoder(encoding)()
158         )
159         self.detector = chardet.universaldetector.UniversalDetector()
160 
161         # This buffer is only needed if 'decoder' is 'None'
162         # we want to trigger errors if data is getting added to
163         # our internal buffer for some silly reason while
164         # a decoder is discovered.
165         self.buffer: typing.Optional[bytearray] = None if self.decoder else bytearray()
166 
167     def decode(self, data: bytes) -> str:
168         try:
169             if self.decoder is not None:
170                 text = self.decoder.decode(data)
171             else:
172                 assert self.buffer is not None
173                 text = ""
174                 self.detector.feed(data)
175                 self.buffer += data
176 
177                 # Should be more than enough data to process, we don't
178                 # want to buffer too long as chardet will wait until
179                 # detector.close() is used to give back common
180                 # encodings like 'utf-8'.
181                 if len(self.buffer) >= 4096:
182                     self.decoder = codecs.getincrementaldecoder(
183                         self._detector_result()
184                     )()
185                     text = self.decoder.decode(bytes(self.buffer), False)
186                     self.buffer = None
187 
188             return text
189         except UnicodeDecodeError:  # pragma: nocover
190             raise DecodingError() from None
191 
192     def flush(self) -> str:
193         try:
194             if self.decoder is None:
195                 # Empty string case as chardet is guaranteed to not have a guess.
196                 assert self.buffer is not None
197                 if len(self.buffer) == 0:
198                     return ""
199                 return bytes(self.buffer).decode(self._detector_result())
200 
201             return self.decoder.decode(b"", True)
202         except UnicodeDecodeError:  # pragma: nocover
203             raise DecodingError() from None
204 
205     def _detector_result(self) -> str:
206         self.detector.close()
207         result = self.detector.result["encoding"]
208         if not result:  # pragma: nocover
209             raise DecodingError("Unable to determine encoding of content")
210 
211         return result
212 
213 
214 class LineDecoder:
215     """
216     Handles incrementally reading lines from text.
217 
218     Uses universal line decoding, supporting any of `\n`, `\r`, or `\r\n`
219     as line endings, normalizing to `\n`.
220     """
221 
222     def __init__(self) -> None:
223         self.buffer = ""
224 
225     def decode(self, text: str) -> typing.List[str]:
226         lines = []
227 
228         if text.startswith("\n") and self.buffer and self.buffer[-1] == "\r":
229             # Handle the case where we have an "\r\n" split across
230             # our previous input, and our new chunk.
231             lines.append(self.buffer[:-1] + "\n")
232             self.buffer = ""
233             text = text[1:]
234 
235         while text:
236             num_chars = len(text)
237             for idx in range(num_chars):
238                 char = text[idx]
239                 next_char = None if idx + 1 == num_chars else text[idx + 1]
240                 if char == "\n":
241                     lines.append(self.buffer + text[: idx + 1])
242                     self.buffer = ""
243                     text = text[idx + 1 :]
244                     break
245                 elif char == "\r" and next_char == "\n":
246                     lines.append(self.buffer + text[:idx] + "\n")
247                     self.buffer = ""
248                     text = text[idx + 2 :]
249                     break
250                 elif char == "\r" and next_char is not None:
251                     lines.append(self.buffer + text[:idx] + "\n")
252                     self.buffer = ""
253                     text = text[idx + 1 :]
254                     break
255                 elif next_char is None:
256                     self.buffer = text
257                     text = ""
258                     break
259 
260         return lines
261 
262     def flush(self) -> typing.List[str]:
263         if self.buffer.endswith("\r"):
264             # Handle the case where we had a trailing '\r', which could have
265             # been a '\r\n' pair.
266             lines = [self.buffer[:-1] + "\n"]
267         elif self.buffer:
268             lines = [self.buffer]
269         else:
270             lines = []
271         self.buffer = ""
272         return lines
273 
274 
275 SUPPORTED_DECODERS = {
276     "identity": IdentityDecoder,
277     "gzip": GZipDecoder,
278     "deflate": DeflateDecoder,
279     "br": BrotliDecoder,
280 }
281 
282 
283 if brotli is None:
284     SUPPORTED_DECODERS.pop("br")  # pragma: nocover
285
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/httpx/decoders.py b/httpx/decoders.py
--- a/httpx/decoders.py
+++ b/httpx/decoders.py
@@ -45,12 +45,18 @@
     """
 
     def __init__(self) -> None:
-        self.decompressor = zlib.decompressobj(-zlib.MAX_WBITS)
+        self.first_attempt = True
+        self.decompressor = zlib.decompressobj()
 
     def decode(self, data: bytes) -> bytes:
+        was_first_attempt = self.first_attempt
+        self.first_attempt = False
         try:
             return self.decompressor.decompress(data)
         except zlib.error as exc:
+            if was_first_attempt:
+                self.decompressor = zlib.decompressobj(-zlib.MAX_WBITS)
+                return self.decode(data)
             raise DecodingError from exc
 
     def flush(self) -> bytes:

{"golden_diff": "diff --git a/httpx/decoders.py b/httpx/decoders.py\n--- a/httpx/decoders.py\n+++ b/httpx/decoders.py\n@@ -45,12 +45,18 @@\n     \"\"\"\n \n     def __init__(self) -> None:\n-        self.decompressor = zlib.decompressobj(-zlib.MAX_WBITS)\n+        self.first_attempt = True\n+        self.decompressor = zlib.decompressobj()\n \n     def decode(self, data: bytes) -> bytes:\n+        was_first_attempt = self.first_attempt\n+        self.first_attempt = False\n         try:\n             return self.decompressor.decompress(data)\n         except zlib.error as exc:\n+            if was_first_attempt:\n+                self.decompressor = zlib.decompressobj(-zlib.MAX_WBITS)\n+                return self.decode(data)\n             raise DecodingError from exc\n \n     def flush(self) -> bytes:\n", "issue": "DecodingError with zlib-compressed responses\nUsing the following minimal WSGI app:\r\n\r\n```python\r\nimport zlib\r\n\r\ndef app(environ, start_response):\r\n    start_response(\"200 OK\", [(\"Content-Encoding\", \"deflate\")])\r\n    return [zlib.compress(b\"hello world\")]\r\n```\r\n\r\nThis works fine in a web browser.\r\n\r\nRequests is quite happy:\r\n\r\n```\r\n>>> import requests\r\n>>> requests.get(\"http://localhost:9999\")\r\n<Response [200]>\r\n>>> requests.get(\"http://localhost:9999\").text\r\n'hello world'\r\n```\r\n\r\nhttpx says:\r\n\r\n```\r\n>>> httpx.get('http://localhost:9999')\r\nTraceback (most recent call last):\r\n  File \"/Users/jamie/code/httpx/httpx/decoders.py\", line 52, in decode\r\n    return self.decompressor.decompress(data)\r\nzlib.error: Error -3 while decompressing data: invalid stored block lengths\r\n\r\nThe above exception was the direct cause of the following exception:\r\n\r\nTraceback (most recent call last):\r\n  File \"<stdin>\", line 1, in <module>\r\n  File \"/Users/jamie/code/httpx/httpx/api.py\", line 170, in get\r\n    trust_env=trust_env,\r\n  File \"/Users/jamie/code/httpx/httpx/api.py\", line 96, in request\r\n    allow_redirects=allow_redirects,\r\n  File \"/Users/jamie/code/httpx/httpx/client.py\", line 568, in request\r\n    request, auth=auth, allow_redirects=allow_redirects, timeout=timeout,\r\n  File \"/Users/jamie/code/httpx/httpx/client.py\", line 593, in send\r\n    response.read()\r\n  File \"/Users/jamie/code/httpx/httpx/models.py\", line 900, in read\r\n    self._content = b\"\".join([part for part in self.iter_bytes()])\r\n  File \"/Users/jamie/code/httpx/httpx/models.py\", line 900, in <listcomp>\r\n    self._content = b\"\".join([part for part in self.iter_bytes()])\r\n  File \"/Users/jamie/code/httpx/httpx/models.py\", line 912, in iter_bytes\r\n    yield self.decoder.decode(chunk)\r\n  File \"/Users/jamie/code/httpx/httpx/decoders.py\", line 54, in decode\r\n    raise DecodingError from exc\r\nhttpx.exceptions.DecodingError\r\n```\r\n\r\nThe reason this is an issue and not a PR is because I don't understand the code in `decoders.py` - it very explicitly says `self.decompressor = zlib.decompressobj(-zlib.MAX_WBITS)`, which according to [the Python docs](https://docs.python.org/3/library/zlib.html#zlib.compressobj) means \"Uses the absolute value of wbits as the window size logarithm, while producing a raw output stream with no header or trailing checksum.\" I don't know enough about zlib to understand this, but it looks very deliberate so I thought I'd better ask.\n", "before_files": [{"content": "\"\"\"\nHandlers for Content-Encoding.\n\nSee: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding\n\"\"\"\nimport codecs\nimport typing\nimport zlib\n\nimport chardet\n\nfrom .exceptions import DecodingError\n\ntry:\n    import brotli\nexcept ImportError:  # pragma: nocover\n    brotli = None\n\n\nclass Decoder:\n    def decode(self, data: bytes) -> bytes:\n        raise NotImplementedError()  # pragma: nocover\n\n    def flush(self) -> bytes:\n        raise NotImplementedError()  # pragma: nocover\n\n\nclass IdentityDecoder(Decoder):\n    \"\"\"\n    Handle unencoded data.\n    \"\"\"\n\n    def decode(self, data: bytes) -> bytes:\n        return data\n\n    def flush(self) -> bytes:\n        return b\"\"\n\n\nclass DeflateDecoder(Decoder):\n    \"\"\"\n    Handle 'deflate' decoding.\n\n    See: https://stackoverflow.com/questions/1838699\n    \"\"\"\n\n    def __init__(self) -> None:\n        self.decompressor = zlib.decompressobj(-zlib.MAX_WBITS)\n\n    def decode(self, data: bytes) -> bytes:\n        try:\n            return self.decompressor.decompress(data)\n        except zlib.error as exc:\n            raise DecodingError from exc\n\n    def flush(self) -> bytes:\n        try:\n            return self.decompressor.flush()\n        except zlib.error as exc:  # pragma: nocover\n            raise DecodingError from exc\n\n\nclass GZipDecoder(Decoder):\n    \"\"\"\n    Handle 'gzip' decoding.\n\n    See: https://stackoverflow.com/questions/1838699\n    \"\"\"\n\n    def __init__(self) -> None:\n        self.decompressor = zlib.decompressobj(zlib.MAX_WBITS | 16)\n\n    def decode(self, data: bytes) -> bytes:\n        try:\n            return self.decompressor.decompress(data)\n        except zlib.error as exc:\n            raise DecodingError from exc\n\n    def flush(self) -> bytes:\n        try:\n            return self.decompressor.flush()\n        except zlib.error as exc:  # pragma: nocover\n            raise DecodingError from exc\n\n\nclass BrotliDecoder(Decoder):\n    \"\"\"\n    Handle 'brotli' decoding.\n\n    Requires `pip install brotlipy`. See: https://brotlipy.readthedocs.io/\n        or   `pip install brotli`. See https://github.com/google/brotli\n    Supports both 'brotlipy' and 'Brotli' packages since they share an import\n    name. The top branches are for 'brotlipy' and bottom branches for 'Brotli'\n    \"\"\"\n\n    def __init__(self) -> None:\n        assert (\n            brotli is not None\n        ), \"The 'brotlipy' or 'brotli' library must be installed to use 'BrotliDecoder'\"\n        self.decompressor = brotli.Decompressor()\n        self.seen_data = False\n\n    def decode(self, data: bytes) -> bytes:\n        if not data:\n            return b\"\"\n        self.seen_data = True\n        try:\n            if hasattr(self.decompressor, \"decompress\"):\n                return self.decompressor.decompress(data)\n            return self.decompressor.process(data)  # pragma: nocover\n        except brotli.error as exc:\n            raise DecodingError from exc\n\n    def flush(self) -> bytes:\n        if not self.seen_data:\n            return b\"\"\n        try:\n            if hasattr(self.decompressor, \"finish\"):\n                self.decompressor.finish()\n            return b\"\"\n        except brotli.error as exc:  # pragma: nocover\n            raise DecodingError from exc\n\n\nclass MultiDecoder(Decoder):\n    \"\"\"\n    Handle the case where multiple encodings have been applied.\n    \"\"\"\n\n    def __init__(self, children: typing.Sequence[Decoder]) -> None:\n        \"\"\"\n        'children' should be a sequence of decoders in the order in which\n        each was applied.\n        \"\"\"\n        # Note that we reverse the order for decoding.\n        self.children = list(reversed(children))\n\n    def decode(self, data: bytes) -> bytes:\n        for child in self.children:\n            data = child.decode(data)\n        return data\n\n    def flush(self) -> bytes:\n        data = b\"\"\n        for child in self.children:\n            data = child.decode(data) + child.flush()\n        return data\n\n\nclass TextDecoder:\n    \"\"\"\n    Handles incrementally decoding bytes into text\n    \"\"\"\n\n    def __init__(self, encoding: typing.Optional[str] = None):\n        self.decoder: typing.Optional[codecs.IncrementalDecoder] = (\n            None if encoding is None else codecs.getincrementaldecoder(encoding)()\n        )\n        self.detector = chardet.universaldetector.UniversalDetector()\n\n        # This buffer is only needed if 'decoder' is 'None'\n        # we want to trigger errors if data is getting added to\n        # our internal buffer for some silly reason while\n        # a decoder is discovered.\n        self.buffer: typing.Optional[bytearray] = None if self.decoder else bytearray()\n\n    def decode(self, data: bytes) -> str:\n        try:\n            if self.decoder is not None:\n                text = self.decoder.decode(data)\n            else:\n                assert self.buffer is not None\n                text = \"\"\n                self.detector.feed(data)\n                self.buffer += data\n\n                # Should be more than enough data to process, we don't\n                # want to buffer too long as chardet will wait until\n                # detector.close() is used to give back common\n                # encodings like 'utf-8'.\n                if len(self.buffer) >= 4096:\n                    self.decoder = codecs.getincrementaldecoder(\n                        self._detector_result()\n                    )()\n                    text = self.decoder.decode(bytes(self.buffer), False)\n                    self.buffer = None\n\n            return text\n        except UnicodeDecodeError:  # pragma: nocover\n            raise DecodingError() from None\n\n    def flush(self) -> str:\n        try:\n            if self.decoder is None:\n                # Empty string case as chardet is guaranteed to not have a guess.\n                assert self.buffer is not None\n                if len(self.buffer) == 0:\n                    return \"\"\n                return bytes(self.buffer).decode(self._detector_result())\n\n            return self.decoder.decode(b\"\", True)\n        except UnicodeDecodeError:  # pragma: nocover\n            raise DecodingError() from None\n\n    def _detector_result(self) -> str:\n        self.detector.close()\n        result = self.detector.result[\"encoding\"]\n        if not result:  # pragma: nocover\n            raise DecodingError(\"Unable to determine encoding of content\")\n\n        return result\n\n\nclass LineDecoder:\n    \"\"\"\n    Handles incrementally reading lines from text.\n\n    Uses universal line decoding, supporting any of `\\n`, `\\r`, or `\\r\\n`\n    as line endings, normalizing to `\\n`.\n    \"\"\"\n\n    def __init__(self) -> None:\n        self.buffer = \"\"\n\n    def decode(self, text: str) -> typing.List[str]:\n        lines = []\n\n        if text.startswith(\"\\n\") and self.buffer and self.buffer[-1] == \"\\r\":\n            # Handle the case where we have an \"\\r\\n\" split across\n            # our previous input, and our new chunk.\n            lines.append(self.buffer[:-1] + \"\\n\")\n            self.buffer = \"\"\n            text = text[1:]\n\n        while text:\n            num_chars = len(text)\n            for idx in range(num_chars):\n                char = text[idx]\n                next_char = None if idx + 1 == num_chars else text[idx + 1]\n                if char == \"\\n\":\n                    lines.append(self.buffer + text[: idx + 1])\n                    self.buffer = \"\"\n                    text = text[idx + 1 :]\n                    break\n                elif char == \"\\r\" and next_char == \"\\n\":\n                    lines.append(self.buffer + text[:idx] + \"\\n\")\n                    self.buffer = \"\"\n                    text = text[idx + 2 :]\n                    break\n                elif char == \"\\r\" and next_char is not None:\n                    lines.append(self.buffer + text[:idx] + \"\\n\")\n                    self.buffer = \"\"\n                    text = text[idx + 1 :]\n                    break\n                elif next_char is None:\n                    self.buffer = text\n                    text = \"\"\n                    break\n\n        return lines\n\n    def flush(self) -> typing.List[str]:\n        if self.buffer.endswith(\"\\r\"):\n            # Handle the case where we had a trailing '\\r', which could have\n            # been a '\\r\\n' pair.\n            lines = [self.buffer[:-1] + \"\\n\"]\n        elif self.buffer:\n            lines = [self.buffer]\n        else:\n            lines = []\n        self.buffer = \"\"\n        return lines\n\n\nSUPPORTED_DECODERS = {\n    \"identity\": IdentityDecoder,\n    \"gzip\": GZipDecoder,\n    \"deflate\": DeflateDecoder,\n    \"br\": BrotliDecoder,\n}\n\n\nif brotli is None:\n    SUPPORTED_DECODERS.pop(\"br\")  # pragma: nocover\n", "path": "httpx/decoders.py"}], "after_files": [{"content": "\"\"\"\nHandlers for Content-Encoding.\n\nSee: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding\n\"\"\"\nimport codecs\nimport typing\nimport zlib\n\nimport chardet\n\nfrom .exceptions import DecodingError\n\ntry:\n    import brotli\nexcept ImportError:  # pragma: nocover\n    brotli = None\n\n\nclass Decoder:\n    def decode(self, data: bytes) -> bytes:\n        raise NotImplementedError()  # pragma: nocover\n\n    def flush(self) -> bytes:\n        raise NotImplementedError()  # pragma: nocover\n\n\nclass IdentityDecoder(Decoder):\n    \"\"\"\n    Handle unencoded data.\n    \"\"\"\n\n    def decode(self, data: bytes) -> bytes:\n        return data\n\n    def flush(self) -> bytes:\n        return b\"\"\n\n\nclass DeflateDecoder(Decoder):\n    \"\"\"\n    Handle 'deflate' decoding.\n\n    See: https://stackoverflow.com/questions/1838699\n    \"\"\"\n\n    def __init__(self) -> None:\n        self.first_attempt = True\n        self.decompressor = zlib.decompressobj()\n\n    def decode(self, data: bytes) -> bytes:\n        was_first_attempt = self.first_attempt\n        self.first_attempt = False\n        try:\n            return self.decompressor.decompress(data)\n        except zlib.error as exc:\n            if was_first_attempt:\n                self.decompressor = zlib.decompressobj(-zlib.MAX_WBITS)\n                return self.decode(data)\n            raise DecodingError from exc\n\n    def flush(self) -> bytes:\n        try:\n            return self.decompressor.flush()\n        except zlib.error as exc:  # pragma: nocover\n            raise DecodingError from exc\n\n\nclass GZipDecoder(Decoder):\n    \"\"\"\n    Handle 'gzip' decoding.\n\n    See: https://stackoverflow.com/questions/1838699\n    \"\"\"\n\n    def __init__(self) -> None:\n        self.decompressor = zlib.decompressobj(zlib.MAX_WBITS | 16)\n\n    def decode(self, data: bytes) -> bytes:\n        try:\n            return self.decompressor.decompress(data)\n        except zlib.error as exc:\n            raise DecodingError from exc\n\n    def flush(self) -> bytes:\n        try:\n            return self.decompressor.flush()\n        except zlib.error as exc:  # pragma: nocover\n            raise DecodingError from exc\n\n\nclass BrotliDecoder(Decoder):\n    \"\"\"\n    Handle 'brotli' decoding.\n\n    Requires `pip install brotlipy`. See: https://brotlipy.readthedocs.io/\n        or   `pip install brotli`. See https://github.com/google/brotli\n    Supports both 'brotlipy' and 'Brotli' packages since they share an import\n    name. The top branches are for 'brotlipy' and bottom branches for 'Brotli'\n    \"\"\"\n\n    def __init__(self) -> None:\n        assert (\n            brotli is not None\n        ), \"The 'brotlipy' or 'brotli' library must be installed to use 'BrotliDecoder'\"\n        self.decompressor = brotli.Decompressor()\n        self.seen_data = False\n\n    def decode(self, data: bytes) -> bytes:\n        if not data:\n            return b\"\"\n        self.seen_data = True\n        try:\n            if hasattr(self.decompressor, \"decompress\"):\n                return self.decompressor.decompress(data)\n            return self.decompressor.process(data)  # pragma: nocover\n        except brotli.error as exc:\n            raise DecodingError from exc\n\n    def flush(self) -> bytes:\n        if not self.seen_data:\n            return b\"\"\n        try:\n            if hasattr(self.decompressor, \"finish\"):\n                self.decompressor.finish()\n            return b\"\"\n        except brotli.error as exc:  # pragma: nocover\n            raise DecodingError from exc\n\n\nclass MultiDecoder(Decoder):\n    \"\"\"\n    Handle the case where multiple encodings have been applied.\n    \"\"\"\n\n    def __init__(self, children: typing.Sequence[Decoder]) -> None:\n        \"\"\"\n        'children' should be a sequence of decoders in the order in which\n        each was applied.\n        \"\"\"\n        # Note that we reverse the order for decoding.\n        self.children = list(reversed(children))\n\n    def decode(self, data: bytes) -> bytes:\n        for child in self.children:\n            data = child.decode(data)\n        return data\n\n    def flush(self) -> bytes:\n        data = b\"\"\n        for child in self.children:\n            data = child.decode(data) + child.flush()\n        return data\n\n\nclass TextDecoder:\n    \"\"\"\n    Handles incrementally decoding bytes into text\n    \"\"\"\n\n    def __init__(self, encoding: typing.Optional[str] = None):\n        self.decoder: typing.Optional[codecs.IncrementalDecoder] = (\n            None if encoding is None else codecs.getincrementaldecoder(encoding)()\n        )\n        self.detector = chardet.universaldetector.UniversalDetector()\n\n        # This buffer is only needed if 'decoder' is 'None'\n        # we want to trigger errors if data is getting added to\n        # our internal buffer for some silly reason while\n        # a decoder is discovered.\n        self.buffer: typing.Optional[bytearray] = None if self.decoder else bytearray()\n\n    def decode(self, data: bytes) -> str:\n        try:\n            if self.decoder is not None:\n                text = self.decoder.decode(data)\n            else:\n                assert self.buffer is not None\n                text = \"\"\n                self.detector.feed(data)\n                self.buffer += data\n\n                # Should be more than enough data to process, we don't\n                # want to buffer too long as chardet will wait until\n                # detector.close() is used to give back common\n                # encodings like 'utf-8'.\n                if len(self.buffer) >= 4096:\n                    self.decoder = codecs.getincrementaldecoder(\n                        self._detector_result()\n                    )()\n                    text = self.decoder.decode(bytes(self.buffer), False)\n                    self.buffer = None\n\n            return text\n        except UnicodeDecodeError:  # pragma: nocover\n            raise DecodingError() from None\n\n    def flush(self) -> str:\n        try:\n            if self.decoder is None:\n                # Empty string case as chardet is guaranteed to not have a guess.\n                assert self.buffer is not None\n                if len(self.buffer) == 0:\n                    return \"\"\n                return bytes(self.buffer).decode(self._detector_result())\n\n            return self.decoder.decode(b\"\", True)\n        except UnicodeDecodeError:  # pragma: nocover\n            raise DecodingError() from None\n\n    def _detector_result(self) -> str:\n        self.detector.close()\n        result = self.detector.result[\"encoding\"]\n        if not result:  # pragma: nocover\n            raise DecodingError(\"Unable to determine encoding of content\")\n\n        return result\n\n\nclass LineDecoder:\n    \"\"\"\n    Handles incrementally reading lines from text.\n\n    Uses universal line decoding, supporting any of `\\n`, `\\r`, or `\\r\\n`\n    as line endings, normalizing to `\\n`.\n    \"\"\"\n\n    def __init__(self) -> None:\n        self.buffer = \"\"\n\n    def decode(self, text: str) -> typing.List[str]:\n        lines = []\n\n        if text.startswith(\"\\n\") and self.buffer and self.buffer[-1] == \"\\r\":\n            # Handle the case where we have an \"\\r\\n\" split across\n            # our previous input, and our new chunk.\n            lines.append(self.buffer[:-1] + \"\\n\")\n            self.buffer = \"\"\n            text = text[1:]\n\n        while text:\n            num_chars = len(text)\n            for idx in range(num_chars):\n                char = text[idx]\n                next_char = None if idx + 1 == num_chars else text[idx + 1]\n                if char == \"\\n\":\n                    lines.append(self.buffer + text[: idx + 1])\n                    self.buffer = \"\"\n                    text = text[idx + 1 :]\n                    break\n                elif char == \"\\r\" and next_char == \"\\n\":\n                    lines.append(self.buffer + text[:idx] + \"\\n\")\n                    self.buffer = \"\"\n                    text = text[idx + 2 :]\n                    break\n                elif char == \"\\r\" and next_char is not None:\n                    lines.append(self.buffer + text[:idx] + \"\\n\")\n                    self.buffer = \"\"\n                    text = text[idx + 1 :]\n                    break\n                elif next_char is None:\n                    self.buffer = text\n                    text = \"\"\n                    break\n\n        return lines\n\n    def flush(self) -> typing.List[str]:\n        if self.buffer.endswith(\"\\r\"):\n            # Handle the case where we had a trailing '\\r', which could have\n            # been a '\\r\\n' pair.\n            lines = [self.buffer[:-1] + \"\\n\"]\n        elif self.buffer:\n            lines = [self.buffer]\n        else:\n            lines = []\n        self.buffer = \"\"\n        return lines\n\n\nSUPPORTED_DECODERS = {\n    \"identity\": IdentityDecoder,\n    \"gzip\": GZipDecoder,\n    \"deflate\": DeflateDecoder,\n    \"br\": BrotliDecoder,\n}\n\n\nif brotli is None:\n    SUPPORTED_DECODERS.pop(\"br\")  # pragma: nocover\n", "path": "httpx/decoders.py"}]}