Repository: trafficserver Updated Branches: refs/heads/master e895bcc6a -> 51ea4aa9d
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/51ea4aa9/ci/tsqa/tests/test_keepalive.py ---------------------------------------------------------------------- diff --git a/ci/tsqa/tests/test_keepalive.py b/ci/tsqa/tests/test_keepalive.py new file mode 100644 index 0000000..0e501ce --- /dev/null +++ b/ci/tsqa/tests/test_keepalive.py @@ -0,0 +1,476 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import uuid +import requests +import time +import logging +import socket + +import helpers + +import tsqa.test_cases +import tsqa.utils +import tsqa.endpoint + +log = logging.getLogger(__name__) + +import SocketServer + + +class KeepaliveTCPHandler(SocketServer.BaseRequestHandler): + """ + A subclass of RequestHandler which will return a connection uuid + """ + + def handle(self): + # Receive the data in small chunks and retransmit it + start = time.time() + conn_id = uuid.uuid4().hex + while True: + now = time.time() - start + data = self.request.recv(4096).strip() + if data: + log.debug('Sending data back to the client: {uid}'.format(uid=conn_id)) + else: + log.debug('Client disconnected: {timeout}seconds'.format(timeout=now)) + break + body = conn_id + resp = ('HTTP/1.1 200 OK\r\n' + 'Content-Length: {content_length}\r\n' + 'Content-Type: text/html; charset=UTF-8\r\n' + 'Connection: keep-alive\r\n' + '\r\n' + '{body}'.format(content_length=len(body), body=body)) + self.request.sendall(resp) + + +class KeepAliveInMixin(object): + """Mixin for keep alive in. + + TODO: Allow protocol to be specified for ssl traffic + """ + def _get_socket(self): + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.connect(('127.0.0.1', int(self.configs['records.config']['CONFIG']['proxy.config.http.server_ports']))) + return s + + def _headers_to_str(self, headers): + if headers is None: + headers = {} + request = '' + for k, v in headers.iteritems(): + request += '{0}: {1}\r\n'.format(k, v) + return request + + def _aux_KA_working_path_connid(self, protocol, headers=None): + # connect tcp + s = self._get_socket() + + request = ('GET / HTTP/1.1\r\n' + 'Host: foobar.com\r\n') + request += self._headers_to_str(headers) + request += '\r\n' + + for x in xrange(1, 10): + s.send(request) + response = s.recv(4096) + # cheat, since we know what the body should have + if '\r\n\r\n' not in response: + response += s.recv(4096) + self.assertIn('HTTP/1.1 200 OK', response) + self.assertIn('hello', response) + + def _aux_working_path(self, protocol, headers=None): + # connect tcp + s = self._get_socket() + + request = ('GET /exists/ HTTP/1.1\r\n' + 'Host: foobar.com\r\n') + request += self._headers_to_str(headers) + request += '\r\n' + + for x in xrange(1, 10): + s.send(request) + response = s.recv(4096) + # cheat, since we know what the body should have + if not response.endswith('hello'): + response += s.recv(4096) + self.assertIn('HTTP/1.1 200 OK', response) + self.assertIn('hello', response) + + def _aux_error_path(self, protocol, headers=None): + # connect tcp + s = self._get_socket() + + request = ('GET / HTTP/1.1\r\n' + 'Host: foobar.com\r\n') + request += self._headers_to_str(headers) + request += '\r\n' + for x in xrange(1, 10): + s.send(request) + response = s.recv(4096) + self.assertIn('HTTP/1.1 404 Not Found on Accelerator', response) + + def _aux_error_path_post(self, protocol, headers=None): + ''' + Ensure that sending a request with a body doesn't break the keepalive session + ''' + # connect tcp + s = self._get_socket() + + request = ('POST / HTTP/1.1\r\n' + 'Host: foobar.com\r\n' + 'Content-Length: 10\r\n') + request += self._headers_to_str(headers) + request += '\r\n' + request += '1234567890' + + for x in xrange(1, 10): + try: + s.send(request) + except IOError: + s = self._get_socket() + s.send(request) + + response = s.recv(4096) + # Check if client disconnected + if response: + self.assertIn('HTTP/1.1 404 Not Found on Accelerator', response) + + +class BasicTestsOutMixin(object): + + def _aux_KA_origin(self, protocol, headers=None): + ''' + Test that the origin does in fact support keepalive + ''' + conn_id = None + with requests.Session() as s: + url = '{0}://127.0.0.1:{1}/'.format(protocol, self.socket_server.port) + for x in xrange(1, 10): + ret = s.get(url, verify=False, headers=headers) + if not conn_id: + conn_id = ret.text.strip() + self.assertEqual(ret.status_code, 200) + self.assertEqual(ret.text.strip(), conn_id, "Client reports server closed connection") + + def _aux_KA_proxy(self, protocol, headers=None): + ''' + Test that keepalive works through ATS to that origin + ''' + url = '{0}://127.0.0.1:{1}'.format(protocol, + self.configs['records.config']['CONFIG']['proxy.config.http.server_ports']) + conn_id = None + for x in xrange(1, 10): + ret = requests.get(url, verify=False, headers=headers) + if not conn_id: + conn_id = ret.text.strip() + self.assertEqual(ret.status_code, 200) + self.assertEqual(ret.text.strip(), conn_id, "Client reports server closed connection") + +class TimeoutOutMixin(object): + + def _aux_KA_timeout_direct(self, protocol): + '''Tests that origin does not timeout using keepalive.''' + with requests.Session() as s: + url = '{0}://127.0.0.1:{1}/'.format(protocol, self.socket_server.port) + conn_id = None + for x in xrange(0, 3): + ret = s.get(url, verify=False) + if not conn_id: + conn_id = ret.text.strip() + self.assertEqual(ret.text.strip(), conn_id, "Client reports server closed connection") + time.sleep(3) + + def _aux_KA_timeout_proxy(self, protocol): + '''Tests that keepalive timeout is honored through ATS to origin.''' + url = '{0}://127.0.0.1:{1}'.format(protocol, + self.configs['records.config']['CONFIG']['proxy.config.http.server_ports']) + conn_id = None + for x in xrange(0, 3): + ret = requests.get(url, verify=False) + if not conn_id: + conn_id = ret.text.strip() + self.assertEqual(ret.text.strip(), conn_id, "Client reports server closed connection") + time.sleep(3) + + +class OriginMinMaxMixin(object): + + def _aux_KA_min_origin(self, protocol): + '''Tests that origin_min_keep_alive_connections is honored.''' + url = '{0}://127.0.0.1:{1}'.format(protocol, + self.configs['records.config']['CONFIG']['proxy.config.http.server_ports']) + ret = requests.get(url, verify=False) + conn_id = ret.text.strip() + time.sleep(3) + ret = requests.get(url, verify=False) + self.assertEqual(ret.text.strip(), conn_id, "Client reports server closed connection") + + +class TestKeepAliveInHTTP(tsqa.test_cases.DynamicHTTPEndpointCase, helpers.EnvironmentCase, KeepAliveInMixin): + @classmethod + def setUpEnv(cls, env): + + def hello(request): + return 'hello' + cls.http_endpoint.add_handler('/exists/', hello) + + cls.configs['remap.config'].add_line('map /exists/ http://127.0.0.1:{0}/exists/'.format(cls.http_endpoint.address[1])) + + # only add server headers when there weren't any + cls.configs['records.config']['CONFIG']['proxy.config.http.response_server_enabled'] = 2 + cls.configs['records.config']['CONFIG']['proxy.config.http.keep_alive_enabled_in'] = 1 + cls.configs['records.config']['CONFIG']['share_server_session'] = 2 + + # set only one ET_NET thread (so we don't have to worry about the per-thread pools causing issues) + cls.configs['records.config']['CONFIG']['proxy.config.exec_thread.limit'] = 1 + cls.configs['records.config']['CONFIG']['proxy.config.exec_thread.autoconfig'] = 0 + + def test_working_path(self): + self._aux_working_path("http") + + def test_error_path(self): + self._aux_error_path("http") + + def test_error_path_post(self): + ''' + Ensure that sending a request with a body doesn't break the keepalive session + ''' + self._aux_error_path_post("http") + +class TestKeepAliveOriginConnOutHTTP(helpers.EnvironmentCase, OriginMinMaxMixin): + @classmethod + def setUpEnv(cls, env): + ''' + This function is responsible for setting up the environment for this fixture + This includes everything pre-daemon start + ''' + # create a socket server + cls.socket_server = tsqa.endpoint.SocketServerDaemon(KeepaliveTCPHandler) + cls.socket_server.start() + cls.socket_server.ready.wait() + cls.configs['remap.config'].add_line('map / http://127.0.0.1:{0}/'.format(cls.socket_server.port)) + + # only add server headers when there weren't any + cls.configs['records.config']['CONFIG']['proxy.config.http.response_server_enabled'] = 2 + cls.configs['records.config']['CONFIG']['proxy.config.http.keep_alive_enabled_out'] = 1 + cls.configs['records.config']['CONFIG']['share_server_session'] = 2 + + # set only one ET_NET thread (so we don't have to worry about the per-thread pools causing issues) + cls.configs['records.config']['CONFIG']['proxy.config.exec_thread.limit'] = 1 + cls.configs['records.config']['CONFIG']['proxy.config.exec_thread.autoconfig'] = 0 + + # Timeouts + cls.configs['records.config']['CONFIG']['proxy.config.http.keep_alive_no_activity_timeout_out'] = 1 + cls.configs['records.config']['CONFIG']['proxy.config.http.transaction_no_activity_timeout_out'] = 1 + + cls.configs['records.config']['CONFIG']['proxy.config.http.origin_min_keep_alive_connections'] = 1 + + def test_KA_min_origin(self): + '''Tests that origin_min_keep_alive_connections is honored via http.''' + self._aux_KA_min_origin("http") + + +class TestKeepAliveOriginConnOutHTTPS(helpers.EnvironmentCase, OriginMinMaxMixin): + @classmethod + def setUpEnv(cls, env): + ''' + This function is responsible for setting up the environment for this fixture + This includes everything pre-daemon start + ''' + # create a socket server + cls.socket_server = tsqa.endpoint.SSLSocketServerDaemon(KeepaliveTCPHandler, + helpers.tests_file_path('cert.pem'), + helpers.tests_file_path('key.pem')) + cls.socket_server.start() + cls.socket_server.ready.wait() + cls.configs['remap.config'].add_line('map / https://127.0.0.1:{0}/\n'.format(cls.socket_server.port)) + + # only add server headers when there weren't any + cls.configs['records.config']['CONFIG']['proxy.config.http.response_server_enabled'] = 2 + cls.configs['records.config']['CONFIG']['proxy.config.http.keep_alive_enabled_out'] = 1 + cls.configs['records.config']['CONFIG']['share_server_session'] = 2 + + # set only one ET_NET thread (so we don't have to worry about the per-thread pools causing issues) + cls.configs['records.config']['CONFIG']['proxy.config.exec_thread.limit'] = 1 + cls.configs['records.config']['CONFIG']['proxy.config.exec_thread.autoconfig'] = 0 + cls.configs['records.config']['CONFIG']['proxy.config.ssl.number.threads'] = -1 + + # Timeouts + cls.configs['records.config']['CONFIG']['proxy.config.http.keep_alive_no_activity_timeout_out'] = 1 + cls.configs['records.config']['CONFIG']['proxy.config.http.transaction_no_activity_timeout_out'] = 1 + + cls.configs['records.config']['CONFIG']['proxy.config.http.origin_min_keep_alive_connections'] = 1 + + def test_KA_min_origin(self): + '''Tests that origin_min_keep_alive_connections is honored via https.''' + self._aux_KA_min_origin("http") + + +class TestKeepAliveOutHTTP(helpers.EnvironmentCase, BasicTestsOutMixin, TimeoutOutMixin): + @classmethod + def setUpEnv(cls, env): + ''' + This function is responsible for setting up the environment for this fixture + This includes everything pre-daemon start + ''' + # create a socket server + cls.socket_server = tsqa.endpoint.SocketServerDaemon(KeepaliveTCPHandler) + cls.socket_server.start() + cls.socket_server.ready.wait() + cls.configs['remap.config'].add_line('map / http://127.0.0.1:{0}/'.format(cls.socket_server.port)) + + # only add server headers when there weren't any + cls.configs['records.config']['CONFIG']['proxy.config.http.response_server_enabled'] = 2 + cls.configs['records.config']['CONFIG']['proxy.config.http.keep_alive_enabled_out'] = 1 + cls.configs['records.config']['CONFIG']['share_server_session'] = 2 + + # set only one ET_NET thread (so we don't have to worry about the per-thread pools causing issues) + cls.configs['records.config']['CONFIG']['proxy.config.exec_thread.limit'] = 1 + cls.configs['records.config']['CONFIG']['proxy.config.exec_thread.autoconfig'] = 0 + + # Timeouts + cls.configs['records.config']['CONFIG']['proxy.config.http.keep_alive_no_activity_timeout_out'] = 10 + cls.configs['records.config']['CONFIG']['proxy.config.http.transaction_no_activity_timeout_out'] = 2 + + def test_KA_origin(self): + '''Test that the origin does in fact support keepalive via http.''' + self._aux_KA_origin("http") + + def test_KA_proxy(self): + '''Tests that keepalive works through ATS to origin via http.''' + self._aux_KA_proxy("http") + + def test_KA_timeout_direct(self): + '''Tests that origin does not timeout using keepalive via http.''' + self._aux_KA_timeout_direct("http") + + def test_KA_timeout_proxy(self): + '''Tests that keepalive timeout is honored through ATS to origin via http.''' + self._aux_KA_timeout_proxy("http") + + +class TestKeepAliveOutHTTPS(helpers.EnvironmentCase, BasicTestsOutMixin, TimeoutOutMixin): + @classmethod + def setUpEnv(cls, env): + ''' + This function is responsible for setting up the environment for this fixture + This includes everything pre-daemon start + ''' + # create a socket server + cls.socket_server = tsqa.endpoint.SSLSocketServerDaemon(KeepaliveTCPHandler, + helpers.tests_file_path('cert.pem'), + helpers.tests_file_path('key.pem')) + cls.socket_server.start() + cls.socket_server.ready.wait() + cls.configs['remap.config'].add_line('map / https://127.0.0.1:{0}/\n'.format(cls.socket_server.port)) + + # only add server headers when there weren't any + cls.configs['records.config']['CONFIG']['proxy.config.http.response_server_enabled'] = 2 + cls.configs['records.config']['CONFIG']['proxy.config.http.keep_alive_enabled_out'] = 1 + cls.configs['records.config']['CONFIG']['share_server_session'] = 2 + + # set only one ET_NET thread (so we don't have to worry about the per-thread pools causing issues) + cls.configs['records.config']['CONFIG']['proxy.config.exec_thread.limit'] = 1 + cls.configs['records.config']['CONFIG']['proxy.config.exec_thread.autoconfig'] = 0 + cls.configs['records.config']['CONFIG']['proxy.config.ssl.number.threads'] = -1 + + # Timeouts + cls.configs['records.config']['CONFIG']['proxy.config.http.keep_alive_no_activity_timeout_out'] = 10 + cls.configs['records.config']['CONFIG']['proxy.config.http.transaction_no_activity_timeout_out'] = 2 + + def test_KA_origin(self): + '''Test that the origin does in fact support keepalive via https.''' + self._aux_KA_origin("https") + + def test_KA_proxy(self): + '''Tests that keepalive works through ATS to origin via https.''' + self._aux_KA_proxy("http") + + def test_KA_timeout_direct(self): + '''Tests that origin does not timeout using keepalive via https.''' + self._aux_KA_timeout_direct("https") + + def test_KA_timeout_proxy(self): + '''Tests that keepalive timeout is honored through ATS to origin via https.''' + self._aux_KA_timeout_proxy("http") + + + +# TODO: refactor these tests, these are *very* similar, we should paramatarize them +## Some basic tests for auth_sever_session_private +class TestKeepAlive_Authorization_private(helpers.EnvironmentCase, BasicTestsOutMixin, KeepAliveInMixin): + @classmethod + def setUpEnv(cls, env): + + cls.socket_server = tsqa.endpoint.SocketServerDaemon(KeepaliveTCPHandler) + cls.socket_server.start() + cls.socket_server.ready.wait() + cls.configs['remap.config'].add_line('map / http://127.0.0.1:{0}/exists/'.format(cls.socket_server.port)) + + # only add server headers when there weren't any + cls.configs['records.config']['CONFIG']['proxy.config.http.response_server_enabled'] = 2 + cls.configs['records.config']['CONFIG']['proxy.config.http.keep_alive_enabled_in'] = 1 + cls.configs['records.config']['CONFIG']['share_server_session'] = 2 + + # set only one ET_NET thread (so we don't have to worry about the per-thread pools causing issues) + cls.configs['records.config']['CONFIG']['proxy.config.exec_thread.limit'] = 1 + cls.configs['records.config']['CONFIG']['proxy.config.exec_thread.autoconfig'] = 0 + + # make auth sessions private + cls.configs['records.config']['CONFIG']['proxy.config.auth_server_session_private'] = 1 + + def test_KA_server(self): + '''Tests that keepalive works through ATS to origin via https.''' + with self.assertRaises(AssertionError): + self._aux_KA_proxy("http", headers={'Authorization': 'Foo'}) + + def test_KA_client(self): + '''Tests that keepalive works through ATS to origin via https.''' + with self.assertRaises(AssertionError): + self._aux_KA_working_path_connid("http", headers={'Authorization': 'Foo'}) + + +class TestKeepAlive_Authorization_no_private(helpers.EnvironmentCase, BasicTestsOutMixin, KeepAliveInMixin): + @classmethod + def setUpEnv(cls, env): + + cls.socket_server = tsqa.endpoint.SocketServerDaemon(KeepaliveTCPHandler) + cls.socket_server.start() + cls.socket_server.ready.wait() + cls.configs['remap.config'].add_line('map / http://127.0.0.1:{0}/exists/'.format(cls.socket_server.port)) + + # only add server headers when there weren't any + cls.configs['records.config']['CONFIG']['proxy.config.http.response_server_enabled'] = 2 + cls.configs['records.config']['CONFIG']['proxy.config.http.keep_alive_enabled_in'] = 1 + cls.configs['records.config']['CONFIG']['share_server_session'] = 2 + + # set only one ET_NET thread (so we don't have to worry about the per-thread pools causing issues) + cls.configs['records.config']['CONFIG']['proxy.config.exec_thread.limit'] = 1 + cls.configs['records.config']['CONFIG']['proxy.config.exec_thread.autoconfig'] = 0 + + # make auth sessions private + cls.configs['records.config']['CONFIG']['proxy.config.http.auth_server_session_private'] = 0 + + def test_KA_server(self): + '''Tests that keepalive works through ATS to origin via https.''' + self._aux_KA_proxy("http", headers={'Authorization': 'Foo'}) + + def test_KA_client(self): + '''Tests that keepalive works through ATS to origin via https.''' + self._aux_KA_working_path_connid("http", headers={'Authorization': 'Foo'}) http://git-wip-us.apache.org/repos/asf/trafficserver/blob/51ea4aa9/ci/tsqa/tests/test_redirection.py ---------------------------------------------------------------------- diff --git a/ci/tsqa/tests/test_redirection.py b/ci/tsqa/tests/test_redirection.py new file mode 100644 index 0000000..1100dc1 --- /dev/null +++ b/ci/tsqa/tests/test_redirection.py @@ -0,0 +1,41 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import requests +import helpers +import tsqa.test_cases +import tsqa.utils +import tsqa.endpoint + +class TestRedirection(helpers.EnvironmentCase, tsqa.test_cases.HTTPBinCase): + @classmethod + def setUpEnv(cls, env): + cls.configs['records.config']['CONFIG'].update({ + 'proxy.config.http.redirection_enabled': 1, + 'proxy.config.http.number_of_redirections': 10 + }) + cls.configs['remap.config'].add_line('map / http://127.0.0.1:{0}'.format(cls.http_endpoint.address[1])) + + def test_redirection(self): + server_ports = self.configs['records.config']['CONFIG']['proxy.config.http.server_ports'] + + # By default Requests will perform location redirection + # Disable redirection handling with the allow_redirects parameter + r = requests.get('http://127.0.0.1:{0}/redirect/9'.format(server_ports), allow_redirects=False) + self.assertEqual(r.status_code, 200) + + r = requests.get('http://127.0.0.1:{0}/redirect/10'.format(server_ports), allow_redirects=False) + self.assertEqual(r.status_code, 302) http://git-wip-us.apache.org/repos/asf/trafficserver/blob/51ea4aa9/ci/tsqa/tests/test_regressions.py ---------------------------------------------------------------------- diff --git a/ci/tsqa/tests/test_regressions.py b/ci/tsqa/tests/test_regressions.py new file mode 100644 index 0000000..8fec2eb --- /dev/null +++ b/ci/tsqa/tests/test_regressions.py @@ -0,0 +1,82 @@ +''' +Run the built-in regression tests with experimental build configurations. +''' + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import sys +import requests +import time +import subprocess +import logging + +import helpers +import tsqa.test_cases +import tsqa.utils + +log = logging.getLogger(__name__) + +class TestRegressions(helpers.EnvironmentCase): + ''' + Run the built-in traffic_server regression test suite. + ''' + + # NOTE: we need to stop the running Traffic Server in the environment so + # that we can start up our own. Make sure to restart it when we are done so + # that the EnvironmentCase doesn't get upset. + + @classmethod + def setUpClass(cls): + super(TestRegressions, cls).setUpClass() + cls.environment.stop() + + @classmethod + def tearDownClass(cls): + cls.environment.start() + super(TestRegressions, cls).tearDownClass() + + def test_regressions(self): + cmd = [os.path.join(self.environment.layout.bindir, 'traffic_server'), '-R', '1'] + tsqa.utils.run_sync_command(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + +class TestRegressionsReclaimableFreelist(TestRegressions): + ''' + Run the built-in traffic_server regression test suite with + --enable-reclaimable-freelist. + ''' + environment_factory = { + 'configure': { 'enable-reclaimable-freelist': None }, + } + +class TestRegressionsInterimCache(TestRegressions): + ''' + Run the built-in traffic_server regression test suite with + --enable-interim-cache. + ''' + environment_factory = { + 'configure': { 'enable-interim-cache': None }, + } + +class TestRegressionsLinuxNativeAIO(TestRegressions): + ''' + Run the built-in traffic_server regression test suite with + --enable-linux-native-aio. + ''' + environment_factory = { + 'configure': { 'enable-linux-native-aio': None }, + } http://git-wip-us.apache.org/repos/asf/trafficserver/blob/51ea4aa9/ci/tsqa/tests/test_remap.py ---------------------------------------------------------------------- diff --git a/ci/tsqa/tests/test_remap.py b/ci/tsqa/tests/test_remap.py new file mode 100644 index 0000000..aac6e8e --- /dev/null +++ b/ci/tsqa/tests/test_remap.py @@ -0,0 +1,125 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import requests +import time +import logging + +import helpers + +import tsqa.test_cases +import tsqa.utils +import tsqa.endpoint + +log = logging.getLogger(__name__) + +class TestRemapHTTP(tsqa.test_cases.DynamicHTTPEndpointCase, helpers.EnvironmentCase): + @classmethod + def setUpEnv(cls, env): + cls.configs['records.config']['CONFIG'].update({ + 'proxy.config.diags.debug.enabled': 1, + 'proxy.config.diags.debug.tags': 'url.*', + }) + + cls.configs['remap.config'].add_line( + 'map http://www.example.com http://127.0.0.1:{0}'.format(cls.http_endpoint.address[1])); + cls.configs['remap.config'].add_line( + 'map http://www.example.com:8080 http://127.0.0.1:{0}'.format(cls.http_endpoint.address[1])); + + def hello(request): + return 'hello' + cls.http_endpoint.add_handler('/', hello) + + def test_remap_http(self): + s = requests.Session() + http_port = self.configs['records.config']['CONFIG']['proxy.config.http.server_ports'] + url = 'http://127.0.0.1:{0}/'.format(http_port) + + ret = s.get(url) + self.assertEqual(ret.status_code, 404) + + s.headers.update({'Host': 'www.example.com'}) + ret = s.get(url) + self.assertEqual(ret.status_code, 200) + + s.headers.update({'Host': 'www.example.com:80'}) + ret = s.get(url) + self.assertEqual(ret.status_code, 200) + + s.headers.update({'Host': 'www.example.com:8080'}) + ret = s.get(url) + self.assertEqual(ret.status_code, 200) + + s.headers.update({'Host': 'www.test.com'}) + ret = s.get(url) + self.assertEqual(ret.status_code, 404) + + s.headers.update({'Host': 'www.example.com:1234'}) + ret = s.get(url) + self.assertEqual(ret.status_code, 404) + +class TestRemapHTTPS(tsqa.test_cases.DynamicHTTPEndpointCase, helpers.EnvironmentCase): + @classmethod + def setUpEnv(cls, env): + # set an SSL port to ATS + cls.ssl_port = tsqa.utils.bind_unused_port()[1] + cls.configs['records.config']['CONFIG']['proxy.config.http.server_ports'] += ' {0}:ssl'.format(cls.ssl_port) + cls.configs['records.config']['CONFIG'].update({ + 'proxy.config.diags.debug.enabled': 1, + 'proxy.config.diags.debug.tags': 'url.*' + }) + + cls.configs['remap.config'].add_line( + 'map https://www.example.com http://127.0.0.1:{0}'.format(cls.http_endpoint.address[1])); + cls.configs['remap.config'].add_line( + 'map https://www.example.com:4443 http://127.0.0.1:{0}'.format(cls.http_endpoint.address[1])); + # configure SSL multicert + cls.configs['ssl_multicert.config'].add_line( + 'dest_ip=* ssl_cert_name={0}'.format(helpers.tests_file_path('rsa_keys/www.example.com.pem'))) + + def hello(request): + return 'hello' + cls.http_endpoint.add_handler('/', hello) + + def test_remap_https(self): + s = requests.Session() + url = 'https://127.0.0.1:{0}/'.format(self.ssl_port) + + # We lack of SNI support in requests module, so we do not verify SSL certificate here. + # ret = s.get(url, verify=(helpers.tests_file_path('certs/ca.crt'))) + ret = s.get(url, verify=False) + self.assertEqual(ret.status_code, 404) + + s.headers.update({'Host': 'www.example.com'}) + ret = s.get(url, verify=False) + self.assertEqual(ret.status_code, 200) + + s.headers.update({'Host': 'www.example.com:443'}) + ret = s.get(url) + self.assertEqual(ret.status_code, 200) + + s.headers.update({'Host': 'www.example.com:4443'}) + ret = s.get(url) + self.assertEqual(ret.status_code, 200) + + s.headers.update({'Host': 'www.test.com'}) + ret = s.get(url) + self.assertEqual(ret.status_code, 404) + + s.headers.update({'Host': 'www.example.com:1234'}) + ret = s.get(url) + self.assertEqual(ret.status_code, 404) http://git-wip-us.apache.org/repos/asf/trafficserver/blob/51ea4aa9/ci/tsqa/tests/test_spdy_protocol_select.py ---------------------------------------------------------------------- diff --git a/ci/tsqa/tests/test_spdy_protocol_select.py b/ci/tsqa/tests/test_spdy_protocol_select.py new file mode 100644 index 0000000..50fabce --- /dev/null +++ b/ci/tsqa/tests/test_spdy_protocol_select.py @@ -0,0 +1,156 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import requests +import time +import logging +import subprocess + +import helpers + +import tsqa.test_cases +import tsqa.utils +import tsqa.endpoint + +log = logging.getLogger(__name__) + +#helper function to get spdycat path +def which(program): + def is_exe(fpath): + return os.path.isfile(fpath) and os.access(fpath, os.X_OK) + fpath, fname = os.path.split(program) + if fpath: + if is_exe(program): + return program + else: + for path in os.environ["PATH"].split(os.pathsep): + path = path.strip('"') + exe_file = os.path.join(path, program) + if is_exe(exe_file): + return exe_file + return None + +class TestSPDY(helpers.EnvironmentCase): + environment_factory = { + 'configure': {'enable-spdy': None}, + 'env': {'PKG_CONFIG_PATH': os.getenv("SPDY_PKG_CONFIG_PATH", "/opt/spdylay/lib/pkgconfig/")}, + } + + @classmethod + def setUpEnv(cls, env): + ''' + This function is responsible for setting up the environment for this fixture + This includes everything pre-daemon start + ''' + # set up spdycat + cls.client = which('spdycat') + if cls.client is None: + build_dir = os.environ.get('top_builddir', '../..') + log.info('top build_dir = {0}'.format(build_dir)) + cls.client = '%s/spdylay/src/spdycat' % build_dir + if os.path.isfile(cls.client) is False: + raise helpers.unittest.SkipTest('Cannot find spdycat. skipping test.') + + log.info('spdycat path = {0}'.format(cls.client)) + + # get spdy server ports + cls.spdy_port = tsqa.utils.bind_unused_port()[1] + log.info('spdy server port = {0}'.format(cls.spdy_port)) + cls.http_port = tsqa.utils.bind_unused_port()[1] + log.info('http server port = {0}'.format(cls.http_port)) + + cls.configs['remap.config'].add_line('map / https://docs.trafficserver.apache.org/\n') + + # set only one ET_NET thread (so we don't have to worry about the per-thread pools causing issues) + cls.configs['records.config']['CONFIG']['proxy.config.exec_thread.limit'] = 1 + cls.configs['records.config']['CONFIG']['proxy.config.exec_thread.autoconfig'] = 0 + + # SPDY configs + cls.configs['records.config']['CONFIG']['proxy.config.http.server_ports'] += ' {0}:ssl {1}:proto=http:ssl'.format(cls.spdy_port, cls.http_port) + cls.configs['records.config']['CONFIG']['proxy.config.ssl.server.cert.path'] = helpers.tests_file_path('rsa_keys') + + # configure SSL multicert + cls.configs['ssl_multicert.config'].add_line('dest_ip=* ssl_cert_name={0}\n'.format(helpers.tests_file_path('rsa_keys/www.example.com.pem'))) + + @classmethod + def callSpdycat(self, port, path, args): + full_args = [self.client,'https://localhost:%d%s' % (port, path)] + args + self.log.info('full args = {0}'.format(full_args)) + p = subprocess.Popen(full_args, stdout=subprocess.PIPE, + stdin=subprocess.PIPE) + self.stdout, self.stderr = p.communicate() + return p.returncode + +""" +TODO: re-add spdy2 tests. looks like support here might be lacking some way. was not able to get ATS to advertise spdy/2 +even when it was explicitly set with proto=spdy/2 +""" +class TestSPDYv2(TestSPDY): + @classmethod + def setUpClass(cls): + ''' + Skip spdy2 tests for now + ''' + raise helpers.unittest.SkipTest('Skipping spdy/2 tests') + + @classmethod + def setUpEnv(cls, env): + ''' + This function is responsible for setting up the environment for this fixture + This includes everything pre-daemon start + ''' + super(TestSPDYv2, cls).setUpEnv(env) + + cls.spdy2_port = tsqa.utils.bind_unused_port()[1] + log.info('spdy2 server port = {0}'.format(cls.spdy2_port)) + # make sure we add port supports spdy2 + cls.configs['records.config']['CONFIG']['proxy.config.http.server_ports'] += ' {0}:proto=spdy/2:ssl'.format(cls.spdy2_port) + + def test_SPDY_v2(self): + ''' + Test that the origin does in fact support spdy 2 + ''' + self.assertEquals(0, self.callSpdycat(self.spdy2_port, '/', ['-nv', '--spdy2'])) #this isn't passing + self.assertIn('version=2', self.stdout) + +class TestSPDYv3(TestSPDY): + def test_SPDY_v3(self): + ''' + Test that the origin does in fact support spdy 3 + ''' + self.assertEquals(0, self.callSpdycat(self.spdy_port, '/', ['-nv', '--spdy3'])) + self.assertIn('NPN selected the protocol: spdy/3', self.stdout) + + def test_SPDY_v3_failed_request(self): + ''' + Test that non spdy port won't advertise spdy + ''' + self.assertEquals(1, self.callSpdycat(self.http_port, '/', ['-nv', '--spdy3'])) + +class TestSPDYv3_1(TestSPDY): + def test_SPDY_v3_1(self): + ''' + Test that the origin does in fact support spdy 3.1 + ''' + self.assertEquals(0, self.callSpdycat(self.spdy_port, '/', ['-nv', '--spdy3-1'])) + self.assertIn('NPN selected the protocol: spdy/3.1', self.stdout) + + def test_SPDY_v3_1_failed_request(self): + ''' + Test that non spdy port won't advertise spdy + ''' + self.assertEquals(1, self.callSpdycat(self.http_port, '/', ['-nv', '--spdy3-1']))