|
16 | 16 | from eventlet import wsgi, websocket
|
17 | 17 | import six
|
18 | 18 |
|
19 |
| -from swift.common.swob import wsgi_quote, wsgi_unquote, \ |
20 |
| - wsgi_quote_plus, wsgi_unquote_plus, wsgi_to_bytes, bytes_to_wsgi |
| 19 | + |
| 20 | +if six.PY2: |
| 21 | + from eventlet.green import httplib as http_client |
| 22 | +else: |
| 23 | + from eventlet.green.http import client as http_client |
21 | 24 |
|
22 | 25 |
|
23 | 26 | class SwiftHttpProtocol(wsgi.HttpProtocol):
|
@@ -62,44 +65,115 @@ def get_default_type(self):
|
62 | 65 | return ''
|
63 | 66 |
|
64 | 67 | def parse_request(self):
|
65 |
| - # Need to track the bytes-on-the-wire for S3 signatures -- eventlet |
66 |
| - # would do it for us, but since we rewrite the path on py3, we need to |
67 |
| - # fix it ourselves later. |
68 |
| - self.__raw_path_info = None |
| 68 | + """Parse a request (inlined from cpython@7e293984). |
| 69 | +
|
| 70 | + The request should be stored in self.raw_requestline; the results |
| 71 | + are in self.command, self.path, self.request_version and |
| 72 | + self.headers. |
| 73 | +
|
| 74 | + Return True for success, False for failure; on failure, any relevant |
| 75 | + error response has already been sent back. |
69 | 76 |
|
| 77 | + """ |
| 78 | + self.command = None # set in case of error on the first line |
| 79 | + self.request_version = version = self.default_request_version |
| 80 | + self.close_connection = True |
| 81 | + requestline = self.raw_requestline |
70 | 82 | if not six.PY2:
|
71 |
| - # request lines *should* be ascii per the RFC, but historically |
72 |
| - # we've allowed (and even have func tests that use) arbitrary |
73 |
| - # bytes. This breaks on py3 (see https://bugs.python.org/issue33973 |
74 |
| - # ) but the work-around is simple: munge the request line to be |
75 |
| - # properly quoted. |
76 |
| - if self.raw_requestline.count(b' ') >= 2: |
77 |
| - parts = self.raw_requestline.split(b' ', 2) |
78 |
| - path, q, query = parts[1].partition(b'?') |
79 |
| - self.__raw_path_info = path |
80 |
| - # unquote first, so we don't over-quote something |
81 |
| - # that was *correctly* quoted |
82 |
| - path = wsgi_to_bytes(wsgi_quote(wsgi_unquote( |
83 |
| - bytes_to_wsgi(path)))) |
84 |
| - query = b'&'.join( |
85 |
| - sep.join([ |
86 |
| - wsgi_to_bytes(wsgi_quote_plus(wsgi_unquote_plus( |
87 |
| - bytes_to_wsgi(key)))), |
88 |
| - wsgi_to_bytes(wsgi_quote_plus(wsgi_unquote_plus( |
89 |
| - bytes_to_wsgi(val)))) |
90 |
| - ]) |
91 |
| - for part in query.split(b'&') |
92 |
| - for key, sep, val in (part.partition(b'='), )) |
93 |
| - parts[1] = path + q + query |
94 |
| - self.raw_requestline = b' '.join(parts) |
95 |
| - # else, mangled protocol, most likely; let base class deal with it |
96 |
| - return wsgi.HttpProtocol.parse_request(self) |
| 83 | + requestline = requestline.decode('iso-8859-1') |
| 84 | + requestline = requestline.rstrip('\r\n') |
| 85 | + self.requestline = requestline |
| 86 | + # Split off \x20 explicitly (see https://bugs.python.org/issue33973) |
| 87 | + words = requestline.split(' ') |
| 88 | + if len(words) == 0: |
| 89 | + return False |
| 90 | + |
| 91 | + if len(words) >= 3: # Enough to determine protocol version |
| 92 | + version = words[-1] |
| 93 | + try: |
| 94 | + if not version.startswith('HTTP/'): |
| 95 | + raise ValueError |
| 96 | + base_version_number = version.split('/', 1)[1] |
| 97 | + version_number = base_version_number.split(".") |
| 98 | + # RFC 2145 section 3.1 says there can be only one "." and |
| 99 | + # - major and minor numbers MUST be treated as |
| 100 | + # separate integers; |
| 101 | + # - HTTP/2.4 is a lower version than HTTP/2.13, which in |
| 102 | + # turn is lower than HTTP/12.3; |
| 103 | + # - Leading zeros MUST be ignored by recipients. |
| 104 | + if len(version_number) != 2: |
| 105 | + raise ValueError |
| 106 | + version_number = int(version_number[0]), int(version_number[1]) |
| 107 | + except (ValueError, IndexError): |
| 108 | + self.send_error( |
| 109 | + 400, |
| 110 | + "Bad request version (%r)" % version) |
| 111 | + return False |
| 112 | + if version_number >= (1, 1) and \ |
| 113 | + self.protocol_version >= "HTTP/1.1": |
| 114 | + self.close_connection = False |
| 115 | + if version_number >= (2, 0): |
| 116 | + self.send_error( |
| 117 | + 505, |
| 118 | + "Invalid HTTP version (%s)" % base_version_number) |
| 119 | + return False |
| 120 | + self.request_version = version |
| 121 | + |
| 122 | + if not 2 <= len(words) <= 3: |
| 123 | + self.send_error( |
| 124 | + 400, |
| 125 | + "Bad request syntax (%r)" % requestline) |
| 126 | + return False |
| 127 | + command, path = words[:2] |
| 128 | + if len(words) == 2: |
| 129 | + self.close_connection = True |
| 130 | + if command != 'GET': |
| 131 | + self.send_error( |
| 132 | + 400, |
| 133 | + "Bad HTTP/0.9 request type (%r)" % command) |
| 134 | + return False |
| 135 | + self.command, self.path = command, path |
| 136 | + |
| 137 | + # Examine the headers and look for a Connection directive. |
| 138 | + if six.PY2: |
| 139 | + self.headers = self.MessageClass(self.rfile, 0) |
| 140 | + else: |
| 141 | + try: |
| 142 | + self.headers = http_client.parse_headers( |
| 143 | + self.rfile, |
| 144 | + _class=self.MessageClass) |
| 145 | + except http_client.LineTooLong as err: |
| 146 | + self.send_error( |
| 147 | + 431, |
| 148 | + "Line too long", |
| 149 | + str(err)) |
| 150 | + return False |
| 151 | + except http_client.HTTPException as err: |
| 152 | + self.send_error( |
| 153 | + 431, |
| 154 | + "Too many headers", |
| 155 | + str(err) |
| 156 | + ) |
| 157 | + return False |
| 158 | + |
| 159 | + conntype = self.headers.get('Connection', "") |
| 160 | + if conntype.lower() == 'close': |
| 161 | + self.close_connection = True |
| 162 | + elif (conntype.lower() == 'keep-alive' and |
| 163 | + self.protocol_version >= "HTTP/1.1"): |
| 164 | + self.close_connection = False |
| 165 | + # Examine the headers and look for an Expect directive |
| 166 | + expect = self.headers.get('Expect', "") |
| 167 | + if (expect.lower() == "100-continue" and |
| 168 | + self.protocol_version >= "HTTP/1.1" and |
| 169 | + self.request_version >= "HTTP/1.1"): |
| 170 | + if not self.handle_expect_100(): |
| 171 | + return False |
| 172 | + return True |
97 | 173 |
|
98 | 174 | if not six.PY2:
|
99 | 175 | def get_environ(self, *args, **kwargs):
|
100 | 176 | environ = wsgi.HttpProtocol.get_environ(self, *args, **kwargs)
|
101 |
| - environ['RAW_PATH_INFO'] = bytes_to_wsgi( |
102 |
| - self.__raw_path_info) |
103 | 177 | header_payload = self.headers.get_payload()
|
104 | 178 | if isinstance(header_payload, list) and len(header_payload) == 1:
|
105 | 179 | header_payload = header_payload[0].get_payload()
|
|
0 commit comments