11626Smax.romanov@nginx.comimport re
21626Smax.romanov@nginx.comimport time
31626Smax.romanov@nginx.comfrom distutils.version import LooseVersion
41626Smax.romanov@nginx.com
51635Szelenkov@nginx.comimport pytest
61635Szelenkov@nginx.com
71654Szelenkov@nginx.comfrom conftest import option
81635Szelenkov@nginx.comfrom conftest import skip_alert
91626Smax.romanov@nginx.comfrom unit.applications.lang.python import TestApplicationPython
101626Smax.romanov@nginx.com
111626Smax.romanov@nginx.com
121626Smax.romanov@nginx.comclass TestASGIApplication(TestApplicationPython):
131626Smax.romanov@nginx.com    prerequisites = {'modules': {'python':
141626Smax.romanov@nginx.com                            lambda v: LooseVersion(v) >= LooseVersion('3.5')}}
151626Smax.romanov@nginx.com    load_module = 'asgi'
161626Smax.romanov@nginx.com
171626Smax.romanov@nginx.com    def findall(self, pattern):
181654Szelenkov@nginx.com        with open(option.temp_dir + '/unit.log', 'r', errors='ignore') as f:
191626Smax.romanov@nginx.com            return re.findall(pattern, f.read())
201626Smax.romanov@nginx.com
211635Szelenkov@nginx.com    def test_asgi_application_variables(self):
221626Smax.romanov@nginx.com        self.load('variables')
231626Smax.romanov@nginx.com
241626Smax.romanov@nginx.com        body = 'Test body string.'
251626Smax.romanov@nginx.com
261626Smax.romanov@nginx.com        resp = self.http(
271626Smax.romanov@nginx.com            b"""POST / HTTP/1.1
281626Smax.romanov@nginx.comHost: localhost
291626Smax.romanov@nginx.comContent-Length: %d
301626Smax.romanov@nginx.comCustom-Header: blah
311626Smax.romanov@nginx.comCustom-hEader: Blah
321626Smax.romanov@nginx.comContent-Type: text/html
331626Smax.romanov@nginx.comConnection: close
341626Smax.romanov@nginx.comcustom-header: BLAH
351626Smax.romanov@nginx.com
361626Smax.romanov@nginx.com%s""" % (len(body), body.encode()),
371626Smax.romanov@nginx.com            raw=True,
381626Smax.romanov@nginx.com        )
391626Smax.romanov@nginx.com
401626Smax.romanov@nginx.com        assert resp['status'] == 200, 'status'
411626Smax.romanov@nginx.com        headers = resp['headers']
421626Smax.romanov@nginx.com        header_server = headers.pop('Server')
431626Smax.romanov@nginx.com        assert re.search(r'Unit/[\d\.]+', header_server), 'server header'
441626Smax.romanov@nginx.com
451626Smax.romanov@nginx.com        date = headers.pop('Date')
461626Smax.romanov@nginx.com        assert date[-4:] == ' GMT', 'date header timezone'
471626Smax.romanov@nginx.com        assert (
481626Smax.romanov@nginx.com            abs(self.date_to_sec_epoch(date) - self.sec_epoch()) < 5
491626Smax.romanov@nginx.com        ), 'date header'
501626Smax.romanov@nginx.com
511626Smax.romanov@nginx.com        assert headers == {
521626Smax.romanov@nginx.com            'Connection': 'close',
531626Smax.romanov@nginx.com            'content-length': str(len(body)),
541626Smax.romanov@nginx.com            'content-type': 'text/html',
551626Smax.romanov@nginx.com            'request-method': 'POST',
561626Smax.romanov@nginx.com            'request-uri': '/',
571626Smax.romanov@nginx.com            'http-host': 'localhost',
581626Smax.romanov@nginx.com            'http-version': '1.1',
591626Smax.romanov@nginx.com            'custom-header': 'blah, Blah, BLAH',
601626Smax.romanov@nginx.com            'asgi-version': '3.0',
611626Smax.romanov@nginx.com            'asgi-spec-version': '2.1',
621626Smax.romanov@nginx.com            'scheme': 'http',
631626Smax.romanov@nginx.com        }, 'headers'
641626Smax.romanov@nginx.com        assert resp['body'] == body, 'body'
651626Smax.romanov@nginx.com
661635Szelenkov@nginx.com    def test_asgi_application_query_string(self):
671626Smax.romanov@nginx.com        self.load('query_string')
681626Smax.romanov@nginx.com
691626Smax.romanov@nginx.com        resp = self.get(url='/?var1=val1&var2=val2')
701626Smax.romanov@nginx.com
711626Smax.romanov@nginx.com        assert (
721626Smax.romanov@nginx.com            resp['headers']['query-string'] == 'var1=val1&var2=val2'
731626Smax.romanov@nginx.com        ), 'query-string header'
741626Smax.romanov@nginx.com
751635Szelenkov@nginx.com    def test_asgi_application_query_string_space(self):
761626Smax.romanov@nginx.com        self.load('query_string')
771626Smax.romanov@nginx.com
781626Smax.romanov@nginx.com        resp = self.get(url='/ ?var1=val1&var2=val2')
791626Smax.romanov@nginx.com        assert (
801626Smax.romanov@nginx.com            resp['headers']['query-string'] == 'var1=val1&var2=val2'
811626Smax.romanov@nginx.com        ), 'query-string space'
821626Smax.romanov@nginx.com
831626Smax.romanov@nginx.com        resp = self.get(url='/ %20?var1=val1&var2=val2')
841626Smax.romanov@nginx.com        assert (
851626Smax.romanov@nginx.com            resp['headers']['query-string'] == 'var1=val1&var2=val2'
861626Smax.romanov@nginx.com        ), 'query-string space 2'
871626Smax.romanov@nginx.com
881626Smax.romanov@nginx.com        resp = self.get(url='/ %20 ?var1=val1&var2=val2')
891626Smax.romanov@nginx.com        assert (
901626Smax.romanov@nginx.com            resp['headers']['query-string'] == 'var1=val1&var2=val2'
911626Smax.romanov@nginx.com        ), 'query-string space 3'
921626Smax.romanov@nginx.com
931626Smax.romanov@nginx.com        resp = self.get(url='/blah %20 blah? var1= val1 & var2=val2')
941626Smax.romanov@nginx.com        assert (
951626Smax.romanov@nginx.com            resp['headers']['query-string'] == ' var1= val1 & var2=val2'
961626Smax.romanov@nginx.com        ), 'query-string space 4'
971626Smax.romanov@nginx.com
981635Szelenkov@nginx.com    def test_asgi_application_query_string_empty(self):
991626Smax.romanov@nginx.com        self.load('query_string')
1001626Smax.romanov@nginx.com
1011626Smax.romanov@nginx.com        resp = self.get(url='/?')
1021626Smax.romanov@nginx.com
1031626Smax.romanov@nginx.com        assert resp['status'] == 200, 'query string empty status'
1041626Smax.romanov@nginx.com        assert resp['headers']['query-string'] == '', 'query string empty'
1051626Smax.romanov@nginx.com
1061635Szelenkov@nginx.com    def test_asgi_application_query_string_absent(self):
1071626Smax.romanov@nginx.com        self.load('query_string')
1081626Smax.romanov@nginx.com
1091626Smax.romanov@nginx.com        resp = self.get()
1101626Smax.romanov@nginx.com
1111626Smax.romanov@nginx.com        assert resp['status'] == 200, 'query string absent status'
1121626Smax.romanov@nginx.com        assert resp['headers']['query-string'] == '', 'query string absent'
1131626Smax.romanov@nginx.com
1141626Smax.romanov@nginx.com    @pytest.mark.skip('not yet')
1151635Szelenkov@nginx.com    def test_asgi_application_server_port(self):
1161626Smax.romanov@nginx.com        self.load('server_port')
1171626Smax.romanov@nginx.com
1181626Smax.romanov@nginx.com        assert (
1191626Smax.romanov@nginx.com            self.get()['headers']['Server-Port'] == '7080'
1201626Smax.romanov@nginx.com        ), 'Server-Port header'
1211626Smax.romanov@nginx.com
1221626Smax.romanov@nginx.com    @pytest.mark.skip('not yet')
1231635Szelenkov@nginx.com    def test_asgi_application_working_directory_invalid(self):
1241626Smax.romanov@nginx.com        self.load('empty')
1251626Smax.romanov@nginx.com
1261626Smax.romanov@nginx.com        assert 'success' in self.conf(
1271626Smax.romanov@nginx.com            '"/blah"', 'applications/empty/working_directory'
1281626Smax.romanov@nginx.com        ), 'configure invalid working_directory'
1291626Smax.romanov@nginx.com
1301626Smax.romanov@nginx.com        assert self.get()['status'] == 500, 'status'
1311626Smax.romanov@nginx.com
1321635Szelenkov@nginx.com    def test_asgi_application_204_transfer_encoding(self):
1331626Smax.romanov@nginx.com        self.load('204_no_content')
1341626Smax.romanov@nginx.com
1351626Smax.romanov@nginx.com        assert (
1361626Smax.romanov@nginx.com            'Transfer-Encoding' not in self.get()['headers']
1371626Smax.romanov@nginx.com        ), '204 header transfer encoding'
1381626Smax.romanov@nginx.com
1391635Szelenkov@nginx.com    def test_asgi_application_shm_ack_handle(self):
1401626Smax.romanov@nginx.com        self.load('mirror')
1411626Smax.romanov@nginx.com
1421626Smax.romanov@nginx.com        # Minimum possible limit
1431626Smax.romanov@nginx.com        shm_limit = 10 * 1024 * 1024
1441626Smax.romanov@nginx.com
1451626Smax.romanov@nginx.com        assert (
1461626Smax.romanov@nginx.com            'success' in self.conf('{"shm": ' + str(shm_limit) + '}',
1471626Smax.romanov@nginx.com                                 'applications/mirror/limits')
1481626Smax.romanov@nginx.com        )
1491626Smax.romanov@nginx.com
1501626Smax.romanov@nginx.com        # Should exceed shm_limit
1511626Smax.romanov@nginx.com        max_body_size = 12 * 1024 * 1024
1521626Smax.romanov@nginx.com
1531626Smax.romanov@nginx.com        assert (
1541626Smax.romanov@nginx.com            'success' in self.conf('{"http":{"max_body_size": '
1551626Smax.romanov@nginx.com                                  + str(max_body_size) + ' }}',
1561626Smax.romanov@nginx.com                                 'settings')
1571626Smax.romanov@nginx.com        )
1581626Smax.romanov@nginx.com
1591626Smax.romanov@nginx.com        assert self.get()['status'] == 200, 'init'
1601626Smax.romanov@nginx.com
1611626Smax.romanov@nginx.com        body = '0123456789AB' * 1024 * 1024  # 12 Mb
1621626Smax.romanov@nginx.com        resp = self.post(
1631626Smax.romanov@nginx.com            headers={
1641626Smax.romanov@nginx.com                'Host': 'localhost',
1651626Smax.romanov@nginx.com                'Connection': 'close',
1661626Smax.romanov@nginx.com                'Content-Type': 'text/html',
1671626Smax.romanov@nginx.com            },
1681626Smax.romanov@nginx.com            body=body,
1691626Smax.romanov@nginx.com            read_buffer_size=1024 * 1024,
1701626Smax.romanov@nginx.com        )
1711626Smax.romanov@nginx.com
1721626Smax.romanov@nginx.com        assert resp['body'] == body, 'keep-alive 1'
1731626Smax.romanov@nginx.com
1741626Smax.romanov@nginx.com    def test_asgi_keepalive_body(self):
1751626Smax.romanov@nginx.com        self.load('mirror')
1761626Smax.romanov@nginx.com
1771626Smax.romanov@nginx.com        assert self.get()['status'] == 200, 'init'
1781626Smax.romanov@nginx.com
1791626Smax.romanov@nginx.com        body = '0123456789' * 500
1801626Smax.romanov@nginx.com        (resp, sock) = self.post(
1811626Smax.romanov@nginx.com            headers={
1821626Smax.romanov@nginx.com                'Host': 'localhost',
1831626Smax.romanov@nginx.com                'Connection': 'keep-alive',
1841626Smax.romanov@nginx.com                'Content-Type': 'text/html',
1851626Smax.romanov@nginx.com            },
1861626Smax.romanov@nginx.com            start=True,
1871626Smax.romanov@nginx.com            body=body,
1881626Smax.romanov@nginx.com            read_timeout=1,
1891626Smax.romanov@nginx.com        )
1901626Smax.romanov@nginx.com
1911626Smax.romanov@nginx.com        assert resp['body'] == body, 'keep-alive 1'
1921626Smax.romanov@nginx.com
1931626Smax.romanov@nginx.com        body = '0123456789'
1941626Smax.romanov@nginx.com        resp = self.post(
1951626Smax.romanov@nginx.com            headers={
1961626Smax.romanov@nginx.com                'Host': 'localhost',
1971626Smax.romanov@nginx.com                'Connection': 'close',
1981626Smax.romanov@nginx.com                'Content-Type': 'text/html',
1991626Smax.romanov@nginx.com            },
2001626Smax.romanov@nginx.com            sock=sock,
2011626Smax.romanov@nginx.com            body=body,
2021626Smax.romanov@nginx.com        )
2031626Smax.romanov@nginx.com
2041626Smax.romanov@nginx.com        assert resp['body'] == body, 'keep-alive 2'
2051626Smax.romanov@nginx.com
2061626Smax.romanov@nginx.com    def test_asgi_keepalive_reconfigure(self):
2071626Smax.romanov@nginx.com        skip_alert(
2081626Smax.romanov@nginx.com            r'pthread_mutex.+failed',
2091626Smax.romanov@nginx.com            r'failed to apply',
2101626Smax.romanov@nginx.com            r'process \d+ exited on signal',
2111626Smax.romanov@nginx.com        )
2121626Smax.romanov@nginx.com        self.load('mirror')
2131626Smax.romanov@nginx.com
2141626Smax.romanov@nginx.com        assert self.get()['status'] == 200, 'init'
2151626Smax.romanov@nginx.com
2161626Smax.romanov@nginx.com        body = '0123456789'
2171626Smax.romanov@nginx.com        conns = 3
2181626Smax.romanov@nginx.com        socks = []
2191626Smax.romanov@nginx.com
2201626Smax.romanov@nginx.com        for i in range(conns):
2211626Smax.romanov@nginx.com            (resp, sock) = self.post(
2221626Smax.romanov@nginx.com                headers={
2231626Smax.romanov@nginx.com                    'Host': 'localhost',
2241626Smax.romanov@nginx.com                    'Connection': 'keep-alive',
2251626Smax.romanov@nginx.com                    'Content-Type': 'text/html',
2261626Smax.romanov@nginx.com                },
2271626Smax.romanov@nginx.com                start=True,
2281626Smax.romanov@nginx.com                body=body,
2291626Smax.romanov@nginx.com                read_timeout=1,
2301626Smax.romanov@nginx.com            )
2311626Smax.romanov@nginx.com
2321626Smax.romanov@nginx.com            assert resp['body'] == body, 'keep-alive open'
2331626Smax.romanov@nginx.com            assert 'success' in self.conf(
2341626Smax.romanov@nginx.com                str(i + 1), 'applications/mirror/processes'
2351626Smax.romanov@nginx.com            ), 'reconfigure'
2361626Smax.romanov@nginx.com
2371626Smax.romanov@nginx.com            socks.append(sock)
2381626Smax.romanov@nginx.com
2391626Smax.romanov@nginx.com        for i in range(conns):
2401626Smax.romanov@nginx.com            (resp, sock) = self.post(
2411626Smax.romanov@nginx.com                headers={
2421626Smax.romanov@nginx.com                    'Host': 'localhost',
2431626Smax.romanov@nginx.com                    'Connection': 'keep-alive',
2441626Smax.romanov@nginx.com                    'Content-Type': 'text/html',
2451626Smax.romanov@nginx.com                },
2461626Smax.romanov@nginx.com                start=True,
2471626Smax.romanov@nginx.com                sock=socks[i],
2481626Smax.romanov@nginx.com                body=body,
2491626Smax.romanov@nginx.com                read_timeout=1,
2501626Smax.romanov@nginx.com            )
2511626Smax.romanov@nginx.com
2521626Smax.romanov@nginx.com            assert resp['body'] == body, 'keep-alive request'
2531626Smax.romanov@nginx.com            assert 'success' in self.conf(
2541626Smax.romanov@nginx.com                str(i + 1), 'applications/mirror/processes'
2551626Smax.romanov@nginx.com            ), 'reconfigure 2'
2561626Smax.romanov@nginx.com
2571626Smax.romanov@nginx.com        for i in range(conns):
2581626Smax.romanov@nginx.com            resp = self.post(
2591626Smax.romanov@nginx.com                headers={
2601626Smax.romanov@nginx.com                    'Host': 'localhost',
2611626Smax.romanov@nginx.com                    'Connection': 'close',
2621626Smax.romanov@nginx.com                    'Content-Type': 'text/html',
2631626Smax.romanov@nginx.com                },
2641626Smax.romanov@nginx.com                sock=socks[i],
2651626Smax.romanov@nginx.com                body=body,
2661626Smax.romanov@nginx.com            )
2671626Smax.romanov@nginx.com
2681626Smax.romanov@nginx.com            assert resp['body'] == body, 'keep-alive close'
2691626Smax.romanov@nginx.com            assert 'success' in self.conf(
2701626Smax.romanov@nginx.com                str(i + 1), 'applications/mirror/processes'
2711626Smax.romanov@nginx.com            ), 'reconfigure 3'
2721626Smax.romanov@nginx.com
2731626Smax.romanov@nginx.com    def test_asgi_keepalive_reconfigure_2(self):
2741626Smax.romanov@nginx.com        self.load('mirror')
2751626Smax.romanov@nginx.com
2761626Smax.romanov@nginx.com        assert self.get()['status'] == 200, 'init'
2771626Smax.romanov@nginx.com
2781626Smax.romanov@nginx.com        body = '0123456789'
2791626Smax.romanov@nginx.com
2801626Smax.romanov@nginx.com        (resp, sock) = self.post(
2811626Smax.romanov@nginx.com            headers={
2821626Smax.romanov@nginx.com                'Host': 'localhost',
2831626Smax.romanov@nginx.com                'Connection': 'keep-alive',
2841626Smax.romanov@nginx.com                'Content-Type': 'text/html',
2851626Smax.romanov@nginx.com            },
2861626Smax.romanov@nginx.com            start=True,
2871626Smax.romanov@nginx.com            body=body,
2881626Smax.romanov@nginx.com            read_timeout=1,
2891626Smax.romanov@nginx.com        )
2901626Smax.romanov@nginx.com
2911626Smax.romanov@nginx.com        assert resp['body'] == body, 'reconfigure 2 keep-alive 1'
2921626Smax.romanov@nginx.com
2931626Smax.romanov@nginx.com        self.load('empty')
2941626Smax.romanov@nginx.com
2951626Smax.romanov@nginx.com        assert self.get()['status'] == 200, 'init'
2961626Smax.romanov@nginx.com
2971626Smax.romanov@nginx.com        (resp, sock) = self.post(
2981626Smax.romanov@nginx.com            headers={
2991626Smax.romanov@nginx.com                'Host': 'localhost',
3001626Smax.romanov@nginx.com                'Connection': 'close',
3011626Smax.romanov@nginx.com                'Content-Type': 'text/html',
3021626Smax.romanov@nginx.com            },
3031626Smax.romanov@nginx.com            start=True,
3041626Smax.romanov@nginx.com            sock=sock,
3051626Smax.romanov@nginx.com            body=body,
3061626Smax.romanov@nginx.com        )
3071626Smax.romanov@nginx.com
3081626Smax.romanov@nginx.com        assert resp['status'] == 200, 'reconfigure 2 keep-alive 2'
3091626Smax.romanov@nginx.com        assert resp['body'] == '', 'reconfigure 2 keep-alive 2 body'
3101626Smax.romanov@nginx.com
3111626Smax.romanov@nginx.com        assert 'success' in self.conf(
3121626Smax.romanov@nginx.com            {"listeners": {}, "applications": {}}
3131626Smax.romanov@nginx.com        ), 'reconfigure 2 clear configuration'
3141626Smax.romanov@nginx.com
3151626Smax.romanov@nginx.com        resp = self.get(sock=sock)
3161626Smax.romanov@nginx.com
3171626Smax.romanov@nginx.com        assert resp == {}, 'reconfigure 2 keep-alive 3'
3181626Smax.romanov@nginx.com
3191626Smax.romanov@nginx.com    def test_asgi_keepalive_reconfigure_3(self):
3201626Smax.romanov@nginx.com        self.load('empty')
3211626Smax.romanov@nginx.com
3221626Smax.romanov@nginx.com        assert self.get()['status'] == 200, 'init'
3231626Smax.romanov@nginx.com
3241626Smax.romanov@nginx.com        (_, sock) = self.http(
3251626Smax.romanov@nginx.com            b"""GET / HTTP/1.1
3261626Smax.romanov@nginx.com""",
3271626Smax.romanov@nginx.com            start=True,
3281626Smax.romanov@nginx.com            raw=True,
3291626Smax.romanov@nginx.com            no_recv=True,
3301626Smax.romanov@nginx.com        )
3311626Smax.romanov@nginx.com
3321626Smax.romanov@nginx.com        assert self.get()['status'] == 200
3331626Smax.romanov@nginx.com
3341626Smax.romanov@nginx.com        assert 'success' in self.conf(
3351626Smax.romanov@nginx.com            {"listeners": {}, "applications": {}}
3361626Smax.romanov@nginx.com        ), 'reconfigure 3 clear configuration'
3371626Smax.romanov@nginx.com
3381626Smax.romanov@nginx.com        resp = self.http(
3391626Smax.romanov@nginx.com            b"""Host: localhost
3401626Smax.romanov@nginx.comConnection: close
3411626Smax.romanov@nginx.com
3421626Smax.romanov@nginx.com""",
3431626Smax.romanov@nginx.com            sock=sock,
3441626Smax.romanov@nginx.com            raw=True,
3451626Smax.romanov@nginx.com        )
3461626Smax.romanov@nginx.com
3471626Smax.romanov@nginx.com        assert resp['status'] == 200, 'reconfigure 3'
3481626Smax.romanov@nginx.com
3491626Smax.romanov@nginx.com    def test_asgi_process_switch(self):
3501626Smax.romanov@nginx.com        self.load('delayed')
3511626Smax.romanov@nginx.com
3521626Smax.romanov@nginx.com        assert 'success' in self.conf(
3531626Smax.romanov@nginx.com            '2', 'applications/delayed/processes'
3541626Smax.romanov@nginx.com        ), 'configure 2 processes'
3551626Smax.romanov@nginx.com
3561626Smax.romanov@nginx.com        self.get(
3571626Smax.romanov@nginx.com            headers={
3581626Smax.romanov@nginx.com                'Host': 'localhost',
3591626Smax.romanov@nginx.com                'Content-Length': '0',
3601626Smax.romanov@nginx.com                'X-Delay': '5',
3611626Smax.romanov@nginx.com                'Connection': 'close',
3621626Smax.romanov@nginx.com            },
3631626Smax.romanov@nginx.com            no_recv=True,
3641626Smax.romanov@nginx.com        )
3651626Smax.romanov@nginx.com
3661626Smax.romanov@nginx.com        headers_delay_1 = {
3671626Smax.romanov@nginx.com            'Connection': 'close',
3681626Smax.romanov@nginx.com            'Host': 'localhost',
3691626Smax.romanov@nginx.com            'Content-Length': '0',
3701626Smax.romanov@nginx.com            'X-Delay': '1',
3711626Smax.romanov@nginx.com        }
3721626Smax.romanov@nginx.com
3731626Smax.romanov@nginx.com        self.get(headers=headers_delay_1, no_recv=True)
3741626Smax.romanov@nginx.com
3751626Smax.romanov@nginx.com        time.sleep(0.5)
3761626Smax.romanov@nginx.com
3771626Smax.romanov@nginx.com        for _ in range(10):
3781626Smax.romanov@nginx.com            self.get(headers=headers_delay_1, no_recv=True)
3791626Smax.romanov@nginx.com
3801626Smax.romanov@nginx.com        self.get(headers=headers_delay_1)
3811626Smax.romanov@nginx.com
3821635Szelenkov@nginx.com    def test_asgi_application_loading_error(self):
3831626Smax.romanov@nginx.com        skip_alert(r'Python failed to import module "blah"')
3841626Smax.romanov@nginx.com
3851626Smax.romanov@nginx.com        self.load('empty')
3861626Smax.romanov@nginx.com
3871626Smax.romanov@nginx.com        assert 'success' in self.conf('"blah"', 'applications/empty/module')
3881626Smax.romanov@nginx.com
3891626Smax.romanov@nginx.com        assert self.get()['status'] == 503, 'loading error'
3901626Smax.romanov@nginx.com
3911635Szelenkov@nginx.com    def test_asgi_application_threading(self):
3921626Smax.romanov@nginx.com        """wait_for_record() timeouts after 5s while every thread works at
3931626Smax.romanov@nginx.com        least 3s.  So without releasing GIL test should fail.
3941626Smax.romanov@nginx.com        """
3951626Smax.romanov@nginx.com
3961626Smax.romanov@nginx.com        self.load('threading')
3971626Smax.romanov@nginx.com
3981626Smax.romanov@nginx.com        for _ in range(10):
3991626Smax.romanov@nginx.com            self.get(no_recv=True)
4001626Smax.romanov@nginx.com
4011626Smax.romanov@nginx.com        assert (
4021626Smax.romanov@nginx.com            self.wait_for_record(r'\(5\) Thread: 100') is not None
4031626Smax.romanov@nginx.com        ), 'last thread finished'
404*1683Smax.romanov@nginx.com
405*1683Smax.romanov@nginx.com    def test_asgi_application_threads(self):
406*1683Smax.romanov@nginx.com        self.load('threads')
407*1683Smax.romanov@nginx.com
408*1683Smax.romanov@nginx.com        assert 'success' in self.conf(
409*1683Smax.romanov@nginx.com            '4', 'applications/threads/threads'
410*1683Smax.romanov@nginx.com        ), 'configure 4 threads'
411*1683Smax.romanov@nginx.com
412*1683Smax.romanov@nginx.com        socks = []
413*1683Smax.romanov@nginx.com
414*1683Smax.romanov@nginx.com        for i in range(4):
415*1683Smax.romanov@nginx.com            (_, sock) = self.get(
416*1683Smax.romanov@nginx.com                headers={
417*1683Smax.romanov@nginx.com                    'Host': 'localhost',
418*1683Smax.romanov@nginx.com                    'X-Delay': '2',
419*1683Smax.romanov@nginx.com                    'Connection': 'close',
420*1683Smax.romanov@nginx.com                },
421*1683Smax.romanov@nginx.com                no_recv=True,
422*1683Smax.romanov@nginx.com                start=True,
423*1683Smax.romanov@nginx.com            )
424*1683Smax.romanov@nginx.com
425*1683Smax.romanov@nginx.com            socks.append(sock)
426*1683Smax.romanov@nginx.com
427*1683Smax.romanov@nginx.com            time.sleep(0.25) # required to avoid greedy request reading
428*1683Smax.romanov@nginx.com
429*1683Smax.romanov@nginx.com        threads = set()
430*1683Smax.romanov@nginx.com
431*1683Smax.romanov@nginx.com        for sock in socks:
432*1683Smax.romanov@nginx.com            resp = self.recvall(sock).decode('utf-8')
433*1683Smax.romanov@nginx.com
434*1683Smax.romanov@nginx.com            self.log_in(resp)
435*1683Smax.romanov@nginx.com
436*1683Smax.romanov@nginx.com            resp = self._resp_to_dict(resp)
437*1683Smax.romanov@nginx.com
438*1683Smax.romanov@nginx.com            assert resp['status'] == 200, 'status'
439*1683Smax.romanov@nginx.com
440*1683Smax.romanov@nginx.com            threads.add(resp['headers']['x-thread'])
441*1683Smax.romanov@nginx.com
442*1683Smax.romanov@nginx.com            sock.close()
443*1683Smax.romanov@nginx.com
444*1683Smax.romanov@nginx.com        assert len(socks) == len(threads), 'threads differs'
445