We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Transfer-Encoding: Chunked
Responses need to support `Transfer-Encoding: Chunked`

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `uvicorn/protocols/http.py`
Content:
```
1 import asyncio
2 import collections
3 import email
4 import http
5 import httptools
6 import os
7 import time
8 
9 from uvicorn.protocols.websocket import websocket_upgrade
10 
11 
12 def set_time_and_date():
13     global CURRENT_TIME
14     global DATE_HEADER
15 
16     CURRENT_TIME = time.time()
17     DATE_HEADER = b''.join([
18         b'date: ',
19         email.utils.formatdate(CURRENT_TIME, usegmt=True).encode(),
20         b'\r\n'
21     ])
22 
23 
24 def get_status_line(status_code):
25     try:
26         phrase = http.HTTPStatus(status_code).phrase.encode()
27     except ValueError:
28         phrase = b''
29     return b''.join([
30         b'HTTP/1.1 ', str(status_code).encode(), b' ', phrase, b'\r\n'
31     ])
32 
33 
34 CURRENT_TIME = 0.0
35 DATE_HEADER = b''
36 SERVER_HEADER = b'server: uvicorn\r\n'
37 STATUS_LINE = {
38     status_code: get_status_line(status_code) for status_code in range(100, 600)
39 }
40 
41 LOW_WATER_LIMIT = 16384
42 HIGH_WATER_LIMIT = 65536
43 MAX_PIPELINED_REQUESTS = 20
44 
45 set_time_and_date()
46 
47 
48 class BodyChannel(object):
49     __slots__ = ['_queue', '_protocol', 'name']
50 
51     def __init__(self, protocol):
52         self._queue = asyncio.Queue()
53         self._protocol = protocol
54         self.name = 'body:%d' % id(self)
55 
56     def _put(self, message):
57         self._queue.put_nowait(message)
58         self._protocol.buffer_size += len(message['content'])
59         self._protocol.check_pause_reading()
60 
61     async def receive(self):
62         message = await self._queue.get()
63         self._protocol.buffer_size -= len(message['content'])
64         self._protocol.check_resume_reading()
65 
66 
67 class ReplyChannel(object):
68     __slots__ = ['_protocol', 'name']
69 
70     def __init__(self, protocol):
71         self._protocol = protocol
72         self.name = 'reply:%d' % id(self)
73 
74     async def send(self, message):
75         protocol = self._protocol
76         transport = protocol.transport
77 
78         if transport is None:
79             return
80 
81         if protocol.write_paused:
82             await transport.drain()
83 
84         status = message.get('status')
85         headers = message.get('headers')
86         content = message.get('content')
87         more_content = message.get('more_content', False)
88 
89         if status is not None:
90             response = [
91                 STATUS_LINE[status],
92                 SERVER_HEADER,
93                 DATE_HEADER,
94             ]
95             transport.write(b''.join(response))
96 
97         if headers is not None:
98             response = []
99             if content is not None and not more_content:
100                 response = [b'content-length: ', str(len(content)).encode(), b'\r\n']
101 
102             for header_name, header_value in headers:
103                 response.extend([header_name, b': ', header_value, b'\r\n'])
104             response.append(b'\r\n')
105 
106             transport.write(b''.join(response))
107 
108         if content is not None:
109             transport.write(content)
110 
111         if not more_content:
112             if (not status) or (not self._protocol.request_parser.should_keep_alive()):
113                 transport.close()
114             elif protocol.pipeline_queue:
115                 message, channels = protocol.pipeline_queue.popleft()
116                 protocol.loop.create_task(protocol.consumer(message, channels))
117                 protocol.check_resume_reading()
118             else:
119                 protocol.has_active_request = False
120 
121 
122 class HttpProtocol(asyncio.Protocol):
123     __slots__ = [
124         'consumer', 'loop', 'request_parser',
125         'base_message', 'base_channels',
126         'transport', 'message', 'channels', 'headers', 'upgrade',
127         'read_paused', 'write_paused',
128         'buffer_size', 'high_water_limit', 'low_water_limit',
129         'has_active_request', 'max_pipelined_requests', 'pipeline_queue'
130     ]
131 
132     def __init__(self, consumer, loop, sock, cfg):
133         self.consumer = consumer
134         self.loop = loop
135         self.request_parser = httptools.HttpRequestParser(self)
136 
137         self.base_message = {
138             'channel': 'http.request',
139             'scheme': 'https' if cfg.is_ssl else 'http',
140             'root_path': os.environ.get('SCRIPT_NAME', ''),
141             'server': sock.getsockname()
142         }
143         self.base_channels = {
144             'reply': ReplyChannel(self)
145         }
146 
147         self.transport = None
148         self.message = None
149         self.channels = None
150         self.headers = None
151         self.upgrade = None
152 
153         self.read_paused = False
154         self.write_paused = False
155 
156         self.buffer_size = 0
157         self.high_water_limit = HIGH_WATER_LIMIT
158         self.low_water_limit = LOW_WATER_LIMIT
159 
160         self.has_active_request = False
161         self.max_pipelined_requests = MAX_PIPELINED_REQUESTS
162         self.pipeline_queue = collections.deque()
163 
164     # The asyncio.Protocol hooks...
165     def connection_made(self, transport):
166         self.transport = transport
167 
168     def connection_lost(self, exc):
169         self.transport = None
170 
171     def eof_received(self):
172         pass
173 
174     def data_received(self, data):
175         try:
176             self.request_parser.feed_data(data)
177         except httptools.HttpParserUpgrade:
178             websocket_upgrade(self)
179 
180     # Flow control...
181     def pause_writing(self):
182         self.write_paused = True
183 
184     def resume_writing(self):
185         self.write_paused = False
186 
187     def check_pause_reading(self):
188         if self.transport is None or self.read_paused:
189             return
190         if (self.buffer_size > self.high_water_limit or
191             len(self.pipeline_queue) >= self.max_pipelined_requests):
192             self.transport.pause_reading()
193             self.read_paused = True
194 
195     def check_resume_reading(self):
196         if self.transport is None or not self.read_paused:
197             return
198         if (self.buffer_size < self.low_water_limit and
199             len(self.pipeline_queue) < self.max_pipelined_requests):
200             self.transport.resume_reading()
201             self.read_paused = False
202 
203     # Event hooks called back into by HttpRequestParser...
204     def on_message_begin(self):
205         self.message = self.base_message.copy()
206         self.channels = self.base_channels.copy()
207         self.headers = []
208 
209     def on_url(self, url):
210         parsed = httptools.parse_url(url)
211         method = self.request_parser.get_method()
212         http_version = self.request_parser.get_http_version()
213         self.message.update({
214             'http_version': http_version,
215             'method': method.decode('ascii'),
216             'path': parsed.path.decode('ascii'),
217             'query_string': parsed.query if parsed.query else b'',
218             'headers': self.headers
219         })
220 
221     def on_header(self, name: bytes, value: bytes):
222         name = name.lower()
223         if name == b'upgrade':
224             self.upgrade = value
225         elif name == b'expect' and value.lower() == b'100-continue':
226             self.transport.write(b'HTTP/1.1 100 Continue\r\n\r\n')
227         self.headers.append([name, value])
228 
229     def on_body(self, body: bytes):
230         if 'body' not in self.channels:
231             self.channels['body'] = BodyChannel(self)
232             if not self.has_active_request:
233                 self.loop.create_task(self.consumer(self.message, self.channels))
234                 self.has_active_request = True
235             else:
236                 self.pipeline_queue.append((self.message, self.channels))
237                 self.check_pause_reading()
238         message = {
239             'content': body,
240             'more_content': True
241         }
242         self.channels['body']._put(message)
243 
244     def on_message_complete(self):
245         if self.upgrade is not None:
246             return
247 
248         if 'body' not in self.channels:
249             if not self.has_active_request:
250                 self.loop.create_task(self.consumer(self.message, self.channels))
251                 self.has_active_request = True
252             else:
253                 self.pipeline_queue.append((self.message, self.channels))
254                 self.check_pause_reading()
255         else:
256             message = {
257                 'content': b'',
258                 'more_content': False
259             }
260             self.channels['body']._put(message)
261 
262     def on_chunk_header(self):
263         pass
264 
265     def on_chunk_complete(self):
266         pass
267
```
--- 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/uvicorn/protocols/http.py b/uvicorn/protocols/http.py
--- a/uvicorn/protocols/http.py
+++ b/uvicorn/protocols/http.py
@@ -65,10 +65,11 @@
 
 
 class ReplyChannel(object):
-    __slots__ = ['_protocol', 'name']
+    __slots__ = ['_protocol', '_use_chunked_encoding', 'name']
 
     def __init__(self, protocol):
         self._protocol = protocol
+        self._use_chunked_encoding = False
         self.name = 'reply:%d' % id(self)
 
     async def send(self, message):
@@ -82,8 +83,8 @@
             await transport.drain()
 
         status = message.get('status')
-        headers = message.get('headers')
-        content = message.get('content')
+        headers = message.get('headers', [])
+        content = message.get('content', b'')
         more_content = message.get('more_content', False)
 
         if status is not None:
@@ -92,24 +93,37 @@
                 SERVER_HEADER,
                 DATE_HEADER,
             ]
-            transport.write(b''.join(response))
-
-        if headers is not None:
-            response = []
-            if content is not None and not more_content:
-                response = [b'content-length: ', str(len(content)).encode(), b'\r\n']
 
+            seen_content_length = False
             for header_name, header_value in headers:
+                if header_name.lower() == b'content-length':
+                    seen_content_length = True
                 response.extend([header_name, b': ', header_value, b'\r\n'])
-            response.append(b'\r\n')
 
+            if not seen_content_length:
+                if more_content:
+                    self._use_chunked_encoding = True
+                    response.append(b'transfer-encoding: chunked\r\n')
+                elif status != 204:
+                    response.append(b'content-length: ', str(len(content)).encode(), b'\r\n')
+
+            response.append(b'\r\n')
             transport.write(b''.join(response))
 
-        if content is not None:
-            transport.write(content)
+        if content:
+            if self._use_chunked_encoding:
+                transport.write(b'%x\r\n' % len(content))
+                transport.write(content)
+                transport.write(b'\r\n')
+            else:
+                transport.write(content)
 
         if not more_content:
-            if (not status) or (not self._protocol.request_parser.should_keep_alive()):
+            if self._use_chunked_encoding:
+                transport.write(b'0\r\n\r\n')
+                self._use_chunked_encoding = False
+
+            if not self._protocol.request_parser.should_keep_alive():
                 transport.close()
             elif protocol.pipeline_queue:
                 message, channels = protocol.pipeline_queue.popleft()
@@ -258,9 +272,3 @@
                 'more_content': False
             }
             self.channels['body']._put(message)
-
-    def on_chunk_header(self):
-        pass
-
-    def on_chunk_complete(self):
-        pass

{"golden_diff": "diff --git a/uvicorn/protocols/http.py b/uvicorn/protocols/http.py\n--- a/uvicorn/protocols/http.py\n+++ b/uvicorn/protocols/http.py\n@@ -65,10 +65,11 @@\n \n \n class ReplyChannel(object):\n-    __slots__ = ['_protocol', 'name']\n+    __slots__ = ['_protocol', '_use_chunked_encoding', 'name']\n \n     def __init__(self, protocol):\n         self._protocol = protocol\n+        self._use_chunked_encoding = False\n         self.name = 'reply:%d' % id(self)\n \n     async def send(self, message):\n@@ -82,8 +83,8 @@\n             await transport.drain()\n \n         status = message.get('status')\n-        headers = message.get('headers')\n-        content = message.get('content')\n+        headers = message.get('headers', [])\n+        content = message.get('content', b'')\n         more_content = message.get('more_content', False)\n \n         if status is not None:\n@@ -92,24 +93,37 @@\n                 SERVER_HEADER,\n                 DATE_HEADER,\n             ]\n-            transport.write(b''.join(response))\n-\n-        if headers is not None:\n-            response = []\n-            if content is not None and not more_content:\n-                response = [b'content-length: ', str(len(content)).encode(), b'\\r\\n']\n \n+            seen_content_length = False\n             for header_name, header_value in headers:\n+                if header_name.lower() == b'content-length':\n+                    seen_content_length = True\n                 response.extend([header_name, b': ', header_value, b'\\r\\n'])\n-            response.append(b'\\r\\n')\n \n+            if not seen_content_length:\n+                if more_content:\n+                    self._use_chunked_encoding = True\n+                    response.append(b'transfer-encoding: chunked\\r\\n')\n+                elif status != 204:\n+                    response.append(b'content-length: ', str(len(content)).encode(), b'\\r\\n')\n+\n+            response.append(b'\\r\\n')\n             transport.write(b''.join(response))\n \n-        if content is not None:\n-            transport.write(content)\n+        if content:\n+            if self._use_chunked_encoding:\n+                transport.write(b'%x\\r\\n' % len(content))\n+                transport.write(content)\n+                transport.write(b'\\r\\n')\n+            else:\n+                transport.write(content)\n \n         if not more_content:\n-            if (not status) or (not self._protocol.request_parser.should_keep_alive()):\n+            if self._use_chunked_encoding:\n+                transport.write(b'0\\r\\n\\r\\n')\n+                self._use_chunked_encoding = False\n+\n+            if not self._protocol.request_parser.should_keep_alive():\n                 transport.close()\n             elif protocol.pipeline_queue:\n                 message, channels = protocol.pipeline_queue.popleft()\n@@ -258,9 +272,3 @@\n                 'more_content': False\n             }\n             self.channels['body']._put(message)\n-\n-    def on_chunk_header(self):\n-        pass\n-\n-    def on_chunk_complete(self):\n-        pass\n", "issue": "Transfer-Encoding: Chunked\nResponses need to support `Transfer-Encoding: Chunked`\n", "before_files": [{"content": "import asyncio\nimport collections\nimport email\nimport http\nimport httptools\nimport os\nimport time\n\nfrom uvicorn.protocols.websocket import websocket_upgrade\n\n\ndef set_time_and_date():\n    global CURRENT_TIME\n    global DATE_HEADER\n\n    CURRENT_TIME = time.time()\n    DATE_HEADER = b''.join([\n        b'date: ',\n        email.utils.formatdate(CURRENT_TIME, usegmt=True).encode(),\n        b'\\r\\n'\n    ])\n\n\ndef get_status_line(status_code):\n    try:\n        phrase = http.HTTPStatus(status_code).phrase.encode()\n    except ValueError:\n        phrase = b''\n    return b''.join([\n        b'HTTP/1.1 ', str(status_code).encode(), b' ', phrase, b'\\r\\n'\n    ])\n\n\nCURRENT_TIME = 0.0\nDATE_HEADER = b''\nSERVER_HEADER = b'server: uvicorn\\r\\n'\nSTATUS_LINE = {\n    status_code: get_status_line(status_code) for status_code in range(100, 600)\n}\n\nLOW_WATER_LIMIT = 16384\nHIGH_WATER_LIMIT = 65536\nMAX_PIPELINED_REQUESTS = 20\n\nset_time_and_date()\n\n\nclass BodyChannel(object):\n    __slots__ = ['_queue', '_protocol', 'name']\n\n    def __init__(self, protocol):\n        self._queue = asyncio.Queue()\n        self._protocol = protocol\n        self.name = 'body:%d' % id(self)\n\n    def _put(self, message):\n        self._queue.put_nowait(message)\n        self._protocol.buffer_size += len(message['content'])\n        self._protocol.check_pause_reading()\n\n    async def receive(self):\n        message = await self._queue.get()\n        self._protocol.buffer_size -= len(message['content'])\n        self._protocol.check_resume_reading()\n\n\nclass ReplyChannel(object):\n    __slots__ = ['_protocol', 'name']\n\n    def __init__(self, protocol):\n        self._protocol = protocol\n        self.name = 'reply:%d' % id(self)\n\n    async def send(self, message):\n        protocol = self._protocol\n        transport = protocol.transport\n\n        if transport is None:\n            return\n\n        if protocol.write_paused:\n            await transport.drain()\n\n        status = message.get('status')\n        headers = message.get('headers')\n        content = message.get('content')\n        more_content = message.get('more_content', False)\n\n        if status is not None:\n            response = [\n                STATUS_LINE[status],\n                SERVER_HEADER,\n                DATE_HEADER,\n            ]\n            transport.write(b''.join(response))\n\n        if headers is not None:\n            response = []\n            if content is not None and not more_content:\n                response = [b'content-length: ', str(len(content)).encode(), b'\\r\\n']\n\n            for header_name, header_value in headers:\n                response.extend([header_name, b': ', header_value, b'\\r\\n'])\n            response.append(b'\\r\\n')\n\n            transport.write(b''.join(response))\n\n        if content is not None:\n            transport.write(content)\n\n        if not more_content:\n            if (not status) or (not self._protocol.request_parser.should_keep_alive()):\n                transport.close()\n            elif protocol.pipeline_queue:\n                message, channels = protocol.pipeline_queue.popleft()\n                protocol.loop.create_task(protocol.consumer(message, channels))\n                protocol.check_resume_reading()\n            else:\n                protocol.has_active_request = False\n\n\nclass HttpProtocol(asyncio.Protocol):\n    __slots__ = [\n        'consumer', 'loop', 'request_parser',\n        'base_message', 'base_channels',\n        'transport', 'message', 'channels', 'headers', 'upgrade',\n        'read_paused', 'write_paused',\n        'buffer_size', 'high_water_limit', 'low_water_limit',\n        'has_active_request', 'max_pipelined_requests', 'pipeline_queue'\n    ]\n\n    def __init__(self, consumer, loop, sock, cfg):\n        self.consumer = consumer\n        self.loop = loop\n        self.request_parser = httptools.HttpRequestParser(self)\n\n        self.base_message = {\n            'channel': 'http.request',\n            'scheme': 'https' if cfg.is_ssl else 'http',\n            'root_path': os.environ.get('SCRIPT_NAME', ''),\n            'server': sock.getsockname()\n        }\n        self.base_channels = {\n            'reply': ReplyChannel(self)\n        }\n\n        self.transport = None\n        self.message = None\n        self.channels = None\n        self.headers = None\n        self.upgrade = None\n\n        self.read_paused = False\n        self.write_paused = False\n\n        self.buffer_size = 0\n        self.high_water_limit = HIGH_WATER_LIMIT\n        self.low_water_limit = LOW_WATER_LIMIT\n\n        self.has_active_request = False\n        self.max_pipelined_requests = MAX_PIPELINED_REQUESTS\n        self.pipeline_queue = collections.deque()\n\n    # The asyncio.Protocol hooks...\n    def connection_made(self, transport):\n        self.transport = transport\n\n    def connection_lost(self, exc):\n        self.transport = None\n\n    def eof_received(self):\n        pass\n\n    def data_received(self, data):\n        try:\n            self.request_parser.feed_data(data)\n        except httptools.HttpParserUpgrade:\n            websocket_upgrade(self)\n\n    # Flow control...\n    def pause_writing(self):\n        self.write_paused = True\n\n    def resume_writing(self):\n        self.write_paused = False\n\n    def check_pause_reading(self):\n        if self.transport is None or self.read_paused:\n            return\n        if (self.buffer_size > self.high_water_limit or\n            len(self.pipeline_queue) >= self.max_pipelined_requests):\n            self.transport.pause_reading()\n            self.read_paused = True\n\n    def check_resume_reading(self):\n        if self.transport is None or not self.read_paused:\n            return\n        if (self.buffer_size < self.low_water_limit and\n            len(self.pipeline_queue) < self.max_pipelined_requests):\n            self.transport.resume_reading()\n            self.read_paused = False\n\n    # Event hooks called back into by HttpRequestParser...\n    def on_message_begin(self):\n        self.message = self.base_message.copy()\n        self.channels = self.base_channels.copy()\n        self.headers = []\n\n    def on_url(self, url):\n        parsed = httptools.parse_url(url)\n        method = self.request_parser.get_method()\n        http_version = self.request_parser.get_http_version()\n        self.message.update({\n            'http_version': http_version,\n            'method': method.decode('ascii'),\n            'path': parsed.path.decode('ascii'),\n            'query_string': parsed.query if parsed.query else b'',\n            'headers': self.headers\n        })\n\n    def on_header(self, name: bytes, value: bytes):\n        name = name.lower()\n        if name == b'upgrade':\n            self.upgrade = value\n        elif name == b'expect' and value.lower() == b'100-continue':\n            self.transport.write(b'HTTP/1.1 100 Continue\\r\\n\\r\\n')\n        self.headers.append([name, value])\n\n    def on_body(self, body: bytes):\n        if 'body' not in self.channels:\n            self.channels['body'] = BodyChannel(self)\n            if not self.has_active_request:\n                self.loop.create_task(self.consumer(self.message, self.channels))\n                self.has_active_request = True\n            else:\n                self.pipeline_queue.append((self.message, self.channels))\n                self.check_pause_reading()\n        message = {\n            'content': body,\n            'more_content': True\n        }\n        self.channels['body']._put(message)\n\n    def on_message_complete(self):\n        if self.upgrade is not None:\n            return\n\n        if 'body' not in self.channels:\n            if not self.has_active_request:\n                self.loop.create_task(self.consumer(self.message, self.channels))\n                self.has_active_request = True\n            else:\n                self.pipeline_queue.append((self.message, self.channels))\n                self.check_pause_reading()\n        else:\n            message = {\n                'content': b'',\n                'more_content': False\n            }\n            self.channels['body']._put(message)\n\n    def on_chunk_header(self):\n        pass\n\n    def on_chunk_complete(self):\n        pass\n", "path": "uvicorn/protocols/http.py"}], "after_files": [{"content": "import asyncio\nimport collections\nimport email\nimport http\nimport httptools\nimport os\nimport time\n\nfrom uvicorn.protocols.websocket import websocket_upgrade\n\n\ndef set_time_and_date():\n    global CURRENT_TIME\n    global DATE_HEADER\n\n    CURRENT_TIME = time.time()\n    DATE_HEADER = b''.join([\n        b'date: ',\n        email.utils.formatdate(CURRENT_TIME, usegmt=True).encode(),\n        b'\\r\\n'\n    ])\n\n\ndef get_status_line(status_code):\n    try:\n        phrase = http.HTTPStatus(status_code).phrase.encode()\n    except ValueError:\n        phrase = b''\n    return b''.join([\n        b'HTTP/1.1 ', str(status_code).encode(), b' ', phrase, b'\\r\\n'\n    ])\n\n\nCURRENT_TIME = 0.0\nDATE_HEADER = b''\nSERVER_HEADER = b'server: uvicorn\\r\\n'\nSTATUS_LINE = {\n    status_code: get_status_line(status_code) for status_code in range(100, 600)\n}\n\nLOW_WATER_LIMIT = 16384\nHIGH_WATER_LIMIT = 65536\nMAX_PIPELINED_REQUESTS = 20\n\nset_time_and_date()\n\n\nclass BodyChannel(object):\n    __slots__ = ['_queue', '_protocol', 'name']\n\n    def __init__(self, protocol):\n        self._queue = asyncio.Queue()\n        self._protocol = protocol\n        self.name = 'body:%d' % id(self)\n\n    def _put(self, message):\n        self._queue.put_nowait(message)\n        self._protocol.buffer_size += len(message['content'])\n        self._protocol.check_pause_reading()\n\n    async def receive(self):\n        message = await self._queue.get()\n        self._protocol.buffer_size -= len(message['content'])\n        self._protocol.check_resume_reading()\n\n\nclass ReplyChannel(object):\n    __slots__ = ['_protocol', '_use_chunked_encoding', 'name']\n\n    def __init__(self, protocol):\n        self._protocol = protocol\n        self._use_chunked_encoding = False\n        self.name = 'reply:%d' % id(self)\n\n    async def send(self, message):\n        protocol = self._protocol\n        transport = protocol.transport\n\n        if transport is None:\n            return\n\n        if protocol.write_paused:\n            await transport.drain()\n\n        status = message.get('status')\n        headers = message.get('headers', [])\n        content = message.get('content', b'')\n        more_content = message.get('more_content', False)\n\n        if status is not None:\n            response = [\n                STATUS_LINE[status],\n                SERVER_HEADER,\n                DATE_HEADER,\n            ]\n\n            seen_content_length = False\n            for header_name, header_value in headers:\n                if header_name.lower() == b'content-length':\n                    seen_content_length = True\n                response.extend([header_name, b': ', header_value, b'\\r\\n'])\n\n            if not seen_content_length:\n                if more_content:\n                    self._use_chunked_encoding = True\n                    response.append(b'transfer-encoding: chunked\\r\\n')\n                elif status != 204:\n                    response.append(b'content-length: ', str(len(content)).encode(), b'\\r\\n')\n\n            response.append(b'\\r\\n')\n            transport.write(b''.join(response))\n\n        if content:\n            if self._use_chunked_encoding:\n                transport.write(b'%x\\r\\n' % len(content))\n                transport.write(content)\n                transport.write(b'\\r\\n')\n            else:\n                transport.write(content)\n\n        if not more_content:\n            if self._use_chunked_encoding:\n                transport.write(b'0\\r\\n\\r\\n')\n                self._use_chunked_encoding = False\n\n            if not self._protocol.request_parser.should_keep_alive():\n                transport.close()\n            elif protocol.pipeline_queue:\n                message, channels = protocol.pipeline_queue.popleft()\n                protocol.loop.create_task(protocol.consumer(message, channels))\n                protocol.check_resume_reading()\n            else:\n                protocol.has_active_request = False\n\n\nclass HttpProtocol(asyncio.Protocol):\n    __slots__ = [\n        'consumer', 'loop', 'request_parser',\n        'base_message', 'base_channels',\n        'transport', 'message', 'channels', 'headers', 'upgrade',\n        'read_paused', 'write_paused',\n        'buffer_size', 'high_water_limit', 'low_water_limit',\n        'has_active_request', 'max_pipelined_requests', 'pipeline_queue'\n    ]\n\n    def __init__(self, consumer, loop, sock, cfg):\n        self.consumer = consumer\n        self.loop = loop\n        self.request_parser = httptools.HttpRequestParser(self)\n\n        self.base_message = {\n            'channel': 'http.request',\n            'scheme': 'https' if cfg.is_ssl else 'http',\n            'root_path': os.environ.get('SCRIPT_NAME', ''),\n            'server': sock.getsockname()\n        }\n        self.base_channels = {\n            'reply': ReplyChannel(self)\n        }\n\n        self.transport = None\n        self.message = None\n        self.channels = None\n        self.headers = None\n        self.upgrade = None\n\n        self.read_paused = False\n        self.write_paused = False\n\n        self.buffer_size = 0\n        self.high_water_limit = HIGH_WATER_LIMIT\n        self.low_water_limit = LOW_WATER_LIMIT\n\n        self.has_active_request = False\n        self.max_pipelined_requests = MAX_PIPELINED_REQUESTS\n        self.pipeline_queue = collections.deque()\n\n    # The asyncio.Protocol hooks...\n    def connection_made(self, transport):\n        self.transport = transport\n\n    def connection_lost(self, exc):\n        self.transport = None\n\n    def eof_received(self):\n        pass\n\n    def data_received(self, data):\n        try:\n            self.request_parser.feed_data(data)\n        except httptools.HttpParserUpgrade:\n            websocket_upgrade(self)\n\n    # Flow control...\n    def pause_writing(self):\n        self.write_paused = True\n\n    def resume_writing(self):\n        self.write_paused = False\n\n    def check_pause_reading(self):\n        if self.transport is None or self.read_paused:\n            return\n        if (self.buffer_size > self.high_water_limit or\n            len(self.pipeline_queue) >= self.max_pipelined_requests):\n            self.transport.pause_reading()\n            self.read_paused = True\n\n    def check_resume_reading(self):\n        if self.transport is None or not self.read_paused:\n            return\n        if (self.buffer_size < self.low_water_limit and\n            len(self.pipeline_queue) < self.max_pipelined_requests):\n            self.transport.resume_reading()\n            self.read_paused = False\n\n    # Event hooks called back into by HttpRequestParser...\n    def on_message_begin(self):\n        self.message = self.base_message.copy()\n        self.channels = self.base_channels.copy()\n        self.headers = []\n\n    def on_url(self, url):\n        parsed = httptools.parse_url(url)\n        method = self.request_parser.get_method()\n        http_version = self.request_parser.get_http_version()\n        self.message.update({\n            'http_version': http_version,\n            'method': method.decode('ascii'),\n            'path': parsed.path.decode('ascii'),\n            'query_string': parsed.query if parsed.query else b'',\n            'headers': self.headers\n        })\n\n    def on_header(self, name: bytes, value: bytes):\n        name = name.lower()\n        if name == b'upgrade':\n            self.upgrade = value\n        elif name == b'expect' and value.lower() == b'100-continue':\n            self.transport.write(b'HTTP/1.1 100 Continue\\r\\n\\r\\n')\n        self.headers.append([name, value])\n\n    def on_body(self, body: bytes):\n        if 'body' not in self.channels:\n            self.channels['body'] = BodyChannel(self.transport)\n            if not self.has_active_request:\n                self.loop.create_task(self.consumer(self.message, self.channels))\n                self.has_active_request = True\n            else:\n                self.pipeline_queue.append((self.message, self.channels))\n                self.check_pause_reading()\n        message = {\n            'content': body,\n            'more_content': True\n        }\n        self.channels['body']._put(message)\n\n    def on_message_complete(self):\n        if self.upgrade is not None:\n            return\n\n        if 'body' not in self.channels:\n            if not self.has_active_request:\n                self.loop.create_task(self.consumer(self.message, self.channels))\n                self.has_active_request = True\n            else:\n                self.pipeline_queue.append((self.message, self.channels))\n                self.check_pause_reading()\n        else:\n            message = {\n                'content': b'',\n                'more_content': False\n            }\n            self.channels['body']._put(message)\n", "path": "uvicorn/protocols/http.py"}]}