From b45aa4a6e9c20070b77916b2696c27951add106e Mon Sep 17 00:00:00 2001 From: Bill Brower Date: Tue, 16 Aug 2016 15:35:28 -0400 Subject: [PATCH 1/3] Made the set_cors_headers method in core.py compatible with resp.headers objects that are standard python dicts, by copying the underlying data into a new MultiDict. --- flask_cors/core.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/flask_cors/core.py b/flask_cors/core.py index b37c9b3..cee9966 100644 --- a/flask_cors/core.py +++ b/flask_cors/core.py @@ -226,6 +226,14 @@ def set_cors_headers(resp, options): LOG.debug('CORS have been already evaluated, skipping') return resp + # If resp.headers is not a MultiDict, set resp.headers to a new + # MultiDict, copying the underlying dict. + if type(resp.headers) is MultiDict: + multidict = MultiDict() + for k, v in resp.headers.items(): + multidict.add(k, v) + resp.headers = multidict + headers_to_set = get_cors_headers(options, request.headers, request.method) LOG.debug('Settings CORS headers: %s', str(headers_to_set)) From d1251b4ae16af606cdf02aa4ef9705a93743cae8 Mon Sep 17 00:00:00 2001 From: Bill Brower Date: Tue, 16 Aug 2016 16:19:48 -0400 Subject: [PATCH 2/3] Simplified the non MultiDict workaround in the set_cors_headers method in core.py and made it more pythonic. --- flask_cors/core.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/flask_cors/core.py b/flask_cors/core.py index cee9966..6985003 100644 --- a/flask_cors/core.py +++ b/flask_cors/core.py @@ -226,13 +226,9 @@ def set_cors_headers(resp, options): LOG.debug('CORS have been already evaluated, skipping') return resp - # If resp.headers is not a MultiDict, set resp.headers to a new - # MultiDict, copying the underlying dict. - if type(resp.headers) is MultiDict: - multidict = MultiDict() - for k, v in resp.headers.items(): - multidict.add(k, v) - resp.headers = multidict + # OauthLib sets resp.headers to a non MultiDict. Weird, but let's work around it. + if not isinstance(resp.headers, MultiDict): + resp.headers = MultiDict(resp.headers) headers_to_set = get_cors_headers(options, request.headers, request.method) From 989fcedd2028c5f3a366e353d9c7d200fcfd63f2 Mon Sep 17 00:00:00 2001 From: Bill Brower Date: Wed, 17 Aug 2016 11:03:52 -0400 Subject: [PATCH 3/3] Added test coverage for overridden response headers and modified the set_cors_headers method in core.py to respect Werkzeug Headers objects as well (because they allow repeated values and some of the pre-existing tests use them). --- flask_cors/core.py | 9 +++++--- tests/core/test_override_headers.py | 33 +++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 3 deletions(-) create mode 100644 tests/core/test_override_headers.py diff --git a/flask_cors/core.py b/flask_cors/core.py index 6985003..c46f72d 100644 --- a/flask_cors/core.py +++ b/flask_cors/core.py @@ -13,7 +13,7 @@ from datetime import timedelta from six import string_types from flask import request, current_app -from werkzeug.datastructures import MultiDict +from werkzeug.datastructures import Headers, MultiDict LOG = logging.getLogger(__name__) @@ -226,8 +226,11 @@ def set_cors_headers(resp, options): LOG.debug('CORS have been already evaluated, skipping') return resp - # OauthLib sets resp.headers to a non MultiDict. Weird, but let's work around it. - if not isinstance(resp.headers, MultiDict): + # Some libraries, like OAuthlib, set resp.headers to non Multidict + # objects (Werkzeug Headers work as well). This is a problem because + # headers allow repeated values. + if (not isinstance(resp.headers, Headers) + and not isinstance(resp.headers, MultiDict)): resp.headers = MultiDict(resp.headers) headers_to_set = get_cors_headers(options, request.headers, request.method) diff --git a/tests/core/test_override_headers.py b/tests/core/test_override_headers.py new file mode 100644 index 0000000..f76a1cd --- /dev/null +++ b/tests/core/test_override_headers.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +""" + test + ~~~~ + + Flask-Cors tests module +""" + +from ..base_test import FlaskCorsTestCase +from flask import Flask, Response + +from flask_cors import * +from flask_cors.core import * + +class ResponseHeadersOverrideTestCaseIntegration(FlaskCorsTestCase): + def setUp(self): + self.app = Flask(__name__) + CORS(self.app) + + @self.app.route('/') + def index(): + response = Response(headers={"custom": "dictionary"}) + return 'Welcome' + + def test_override_headers(self): + ''' + Ensure we work even if response.headers is set to something other than a MultiDict. + ''' + for resp in self.iter_responses('/'): + self.assertTrue(ACL_ORIGIN in resp.headers) + +if __name__ == "__main__": + unittest.main()