diff --git a/glance/api/middleware/cache.py b/glance/api/middleware/cache.py index 8c4016b431..7304c63b54 100644 --- a/glance/api/middleware/cache.py +++ b/glance/api/middleware/cache.py @@ -23,6 +23,7 @@ the local cached copy of the image file is returned. """ import re +import six from oslo_log import log as logging import webob @@ -237,7 +238,8 @@ class CacheFilter(wsgi.Middleware): # content-length got by the method "download" because of this issue: # https://github.com/Pylons/webob/issues/86 response.headers['Content-Type'] = 'application/octet-stream' - response.headers['Content-MD5'] = image.checksum + response.headers['Content-MD5'] = (image.checksum.encode('utf-8') + if six.PY2 else image.checksum) response.headers['Content-Length'] = str(image.size) return response diff --git a/glance/common/wsgi.py b/glance/common/wsgi.py index 0059852a41..6b398bc5cd 100644 --- a/glance/common/wsgi.py +++ b/glance/common/wsgi.py @@ -916,6 +916,10 @@ class Resource(object): try: response = webob.Response(request=request) self.dispatch(self.serializer, action, response, action_result) + # encode all headers in response to utf-8 to prevent unicode errors + for name, value in list(response.headers.items()): + if six.PY2 and isinstance(value, six.text_type): + response.headers[name] = encodeutils.safe_encode(value) return response except webob.exc.WSGIHTTPException as e: return translate_exception(request, e) diff --git a/glance/tests/unit/common/test_wsgi.py b/glance/tests/unit/common/test_wsgi.py index daa3b9f6eb..5d7934d137 100644 --- a/glance/tests/unit/common/test_wsgi.py +++ b/glance/tests/unit/common/test_wsgi.py @@ -374,6 +374,32 @@ class ResourceTest(test_utils.BaseTestCase): e = wsgi.translate_exception(req, e) self.assertEqual('No Encontrado', e.explanation) + def test_response_headers_encoded(self): + # prepare environment + for_openstack_comrades = \ + u'\u0417\u0430 \u043e\u043f\u0435\u043d\u0441\u0442\u0435\u043a, ' \ + u'\u0442\u043e\u0432\u0430\u0440\u0438\u0449\u0438' + + class FakeController(object): + def index(self, shirt, pants=None): + return (shirt, pants) + + class FakeSerializer(object): + def index(self, response, result): + response.headers['unicode_test'] = for_openstack_comrades + + # make request + resource = wsgi.Resource(FakeController(), None, FakeSerializer()) + actions = {'action': 'index'} + env = {'wsgiorg.routing_args': [None, actions]} + request = wsgi.Request.blank('/tests/123', environ=env) + response = resource.__call__(request) + + # ensure it has been encoded correctly + value = (response.headers['unicode_test'].decode('utf-8') + if six.PY2 else response.headers['unicode_test']) + self.assertEqual(for_openstack_comrades, value) + class JSONResponseSerializerTest(test_utils.BaseTestCase):