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' 4041683Smax.romanov@nginx.com 4051683Smax.romanov@nginx.com def test_asgi_application_threads(self): 4061683Smax.romanov@nginx.com self.load('threads') 4071683Smax.romanov@nginx.com 4081683Smax.romanov@nginx.com assert 'success' in self.conf( 409*1692Smax.romanov@nginx.com '2', 'applications/threads/threads' 410*1692Smax.romanov@nginx.com ), 'configure 2 threads' 4111683Smax.romanov@nginx.com 4121683Smax.romanov@nginx.com socks = [] 4131683Smax.romanov@nginx.com 414*1692Smax.romanov@nginx.com for i in range(2): 4151683Smax.romanov@nginx.com (_, sock) = self.get( 4161683Smax.romanov@nginx.com headers={ 4171683Smax.romanov@nginx.com 'Host': 'localhost', 418*1692Smax.romanov@nginx.com 'X-Delay': '3', 4191683Smax.romanov@nginx.com 'Connection': 'close', 4201683Smax.romanov@nginx.com }, 4211683Smax.romanov@nginx.com no_recv=True, 4221683Smax.romanov@nginx.com start=True, 4231683Smax.romanov@nginx.com ) 4241683Smax.romanov@nginx.com 4251683Smax.romanov@nginx.com socks.append(sock) 4261683Smax.romanov@nginx.com 427*1692Smax.romanov@nginx.com time.sleep(1.0) # required to avoid greedy request reading 4281683Smax.romanov@nginx.com 4291683Smax.romanov@nginx.com threads = set() 4301683Smax.romanov@nginx.com 4311683Smax.romanov@nginx.com for sock in socks: 4321683Smax.romanov@nginx.com resp = self.recvall(sock).decode('utf-8') 4331683Smax.romanov@nginx.com 4341683Smax.romanov@nginx.com self.log_in(resp) 4351683Smax.romanov@nginx.com 4361683Smax.romanov@nginx.com resp = self._resp_to_dict(resp) 4371683Smax.romanov@nginx.com 4381683Smax.romanov@nginx.com assert resp['status'] == 200, 'status' 4391683Smax.romanov@nginx.com 4401683Smax.romanov@nginx.com threads.add(resp['headers']['x-thread']) 4411683Smax.romanov@nginx.com 4421683Smax.romanov@nginx.com sock.close() 4431683Smax.romanov@nginx.com 4441683Smax.romanov@nginx.com assert len(socks) == len(threads), 'threads differs' 445