Disable case-changing behavior in Eventlet

RFC 2616 says that HTTP header fields are case-insensitive.  However, there are
some S3 clients who don't accept normalized header by Swift and Eventlet.  For
example, AWS Java SDK expects that an etag header is 'ETag', not 'Etag'.

This patch disables Eventlet's header capitalization so that the swift3
middleware can normalize the response headers as those clients expect.

Note that this change requires a fix for Eventlet, which will be included in
the next Eventlet release (v0.15).

Change-Id: I6d3428b0dafef776bdb3ebac7639b3126fa5e60d
This commit is contained in:
MORITA Kazutaka 2014-04-22 09:28:15 +09:00
parent 92fb1c15da
commit f2774a4d11
3 changed files with 59 additions and 12 deletions

View File

@ -253,10 +253,10 @@ class ProxyLoggingMiddleware(object):
break
else:
if not chunk:
start_response_args[0][1].append(('content-length', '0'))
start_response_args[0][1].append(('Content-Length', '0'))
elif isinstance(iterable, list):
start_response_args[0][1].append(
('content-length', str(sum(len(i) for i in iterable))))
('Content-Length', str(sum(len(i) for i in iterable))))
start_response(*start_response_args[0])
req = Request(env)

View File

@ -16,6 +16,7 @@
"""WSGI tools for use with swift."""
import errno
import inspect
import os
import signal
import time
@ -386,7 +387,14 @@ def run_server(conf, logger, sock, global_conf=None):
max_clients = int(conf.get('max_clients', '1024'))
pool = RestrictedGreenPool(size=max_clients)
try:
wsgi.server(sock, app, NullLogger(), custom_pool=pool)
# Disable capitalizing headers in Eventlet if possible. This is
# necessary for the AWS SDK to work with swift3 middleware.
argspec = inspect.getargspec(wsgi.server)
if 'capitalize_response_headers' in argspec.args:
wsgi.server(sock, app, NullLogger(), custom_pool=pool,
capitalize_response_headers=False)
else:
wsgi.server(sock, app, NullLogger(), custom_pool=pool)
except socket.error as err:
if err[0] != errno.EINVAL:
raise

View File

@ -335,10 +335,11 @@ class TestWSGI(unittest.TestCase):
'modify_wsgi_pipeline'):
with mock.patch('swift.common.wsgi.wsgi') as _wsgi:
with mock.patch('swift.common.wsgi.eventlet') as _eventlet:
conf = wsgi.appconfig(conf_file)
logger = logging.getLogger('test')
sock = listen(('localhost', 0))
wsgi.run_server(conf, logger, sock)
with mock.patch('swift.common.wsgi.inspect'):
conf = wsgi.appconfig(conf_file)
logger = logging.getLogger('test')
sock = listen(('localhost', 0))
wsgi.run_server(conf, logger, sock)
self.assertEquals('HTTP/1.0',
_wsgi.HttpProtocol.default_request_version)
self.assertEquals(30, _wsgi.WRITE_TIMEOUT)
@ -356,6 +357,43 @@ class TestWSGI(unittest.TestCase):
self.assert_('custom_pool' in kwargs)
self.assertEquals(1000, kwargs['custom_pool'].size)
def test_run_server_with_latest_eventlet(self):
config = """
[DEFAULT]
swift_dir = TEMPDIR
[pipeline:main]
pipeline = proxy-server
[app:proxy-server]
use = egg:swift#proxy
"""
def argspec_stub(server):
return mock.MagicMock(args=['capitalize_response_headers'])
contents = dedent(config)
with temptree(['proxy-server.conf']) as t:
conf_file = os.path.join(t, 'proxy-server.conf')
with open(conf_file, 'w') as f:
f.write(contents.replace('TEMPDIR', t))
_fake_rings(t)
with nested(
mock.patch('swift.proxy.server.Application.'
'modify_wsgi_pipeline'),
mock.patch('swift.common.wsgi.wsgi'),
mock.patch('swift.common.wsgi.eventlet'),
mock.patch('swift.common.wsgi.inspect',
getargspec=argspec_stub)) as (_, _wsgi, _, _):
conf = wsgi.appconfig(conf_file)
logger = logging.getLogger('test')
sock = listen(('localhost', 0))
wsgi.run_server(conf, logger, sock)
_wsgi.server.assert_called()
args, kwargs = _wsgi.server.call_args
self.assertEquals(kwargs.get('capitalize_response_headers'), False)
def test_run_server_conf_dir(self):
config_dir = {
'proxy-server.conf.d/pipeline.conf': """
@ -384,11 +422,12 @@ class TestWSGI(unittest.TestCase):
with mock.patch('swift.common.wsgi.wsgi') as _wsgi:
with mock.patch('swift.common.wsgi.eventlet') as _eventlet:
with mock.patch.dict('os.environ', {'TZ': ''}):
conf = wsgi.appconfig(conf_dir)
logger = logging.getLogger('test')
sock = listen(('localhost', 0))
wsgi.run_server(conf, logger, sock)
self.assert_(os.environ['TZ'] is not '')
with mock.patch('swift.common.wsgi.inspect'):
conf = wsgi.appconfig(conf_dir)
logger = logging.getLogger('test')
sock = listen(('localhost', 0))
wsgi.run_server(conf, logger, sock)
self.assert_(os.environ['TZ'] is not '')
self.assertEquals('HTTP/1.0',
_wsgi.HttpProtocol.default_request_version)