diff options
Diffstat (limited to 'test/unit/common/test_wsgi.py')
-rw-r--r-- | test/unit/common/test_wsgi.py | 334 |
1 files changed, 1 insertions, 333 deletions
diff --git a/test/unit/common/test_wsgi.py b/test/unit/common/test_wsgi.py index da04d21f3..52a72efc8 100644 --- a/test/unit/common/test_wsgi.py +++ b/test/unit/common/test_wsgi.py @@ -15,14 +15,11 @@ """Tests for swift.common.wsgi""" -from argparse import Namespace import errno import logging import socket import unittest import os -import types -import eventlet.wsgi from collections import defaultdict from io import BytesIO @@ -40,7 +37,7 @@ import swift.proxy.server import swift.obj.server as obj_server import swift.container.server as container_server import swift.account.server as account_server -from swift.common.swob import Request, wsgi_to_bytes +from swift.common.swob import Request from swift.common import wsgi, utils from swift.common.storage_policy import POLICIES @@ -1000,335 +997,6 @@ class TestWSGI(unittest.TestCase): self.assertIs(newenv.get('swift.infocache'), oldenv['swift.infocache']) -class TestSwiftHttpProtocol(unittest.TestCase): - def _proto_obj(self): - # Make an object we can exercise... note the base class's __init__() - # does a bunch of work, so we just new up an object like eventlet.wsgi - # does. - proto_class = wsgi.SwiftHttpProtocol - try: - the_obj = types.InstanceType(proto_class) - except AttributeError: - the_obj = proto_class.__new__(proto_class) - # Install some convenience mocks - the_obj.server = Namespace(app=Namespace(logger=mock.Mock()), - url_length_limit=777, - log=mock.Mock()) - the_obj.send_error = mock.Mock() - - return the_obj - - def test_swift_http_protocol_log_request(self): - proto_obj = self._proto_obj() - self.assertEqual(None, proto_obj.log_request('ignored')) - - def test_swift_http_protocol_log_message(self): - proto_obj = self._proto_obj() - - proto_obj.log_message('a%sc', 'b') - self.assertEqual([mock.call.error('ERROR WSGI: a%sc', 'b')], - proto_obj.server.app.logger.mock_calls) - - def test_swift_http_protocol_log_message_no_logger(self): - # If the app somehow had no logger attribute or it was None, don't blow - # up - proto_obj = self._proto_obj() - delattr(proto_obj.server.app, 'logger') - - proto_obj.log_message('a%sc', 'b') - self.assertEqual([mock.call.info('ERROR WSGI: a%sc', 'b')], - proto_obj.server.log.mock_calls) - - proto_obj.server.log.reset_mock() - proto_obj.server.app.logger = None - - proto_obj.log_message('a%sc', 'b') - self.assertEqual([mock.call.info('ERROR WSGI: a%sc', 'b')], - proto_obj.server.log.mock_calls) - - def test_swift_http_protocol_parse_request_no_proxy(self): - proto_obj = self._proto_obj() - proto_obj.raw_requestline = b'jimmy jam' - proto_obj.client_address = ('a', '123') - - self.assertEqual(False, proto_obj.parse_request()) - - self.assertEqual([ - mock.call(400, "Bad HTTP/0.9 request type ('jimmy')"), - ], proto_obj.send_error.mock_calls) - self.assertEqual(('a', '123'), proto_obj.client_address) - - def test_request_line_cleanup(self): - def do_test(line_from_socket, expected_line=None): - if expected_line is None: - expected_line = line_from_socket - - proto_obj = self._proto_obj() - proto_obj.raw_requestline = line_from_socket - with mock.patch('swift.common.wsgi.wsgi.HttpProtocol') \ - as mock_super: - proto_obj.parse_request() - - self.assertEqual([mock.call.parse_request(proto_obj)], - mock_super.mock_calls) - self.assertEqual(proto_obj.raw_requestline, expected_line) - - do_test(b'GET / HTTP/1.1') - do_test(b'GET /%FF HTTP/1.1') - - if not six.PY2: - do_test(b'GET /\xff HTTP/1.1', b'GET /%FF HTTP/1.1') - do_test(b'PUT /Here%20Is%20A%20SnowMan:\xe2\x98\x83 HTTP/1.0', - b'PUT /Here%20Is%20A%20SnowMan%3A%E2%98%83 HTTP/1.0') - do_test( - b'POST /?and%20it=fixes+params&' - b'PALMTREE=\xf0%9f\x8c%b4 HTTP/1.1', - b'POST /?and+it=fixes+params&PALMTREE=%F0%9F%8C%B4 HTTP/1.1') - - -class ProtocolTest(unittest.TestCase): - def _run_bytes_through_protocol(self, bytes_from_client): - rfile = BytesIO(bytes_from_client) - wfile = BytesIO() - - # All this fakery is needed to make the WSGI server process one - # connection, possibly with multiple requests, in the main - # greenthread. It doesn't hurt correctness if the function is called - # in a separate greenthread, but it makes using the debugger harder. - class FakeGreenthread(object): - def link(self, a_callable, *args): - a_callable(self, *args) - - class FakePool(object): - def spawn(self, a_callable, *args, **kwargs): - a_callable(*args, **kwargs) - return FakeGreenthread() - - def spawn_n(self, a_callable, *args, **kwargs): - a_callable(*args, **kwargs) - - def waitall(self): - pass - - addr = ('127.0.0.1', 8359) - fake_tcp_socket = mock.Mock( - setsockopt=lambda *a: None, - makefile=lambda mode, bufsize: rfile if 'r' in mode else wfile, - getsockname=lambda *a: addr - ) - fake_listen_socket = mock.Mock( - accept=mock.MagicMock( - side_effect=[[fake_tcp_socket, addr], - # KeyboardInterrupt breaks the WSGI server out of - # its infinite accept-process-close loop. - KeyboardInterrupt]), - getsockname=lambda *a: addr) - del fake_listen_socket.do_handshake - - # If we let the WSGI server close rfile/wfile then we can't access - # their contents any more. - with mock.patch.object(wfile, 'close', lambda: None), \ - mock.patch.object(rfile, 'close', lambda: None): - eventlet.wsgi.server( - fake_listen_socket, self.app, - protocol=self.protocol_class, - custom_pool=FakePool(), - log_output=False, # quiet the test run - ) - return wfile.getvalue() - - -class TestSwiftHttpProtocolSomeMore(ProtocolTest): - protocol_class = wsgi.SwiftHttpProtocol - - @staticmethod - def app(env, start_response): - start_response("200 OK", []) - return [wsgi_to_bytes(env['RAW_PATH_INFO'])] - - def test_simple(self): - bytes_out = self._run_bytes_through_protocol(( - b"GET /someurl HTTP/1.0\r\n" - b"User-Agent: something or other\r\n" - b"\r\n" - )) - - lines = [l for l in bytes_out.split(b"\r\n") if l] - self.assertEqual(lines[0], b"HTTP/1.1 200 OK") # sanity check - self.assertEqual(lines[-1], b'/someurl') - - def test_quoted(self): - bytes_out = self._run_bytes_through_protocol(( - b"GET /some%fFpath%D8%AA HTTP/1.0\r\n" - b"User-Agent: something or other\r\n" - b"\r\n" - )) - - lines = [l for l in bytes_out.split(b"\r\n") if l] - self.assertEqual(lines[0], b"HTTP/1.1 200 OK") # sanity check - self.assertEqual(lines[-1], b'/some%fFpath%D8%AA') - - def test_messy(self): - bytes_out = self._run_bytes_through_protocol(( - b"GET /oh\xffboy%what$now%E2%80%bd HTTP/1.0\r\n" - b"User-Agent: something or other\r\n" - b"\r\n" - )) - - lines = [l for l in bytes_out.split(b"\r\n") if l] - self.assertEqual(lines[-1], b'/oh\xffboy%what$now%E2%80%bd') - - -class TestProxyProtocol(ProtocolTest): - protocol_class = wsgi.SwiftHttpProxiedProtocol - - @staticmethod - def app(env, start_response): - start_response("200 OK", []) - body = '\r\n'.join([ - 'got addr: %s %s' % ( - env.get("REMOTE_ADDR", "<missing>"), - env.get("REMOTE_PORT", "<missing>")), - 'on addr: %s %s' % ( - env.get("SERVER_ADDR", "<missing>"), - env.get("SERVER_PORT", "<missing>")), - 'https is %s (scheme %s)' % ( - env.get("HTTPS", "<missing>"), - env.get("wsgi.url_scheme", "<missing>")), - ]) + '\r\n' - return [body.encode("utf-8")] - - def test_request_with_proxy(self): - bytes_out = self._run_bytes_through_protocol(( - b"PROXY TCP4 192.168.0.1 192.168.0.11 56423 4433\r\n" - b"GET /someurl HTTP/1.0\r\n" - b"User-Agent: something or other\r\n" - b"\r\n" - )) - - lines = [l for l in bytes_out.split(b"\r\n") if l] - self.assertEqual(lines[0], b"HTTP/1.1 200 OK") # sanity check - self.assertEqual(lines[-3:], [ - b"got addr: 192.168.0.1 56423", - b"on addr: 192.168.0.11 4433", - b"https is <missing> (scheme http)", - ]) - - def test_request_with_proxy_https(self): - bytes_out = self._run_bytes_through_protocol(( - b"PROXY TCP4 192.168.0.1 192.168.0.11 56423 443\r\n" - b"GET /someurl HTTP/1.0\r\n" - b"User-Agent: something or other\r\n" - b"\r\n" - )) - - lines = [l for l in bytes_out.split(b"\r\n") if l] - self.assertEqual(lines[0], b"HTTP/1.1 200 OK") # sanity check - self.assertEqual(lines[-3:], [ - b"got addr: 192.168.0.1 56423", - b"on addr: 192.168.0.11 443", - b"https is on (scheme https)", - ]) - - def test_multiple_requests_with_proxy(self): - bytes_out = self._run_bytes_through_protocol(( - b"PROXY TCP4 192.168.0.1 192.168.0.11 56423 443\r\n" - b"GET /someurl HTTP/1.1\r\n" - b"User-Agent: something or other\r\n" - b"\r\n" - b"GET /otherurl HTTP/1.1\r\n" - b"User-Agent: something or other\r\n" - b"Connection: close\r\n" - b"\r\n" - )) - - lines = bytes_out.split(b"\r\n") - self.assertEqual(lines[0], b"HTTP/1.1 200 OK") # sanity check - - # the address in the PROXY line is applied to every request - addr_lines = [l for l in lines if l.startswith(b"got addr")] - self.assertEqual(addr_lines, [b"got addr: 192.168.0.1 56423"] * 2) - addr_lines = [l for l in lines if l.startswith(b"on addr")] - self.assertEqual(addr_lines, [b"on addr: 192.168.0.11 443"] * 2) - addr_lines = [l for l in lines if l.startswith(b"https is")] - self.assertEqual(addr_lines, [b"https is on (scheme https)"] * 2) - - def test_missing_proxy_line(self): - bytes_out = self._run_bytes_through_protocol(( - # whoops, no PROXY line here - b"GET /someurl HTTP/1.0\r\n" - b"User-Agent: something or other\r\n" - b"\r\n" - )) - - lines = [l for l in bytes_out.split(b"\r\n") if l] - self.assertIn(b"400 Invalid PROXY line", lines[0]) - - def test_malformed_proxy_lines(self): - for bad_line in [b'PROXY jojo', - b'PROXYjojo a b c d e', - b'PROXY a b c d e', # bad INET protocol and family - ]: - bytes_out = self._run_bytes_through_protocol(bad_line) - lines = [l for l in bytes_out.split(b"\r\n") if l] - self.assertIn(b"400 Invalid PROXY line", lines[0]) - - def test_unknown_client_addr(self): - # For "UNKNOWN", the rest of the line before the CRLF may be omitted by - # the sender, and the receiver must ignore anything presented before - # the CRLF is found. - for unknown_line in [b'PROXY UNKNOWN', # mimimal valid unknown - b'PROXY UNKNOWNblahblah', # also valid - b'PROXY UNKNOWN a b c d']: - bytes_out = self._run_bytes_through_protocol(( - unknown_line + (b"\r\n" - b"GET /someurl HTTP/1.0\r\n" - b"User-Agent: something or other\r\n" - b"\r\n") - )) - lines = [l for l in bytes_out.split(b"\r\n") if l] - self.assertIn(b"200 OK", lines[0]) - - def test_address_and_environ(self): - # Make an object we can exercise... note the base class's __init__() - # does a bunch of work, so we just new up an object like eventlet.wsgi - # does. - dummy_env = {'OTHER_ENV_KEY': 'OTHER_ENV_VALUE'} - mock_protocol = mock.Mock(get_environ=lambda s: dummy_env) - patcher = mock.patch( - 'swift.common.wsgi.SwiftHttpProtocol', mock_protocol - ) - self.mock_super = patcher.start() - self.addCleanup(patcher.stop) - - proto_class = wsgi.SwiftHttpProxiedProtocol - try: - proxy_obj = types.InstanceType(proto_class) - except AttributeError: - proxy_obj = proto_class.__new__(proto_class) - - # Install some convenience mocks - proxy_obj.server = Namespace(app=Namespace(logger=mock.Mock()), - url_length_limit=777, - log=mock.Mock()) - proxy_obj.send_error = mock.Mock() - - proxy_obj.rfile = BytesIO( - b'PROXY TCP4 111.111.111.111 222.222.222.222 111 222' - ) - - assert proxy_obj.handle() - - self.assertEqual(proxy_obj.client_address, ('111.111.111.111', '111')) - self.assertEqual(proxy_obj.proxy_address, ('222.222.222.222', '222')) - expected_env = { - 'SERVER_PORT': '222', - 'SERVER_ADDR': '222.222.222.222', - 'OTHER_ENV_KEY': 'OTHER_ENV_VALUE' - } - self.assertEqual(proxy_obj.get_environ(), expected_env) - - class CommonTestMixin(object): def test_post_fork_hook(self): |