Skip to content

Commit 76fde89

Browse files
tipabumatthewoliver
authored andcommitted
py3: Be able to read and write non-ASCII headers
Apparently Python's stdlib got more picky about what a header should look like. As a result, if an account, container, or object had a non-ASCII metadata name (values were fine), the proxy-server wouldn't parse all of the headers. See https://bugs.python.org/issue37093 for more information. This presented several problems: - Since the non-ASCII header aborts parsing, we may lose important HTTP-level information like Content-Length or Transfer-Encoding. - Since the offending header wouldn't get parsed, the client wouldn't even know what the problem was. - Even if the client knew what the bad header was, it would have no way to clear it, as the server uses the same logic to parse incoming requests. So, hack in our own header parsing if we detect that parsing was aborted. Note that we also have to mangle bufferedhttp's putheader so we can get non-ASCII headers to the backend servers. Now, we can run the test_unicode_metadata tests in test/functional/test_account.py and test/functional/test_container.py under py2 against services running under py3. Change-Id: I0f03c211f35a9a49e047a5718a9907b515ca88d7
1 parent bf3e254 commit 76fde89

File tree

2 files changed

+48
-0
lines changed

2 files changed

+48
-0
lines changed

swift/common/bufferedhttp.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,23 @@ def __init__(self, sock, debuglevel=0, strict=0,
8383
self.will_close = _UNKNOWN # conn will close at end of response
8484
self._readline_buffer = b''
8585

86+
if not six.PY2:
87+
def begin(self):
88+
HTTPResponse.begin(self)
89+
header_payload = self.headers.get_payload()
90+
if header_payload:
91+
# This shouldn't be here. We must've bumped up against
92+
# https://bugs.python.org/issue37093
93+
for line in header_payload.rstrip('\r\n').split('\n'):
94+
if ':' not in line or line[:1] in ' \t':
95+
# Well, we're no more broken than we were before...
96+
# Should we support line folding?
97+
# How can/should we handle a bad header line?
98+
break
99+
header, value = line.split(':', 1)
100+
value = value.strip(' \t\n\r')
101+
self.headers.add_header(header, value)
102+
86103
def expect_response(self):
87104
if self.fp:
88105
self.fp.close()
@@ -198,6 +215,11 @@ def putrequest(self, method, url, skip_host=0, skip_accept_encoding=0):
198215
return HTTPConnection.putrequest(self, method, url, skip_host,
199216
skip_accept_encoding)
200217

218+
def putheader(self, header, value):
219+
if not isinstance(header, bytes):
220+
header = header.encode('latin-1')
221+
HTTPConnection.putheader(self, header, value)
222+
201223
def getexpect(self):
202224
kwargs = {'method': self._method}
203225
if hasattr(self, 'strict'):

swift/common/wsgi.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,32 @@ def parse_request(self):
464464
# else, mangled protocol, most likely; let base class deal with it
465465
return wsgi.HttpProtocol.parse_request(self)
466466

467+
if not six.PY2:
468+
def get_environ(self, *args, **kwargs):
469+
environ = wsgi.HttpProtocol.get_environ(self, *args, **kwargs)
470+
header_payload = self.headers.get_payload()
471+
if header_payload:
472+
# This shouldn't be here. We must've bumped up against
473+
# https://bugs.python.org/issue37093
474+
headers_raw = list(environ['headers_raw'])
475+
for line in header_payload.rstrip('\r\n').split('\n'):
476+
if ':' not in line or line[:1] in ' \t':
477+
# Well, we're no more broken than we were before...
478+
# Should we support line folding?
479+
# Should we 400 a bad header line?
480+
break
481+
header, value = line.split(':', 1)
482+
value = value.strip(' \t\n\r')
483+
headers_raw.append((header, value))
484+
wsgi_key = 'HTTP_' + header.replace('-', '_').encode(
485+
'latin1').upper().decode('latin1')
486+
if wsgi_key in ('HTTP_CONTENT_LENGTH',
487+
'HTTP_CONTENT_TYPE'):
488+
wsgi_key = wsgi_key[5:]
489+
environ[wsgi_key] = value
490+
environ['headers_raw'] = tuple(headers_raw)
491+
return environ
492+
467493

468494
class SwiftHttpProxiedProtocol(SwiftHttpProtocol):
469495
"""

0 commit comments

Comments
 (0)