From 67c1942da4c201c049b3073e1a8c6229025a3b7a Mon Sep 17 00:00:00 2001 From: Justin Blake Date: Thu, 5 Mar 2015 04:49:42 -0500 Subject: [PATCH 1/7] whitespace fix --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 7f2a6ca..b179917 100644 --- a/setup.py +++ b/setup.py @@ -19,7 +19,7 @@ 'Programming Language :: Python :: 2', 'Topic :: Internet :: WWW/HTTP :: WSGI :: Middleware', ], - extras_require = { + extras_require={ 'djproxy': ['djproxy>=2.0.0'], }, ) From 3a642c5b631abd568d16e6516d2b39ca1015566f Mon Sep 17 00:00:00 2001 From: Justin Blake Date: Thu, 5 Mar 2015 04:52:01 -0500 Subject: [PATCH 2/7] No need for this to be an attribute --- tests/test_reverse_proxied_app.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_reverse_proxied_app.py b/tests/test_reverse_proxied_app.py index 29cae2d..2dd9bd8 100644 --- a/tests/test_reverse_proxied_app.py +++ b/tests/test_reverse_proxied_app.py @@ -6,9 +6,9 @@ class TestReverseProxiedApp(TestCase): def setUp(self): - self.prefix_paths_patcher = patch('proxyprefix.wsgi.prefix_paths') - self.prefix_paths = self.prefix_paths_patcher.start() - self.addCleanup(self.prefix_paths_patcher.stop) + prefix_paths_patcher = patch('proxyprefix.wsgi.prefix_paths') + self.prefix_paths = prefix_paths_patcher.start() + self.addCleanup(prefix_paths_patcher.stop) self.environ = {} self.start_response = Mock() From 45becf15a42ba470169694105dca0a9bddd8dd6a Mon Sep 17 00:00:00 2001 From: Justin Blake Date: Thu, 5 Mar 2015 06:02:42 -0500 Subject: [PATCH 3/7] Set wsgi.url_scheme = HTTP_X_FORWARDED_PROTO This will let non-secure proxies tell a secure service to construct http URLs instead of https. --- proxyprefix/wsgi.py | 6 ++++++ tests/test_reverse_proxied_app.py | 9 +++++++++ 2 files changed, 15 insertions(+) diff --git a/proxyprefix/wsgi.py b/proxyprefix/wsgi.py index aeb84d8..e8f6a28 100644 --- a/proxyprefix/wsgi.py +++ b/proxyprefix/wsgi.py @@ -6,8 +6,14 @@ def __init__(self, app): def __call__(self, environ, start_response): prefix = environ.get('HTTP_X_FORWARDED_PREFIX') + scheme = environ.get('HTTP_X_FORWARDED_PROTO') + if prefix: prefix_paths(environ, prefix) + + if scheme: + environ['wsgi.url_scheme'] = scheme + return self.app(environ, start_response) diff --git a/tests/test_reverse_proxied_app.py b/tests/test_reverse_proxied_app.py index 2dd9bd8..ee7975b 100644 --- a/tests/test_reverse_proxied_app.py +++ b/tests/test_reverse_proxied_app.py @@ -30,6 +30,15 @@ def test_it_prefixes_paths_with_HTTP_X_FORWARDED_PREFIX(self): self.proxied_app(self.environ, self.start_response) self.prefix_paths.assert_called_with(self.environ, 'prefix') + def test_it_does_not_set_scheme_if_no_HTTP_X_FORWARDED_PROTO(self): + self.proxied_app(self.environ, self.start_response) + self.assertIsNone(self.environ.get('wsgi.url_scheme')) + + def test_it_sets_scheme_to_HTTP_X_FORWARDED_PROTO(self): + self.environ['HTTP_X_FORWARDED_PROTO'] = 'http' + self.proxied_app(self.environ, self.start_response) + self.assertEqual(self.environ.get('wsgi.url_scheme'), 'http') + class TestPrefixPaths(TestCase): """prefix_paths(environ, prefix)""" From b2256cd97c361f8335f0720daa9b79b8b6e9b9da Mon Sep 17 00:00:00 2001 From: Justin Blake Date: Thu, 5 Mar 2015 06:31:22 -0500 Subject: [PATCH 4/7] Only allow setting scheme to http or https and default to http Slightly more secure? --- proxyprefix/wsgi.py | 1 + 1 file changed, 1 insertion(+) diff --git a/proxyprefix/wsgi.py b/proxyprefix/wsgi.py index e8f6a28..f60038e 100644 --- a/proxyprefix/wsgi.py +++ b/proxyprefix/wsgi.py @@ -12,6 +12,7 @@ def __call__(self, environ, start_response): prefix_paths(environ, prefix) if scheme: + scheme = 'https' if scheme == 'https' else 'http' environ['wsgi.url_scheme'] = scheme return self.app(environ, start_response) From bcc2f5fe39504e3e1e3d098e3a9be20faa07496c Mon Sep 17 00:00:00 2001 From: Justin Blake Date: Fri, 6 Mar 2015 05:24:09 -0500 Subject: [PATCH 5/7] Refactor: move scheme setting to a function I'm about to expand on what it means to set the scheme so I moved it to a function. --- proxyprefix/wsgi.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/proxyprefix/wsgi.py b/proxyprefix/wsgi.py index f60038e..b021949 100644 --- a/proxyprefix/wsgi.py +++ b/proxyprefix/wsgi.py @@ -12,8 +12,7 @@ def __call__(self, environ, start_response): prefix_paths(environ, prefix) if scheme: - scheme = 'https' if scheme == 'https' else 'http' - environ['wsgi.url_scheme'] = scheme + set_scheme(environ, scheme) return self.app(environ, start_response) @@ -32,3 +31,9 @@ def prefix_paths(environ, prefix): # this django quirk: if environ.get('SCRIPT_URL'): environ['SCRIPT_URL'] = '' + + +def set_scheme(environ, scheme): + """Force environ to be http or https.""" + scheme = 'https' if scheme == 'https' else 'http' + environ['wsgi.url_scheme'] = scheme From 11ac8cde4c194300f154053242520491ae775edc Mon Sep 17 00:00:00 2001 From: Justin Blake Date: Fri, 6 Mar 2015 05:37:51 -0500 Subject: [PATCH 6/7] Move set_scheme tests to their own class I'm about to expand on what happens in this function --- tests/test_reverse_proxied_app.py | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/tests/test_reverse_proxied_app.py b/tests/test_reverse_proxied_app.py index ee7975b..cd61f56 100644 --- a/tests/test_reverse_proxied_app.py +++ b/tests/test_reverse_proxied_app.py @@ -1,7 +1,7 @@ from mock import Mock, patch from unittest2 import TestCase -from proxyprefix.wsgi import prefix_paths, ReverseProxiedApp +from proxyprefix.wsgi import prefix_paths, set_scheme, ReverseProxiedApp class TestReverseProxiedApp(TestCase): @@ -10,6 +10,10 @@ def setUp(self): self.prefix_paths = prefix_paths_patcher.start() self.addCleanup(prefix_paths_patcher.stop) + set_scheme_patcher = patch('proxyprefix.wsgi.set_scheme') + self.set_scheme = set_scheme_patcher.start() + self.addCleanup(set_scheme_patcher.stop) + self.environ = {} self.start_response = Mock() self.inner_app = Mock() @@ -32,12 +36,12 @@ def test_it_prefixes_paths_with_HTTP_X_FORWARDED_PREFIX(self): def test_it_does_not_set_scheme_if_no_HTTP_X_FORWARDED_PROTO(self): self.proxied_app(self.environ, self.start_response) - self.assertIsNone(self.environ.get('wsgi.url_scheme')) + self.assertFalse(self.set_scheme.called) def test_it_sets_scheme_to_HTTP_X_FORWARDED_PROTO(self): self.environ['HTTP_X_FORWARDED_PROTO'] = 'http' self.proxied_app(self.environ, self.start_response) - self.assertEqual(self.environ.get('wsgi.url_scheme'), 'http') + self.set_scheme.assert_called_with(self.environ, 'http') class TestPrefixPaths(TestCase): @@ -60,3 +64,18 @@ def test_it_removes_SCRIPT_URL_if_present(self): self.environ['SCRIPT_URL'] = '/script/url' prefix_paths(self.environ, 'prefix') self.assertFalse(self.environ['SCRIPT_URL']) + + +class TestSetScheme(TestCase): + """set_scheme(environ, scheme)""" + + def setUp(self): + self.environ = {} + + def test_sets_wsgi_url_scheme_to_https_if_scheme_is_https(self): + set_scheme(self.environ, 'https') + self.assertEqual(self.environ['wsgi.url_scheme'], 'https') + + def test_sets_wsgi_url_scheme_to_http_if_scheme_is_not_https(self): + set_scheme(self.environ, 'foo') + self.assertEqual(self.environ['wsgi.url_scheme'], 'http') From 3be1da7c254c80fa7f770bb7a9cdd5eb9bb432e0 Mon Sep 17 00:00:00 2001 From: Justin Blake Date: Fri, 6 Mar 2015 05:42:20 -0500 Subject: [PATCH 7/7] Set HTTPS in environ based on forwarded scheme See: https://github.com/yola/proxyprefix/pull/7#issuecomment-77440608 --- proxyprefix/wsgi.py | 1 + tests/test_reverse_proxied_app.py | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/proxyprefix/wsgi.py b/proxyprefix/wsgi.py index b021949..aa82f20 100644 --- a/proxyprefix/wsgi.py +++ b/proxyprefix/wsgi.py @@ -37,3 +37,4 @@ def set_scheme(environ, scheme): """Force environ to be http or https.""" scheme = 'https' if scheme == 'https' else 'http' environ['wsgi.url_scheme'] = scheme + environ['HTTPS'] = 'on' if scheme == 'https' else 'off' diff --git a/tests/test_reverse_proxied_app.py b/tests/test_reverse_proxied_app.py index cd61f56..ec69c54 100644 --- a/tests/test_reverse_proxied_app.py +++ b/tests/test_reverse_proxied_app.py @@ -79,3 +79,11 @@ def test_sets_wsgi_url_scheme_to_https_if_scheme_is_https(self): def test_sets_wsgi_url_scheme_to_http_if_scheme_is_not_https(self): set_scheme(self.environ, 'foo') self.assertEqual(self.environ['wsgi.url_scheme'], 'http') + + def test_sets_HTTPS_to_on_if_scheme_is_https(self): + set_scheme(self.environ, 'https') + self.assertEqual(self.environ['HTTPS'], 'on') + + def test_sets_HTTPS_to_off_if_scheme_is_not_https(self): + set_scheme(self.environ, 'foo') + self.assertEqual(self.environ['HTTPS'], 'off')