Repository: trafficserver
Updated Branches:
  refs/heads/master a39de23ff -> ae30cce25


Add tests for connect_attempts within (retries for connection to origin)


Project: http://git-wip-us.apache.org/repos/asf/trafficserver/repo
Commit: http://git-wip-us.apache.org/repos/asf/trafficserver/commit/eb5a3d3c
Tree: http://git-wip-us.apache.org/repos/asf/trafficserver/tree/eb5a3d3c
Diff: http://git-wip-us.apache.org/repos/asf/trafficserver/diff/eb5a3d3c

Branch: refs/heads/master
Commit: eb5a3d3cb932469b8d37d0a6891353de9fe2770f
Parents: a39de23
Author: Thomas Jackson <[email protected]>
Authored: Thu Mar 12 19:06:05 2015 -0700
Committer: Thomas Jackson <[email protected]>
Committed: Mon Mar 16 11:32:27 2015 -0700

----------------------------------------------------------------------
 ci/new_tsqa/tests/test_connect_attempts.py | 209 ++++++++++++++++++++++++
 1 file changed, 209 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/trafficserver/blob/eb5a3d3c/ci/new_tsqa/tests/test_connect_attempts.py
----------------------------------------------------------------------
diff --git a/ci/new_tsqa/tests/test_connect_attempts.py 
b/ci/new_tsqa/tests/test_connect_attempts.py
new file mode 100644
index 0000000..9979e33
--- /dev/null
+++ b/ci/new_tsqa/tests/test_connect_attempts.py
@@ -0,0 +1,209 @@
+'''
+Test Origin Server Connect Attempts
+'''
+#  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 time
+import logging
+import socket
+import struct
+import select
+import threading
+
+import helpers
+
+import tsqa.test_cases
+import tsqa.utils
+import tsqa.endpoint
+
+log = logging.getLogger(__name__)
+
+
+def thread_die_on_connect(sock):
+    sock.listen(0)
+    # poll
+    read_sock = select.select([sock], [], [])
+    # exit
+    sock.close()
+
+def thread_delayed_accept_after_connect(sock):
+    '''
+    Thread to sleep a decreasing amount of time before requests
+
+    sleep times: 2 -> 1 -> 0
+    '''
+    sock.listen(0)
+    sleep_time = 2
+    requests = 0
+    # poll
+    while True:
+        read_sock = select.select([sock], [], [])
+        time.sleep(sleep_time)
+        try:
+            connection, addr = sock.accept()
+            connection.send(('HTTP/1.1 200 OK\r\n'
+                    'Content-Length: {body_len}\r\n'
+                    'Content-Type: text/html; charset=UTF-8\r\n'
+                    'Connection: 
close\r\n\r\n{body}'.format(body_len=len(str(requests)), body=requests)))
+            connection.close()
+            requests += 1
+        except Exception as e:
+            print 'connection died!', e
+            pass
+        if sleep_time > 0:
+            sleep_time -= 1
+
+
+def thread_reset_after_accept(sock):
+    sock.listen(0)
+    first = True
+    requests = 0
+    while True:
+        connection, addr = sock.accept()
+        requests += 1
+        if first:
+            first = False
+            connection.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, 
struct.pack('ii', 1, 0))
+            connection.close()
+        else:
+            connection.send(('HTTP/1.1 200 OK\r\n'
+                    'Content-Length: {body_len}\r\n'
+                    'Content-Type: text/html; charset=UTF-8\r\n'
+                    'Connection: 
close\r\n\r\n{body}'.format(body_len=len(str(requests)), body=requests)))
+            connection.close()
+
+def thread_partial_response(sock):
+    sock.listen(0)
+    first = True
+    requests = 0
+    while True:
+        connection, addr = sock.accept()
+        requests += 1
+        if first:
+            connection.send('HTTP/1.1 200 OK\r\n')
+            connection.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, 
struct.pack('ii', 1, 0))
+            connection.close()
+            first = False
+        else:
+            connection.send(('HTTP/1.1 200 OK\r\n'
+                    'Content-Length: {body_len}\r\n'
+                    'Content-Type: text/html; charset=UTF-8\r\n'
+                    'Connection: 
close\r\n\r\n{body}'.format(body_len=len(str(requests)), body=requests)))
+            connection.close()
+
+
+
+class TestOriginServerConnectAttempts(helpers.EnvironmentCase):
+    @classmethod
+    def setUpEnv(cls, env):
+        '''
+        This function is responsible for setting up the environment for this 
fixture
+        This includes everything pre-daemon start
+        '''
+        cls.sock_map = {}
+        def _add_sock(name):
+            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+            sock.bind(('127.0.0.1', 0))
+            cls.sock_map[name] = sock.getsockname()[1]
+            cls.configs['remap.config'].add_line('map /{0}/ 
http://127.0.0.1:{1}/'.format(name, cls.sock_map[name]))
+            return sock
+        # create a socket where we just bind
+        _add_sock('bound')
+
+        # create a socket where we bind + listen
+        sock = _add_sock('listen')
+        sock.listen(1)
+
+        # create a bunch of special socket servers
+        sock = _add_sock('die_on_connect')
+        t = threading.Thread(target=thread_die_on_connect, args=(sock,))
+        t.daemon = True
+        t.start()
+
+        sock = _add_sock('reset_after_accept')
+        t = threading.Thread(target=thread_reset_after_accept, args=(sock,))
+        t.daemon = True
+        t.start()
+
+        sock = _add_sock('delayed_accept_after_connect')
+        t = threading.Thread(target=thread_delayed_accept_after_connect, 
args=(sock,))
+        t.daemon = True
+        t.start()
+
+        sock = _add_sock('partial_response')
+        t = threading.Thread(target=thread_partial_response, args=(sock,))
+        t.daemon = True
+        t.start()
+
+        # only add server headers when there weren't any
+        
cls.configs['records.config']['CONFIG']['proxy.config.http.response_server_enabled']
 = 2
+
+        # enable re-connects, timeout of 1s, max retires of 3
+        
cls.configs['records.config']['CONFIG']['proxy.config.http.connect_attempts_timeout']
 = 1
+        
cls.configs['records.config']['CONFIG']['proxy.config.http.connect_attempts_max_retries']
 = 3
+
+    def test_bound_origin(self):
+        '''Verify that we get 502s from an origin which just did a bind'''
+        url = 
'http://127.0.0.1:{0}/bound/s'.format(self.configs['records.config']['CONFIG']['proxy.config.http.server_ports'])
+        ret = requests.get(url)
+        self.assertEqual(ret.status_code, 502)
+
+    def test_listen_origin(self):
+        '''Verify that we get 502s from origins that bind + listen'''
+        url = 
'http://127.0.0.1:{0}/listen/s'.format(self.configs['records.config']['CONFIG']['proxy.config.http.server_ports'])
+        ret = requests.get(url)
+        self.assertEqual(ret.status_code, 502)
+
+        url = 
'http://127.0.0.1:{0}/listen/s'.format(self.configs['records.config']['CONFIG']['proxy.config.http.server_ports'])
+        ret = requests.get(url)
+        self.assertEqual(ret.status_code, 502)
+
+    def test_die_on_connect_origin(self):
+        '''Verify that we get 504s from origins that die_on_connect'''
+        url = 
'http://127.0.0.1:{0}/die_on_connect/s'.format(self.configs['records.config']['CONFIG']['proxy.config.http.server_ports'])
+        ret = requests.get(url)
+        self.assertEqual(ret.status_code, 504)
+
+    # TODO: FIX THIS!!! The test is correct, ATS isn't!
+    # we should fail in this case-- or at least have a config which lets you 
control
+    def test_partial_response_origin(self):
+        '''
+        Verify that we get 504s from origins that return a partial_response
+
+        We want to bail out-- since the origin already got the request, we 
can't
+        gaurantee that the request is re-entrant
+        '''
+        url = 
'http://127.0.0.1:{0}/partial_response/s'.format(self.configs['records.config']['CONFIG']['proxy.config.http.server_ports'])
+        ret = requests.get(url)
+        self.assertEqual(ret.status_code, 502)
+
+    def test_reset_after_accept_origin(self):
+        '''Verify that we get 200s from origins that reset_after_accept'''
+        url = 
'http://127.0.0.1:{0}/reset_after_accept/s'.format(self.configs['records.config']['CONFIG']['proxy.config.http.server_ports'])
+        ret = requests.get(url)
+        self.assertEqual(ret.status_code, 200)
+        self.assertGreater(int(ret.text), 0)
+
+    def test_delayed_accept_after_connect_origin(self):
+        '''Verify that we get 200s from origins that 
delayed_accept_after_connect'''
+        url = 
'http://127.0.0.1:{0}/delayed_accept_after_connect/s'.format(self.configs['records.config']['CONFIG']['proxy.config.http.server_ports'])
+        ret = requests.get(url)
+        # make sure it worked
+        self.assertEqual(ret.status_code, 200)
+        # make sure its not the first one (otherwise the test messed up 
somehow)
+        self.assertGreater(int(ret.text), 0)

Reply via email to