We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Bad Request when sending utf-8 encoded http path under python3
This is the sent data:
```
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('127.0.0.1', 8080))
print(s.send('GET /à%20k HTTP/1.0\r\n\r\n'.encode('utf-8')))
print(s.recv(100000).decode('ascii'))
```

This is the response
```
HTTP/1.1 400 Bad Request
Connection: close
Content-Type: text/html
Content-Length: 181

<html>
  <head>
    <title>Bad Request</title>
  </head>
  <body>
    <h1><p>Bad Request</p></h1>
    Invalid HTTP Version 'Invalid HTTP Version: '%20k HTTP/1.0''
  </body>
</html>
```

This is because the request line is first decoded as latin1 using the _compat:bytes_to_str this causes the "à" to be returned as "\xc3\xa0", then the request line is split using line.split(None, 2) which will consider the \xa0 (non breaking space) as whitespace and strip it, thus rendering the request line invalid.

A first attempt would be to use line.split(' ', 2) but then the split will no longer eat up all consecutive whitespaces and may introduce other bugs.

I'm not sure what would be the best solution here.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `gunicorn/http/message.py`
Content:
```
1 # -*- coding: utf-8 -
2 #
3 # This file is part of gunicorn released under the MIT license.
4 # See the NOTICE for more information.
5 
6 import re
7 import socket
8 from errno import ENOTCONN
9 
10 from gunicorn._compat import bytes_to_str
11 from gunicorn.http.unreader import SocketUnreader
12 from gunicorn.http.body import ChunkedReader, LengthReader, EOFReader, Body
13 from gunicorn.http.errors import (InvalidHeader, InvalidHeaderName, NoMoreData,
14     InvalidRequestLine, InvalidRequestMethod, InvalidHTTPVersion,
15     LimitRequestLine, LimitRequestHeaders)
16 from gunicorn.http.errors import InvalidProxyLine, ForbiddenProxyRequest
17 from gunicorn.six import BytesIO
18 from gunicorn._compat import urlsplit
19 
20 MAX_REQUEST_LINE = 8190
21 MAX_HEADERS = 32768
22 DEFAULT_MAX_HEADERFIELD_SIZE = 8190
23 
24 HEADER_RE = re.compile("[\x00-\x1F\x7F()<>@,;:\[\]={} \t\\\\\"]")
25 METH_RE = re.compile(r"[A-Z0-9$-_.]{3,20}")
26 VERSION_RE = re.compile(r"HTTP/(\d+)\.(\d+)")
27 
28 
29 class Message(object):
30     def __init__(self, cfg, unreader):
31         self.cfg = cfg
32         self.unreader = unreader
33         self.version = None
34         self.headers = []
35         self.trailers = []
36         self.body = None
37 
38         # set headers limits
39         self.limit_request_fields = cfg.limit_request_fields
40         if (self.limit_request_fields <= 0
41             or self.limit_request_fields > MAX_HEADERS):
42             self.limit_request_fields = MAX_HEADERS
43         self.limit_request_field_size = cfg.limit_request_field_size
44         if self.limit_request_field_size < 0:
45             self.limit_request_field_size = DEFAULT_MAX_HEADERFIELD_SIZE
46 
47         # set max header buffer size
48         max_header_field_size = self.limit_request_field_size or DEFAULT_MAX_HEADERFIELD_SIZE
49         self.max_buffer_headers = self.limit_request_fields * \
50             (max_header_field_size + 2) + 4
51 
52         unused = self.parse(self.unreader)
53         self.unreader.unread(unused)
54         self.set_body_reader()
55 
56     def parse(self, unreader):
57         raise NotImplementedError()
58 
59     def parse_headers(self, data):
60         headers = []
61 
62         # Split lines on \r\n keeping the \r\n on each line
63         lines = [bytes_to_str(line) + "\r\n" for line in data.split(b"\r\n")]
64 
65         # Parse headers into key/value pairs paying attention
66         # to continuation lines.
67         while lines:
68             if len(headers) >= self.limit_request_fields:
69                 raise LimitRequestHeaders("limit request headers fields")
70 
71             # Parse initial header name : value pair.
72             curr = lines.pop(0)
73             header_length = len(curr)
74             if curr.find(":") < 0:
75                 raise InvalidHeader(curr.strip())
76             name, value = curr.split(":", 1)
77             name = name.rstrip(" \t").upper()
78             if HEADER_RE.search(name):
79                 raise InvalidHeaderName(name)
80 
81             name, value = name.strip(), [value.lstrip()]
82 
83             # Consume value continuation lines
84             while lines and lines[0].startswith((" ", "\t")):
85                 curr = lines.pop(0)
86                 header_length += len(curr)
87                 if header_length > self.limit_request_field_size > 0:
88                     raise LimitRequestHeaders("limit request headers "
89                             + "fields size")
90                 value.append(curr)
91             value = ''.join(value).rstrip()
92 
93             if header_length > self.limit_request_field_size > 0:
94                 raise LimitRequestHeaders("limit request headers fields size")
95             headers.append((name, value))
96         return headers
97 
98     def set_body_reader(self):
99         chunked = False
100         content_length = None
101         for (name, value) in self.headers:
102             if name == "CONTENT-LENGTH":
103                 content_length = value
104             elif name == "TRANSFER-ENCODING":
105                 chunked = value.lower() == "chunked"
106             elif name == "SEC-WEBSOCKET-KEY1":
107                 content_length = 8
108 
109         if chunked:
110             self.body = Body(ChunkedReader(self, self.unreader))
111         elif content_length is not None:
112             try:
113                 content_length = int(content_length)
114             except ValueError:
115                 raise InvalidHeader("CONTENT-LENGTH", req=self)
116 
117             if content_length < 0:
118                 raise InvalidHeader("CONTENT-LENGTH", req=self)
119 
120             self.body = Body(LengthReader(self.unreader, content_length))
121         else:
122             self.body = Body(EOFReader(self.unreader))
123 
124     def should_close(self):
125         for (h, v) in self.headers:
126             if h == "CONNECTION":
127                 v = v.lower().strip()
128                 if v == "close":
129                     return True
130                 elif v == "keep-alive":
131                     return False
132                 break
133         return self.version <= (1, 0)
134 
135 
136 class Request(Message):
137     def __init__(self, cfg, unreader, req_number=1):
138         self.method = None
139         self.uri = None
140         self.path = None
141         self.query = None
142         self.fragment = None
143 
144         # get max request line size
145         self.limit_request_line = cfg.limit_request_line
146         if (self.limit_request_line < 0
147             or self.limit_request_line >= MAX_REQUEST_LINE):
148             self.limit_request_line = MAX_REQUEST_LINE
149 
150         self.req_number = req_number
151         self.proxy_protocol_info = None
152         super(Request, self).__init__(cfg, unreader)
153 
154     def get_data(self, unreader, buf, stop=False):
155         data = unreader.read()
156         if not data:
157             if stop:
158                 raise StopIteration()
159             raise NoMoreData(buf.getvalue())
160         buf.write(data)
161 
162     def parse(self, unreader):
163         buf = BytesIO()
164         self.get_data(unreader, buf, stop=True)
165 
166         # get request line
167         line, rbuf = self.read_line(unreader, buf, self.limit_request_line)
168 
169         # proxy protocol
170         if self.proxy_protocol(bytes_to_str(line)):
171             # get next request line
172             buf = BytesIO()
173             buf.write(rbuf)
174             line, rbuf = self.read_line(unreader, buf, self.limit_request_line)
175 
176         self.parse_request_line(bytes_to_str(line))
177         buf = BytesIO()
178         buf.write(rbuf)
179 
180         # Headers
181         data = buf.getvalue()
182         idx = data.find(b"\r\n\r\n")
183 
184         done = data[:2] == b"\r\n"
185         while True:
186             idx = data.find(b"\r\n\r\n")
187             done = data[:2] == b"\r\n"
188 
189             if idx < 0 and not done:
190                 self.get_data(unreader, buf)
191                 data = buf.getvalue()
192                 if len(data) > self.max_buffer_headers:
193                     raise LimitRequestHeaders("max buffer headers")
194             else:
195                 break
196 
197         if done:
198             self.unreader.unread(data[2:])
199             return b""
200 
201         self.headers = self.parse_headers(data[:idx])
202 
203         ret = data[idx + 4:]
204         buf = BytesIO()
205         return ret
206 
207     def read_line(self, unreader, buf, limit=0):
208         data = buf.getvalue()
209 
210         while True:
211             idx = data.find(b"\r\n")
212             if idx >= 0:
213                 # check if the request line is too large
214                 if idx > limit > 0:
215                     raise LimitRequestLine(idx, limit)
216                 break
217             elif len(data) - 2 > limit > 0:
218                 raise LimitRequestLine(len(data), limit)
219             self.get_data(unreader, buf)
220             data = buf.getvalue()
221 
222         return (data[:idx],  # request line,
223                 data[idx + 2:])  # residue in the buffer, skip \r\n
224 
225     def proxy_protocol(self, line):
226         """\
227         Detect, check and parse proxy protocol.
228 
229         :raises: ForbiddenProxyRequest, InvalidProxyLine.
230         :return: True for proxy protocol line else False
231         """
232         if not self.cfg.proxy_protocol:
233             return False
234 
235         if self.req_number != 1:
236             return False
237 
238         if not line.startswith("PROXY"):
239             return False
240 
241         self.proxy_protocol_access_check()
242         self.parse_proxy_protocol(line)
243 
244         return True
245 
246     def proxy_protocol_access_check(self):
247         # check in allow list
248         if isinstance(self.unreader, SocketUnreader):
249             try:
250                 remote_host = self.unreader.sock.getpeername()[0]
251             except socket.error as e:
252                 if e.args[0] == ENOTCONN:
253                     raise ForbiddenProxyRequest("UNKNOW")
254                 raise
255             if ("*" not in self.cfg.proxy_allow_ips and
256                     remote_host not in self.cfg.proxy_allow_ips):
257                 raise ForbiddenProxyRequest(remote_host)
258 
259     def parse_proxy_protocol(self, line):
260         bits = line.split()
261 
262         if len(bits) != 6:
263             raise InvalidProxyLine(line)
264 
265         # Extract data
266         proto = bits[1]
267         s_addr = bits[2]
268         d_addr = bits[3]
269 
270         # Validation
271         if proto not in ["TCP4", "TCP6"]:
272             raise InvalidProxyLine("protocol '%s' not supported" % proto)
273         if proto == "TCP4":
274             try:
275                 socket.inet_pton(socket.AF_INET, s_addr)
276                 socket.inet_pton(socket.AF_INET, d_addr)
277             except socket.error:
278                 raise InvalidProxyLine(line)
279         elif proto == "TCP6":
280             try:
281                 socket.inet_pton(socket.AF_INET6, s_addr)
282                 socket.inet_pton(socket.AF_INET6, d_addr)
283             except socket.error:
284                 raise InvalidProxyLine(line)
285 
286         try:
287             s_port = int(bits[4])
288             d_port = int(bits[5])
289         except ValueError:
290             raise InvalidProxyLine("invalid port %s" % line)
291 
292         if not ((0 <= s_port <= 65535) and (0 <= d_port <= 65535)):
293             raise InvalidProxyLine("invalid port %s" % line)
294 
295         # Set data
296         self.proxy_protocol_info = {
297             "proxy_protocol": proto,
298             "client_addr": s_addr,
299             "client_port": s_port,
300             "proxy_addr": d_addr,
301             "proxy_port": d_port
302         }
303 
304     def parse_request_line(self, line):
305         bits = line.split(None, 2)
306         if len(bits) != 3:
307             raise InvalidRequestLine(line)
308 
309         # Method
310         if not METH_RE.match(bits[0]):
311             raise InvalidRequestMethod(bits[0])
312         self.method = bits[0].upper()
313 
314         # URI
315         # When the path starts with //, urlsplit considers it as a
316         # relative uri while the RDF says it shouldnt
317         # http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2
318         # considers it as an absolute url.
319         # fix issue #297
320         if bits[1].startswith("//"):
321             self.uri = bits[1][1:]
322         else:
323             self.uri = bits[1]
324 
325         try:
326             parts = urlsplit(self.uri)
327         except ValueError:
328             raise InvalidRequestLine(line)
329         self.path = parts.path or ""
330         self.query = parts.query or ""
331         self.fragment = parts.fragment or ""
332 
333         # Version
334         match = VERSION_RE.match(bits[2])
335         if match is None:
336             raise InvalidHTTPVersion(bits[2])
337         self.version = (int(match.group(1)), int(match.group(2)))
338 
339     def set_body_reader(self):
340         super(Request, self).set_body_reader()
341         if isinstance(self.body.reader, EOFReader):
342             self.body = Body(LengthReader(self.unreader, 0))
343
```
--- 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/gunicorn/http/message.py b/gunicorn/http/message.py
--- a/gunicorn/http/message.py
+++ b/gunicorn/http/message.py
@@ -173,7 +173,7 @@
             buf.write(rbuf)
             line, rbuf = self.read_line(unreader, buf, self.limit_request_line)
 
-        self.parse_request_line(bytes_to_str(line))
+        self.parse_request_line(line)
         buf = BytesIO()
         buf.write(rbuf)
 
@@ -301,10 +301,10 @@
             "proxy_port": d_port
         }
 
-    def parse_request_line(self, line):
-        bits = line.split(None, 2)
+    def parse_request_line(self, line_bytes):
+        bits = [bytes_to_str(bit) for bit in line_bytes.split(None, 2)]
         if len(bits) != 3:
-            raise InvalidRequestLine(line)
+            raise InvalidRequestLine(bytes_to_str(line_bytes))
 
         # Method
         if not METH_RE.match(bits[0]):
@@ -325,7 +325,7 @@
         try:
             parts = urlsplit(self.uri)
         except ValueError:
-            raise InvalidRequestLine(line)
+            raise InvalidRequestLine(bytes_to_str(line_bytes))
         self.path = parts.path or ""
         self.query = parts.query or ""
         self.fragment = parts.fragment or ""

{"golden_diff": "diff --git a/gunicorn/http/message.py b/gunicorn/http/message.py\n--- a/gunicorn/http/message.py\n+++ b/gunicorn/http/message.py\n@@ -173,7 +173,7 @@\n             buf.write(rbuf)\n             line, rbuf = self.read_line(unreader, buf, self.limit_request_line)\n \n-        self.parse_request_line(bytes_to_str(line))\n+        self.parse_request_line(line)\n         buf = BytesIO()\n         buf.write(rbuf)\n \n@@ -301,10 +301,10 @@\n             \"proxy_port\": d_port\n         }\n \n-    def parse_request_line(self, line):\n-        bits = line.split(None, 2)\n+    def parse_request_line(self, line_bytes):\n+        bits = [bytes_to_str(bit) for bit in line_bytes.split(None, 2)]\n         if len(bits) != 3:\n-            raise InvalidRequestLine(line)\n+            raise InvalidRequestLine(bytes_to_str(line_bytes))\n \n         # Method\n         if not METH_RE.match(bits[0]):\n@@ -325,7 +325,7 @@\n         try:\n             parts = urlsplit(self.uri)\n         except ValueError:\n-            raise InvalidRequestLine(line)\n+            raise InvalidRequestLine(bytes_to_str(line_bytes))\n         self.path = parts.path or \"\"\n         self.query = parts.query or \"\"\n         self.fragment = parts.fragment or \"\"\n", "issue": "Bad Request when sending utf-8 encoded http path under python3\nThis is the sent data:\r\n```\r\nimport socket\r\ns = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\r\ns.connect(('127.0.0.1', 8080))\r\nprint(s.send('GET /\u00e0%20k HTTP/1.0\\r\\n\\r\\n'.encode('utf-8')))\r\nprint(s.recv(100000).decode('ascii'))\r\n```\r\n\r\nThis is the response\r\n```\r\nHTTP/1.1 400 Bad Request\r\nConnection: close\r\nContent-Type: text/html\r\nContent-Length: 181\r\n\r\n<html>\r\n  <head>\r\n    <title>Bad Request</title>\r\n  </head>\r\n  <body>\r\n    <h1><p>Bad Request</p></h1>\r\n    Invalid HTTP Version 'Invalid HTTP Version: '%20k HTTP/1.0''\r\n  </body>\r\n</html>\r\n```\r\n\r\nThis is because the request line is first decoded as latin1 using the _compat:bytes_to_str this causes the \"\u00e0\" to be returned as \"\\xc3\\xa0\", then the request line is split using line.split(None, 2) which will consider the \\xa0 (non breaking space) as whitespace and strip it, thus rendering the request line invalid.\r\n\r\nA first attempt would be to use line.split(' ', 2) but then the split will no longer eat up all consecutive whitespaces and may introduce other bugs.\r\n\r\nI'm not sure what would be the best solution here.\n", "before_files": [{"content": "# -*- coding: utf-8 -\n#\n# This file is part of gunicorn released under the MIT license.\n# See the NOTICE for more information.\n\nimport re\nimport socket\nfrom errno import ENOTCONN\n\nfrom gunicorn._compat import bytes_to_str\nfrom gunicorn.http.unreader import SocketUnreader\nfrom gunicorn.http.body import ChunkedReader, LengthReader, EOFReader, Body\nfrom gunicorn.http.errors import (InvalidHeader, InvalidHeaderName, NoMoreData,\n    InvalidRequestLine, InvalidRequestMethod, InvalidHTTPVersion,\n    LimitRequestLine, LimitRequestHeaders)\nfrom gunicorn.http.errors import InvalidProxyLine, ForbiddenProxyRequest\nfrom gunicorn.six import BytesIO\nfrom gunicorn._compat import urlsplit\n\nMAX_REQUEST_LINE = 8190\nMAX_HEADERS = 32768\nDEFAULT_MAX_HEADERFIELD_SIZE = 8190\n\nHEADER_RE = re.compile(\"[\\x00-\\x1F\\x7F()<>@,;:\\[\\]={} \\t\\\\\\\\\\\"]\")\nMETH_RE = re.compile(r\"[A-Z0-9$-_.]{3,20}\")\nVERSION_RE = re.compile(r\"HTTP/(\\d+)\\.(\\d+)\")\n\n\nclass Message(object):\n    def __init__(self, cfg, unreader):\n        self.cfg = cfg\n        self.unreader = unreader\n        self.version = None\n        self.headers = []\n        self.trailers = []\n        self.body = None\n\n        # set headers limits\n        self.limit_request_fields = cfg.limit_request_fields\n        if (self.limit_request_fields <= 0\n            or self.limit_request_fields > MAX_HEADERS):\n            self.limit_request_fields = MAX_HEADERS\n        self.limit_request_field_size = cfg.limit_request_field_size\n        if self.limit_request_field_size < 0:\n            self.limit_request_field_size = DEFAULT_MAX_HEADERFIELD_SIZE\n\n        # set max header buffer size\n        max_header_field_size = self.limit_request_field_size or DEFAULT_MAX_HEADERFIELD_SIZE\n        self.max_buffer_headers = self.limit_request_fields * \\\n            (max_header_field_size + 2) + 4\n\n        unused = self.parse(self.unreader)\n        self.unreader.unread(unused)\n        self.set_body_reader()\n\n    def parse(self, unreader):\n        raise NotImplementedError()\n\n    def parse_headers(self, data):\n        headers = []\n\n        # Split lines on \\r\\n keeping the \\r\\n on each line\n        lines = [bytes_to_str(line) + \"\\r\\n\" for line in data.split(b\"\\r\\n\")]\n\n        # Parse headers into key/value pairs paying attention\n        # to continuation lines.\n        while lines:\n            if len(headers) >= self.limit_request_fields:\n                raise LimitRequestHeaders(\"limit request headers fields\")\n\n            # Parse initial header name : value pair.\n            curr = lines.pop(0)\n            header_length = len(curr)\n            if curr.find(\":\") < 0:\n                raise InvalidHeader(curr.strip())\n            name, value = curr.split(\":\", 1)\n            name = name.rstrip(\" \\t\").upper()\n            if HEADER_RE.search(name):\n                raise InvalidHeaderName(name)\n\n            name, value = name.strip(), [value.lstrip()]\n\n            # Consume value continuation lines\n            while lines and lines[0].startswith((\" \", \"\\t\")):\n                curr = lines.pop(0)\n                header_length += len(curr)\n                if header_length > self.limit_request_field_size > 0:\n                    raise LimitRequestHeaders(\"limit request headers \"\n                            + \"fields size\")\n                value.append(curr)\n            value = ''.join(value).rstrip()\n\n            if header_length > self.limit_request_field_size > 0:\n                raise LimitRequestHeaders(\"limit request headers fields size\")\n            headers.append((name, value))\n        return headers\n\n    def set_body_reader(self):\n        chunked = False\n        content_length = None\n        for (name, value) in self.headers:\n            if name == \"CONTENT-LENGTH\":\n                content_length = value\n            elif name == \"TRANSFER-ENCODING\":\n                chunked = value.lower() == \"chunked\"\n            elif name == \"SEC-WEBSOCKET-KEY1\":\n                content_length = 8\n\n        if chunked:\n            self.body = Body(ChunkedReader(self, self.unreader))\n        elif content_length is not None:\n            try:\n                content_length = int(content_length)\n            except ValueError:\n                raise InvalidHeader(\"CONTENT-LENGTH\", req=self)\n\n            if content_length < 0:\n                raise InvalidHeader(\"CONTENT-LENGTH\", req=self)\n\n            self.body = Body(LengthReader(self.unreader, content_length))\n        else:\n            self.body = Body(EOFReader(self.unreader))\n\n    def should_close(self):\n        for (h, v) in self.headers:\n            if h == \"CONNECTION\":\n                v = v.lower().strip()\n                if v == \"close\":\n                    return True\n                elif v == \"keep-alive\":\n                    return False\n                break\n        return self.version <= (1, 0)\n\n\nclass Request(Message):\n    def __init__(self, cfg, unreader, req_number=1):\n        self.method = None\n        self.uri = None\n        self.path = None\n        self.query = None\n        self.fragment = None\n\n        # get max request line size\n        self.limit_request_line = cfg.limit_request_line\n        if (self.limit_request_line < 0\n            or self.limit_request_line >= MAX_REQUEST_LINE):\n            self.limit_request_line = MAX_REQUEST_LINE\n\n        self.req_number = req_number\n        self.proxy_protocol_info = None\n        super(Request, self).__init__(cfg, unreader)\n\n    def get_data(self, unreader, buf, stop=False):\n        data = unreader.read()\n        if not data:\n            if stop:\n                raise StopIteration()\n            raise NoMoreData(buf.getvalue())\n        buf.write(data)\n\n    def parse(self, unreader):\n        buf = BytesIO()\n        self.get_data(unreader, buf, stop=True)\n\n        # get request line\n        line, rbuf = self.read_line(unreader, buf, self.limit_request_line)\n\n        # proxy protocol\n        if self.proxy_protocol(bytes_to_str(line)):\n            # get next request line\n            buf = BytesIO()\n            buf.write(rbuf)\n            line, rbuf = self.read_line(unreader, buf, self.limit_request_line)\n\n        self.parse_request_line(bytes_to_str(line))\n        buf = BytesIO()\n        buf.write(rbuf)\n\n        # Headers\n        data = buf.getvalue()\n        idx = data.find(b\"\\r\\n\\r\\n\")\n\n        done = data[:2] == b\"\\r\\n\"\n        while True:\n            idx = data.find(b\"\\r\\n\\r\\n\")\n            done = data[:2] == b\"\\r\\n\"\n\n            if idx < 0 and not done:\n                self.get_data(unreader, buf)\n                data = buf.getvalue()\n                if len(data) > self.max_buffer_headers:\n                    raise LimitRequestHeaders(\"max buffer headers\")\n            else:\n                break\n\n        if done:\n            self.unreader.unread(data[2:])\n            return b\"\"\n\n        self.headers = self.parse_headers(data[:idx])\n\n        ret = data[idx + 4:]\n        buf = BytesIO()\n        return ret\n\n    def read_line(self, unreader, buf, limit=0):\n        data = buf.getvalue()\n\n        while True:\n            idx = data.find(b\"\\r\\n\")\n            if idx >= 0:\n                # check if the request line is too large\n                if idx > limit > 0:\n                    raise LimitRequestLine(idx, limit)\n                break\n            elif len(data) - 2 > limit > 0:\n                raise LimitRequestLine(len(data), limit)\n            self.get_data(unreader, buf)\n            data = buf.getvalue()\n\n        return (data[:idx],  # request line,\n                data[idx + 2:])  # residue in the buffer, skip \\r\\n\n\n    def proxy_protocol(self, line):\n        \"\"\"\\\n        Detect, check and parse proxy protocol.\n\n        :raises: ForbiddenProxyRequest, InvalidProxyLine.\n        :return: True for proxy protocol line else False\n        \"\"\"\n        if not self.cfg.proxy_protocol:\n            return False\n\n        if self.req_number != 1:\n            return False\n\n        if not line.startswith(\"PROXY\"):\n            return False\n\n        self.proxy_protocol_access_check()\n        self.parse_proxy_protocol(line)\n\n        return True\n\n    def proxy_protocol_access_check(self):\n        # check in allow list\n        if isinstance(self.unreader, SocketUnreader):\n            try:\n                remote_host = self.unreader.sock.getpeername()[0]\n            except socket.error as e:\n                if e.args[0] == ENOTCONN:\n                    raise ForbiddenProxyRequest(\"UNKNOW\")\n                raise\n            if (\"*\" not in self.cfg.proxy_allow_ips and\n                    remote_host not in self.cfg.proxy_allow_ips):\n                raise ForbiddenProxyRequest(remote_host)\n\n    def parse_proxy_protocol(self, line):\n        bits = line.split()\n\n        if len(bits) != 6:\n            raise InvalidProxyLine(line)\n\n        # Extract data\n        proto = bits[1]\n        s_addr = bits[2]\n        d_addr = bits[3]\n\n        # Validation\n        if proto not in [\"TCP4\", \"TCP6\"]:\n            raise InvalidProxyLine(\"protocol '%s' not supported\" % proto)\n        if proto == \"TCP4\":\n            try:\n                socket.inet_pton(socket.AF_INET, s_addr)\n                socket.inet_pton(socket.AF_INET, d_addr)\n            except socket.error:\n                raise InvalidProxyLine(line)\n        elif proto == \"TCP6\":\n            try:\n                socket.inet_pton(socket.AF_INET6, s_addr)\n                socket.inet_pton(socket.AF_INET6, d_addr)\n            except socket.error:\n                raise InvalidProxyLine(line)\n\n        try:\n            s_port = int(bits[4])\n            d_port = int(bits[5])\n        except ValueError:\n            raise InvalidProxyLine(\"invalid port %s\" % line)\n\n        if not ((0 <= s_port <= 65535) and (0 <= d_port <= 65535)):\n            raise InvalidProxyLine(\"invalid port %s\" % line)\n\n        # Set data\n        self.proxy_protocol_info = {\n            \"proxy_protocol\": proto,\n            \"client_addr\": s_addr,\n            \"client_port\": s_port,\n            \"proxy_addr\": d_addr,\n            \"proxy_port\": d_port\n        }\n\n    def parse_request_line(self, line):\n        bits = line.split(None, 2)\n        if len(bits) != 3:\n            raise InvalidRequestLine(line)\n\n        # Method\n        if not METH_RE.match(bits[0]):\n            raise InvalidRequestMethod(bits[0])\n        self.method = bits[0].upper()\n\n        # URI\n        # When the path starts with //, urlsplit considers it as a\n        # relative uri while the RDF says it shouldnt\n        # http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2\n        # considers it as an absolute url.\n        # fix issue #297\n        if bits[1].startswith(\"//\"):\n            self.uri = bits[1][1:]\n        else:\n            self.uri = bits[1]\n\n        try:\n            parts = urlsplit(self.uri)\n        except ValueError:\n            raise InvalidRequestLine(line)\n        self.path = parts.path or \"\"\n        self.query = parts.query or \"\"\n        self.fragment = parts.fragment or \"\"\n\n        # Version\n        match = VERSION_RE.match(bits[2])\n        if match is None:\n            raise InvalidHTTPVersion(bits[2])\n        self.version = (int(match.group(1)), int(match.group(2)))\n\n    def set_body_reader(self):\n        super(Request, self).set_body_reader()\n        if isinstance(self.body.reader, EOFReader):\n            self.body = Body(LengthReader(self.unreader, 0))\n", "path": "gunicorn/http/message.py"}], "after_files": [{"content": "# -*- coding: utf-8 -\n#\n# This file is part of gunicorn released under the MIT license.\n# See the NOTICE for more information.\n\nimport re\nimport socket\nfrom errno import ENOTCONN\n\nfrom gunicorn._compat import bytes_to_str\nfrom gunicorn.http.unreader import SocketUnreader\nfrom gunicorn.http.body import ChunkedReader, LengthReader, EOFReader, Body\nfrom gunicorn.http.errors import (InvalidHeader, InvalidHeaderName, NoMoreData,\n    InvalidRequestLine, InvalidRequestMethod, InvalidHTTPVersion,\n    LimitRequestLine, LimitRequestHeaders)\nfrom gunicorn.http.errors import InvalidProxyLine, ForbiddenProxyRequest\nfrom gunicorn.six import BytesIO\nfrom gunicorn._compat import urlsplit\n\nMAX_REQUEST_LINE = 8190\nMAX_HEADERS = 32768\nDEFAULT_MAX_HEADERFIELD_SIZE = 8190\n\nHEADER_RE = re.compile(\"[\\x00-\\x1F\\x7F()<>@,;:\\[\\]={} \\t\\\\\\\\\\\"]\")\nMETH_RE = re.compile(r\"[A-Z0-9$-_.]{3,20}\")\nVERSION_RE = re.compile(r\"HTTP/(\\d+)\\.(\\d+)\")\n\n\nclass Message(object):\n    def __init__(self, cfg, unreader):\n        self.cfg = cfg\n        self.unreader = unreader\n        self.version = None\n        self.headers = []\n        self.trailers = []\n        self.body = None\n\n        # set headers limits\n        self.limit_request_fields = cfg.limit_request_fields\n        if (self.limit_request_fields <= 0\n            or self.limit_request_fields > MAX_HEADERS):\n            self.limit_request_fields = MAX_HEADERS\n        self.limit_request_field_size = cfg.limit_request_field_size\n        if self.limit_request_field_size < 0:\n            self.limit_request_field_size = DEFAULT_MAX_HEADERFIELD_SIZE\n\n        # set max header buffer size\n        max_header_field_size = self.limit_request_field_size or DEFAULT_MAX_HEADERFIELD_SIZE\n        self.max_buffer_headers = self.limit_request_fields * \\\n            (max_header_field_size + 2) + 4\n\n        unused = self.parse(self.unreader)\n        self.unreader.unread(unused)\n        self.set_body_reader()\n\n    def parse(self, unreader):\n        raise NotImplementedError()\n\n    def parse_headers(self, data):\n        headers = []\n\n        # Split lines on \\r\\n keeping the \\r\\n on each line\n        lines = [bytes_to_str(line) + \"\\r\\n\" for line in data.split(b\"\\r\\n\")]\n\n        # Parse headers into key/value pairs paying attention\n        # to continuation lines.\n        while lines:\n            if len(headers) >= self.limit_request_fields:\n                raise LimitRequestHeaders(\"limit request headers fields\")\n\n            # Parse initial header name : value pair.\n            curr = lines.pop(0)\n            header_length = len(curr)\n            if curr.find(\":\") < 0:\n                raise InvalidHeader(curr.strip())\n            name, value = curr.split(\":\", 1)\n            name = name.rstrip(\" \\t\").upper()\n            if HEADER_RE.search(name):\n                raise InvalidHeaderName(name)\n\n            name, value = name.strip(), [value.lstrip()]\n\n            # Consume value continuation lines\n            while lines and lines[0].startswith((\" \", \"\\t\")):\n                curr = lines.pop(0)\n                header_length += len(curr)\n                if header_length > self.limit_request_field_size > 0:\n                    raise LimitRequestHeaders(\"limit request headers \"\n                            + \"fields size\")\n                value.append(curr)\n            value = ''.join(value).rstrip()\n\n            if header_length > self.limit_request_field_size > 0:\n                raise LimitRequestHeaders(\"limit request headers fields size\")\n            headers.append((name, value))\n        return headers\n\n    def set_body_reader(self):\n        chunked = False\n        content_length = None\n        for (name, value) in self.headers:\n            if name == \"CONTENT-LENGTH\":\n                content_length = value\n            elif name == \"TRANSFER-ENCODING\":\n                chunked = value.lower() == \"chunked\"\n            elif name == \"SEC-WEBSOCKET-KEY1\":\n                content_length = 8\n\n        if chunked:\n            self.body = Body(ChunkedReader(self, self.unreader))\n        elif content_length is not None:\n            try:\n                content_length = int(content_length)\n            except ValueError:\n                raise InvalidHeader(\"CONTENT-LENGTH\", req=self)\n\n            if content_length < 0:\n                raise InvalidHeader(\"CONTENT-LENGTH\", req=self)\n\n            self.body = Body(LengthReader(self.unreader, content_length))\n        else:\n            self.body = Body(EOFReader(self.unreader))\n\n    def should_close(self):\n        for (h, v) in self.headers:\n            if h == \"CONNECTION\":\n                v = v.lower().strip()\n                if v == \"close\":\n                    return True\n                elif v == \"keep-alive\":\n                    return False\n                break\n        return self.version <= (1, 0)\n\n\nclass Request(Message):\n    def __init__(self, cfg, unreader, req_number=1):\n        self.method = None\n        self.uri = None\n        self.path = None\n        self.query = None\n        self.fragment = None\n\n        # get max request line size\n        self.limit_request_line = cfg.limit_request_line\n        if (self.limit_request_line < 0\n            or self.limit_request_line >= MAX_REQUEST_LINE):\n            self.limit_request_line = MAX_REQUEST_LINE\n\n        self.req_number = req_number\n        self.proxy_protocol_info = None\n        super(Request, self).__init__(cfg, unreader)\n\n    def get_data(self, unreader, buf, stop=False):\n        data = unreader.read()\n        if not data:\n            if stop:\n                raise StopIteration()\n            raise NoMoreData(buf.getvalue())\n        buf.write(data)\n\n    def parse(self, unreader):\n        buf = BytesIO()\n        self.get_data(unreader, buf, stop=True)\n\n        # get request line\n        line, rbuf = self.read_line(unreader, buf, self.limit_request_line)\n\n        # proxy protocol\n        if self.proxy_protocol(bytes_to_str(line)):\n            # get next request line\n            buf = BytesIO()\n            buf.write(rbuf)\n            line, rbuf = self.read_line(unreader, buf, self.limit_request_line)\n\n        self.parse_request_line(line)\n        buf = BytesIO()\n        buf.write(rbuf)\n\n        # Headers\n        data = buf.getvalue()\n        idx = data.find(b\"\\r\\n\\r\\n\")\n\n        done = data[:2] == b\"\\r\\n\"\n        while True:\n            idx = data.find(b\"\\r\\n\\r\\n\")\n            done = data[:2] == b\"\\r\\n\"\n\n            if idx < 0 and not done:\n                self.get_data(unreader, buf)\n                data = buf.getvalue()\n                if len(data) > self.max_buffer_headers:\n                    raise LimitRequestHeaders(\"max buffer headers\")\n            else:\n                break\n\n        if done:\n            self.unreader.unread(data[2:])\n            return b\"\"\n\n        self.headers = self.parse_headers(data[:idx])\n\n        ret = data[idx + 4:]\n        buf = BytesIO()\n        return ret\n\n    def read_line(self, unreader, buf, limit=0):\n        data = buf.getvalue()\n\n        while True:\n            idx = data.find(b\"\\r\\n\")\n            if idx >= 0:\n                # check if the request line is too large\n                if idx > limit > 0:\n                    raise LimitRequestLine(idx, limit)\n                break\n            elif len(data) - 2 > limit > 0:\n                raise LimitRequestLine(len(data), limit)\n            self.get_data(unreader, buf)\n            data = buf.getvalue()\n\n        return (data[:idx],  # request line,\n                data[idx + 2:])  # residue in the buffer, skip \\r\\n\n\n    def proxy_protocol(self, line):\n        \"\"\"\\\n        Detect, check and parse proxy protocol.\n\n        :raises: ForbiddenProxyRequest, InvalidProxyLine.\n        :return: True for proxy protocol line else False\n        \"\"\"\n        if not self.cfg.proxy_protocol:\n            return False\n\n        if self.req_number != 1:\n            return False\n\n        if not line.startswith(\"PROXY\"):\n            return False\n\n        self.proxy_protocol_access_check()\n        self.parse_proxy_protocol(line)\n\n        return True\n\n    def proxy_protocol_access_check(self):\n        # check in allow list\n        if isinstance(self.unreader, SocketUnreader):\n            try:\n                remote_host = self.unreader.sock.getpeername()[0]\n            except socket.error as e:\n                if e.args[0] == ENOTCONN:\n                    raise ForbiddenProxyRequest(\"UNKNOW\")\n                raise\n            if (\"*\" not in self.cfg.proxy_allow_ips and\n                    remote_host not in self.cfg.proxy_allow_ips):\n                raise ForbiddenProxyRequest(remote_host)\n\n    def parse_proxy_protocol(self, line):\n        bits = line.split()\n\n        if len(bits) != 6:\n            raise InvalidProxyLine(line)\n\n        # Extract data\n        proto = bits[1]\n        s_addr = bits[2]\n        d_addr = bits[3]\n\n        # Validation\n        if proto not in [\"TCP4\", \"TCP6\"]:\n            raise InvalidProxyLine(\"protocol '%s' not supported\" % proto)\n        if proto == \"TCP4\":\n            try:\n                socket.inet_pton(socket.AF_INET, s_addr)\n                socket.inet_pton(socket.AF_INET, d_addr)\n            except socket.error:\n                raise InvalidProxyLine(line)\n        elif proto == \"TCP6\":\n            try:\n                socket.inet_pton(socket.AF_INET6, s_addr)\n                socket.inet_pton(socket.AF_INET6, d_addr)\n            except socket.error:\n                raise InvalidProxyLine(line)\n\n        try:\n            s_port = int(bits[4])\n            d_port = int(bits[5])\n        except ValueError:\n            raise InvalidProxyLine(\"invalid port %s\" % line)\n\n        if not ((0 <= s_port <= 65535) and (0 <= d_port <= 65535)):\n            raise InvalidProxyLine(\"invalid port %s\" % line)\n\n        # Set data\n        self.proxy_protocol_info = {\n            \"proxy_protocol\": proto,\n            \"client_addr\": s_addr,\n            \"client_port\": s_port,\n            \"proxy_addr\": d_addr,\n            \"proxy_port\": d_port\n        }\n\n    def parse_request_line(self, line_bytes):\n        bits = [bytes_to_str(bit) for bit in line_bytes.split(None, 2)]\n        if len(bits) != 3:\n            raise InvalidRequestLine(bytes_to_str(line_bytes))\n\n        # Method\n        if not METH_RE.match(bits[0]):\n            raise InvalidRequestMethod(bits[0])\n        self.method = bits[0].upper()\n\n        # URI\n        # When the path starts with //, urlsplit considers it as a\n        # relative uri while the RDF says it shouldnt\n        # http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2\n        # considers it as an absolute url.\n        # fix issue #297\n        if bits[1].startswith(\"//\"):\n            self.uri = bits[1][1:]\n        else:\n            self.uri = bits[1]\n\n        try:\n            parts = urlsplit(self.uri)\n        except ValueError:\n            raise InvalidRequestLine(bytes_to_str(line_bytes))\n        self.path = parts.path or \"\"\n        self.query = parts.query or \"\"\n        self.fragment = parts.fragment or \"\"\n\n        # Version\n        match = VERSION_RE.match(bits[2])\n        if match is None:\n            raise InvalidHTTPVersion(bits[2])\n        self.version = (int(match.group(1)), int(match.group(2)))\n\n    def set_body_reader(self):\n        super(Request, self).set_body_reader()\n        if isinstance(self.body.reader, EOFReader):\n            self.body = Body(LengthReader(self.unreader, 0))\n", "path": "gunicorn/http/message.py"}]}