11304St.nateldemoura@f5.comimport grp 21304St.nateldemoura@f5.comimport pwd 31477Szelenkov@nginx.comimport re 4899Szelenkov@nginx.comimport time 51477Szelenkov@nginx.com 61635Szelenkov@nginx.comimport pytest 71635Szelenkov@nginx.com 81654Szelenkov@nginx.comfrom conftest import option 91635Szelenkov@nginx.comfrom conftest import skip_alert 101654Szelenkov@nginx.comfrom conftest import unit_stop 111019Szelenkov@nginx.comfrom unit.applications.lang.python import TestApplicationPython 12484Szelenkov@nginx.com 131017Szelenkov@nginx.com 141019Szelenkov@nginx.comclass TestPythonApplication(TestApplicationPython): 151467Szelenkov@nginx.com prerequisites = {'modules': {'python': 'all'}} 16484Szelenkov@nginx.com 171283Szelenkov@nginx.com def findall(self, pattern): 181654Szelenkov@nginx.com with open(option.temp_dir + '/unit.log', 'r', errors='ignore') as f: 191283Szelenkov@nginx.com return re.findall(pattern, f.read()) 201283Szelenkov@nginx.com 21552Szelenkov@nginx.com def test_python_application_variables(self): 22552Szelenkov@nginx.com self.load('variables') 23484Szelenkov@nginx.com 24484Szelenkov@nginx.com body = 'Test body string.' 25484Szelenkov@nginx.com 261605Smax.romanov@nginx.com resp = self.http( 271605Smax.romanov@nginx.com b"""POST / HTTP/1.1 281605Smax.romanov@nginx.comHost: localhost 291605Smax.romanov@nginx.comContent-Length: %d 301605Smax.romanov@nginx.comCustom-Header: blah 311605Smax.romanov@nginx.comCustom-hEader: Blah 321605Smax.romanov@nginx.comContent-Type: text/html 331605Smax.romanov@nginx.comConnection: close 341605Smax.romanov@nginx.comcustom-header: BLAH 351605Smax.romanov@nginx.com 361605Smax.romanov@nginx.com%s""" % (len(body), body.encode()), 371605Smax.romanov@nginx.com raw=True, 381017Szelenkov@nginx.com ) 39484Szelenkov@nginx.com 401596Szelenkov@nginx.com assert resp['status'] == 200, 'status' 41505Szelenkov@nginx.com headers = resp['headers'] 42674Szelenkov@nginx.com header_server = headers.pop('Server') 431596Szelenkov@nginx.com assert re.search(r'Unit/[\d\.]+', header_server), 'server header' 441596Szelenkov@nginx.com assert ( 451596Szelenkov@nginx.com headers.pop('Server-Software') == header_server 461596Szelenkov@nginx.com ), 'server software header' 47599Szelenkov@nginx.com 48599Szelenkov@nginx.com date = headers.pop('Date') 491596Szelenkov@nginx.com assert date[-4:] == ' GMT', 'date header timezone' 501596Szelenkov@nginx.com assert ( 511596Szelenkov@nginx.com abs(self.date_to_sec_epoch(date) - self.sec_epoch()) < 5 521596Szelenkov@nginx.com ), 'date header' 53599Szelenkov@nginx.com 541596Szelenkov@nginx.com assert headers == { 551596Szelenkov@nginx.com 'Connection': 'close', 561596Szelenkov@nginx.com 'Content-Length': str(len(body)), 571596Szelenkov@nginx.com 'Content-Type': 'text/html', 581596Szelenkov@nginx.com 'Request-Method': 'POST', 591596Szelenkov@nginx.com 'Request-Uri': '/', 601596Szelenkov@nginx.com 'Http-Host': 'localhost', 611596Szelenkov@nginx.com 'Server-Protocol': 'HTTP/1.1', 621605Smax.romanov@nginx.com 'Custom-Header': 'blah, Blah, BLAH', 631596Szelenkov@nginx.com 'Wsgi-Version': '(1, 0)', 641596Szelenkov@nginx.com 'Wsgi-Url-Scheme': 'http', 651596Szelenkov@nginx.com 'Wsgi-Multithread': 'False', 661596Szelenkov@nginx.com 'Wsgi-Multiprocess': 'True', 671596Szelenkov@nginx.com 'Wsgi-Run-Once': 'False', 681596Szelenkov@nginx.com }, 'headers' 691596Szelenkov@nginx.com assert resp['body'] == body, 'body' 70484Szelenkov@nginx.com 71497Szelenkov@nginx.com def test_python_application_query_string(self): 72552Szelenkov@nginx.com self.load('query_string') 73497Szelenkov@nginx.com 74505Szelenkov@nginx.com resp = self.get(url='/?var1=val1&var2=val2') 75497Szelenkov@nginx.com 761596Szelenkov@nginx.com assert ( 771596Szelenkov@nginx.com resp['headers']['Query-String'] == 'var1=val1&var2=val2' 781596Szelenkov@nginx.com ), 'Query-String header' 79497Szelenkov@nginx.com 801171Svbart@nginx.com def test_python_application_query_string_space(self): 811171Svbart@nginx.com self.load('query_string') 821171Svbart@nginx.com 831171Svbart@nginx.com resp = self.get(url='/ ?var1=val1&var2=val2') 841596Szelenkov@nginx.com assert ( 851596Szelenkov@nginx.com resp['headers']['Query-String'] == 'var1=val1&var2=val2' 861596Szelenkov@nginx.com ), 'Query-String space' 871171Svbart@nginx.com 881171Svbart@nginx.com resp = self.get(url='/ %20?var1=val1&var2=val2') 891596Szelenkov@nginx.com assert ( 901596Szelenkov@nginx.com resp['headers']['Query-String'] == 'var1=val1&var2=val2' 911596Szelenkov@nginx.com ), 'Query-String space 2' 921171Svbart@nginx.com 931171Svbart@nginx.com resp = self.get(url='/ %20 ?var1=val1&var2=val2') 941596Szelenkov@nginx.com assert ( 951596Szelenkov@nginx.com resp['headers']['Query-String'] == 'var1=val1&var2=val2' 961596Szelenkov@nginx.com ), 'Query-String space 3' 971171Svbart@nginx.com 981171Svbart@nginx.com resp = self.get(url='/blah %20 blah? var1= val1 & var2=val2') 991596Szelenkov@nginx.com assert ( 1001596Szelenkov@nginx.com resp['headers']['Query-String'] == ' var1= val1 & var2=val2' 1011596Szelenkov@nginx.com ), 'Query-String space 4' 1021171Svbart@nginx.com 103894Szelenkov@nginx.com def test_python_application_query_string_empty(self): 104894Szelenkov@nginx.com self.load('query_string') 105894Szelenkov@nginx.com 106894Szelenkov@nginx.com resp = self.get(url='/?') 107894Szelenkov@nginx.com 1081596Szelenkov@nginx.com assert resp['status'] == 200, 'query string empty status' 1091596Szelenkov@nginx.com assert resp['headers']['Query-String'] == '', 'query string empty' 110894Szelenkov@nginx.com 111894Szelenkov@nginx.com def test_python_application_query_string_absent(self): 112894Szelenkov@nginx.com self.load('query_string') 113894Szelenkov@nginx.com 114894Szelenkov@nginx.com resp = self.get() 115894Szelenkov@nginx.com 1161596Szelenkov@nginx.com assert resp['status'] == 200, 'query string absent status' 1171596Szelenkov@nginx.com assert resp['headers']['Query-String'] == '', 'query string absent' 118894Szelenkov@nginx.com 1191596Szelenkov@nginx.com @pytest.mark.skip('not yet') 120495Szelenkov@nginx.com def test_python_application_server_port(self): 121552Szelenkov@nginx.com self.load('server_port') 122495Szelenkov@nginx.com 1231596Szelenkov@nginx.com assert ( 1241596Szelenkov@nginx.com self.get()['headers']['Server-Port'] == '7080' 1251596Szelenkov@nginx.com ), 'Server-Port header' 126484Szelenkov@nginx.com 1271596Szelenkov@nginx.com @pytest.mark.skip('not yet') 1281250Szelenkov@nginx.com def test_python_application_working_directory_invalid(self): 1291250Szelenkov@nginx.com self.load('empty') 1301250Szelenkov@nginx.com 1311596Szelenkov@nginx.com assert 'success' in self.conf( 1321596Szelenkov@nginx.com '"/blah"', 'applications/empty/working_directory' 1331596Szelenkov@nginx.com ), 'configure invalid working_directory' 1341250Szelenkov@nginx.com 1351596Szelenkov@nginx.com assert self.get()['status'] == 500, 'status' 1361250Szelenkov@nginx.com 137496Szelenkov@nginx.com def test_python_application_204_transfer_encoding(self): 138552Szelenkov@nginx.com self.load('204_no_content') 139496Szelenkov@nginx.com 1401596Szelenkov@nginx.com assert ( 1411596Szelenkov@nginx.com 'Transfer-Encoding' not in self.get()['headers'] 1421596Szelenkov@nginx.com ), '204 header transfer encoding' 143484Szelenkov@nginx.com 144602Szelenkov@nginx.com def test_python_application_ctx_iter_atexit(self): 145602Szelenkov@nginx.com self.load('ctx_iter_atexit') 146602Szelenkov@nginx.com 1471017Szelenkov@nginx.com resp = self.post( 1481017Szelenkov@nginx.com headers={ 1491017Szelenkov@nginx.com 'Host': 'localhost', 1501017Szelenkov@nginx.com 'Connection': 'close', 1511017Szelenkov@nginx.com 'Content-Type': 'text/html', 1521017Szelenkov@nginx.com }, 1531017Szelenkov@nginx.com body='0123456789', 1541017Szelenkov@nginx.com ) 155602Szelenkov@nginx.com 1561596Szelenkov@nginx.com assert resp['status'] == 200, 'ctx iter status' 1571596Szelenkov@nginx.com assert resp['body'] == '0123456789', 'ctx iter body' 158602Szelenkov@nginx.com 1591017Szelenkov@nginx.com self.conf({"listeners": {}, "applications": {}}) 160602Szelenkov@nginx.com 1611654Szelenkov@nginx.com unit_stop() 162602Szelenkov@nginx.com 1631596Szelenkov@nginx.com assert ( 1641596Szelenkov@nginx.com self.wait_for_record(r'RuntimeError') is not None 1651596Szelenkov@nginx.com ), 'ctx iter atexit' 166602Szelenkov@nginx.com 167603Szelenkov@nginx.com def test_python_keepalive_body(self): 168603Szelenkov@nginx.com self.load('mirror') 169603Szelenkov@nginx.com 1701596Szelenkov@nginx.com assert self.get()['status'] == 200, 'init' 1711029Szelenkov@nginx.com 1721453Szelenkov@nginx.com body = '0123456789' * 500 1731017Szelenkov@nginx.com (resp, sock) = self.post( 1741017Szelenkov@nginx.com headers={ 1751017Szelenkov@nginx.com 'Host': 'localhost', 1761017Szelenkov@nginx.com 'Connection': 'keep-alive', 1771017Szelenkov@nginx.com 'Content-Type': 'text/html', 1781017Szelenkov@nginx.com }, 1791017Szelenkov@nginx.com start=True, 1801453Szelenkov@nginx.com body=body, 1811029Szelenkov@nginx.com read_timeout=1, 1821017Szelenkov@nginx.com ) 183603Szelenkov@nginx.com 1841596Szelenkov@nginx.com assert resp['body'] == body, 'keep-alive 1' 185603Szelenkov@nginx.com 1861453Szelenkov@nginx.com body = '0123456789' 1871017Szelenkov@nginx.com resp = self.post( 1881017Szelenkov@nginx.com headers={ 1891017Szelenkov@nginx.com 'Host': 'localhost', 1901017Szelenkov@nginx.com 'Connection': 'close', 1911017Szelenkov@nginx.com 'Content-Type': 'text/html', 1921017Szelenkov@nginx.com }, 1931017Szelenkov@nginx.com sock=sock, 1941453Szelenkov@nginx.com body=body, 1951017Szelenkov@nginx.com ) 196603Szelenkov@nginx.com 1971596Szelenkov@nginx.com assert resp['body'] == body, 'keep-alive 2' 198603Szelenkov@nginx.com 199684Szelenkov@nginx.com def test_python_keepalive_reconfigure(self): 200684Szelenkov@nginx.com self.load('mirror') 201684Szelenkov@nginx.com 2021596Szelenkov@nginx.com assert self.get()['status'] == 200, 'init' 2031029Szelenkov@nginx.com 204684Szelenkov@nginx.com body = '0123456789' 205684Szelenkov@nginx.com conns = 3 206684Szelenkov@nginx.com socks = [] 207684Szelenkov@nginx.com 208684Szelenkov@nginx.com for i in range(conns): 2091017Szelenkov@nginx.com (resp, sock) = self.post( 2101017Szelenkov@nginx.com headers={ 2111017Szelenkov@nginx.com 'Host': 'localhost', 2121017Szelenkov@nginx.com 'Connection': 'keep-alive', 2131017Szelenkov@nginx.com 'Content-Type': 'text/html', 2141017Szelenkov@nginx.com }, 2151017Szelenkov@nginx.com start=True, 2161017Szelenkov@nginx.com body=body, 2171029Szelenkov@nginx.com read_timeout=1, 2181017Szelenkov@nginx.com ) 219684Szelenkov@nginx.com 2201596Szelenkov@nginx.com assert resp['body'] == body, 'keep-alive open' 221*1695Smax.romanov@nginx.com 222*1695Smax.romanov@nginx.com self.load('mirror', processes=i + 1) 223684Szelenkov@nginx.com 224684Szelenkov@nginx.com socks.append(sock) 225684Szelenkov@nginx.com 226684Szelenkov@nginx.com for i in range(conns): 2271017Szelenkov@nginx.com (resp, sock) = self.post( 2281017Szelenkov@nginx.com headers={ 2291017Szelenkov@nginx.com 'Host': 'localhost', 2301017Szelenkov@nginx.com 'Connection': 'keep-alive', 2311017Szelenkov@nginx.com 'Content-Type': 'text/html', 2321017Szelenkov@nginx.com }, 2331017Szelenkov@nginx.com start=True, 2341017Szelenkov@nginx.com sock=socks[i], 2351017Szelenkov@nginx.com body=body, 2361029Szelenkov@nginx.com read_timeout=1, 2371017Szelenkov@nginx.com ) 238684Szelenkov@nginx.com 2391596Szelenkov@nginx.com assert resp['body'] == body, 'keep-alive request' 240*1695Smax.romanov@nginx.com 241*1695Smax.romanov@nginx.com self.load('mirror', processes=i + 1) 242684Szelenkov@nginx.com 243684Szelenkov@nginx.com for i in range(conns): 2441017Szelenkov@nginx.com resp = self.post( 2451017Szelenkov@nginx.com headers={ 2461017Szelenkov@nginx.com 'Host': 'localhost', 2471017Szelenkov@nginx.com 'Connection': 'close', 2481017Szelenkov@nginx.com 'Content-Type': 'text/html', 2491017Szelenkov@nginx.com }, 2501017Szelenkov@nginx.com sock=socks[i], 2511017Szelenkov@nginx.com body=body, 2521017Szelenkov@nginx.com ) 253684Szelenkov@nginx.com 2541596Szelenkov@nginx.com assert resp['body'] == body, 'keep-alive close' 255*1695Smax.romanov@nginx.com 256*1695Smax.romanov@nginx.com self.load('mirror', processes=i + 1) 257684Szelenkov@nginx.com 258750Szelenkov@nginx.com def test_python_keepalive_reconfigure_2(self): 259750Szelenkov@nginx.com self.load('mirror') 260750Szelenkov@nginx.com 2611596Szelenkov@nginx.com assert self.get()['status'] == 200, 'init' 2621029Szelenkov@nginx.com 263750Szelenkov@nginx.com body = '0123456789' 264750Szelenkov@nginx.com 2651017Szelenkov@nginx.com (resp, sock) = self.post( 2661017Szelenkov@nginx.com headers={ 2671017Szelenkov@nginx.com 'Host': 'localhost', 2681017Szelenkov@nginx.com 'Connection': 'keep-alive', 2691017Szelenkov@nginx.com 'Content-Type': 'text/html', 2701017Szelenkov@nginx.com }, 2711017Szelenkov@nginx.com start=True, 2721017Szelenkov@nginx.com body=body, 2731029Szelenkov@nginx.com read_timeout=1, 2741017Szelenkov@nginx.com ) 275750Szelenkov@nginx.com 2761596Szelenkov@nginx.com assert resp['body'] == body, 'reconfigure 2 keep-alive 1' 277750Szelenkov@nginx.com 278750Szelenkov@nginx.com self.load('empty') 279750Szelenkov@nginx.com 2801596Szelenkov@nginx.com assert self.get()['status'] == 200, 'init' 2811029Szelenkov@nginx.com 2821017Szelenkov@nginx.com (resp, sock) = self.post( 2831017Szelenkov@nginx.com headers={ 2841017Szelenkov@nginx.com 'Host': 'localhost', 2851017Szelenkov@nginx.com 'Connection': 'close', 2861017Szelenkov@nginx.com 'Content-Type': 'text/html', 2871017Szelenkov@nginx.com }, 2881017Szelenkov@nginx.com start=True, 2891017Szelenkov@nginx.com sock=sock, 2901017Szelenkov@nginx.com body=body, 2911017Szelenkov@nginx.com ) 292750Szelenkov@nginx.com 2931596Szelenkov@nginx.com assert resp['status'] == 200, 'reconfigure 2 keep-alive 2' 2941596Szelenkov@nginx.com assert resp['body'] == '', 'reconfigure 2 keep-alive 2 body' 295750Szelenkov@nginx.com 2961596Szelenkov@nginx.com assert 'success' in self.conf( 2971596Szelenkov@nginx.com {"listeners": {}, "applications": {}} 2981596Szelenkov@nginx.com ), 'reconfigure 2 clear configuration' 299750Szelenkov@nginx.com 300750Szelenkov@nginx.com resp = self.get(sock=sock) 301750Szelenkov@nginx.com 3021596Szelenkov@nginx.com assert resp == {}, 'reconfigure 2 keep-alive 3' 303750Szelenkov@nginx.com 304750Szelenkov@nginx.com def test_python_keepalive_reconfigure_3(self): 305750Szelenkov@nginx.com self.load('empty') 306750Szelenkov@nginx.com 3071596Szelenkov@nginx.com assert self.get()['status'] == 200, 'init' 3081029Szelenkov@nginx.com 3091453Szelenkov@nginx.com (_, sock) = self.http( 3101017Szelenkov@nginx.com b"""GET / HTTP/1.1 3111017Szelenkov@nginx.com""", 3121017Szelenkov@nginx.com start=True, 3131017Szelenkov@nginx.com raw=True, 3141453Szelenkov@nginx.com no_recv=True, 3151017Szelenkov@nginx.com ) 316750Szelenkov@nginx.com 3171596Szelenkov@nginx.com assert self.get()['status'] == 200 3181453Szelenkov@nginx.com 3191596Szelenkov@nginx.com assert 'success' in self.conf( 3201596Szelenkov@nginx.com {"listeners": {}, "applications": {}} 3211596Szelenkov@nginx.com ), 'reconfigure 3 clear configuration' 322750Szelenkov@nginx.com 3231017Szelenkov@nginx.com resp = self.http( 3241017Szelenkov@nginx.com b"""Host: localhost 325750Szelenkov@nginx.comConnection: close 326750Szelenkov@nginx.com 3271017Szelenkov@nginx.com""", 3281017Szelenkov@nginx.com sock=sock, 3291017Szelenkov@nginx.com raw=True, 3301017Szelenkov@nginx.com ) 331750Szelenkov@nginx.com 3321596Szelenkov@nginx.com assert resp['status'] == 200, 'reconfigure 3' 333750Szelenkov@nginx.com 334603Szelenkov@nginx.com def test_python_atexit(self): 335603Szelenkov@nginx.com self.load('atexit') 336603Szelenkov@nginx.com 337603Szelenkov@nginx.com self.get() 338603Szelenkov@nginx.com 3391017Szelenkov@nginx.com self.conf({"listeners": {}, "applications": {}}) 340603Szelenkov@nginx.com 3411654Szelenkov@nginx.com unit_stop() 342603Szelenkov@nginx.com 3431596Szelenkov@nginx.com assert self.wait_for_record(r'At exit called\.') is not None, 'atexit' 344603Szelenkov@nginx.com 3451454Szelenkov@nginx.com def test_python_process_switch(self): 346*1695Smax.romanov@nginx.com self.load('delayed', processes=2) 3471454Szelenkov@nginx.com 3481596Szelenkov@nginx.com self.get( 3491596Szelenkov@nginx.com headers={ 3501596Szelenkov@nginx.com 'Host': 'localhost', 3511596Szelenkov@nginx.com 'Content-Length': '0', 3521596Szelenkov@nginx.com 'X-Delay': '5', 3531596Szelenkov@nginx.com 'Connection': 'close', 3541596Szelenkov@nginx.com }, 3551596Szelenkov@nginx.com no_recv=True, 3561596Szelenkov@nginx.com ) 3571454Szelenkov@nginx.com 3581454Szelenkov@nginx.com headers_delay_1 = { 3591454Szelenkov@nginx.com 'Connection': 'close', 3601454Szelenkov@nginx.com 'Host': 'localhost', 3611454Szelenkov@nginx.com 'Content-Length': '0', 3621454Szelenkov@nginx.com 'X-Delay': '1', 3631454Szelenkov@nginx.com } 3641454Szelenkov@nginx.com 3651454Szelenkov@nginx.com self.get(headers=headers_delay_1, no_recv=True) 3661454Szelenkov@nginx.com 3671454Szelenkov@nginx.com time.sleep(0.5) 3681454Szelenkov@nginx.com 3691454Szelenkov@nginx.com for _ in range(10): 3701454Szelenkov@nginx.com self.get(headers=headers_delay_1, no_recv=True) 3711454Szelenkov@nginx.com 3721454Szelenkov@nginx.com self.get(headers=headers_delay_1) 3731454Szelenkov@nginx.com 3741596Szelenkov@nginx.com @pytest.mark.skip('not yet') 375603Szelenkov@nginx.com def test_python_application_start_response_exit(self): 376603Szelenkov@nginx.com self.load('start_response_exit') 377603Szelenkov@nginx.com 3781596Szelenkov@nginx.com assert self.get()['status'] == 500, 'start response exit' 379603Szelenkov@nginx.com 380603Szelenkov@nginx.com def test_python_application_input_iter(self): 381603Szelenkov@nginx.com self.load('input_iter') 382603Szelenkov@nginx.com 3831400Smax.romanov@nginx.com body = '''0123456789 3841400Smax.romanov@nginx.comnext line 3851400Smax.romanov@nginx.com 3861400Smax.romanov@nginx.comlast line''' 3871400Smax.romanov@nginx.com 3881400Smax.romanov@nginx.com resp = self.post(body=body) 3891596Szelenkov@nginx.com assert resp['body'] == body, 'input iter' 3901596Szelenkov@nginx.com assert resp['headers']['X-Lines-Count'] == '4', 'input iter lines' 3911400Smax.romanov@nginx.com 3921400Smax.romanov@nginx.com def test_python_application_input_readline(self): 3931400Smax.romanov@nginx.com self.load('input_readline') 3941400Smax.romanov@nginx.com 3951400Smax.romanov@nginx.com body = '''0123456789 3961400Smax.romanov@nginx.comnext line 3971400Smax.romanov@nginx.com 3981400Smax.romanov@nginx.comlast line''' 3991400Smax.romanov@nginx.com 4001400Smax.romanov@nginx.com resp = self.post(body=body) 4011596Szelenkov@nginx.com assert resp['body'] == body, 'input readline' 4021596Szelenkov@nginx.com assert resp['headers']['X-Lines-Count'] == '4', 'input readline lines' 4031400Smax.romanov@nginx.com 4041400Smax.romanov@nginx.com def test_python_application_input_readline_size(self): 4051400Smax.romanov@nginx.com self.load('input_readline_size') 4061400Smax.romanov@nginx.com 4071400Smax.romanov@nginx.com body = '''0123456789 4081400Smax.romanov@nginx.comnext line 4091400Smax.romanov@nginx.com 4101400Smax.romanov@nginx.comlast line''' 411603Szelenkov@nginx.com 4121596Szelenkov@nginx.com assert self.post(body=body)['body'] == body, 'input readline size' 4131596Szelenkov@nginx.com assert ( 4141596Szelenkov@nginx.com self.post(body='0123')['body'] == '0123' 4151596Szelenkov@nginx.com ), 'input readline size less' 4161400Smax.romanov@nginx.com 4171400Smax.romanov@nginx.com def test_python_application_input_readlines(self): 4181400Smax.romanov@nginx.com self.load('input_readlines') 4191400Smax.romanov@nginx.com 4201400Smax.romanov@nginx.com body = '''0123456789 4211400Smax.romanov@nginx.comnext line 4221400Smax.romanov@nginx.com 4231400Smax.romanov@nginx.comlast line''' 4241400Smax.romanov@nginx.com 4251400Smax.romanov@nginx.com resp = self.post(body=body) 4261596Szelenkov@nginx.com assert resp['body'] == body, 'input readlines' 4271596Szelenkov@nginx.com assert resp['headers']['X-Lines-Count'] == '4', 'input readlines lines' 4281400Smax.romanov@nginx.com 4291400Smax.romanov@nginx.com def test_python_application_input_readlines_huge(self): 4301400Smax.romanov@nginx.com self.load('input_readlines') 4311400Smax.romanov@nginx.com 4321400Smax.romanov@nginx.com body = ( 4331400Smax.romanov@nginx.com '''0123456789 abcdefghi 4341400Smax.romanov@nginx.comnext line: 0123456789 abcdefghi 4351400Smax.romanov@nginx.com 4361400Smax.romanov@nginx.comlast line: 987654321 4371400Smax.romanov@nginx.com''' 4381400Smax.romanov@nginx.com * 512 4391400Smax.romanov@nginx.com ) 4401400Smax.romanov@nginx.com 4411596Szelenkov@nginx.com assert ( 4421596Szelenkov@nginx.com self.post(body=body, read_buffer_size=16384)['body'] == body 4431596Szelenkov@nginx.com ), 'input readlines huge' 444603Szelenkov@nginx.com 445603Szelenkov@nginx.com def test_python_application_input_read_length(self): 446603Szelenkov@nginx.com self.load('input_read_length') 447603Szelenkov@nginx.com 448603Szelenkov@nginx.com body = '0123456789' 449603Szelenkov@nginx.com 4501017Szelenkov@nginx.com resp = self.post( 4511017Szelenkov@nginx.com headers={ 4521017Szelenkov@nginx.com 'Host': 'localhost', 4531017Szelenkov@nginx.com 'Input-Length': '5', 4541017Szelenkov@nginx.com 'Connection': 'close', 4551017Szelenkov@nginx.com }, 4561017Szelenkov@nginx.com body=body, 4571017Szelenkov@nginx.com ) 458603Szelenkov@nginx.com 4591596Szelenkov@nginx.com assert resp['body'] == body[:5], 'input read length lt body' 460603Szelenkov@nginx.com 4611017Szelenkov@nginx.com resp = self.post( 4621017Szelenkov@nginx.com headers={ 4631017Szelenkov@nginx.com 'Host': 'localhost', 4641017Szelenkov@nginx.com 'Input-Length': '15', 4651017Szelenkov@nginx.com 'Connection': 'close', 4661017Szelenkov@nginx.com }, 4671017Szelenkov@nginx.com body=body, 4681017Szelenkov@nginx.com ) 469603Szelenkov@nginx.com 4701596Szelenkov@nginx.com assert resp['body'] == body, 'input read length gt body' 471603Szelenkov@nginx.com 4721017Szelenkov@nginx.com resp = self.post( 4731017Szelenkov@nginx.com headers={ 4741017Szelenkov@nginx.com 'Host': 'localhost', 4751017Szelenkov@nginx.com 'Input-Length': '0', 4761017Szelenkov@nginx.com 'Connection': 'close', 4771017Szelenkov@nginx.com }, 4781017Szelenkov@nginx.com body=body, 4791017Szelenkov@nginx.com ) 480603Szelenkov@nginx.com 4811596Szelenkov@nginx.com assert resp['body'] == '', 'input read length zero' 482603Szelenkov@nginx.com 4831017Szelenkov@nginx.com resp = self.post( 4841017Szelenkov@nginx.com headers={ 4851017Szelenkov@nginx.com 'Host': 'localhost', 4861017Szelenkov@nginx.com 'Input-Length': '-1', 4871017Szelenkov@nginx.com 'Connection': 'close', 4881017Szelenkov@nginx.com }, 4891017Szelenkov@nginx.com body=body, 4901017Szelenkov@nginx.com ) 491603Szelenkov@nginx.com 4921596Szelenkov@nginx.com assert resp['body'] == body, 'input read length negative' 493603Szelenkov@nginx.com 4941596Szelenkov@nginx.com @pytest.mark.skip('not yet') 495603Szelenkov@nginx.com def test_python_application_errors_write(self): 496603Szelenkov@nginx.com self.load('errors_write') 497603Szelenkov@nginx.com 498603Szelenkov@nginx.com self.get() 499603Szelenkov@nginx.com 5001654Szelenkov@nginx.com unit_stop() 501603Szelenkov@nginx.com 5021596Szelenkov@nginx.com assert ( 5031596Szelenkov@nginx.com self.wait_for_record(r'\[error\].+Error in application\.') 5041596Szelenkov@nginx.com is not None 5051596Szelenkov@nginx.com ), 'errors write' 506603Szelenkov@nginx.com 507603Szelenkov@nginx.com def test_python_application_body_array(self): 508603Szelenkov@nginx.com self.load('body_array') 509603Szelenkov@nginx.com 5101596Szelenkov@nginx.com assert self.get()['body'] == '0123456789', 'body array' 511603Szelenkov@nginx.com 512603Szelenkov@nginx.com def test_python_application_body_io(self): 513603Szelenkov@nginx.com self.load('body_io') 514603Szelenkov@nginx.com 5151596Szelenkov@nginx.com assert self.get()['body'] == '0123456789', 'body io' 516603Szelenkov@nginx.com 517603Szelenkov@nginx.com def test_python_application_body_io_file(self): 518603Szelenkov@nginx.com self.load('body_io_file') 519603Szelenkov@nginx.com 5201596Szelenkov@nginx.com assert self.get()['body'] == 'body\n', 'body io file' 521603Szelenkov@nginx.com 5221596Szelenkov@nginx.com @pytest.mark.skip('not yet') 523603Szelenkov@nginx.com def test_python_application_syntax_error(self): 5241596Szelenkov@nginx.com skip_alert(r'Python failed to import module "wsgi"') 525603Szelenkov@nginx.com self.load('syntax_error') 526603Szelenkov@nginx.com 5271596Szelenkov@nginx.com assert self.get()['status'] == 500, 'syntax error' 528603Szelenkov@nginx.com 5291568Szelenkov@nginx.com def test_python_application_loading_error(self): 5301596Szelenkov@nginx.com skip_alert(r'Python failed to import module "blah"') 5311568Szelenkov@nginx.com 532*1695Smax.romanov@nginx.com self.load('empty', module="blah") 5331568Szelenkov@nginx.com 5341596Szelenkov@nginx.com assert self.get()['status'] == 503, 'loading error' 5351568Szelenkov@nginx.com 536603Szelenkov@nginx.com def test_python_application_close(self): 537603Szelenkov@nginx.com self.load('close') 538603Szelenkov@nginx.com 539603Szelenkov@nginx.com self.get() 540603Szelenkov@nginx.com 5411654Szelenkov@nginx.com unit_stop() 542603Szelenkov@nginx.com 5431596Szelenkov@nginx.com assert self.wait_for_record(r'Close called\.') is not None, 'close' 544603Szelenkov@nginx.com 545603Szelenkov@nginx.com def test_python_application_close_error(self): 546603Szelenkov@nginx.com self.load('close_error') 547603Szelenkov@nginx.com 548603Szelenkov@nginx.com self.get() 549603Szelenkov@nginx.com 5501654Szelenkov@nginx.com unit_stop() 551603Szelenkov@nginx.com 5521596Szelenkov@nginx.com assert ( 5531596Szelenkov@nginx.com self.wait_for_record(r'Close called\.') is not None 5541596Szelenkov@nginx.com ), 'close error' 555603Szelenkov@nginx.com 556617Szelenkov@nginx.com def test_python_application_not_iterable(self): 557617Szelenkov@nginx.com self.load('not_iterable') 558617Szelenkov@nginx.com 559665Szelenkov@nginx.com self.get() 560617Szelenkov@nginx.com 5611654Szelenkov@nginx.com unit_stop() 562617Szelenkov@nginx.com 5631596Szelenkov@nginx.com assert ( 5641028Szelenkov@nginx.com self.wait_for_record( 5651017Szelenkov@nginx.com r'\[error\].+the application returned not an iterable object' 5661596Szelenkov@nginx.com ) 5671596Szelenkov@nginx.com is not None 5681596Szelenkov@nginx.com ), 'not iterable' 569617Szelenkov@nginx.com 570664Szelenkov@nginx.com def test_python_application_write(self): 571664Szelenkov@nginx.com self.load('write') 572664Szelenkov@nginx.com 5731596Szelenkov@nginx.com assert self.get()['body'] == '0123456789', 'write' 574664Szelenkov@nginx.com 5751261Szelenkov@nginx.com def test_python_application_threading(self): 5761261Szelenkov@nginx.com """wait_for_record() timeouts after 5s while every thread works at 5771261Szelenkov@nginx.com least 3s. So without releasing GIL test should fail. 5781261Szelenkov@nginx.com """ 5791261Szelenkov@nginx.com 5801261Szelenkov@nginx.com self.load('threading') 5811261Szelenkov@nginx.com 5821261Szelenkov@nginx.com for _ in range(10): 5831261Szelenkov@nginx.com self.get(no_recv=True) 5841261Szelenkov@nginx.com 5851596Szelenkov@nginx.com assert ( 5861596Szelenkov@nginx.com self.wait_for_record(r'\(5\) Thread: 100') is not None 5871596Szelenkov@nginx.com ), 'last thread finished' 5881017Szelenkov@nginx.com 5891283Szelenkov@nginx.com def test_python_application_iter_exception(self): 5901283Szelenkov@nginx.com self.load('iter_exception') 5911283Szelenkov@nginx.com 5921283Szelenkov@nginx.com # Default request doesn't lead to the exception. 5931283Szelenkov@nginx.com 5941283Szelenkov@nginx.com resp = self.get( 5951283Szelenkov@nginx.com headers={ 5961283Szelenkov@nginx.com 'Host': 'localhost', 5971283Szelenkov@nginx.com 'X-Skip': '9', 5981283Szelenkov@nginx.com 'X-Chunked': '1', 5991283Szelenkov@nginx.com 'Connection': 'close', 6001283Szelenkov@nginx.com } 6011283Szelenkov@nginx.com ) 6021596Szelenkov@nginx.com assert resp['status'] == 200, 'status' 6031596Szelenkov@nginx.com assert resp['body'] == 'XXXXXXX', 'body' 6041283Szelenkov@nginx.com 6051283Szelenkov@nginx.com # Exception before start_response(). 6061283Szelenkov@nginx.com 6071596Szelenkov@nginx.com assert self.get()['status'] == 503, 'error' 6081283Szelenkov@nginx.com 6091596Szelenkov@nginx.com assert self.wait_for_record(r'Traceback') is not None, 'traceback' 6101596Szelenkov@nginx.com assert ( 6111596Szelenkov@nginx.com self.wait_for_record(r'raise Exception\(\'first exception\'\)') 6121596Szelenkov@nginx.com is not None 6131596Szelenkov@nginx.com ), 'first exception raise' 6141596Szelenkov@nginx.com assert len(self.findall(r'Traceback')) == 1, 'traceback count 1' 6151283Szelenkov@nginx.com 6161283Szelenkov@nginx.com # Exception after start_response(), before first write(). 6171283Szelenkov@nginx.com 6181596Szelenkov@nginx.com assert ( 6191283Szelenkov@nginx.com self.get( 6201283Szelenkov@nginx.com headers={ 6211283Szelenkov@nginx.com 'Host': 'localhost', 6221283Szelenkov@nginx.com 'X-Skip': '1', 6231283Szelenkov@nginx.com 'Connection': 'close', 6241283Szelenkov@nginx.com } 6251596Szelenkov@nginx.com )['status'] 6261596Szelenkov@nginx.com == 503 6271596Szelenkov@nginx.com ), 'error 2' 6281283Szelenkov@nginx.com 6291596Szelenkov@nginx.com assert ( 6301596Szelenkov@nginx.com self.wait_for_record(r'raise Exception\(\'second exception\'\)') 6311596Szelenkov@nginx.com is not None 6321596Szelenkov@nginx.com ), 'exception raise second' 6331596Szelenkov@nginx.com assert len(self.findall(r'Traceback')) == 2, 'traceback count 2' 6341283Szelenkov@nginx.com 6351283Szelenkov@nginx.com # Exception after first write(), before first __next__(). 6361283Szelenkov@nginx.com 6371283Szelenkov@nginx.com _, sock = self.get( 6381283Szelenkov@nginx.com headers={ 6391283Szelenkov@nginx.com 'Host': 'localhost', 6401283Szelenkov@nginx.com 'X-Skip': '2', 6411283Szelenkov@nginx.com 'Connection': 'keep-alive', 6421283Szelenkov@nginx.com }, 6431283Szelenkov@nginx.com start=True, 6441283Szelenkov@nginx.com ) 6451283Szelenkov@nginx.com 6461596Szelenkov@nginx.com assert ( 6471596Szelenkov@nginx.com self.wait_for_record(r'raise Exception\(\'third exception\'\)') 6481596Szelenkov@nginx.com is not None 6491596Szelenkov@nginx.com ), 'exception raise third' 6501596Szelenkov@nginx.com assert len(self.findall(r'Traceback')) == 3, 'traceback count 3' 6511283Szelenkov@nginx.com 6521596Szelenkov@nginx.com assert self.get(sock=sock) == {}, 'closed connection' 6531283Szelenkov@nginx.com 6541283Szelenkov@nginx.com # Exception after first write(), before first __next__(), 6551283Szelenkov@nginx.com # chunked (incomplete body). 6561283Szelenkov@nginx.com 6571283Szelenkov@nginx.com resp = self.get( 6581283Szelenkov@nginx.com headers={ 6591283Szelenkov@nginx.com 'Host': 'localhost', 6601283Szelenkov@nginx.com 'X-Skip': '2', 6611283Szelenkov@nginx.com 'X-Chunked': '1', 6621283Szelenkov@nginx.com 'Connection': 'close', 6631295St.nateldemoura@f5.com }, 6641596Szelenkov@nginx.com raw_resp=True, 6651283Szelenkov@nginx.com ) 6661295St.nateldemoura@f5.com if resp: 6671596Szelenkov@nginx.com assert resp[-5:] != '0\r\n\r\n', 'incomplete body' 6681596Szelenkov@nginx.com assert len(self.findall(r'Traceback')) == 4, 'traceback count 4' 6691283Szelenkov@nginx.com 6701283Szelenkov@nginx.com # Exception in __next__(). 6711283Szelenkov@nginx.com 6721283Szelenkov@nginx.com _, sock = self.get( 6731283Szelenkov@nginx.com headers={ 6741283Szelenkov@nginx.com 'Host': 'localhost', 6751283Szelenkov@nginx.com 'X-Skip': '3', 6761283Szelenkov@nginx.com 'Connection': 'keep-alive', 6771283Szelenkov@nginx.com }, 6781283Szelenkov@nginx.com start=True, 6791283Szelenkov@nginx.com ) 6801283Szelenkov@nginx.com 6811596Szelenkov@nginx.com assert ( 6821596Szelenkov@nginx.com self.wait_for_record(r'raise Exception\(\'next exception\'\)') 6831596Szelenkov@nginx.com is not None 6841596Szelenkov@nginx.com ), 'exception raise next' 6851596Szelenkov@nginx.com assert len(self.findall(r'Traceback')) == 5, 'traceback count 5' 6861283Szelenkov@nginx.com 6871596Szelenkov@nginx.com assert self.get(sock=sock) == {}, 'closed connection 2' 6881283Szelenkov@nginx.com 6891283Szelenkov@nginx.com # Exception in __next__(), chunked (incomplete body). 6901283Szelenkov@nginx.com 6911283Szelenkov@nginx.com resp = self.get( 6921283Szelenkov@nginx.com headers={ 6931283Szelenkov@nginx.com 'Host': 'localhost', 6941283Szelenkov@nginx.com 'X-Skip': '3', 6951283Szelenkov@nginx.com 'X-Chunked': '1', 6961283Szelenkov@nginx.com 'Connection': 'close', 6971295St.nateldemoura@f5.com }, 6981596Szelenkov@nginx.com raw_resp=True, 6991283Szelenkov@nginx.com ) 7001295St.nateldemoura@f5.com if resp: 7011596Szelenkov@nginx.com assert resp[-5:] != '0\r\n\r\n', 'incomplete body 2' 7021596Szelenkov@nginx.com assert len(self.findall(r'Traceback')) == 6, 'traceback count 6' 7031283Szelenkov@nginx.com 7041283Szelenkov@nginx.com # Exception before start_response() and in close(). 7051283Szelenkov@nginx.com 7061596Szelenkov@nginx.com assert ( 7071283Szelenkov@nginx.com self.get( 7081283Szelenkov@nginx.com headers={ 7091283Szelenkov@nginx.com 'Host': 'localhost', 7101283Szelenkov@nginx.com 'X-Not-Skip-Close': '1', 7111283Szelenkov@nginx.com 'Connection': 'close', 7121283Szelenkov@nginx.com } 7131596Szelenkov@nginx.com )['status'] 7141596Szelenkov@nginx.com == 503 7151596Szelenkov@nginx.com ), 'error' 7161283Szelenkov@nginx.com 7171596Szelenkov@nginx.com assert ( 7181596Szelenkov@nginx.com self.wait_for_record(r'raise Exception\(\'close exception\'\)') 7191596Szelenkov@nginx.com is not None 7201596Szelenkov@nginx.com ), 'exception raise close' 7211596Szelenkov@nginx.com assert len(self.findall(r'Traceback')) == 8, 'traceback count 8' 7221283Szelenkov@nginx.com 7231596Szelenkov@nginx.com def test_python_user_group(self, is_su): 7241596Szelenkov@nginx.com if not is_su: 7251596Szelenkov@nginx.com pytest.skip('requires root') 7261304St.nateldemoura@f5.com 7271304St.nateldemoura@f5.com nobody_uid = pwd.getpwnam('nobody').pw_uid 7281304St.nateldemoura@f5.com 7291304St.nateldemoura@f5.com group = 'nobody' 7301304St.nateldemoura@f5.com 7311304St.nateldemoura@f5.com try: 7321304St.nateldemoura@f5.com group_id = grp.getgrnam(group).gr_gid 7331304St.nateldemoura@f5.com except: 7341304St.nateldemoura@f5.com group = 'nogroup' 7351304St.nateldemoura@f5.com group_id = grp.getgrnam(group).gr_gid 7361304St.nateldemoura@f5.com 7371304St.nateldemoura@f5.com self.load('user_group') 7381304St.nateldemoura@f5.com 7391304St.nateldemoura@f5.com obj = self.getjson()['body'] 7401596Szelenkov@nginx.com assert obj['UID'] == nobody_uid, 'nobody uid' 7411596Szelenkov@nginx.com assert obj['GID'] == group_id, 'nobody gid' 7421304St.nateldemoura@f5.com 7431304St.nateldemoura@f5.com self.load('user_group', user='nobody') 7441304St.nateldemoura@f5.com 7451304St.nateldemoura@f5.com obj = self.getjson()['body'] 7461596Szelenkov@nginx.com assert obj['UID'] == nobody_uid, 'nobody uid user=nobody' 7471596Szelenkov@nginx.com assert obj['GID'] == group_id, 'nobody gid user=nobody' 7481304St.nateldemoura@f5.com 7491304St.nateldemoura@f5.com self.load('user_group', user='nobody', group=group) 7501304St.nateldemoura@f5.com 7511304St.nateldemoura@f5.com obj = self.getjson()['body'] 7521596Szelenkov@nginx.com assert obj['UID'] == nobody_uid, ( 7531596Szelenkov@nginx.com 'nobody uid user=nobody group=%s' % group 7541304St.nateldemoura@f5.com ) 7551304St.nateldemoura@f5.com 7561596Szelenkov@nginx.com assert obj['GID'] == group_id, ( 7571596Szelenkov@nginx.com 'nobody gid user=nobody group=%s' % group 7581304St.nateldemoura@f5.com ) 7591304St.nateldemoura@f5.com 7601304St.nateldemoura@f5.com self.load('user_group', group=group) 7611304St.nateldemoura@f5.com 7621304St.nateldemoura@f5.com obj = self.getjson()['body'] 7631596Szelenkov@nginx.com assert obj['UID'] == nobody_uid, 'nobody uid group=%s' % group 7641304St.nateldemoura@f5.com 7651596Szelenkov@nginx.com assert obj['GID'] == group_id, 'nobody gid group=%s' % group 7661304St.nateldemoura@f5.com 7671304St.nateldemoura@f5.com self.load('user_group', user='root') 7681304St.nateldemoura@f5.com 7691304St.nateldemoura@f5.com obj = self.getjson()['body'] 7701596Szelenkov@nginx.com assert obj['UID'] == 0, 'root uid user=root' 7711596Szelenkov@nginx.com assert obj['GID'] == 0, 'root gid user=root' 7721304St.nateldemoura@f5.com 7731304St.nateldemoura@f5.com group = 'root' 7741304St.nateldemoura@f5.com 7751304St.nateldemoura@f5.com try: 7761304St.nateldemoura@f5.com grp.getgrnam(group) 7771304St.nateldemoura@f5.com group = True 7781304St.nateldemoura@f5.com except: 7791304St.nateldemoura@f5.com group = False 7801304St.nateldemoura@f5.com 7811304St.nateldemoura@f5.com if group: 7821304St.nateldemoura@f5.com self.load('user_group', user='root', group='root') 7831304St.nateldemoura@f5.com 7841304St.nateldemoura@f5.com obj = self.getjson()['body'] 7851596Szelenkov@nginx.com assert obj['UID'] == 0, 'root uid user=root group=root' 7861596Szelenkov@nginx.com assert obj['GID'] == 0, 'root gid user=root group=root' 7871304St.nateldemoura@f5.com 7881304St.nateldemoura@f5.com self.load('user_group', group='root') 7891304St.nateldemoura@f5.com 7901304St.nateldemoura@f5.com obj = self.getjson()['body'] 7911596Szelenkov@nginx.com assert obj['UID'] == nobody_uid, 'root uid group=root' 7921596Szelenkov@nginx.com assert obj['GID'] == 0, 'root gid group=root' 7931604Smax.romanov@nginx.com 7941604Smax.romanov@nginx.com def test_python_application_callable(self): 7951604Smax.romanov@nginx.com skip_alert(r'Python failed to get "blah" from module') 7961604Smax.romanov@nginx.com self.load('callable') 7971604Smax.romanov@nginx.com 7981604Smax.romanov@nginx.com assert self.get()['status'] == 204, 'default application response' 7991604Smax.romanov@nginx.com 800*1695Smax.romanov@nginx.com self.load('callable', callable="app") 8011604Smax.romanov@nginx.com 8021604Smax.romanov@nginx.com assert self.get()['status'] == 200, 'callable response' 8031604Smax.romanov@nginx.com 804*1695Smax.romanov@nginx.com self.load('callable', callable="blah") 8051604Smax.romanov@nginx.com 8061604Smax.romanov@nginx.com assert self.get()['status'] not in [200, 204], 'callable response inv' 8071604Smax.romanov@nginx.com 8081683Smax.romanov@nginx.com def test_python_application_threads(self): 809*1695Smax.romanov@nginx.com self.load('threads', threads=4) 8101683Smax.romanov@nginx.com 8111683Smax.romanov@nginx.com socks = [] 8121683Smax.romanov@nginx.com 8131683Smax.romanov@nginx.com for i in range(4): 8141683Smax.romanov@nginx.com (_, sock) = self.get( 8151683Smax.romanov@nginx.com headers={ 8161683Smax.romanov@nginx.com 'Host': 'localhost', 8171683Smax.romanov@nginx.com 'X-Delay': '2', 8181683Smax.romanov@nginx.com 'Connection': 'close', 8191683Smax.romanov@nginx.com }, 8201683Smax.romanov@nginx.com no_recv=True, 8211683Smax.romanov@nginx.com start=True, 8221683Smax.romanov@nginx.com ) 8231683Smax.romanov@nginx.com 8241683Smax.romanov@nginx.com socks.append(sock) 8251683Smax.romanov@nginx.com 8261683Smax.romanov@nginx.com threads = set() 8271683Smax.romanov@nginx.com 8281683Smax.romanov@nginx.com for sock in socks: 8291683Smax.romanov@nginx.com resp = self.recvall(sock).decode('utf-8') 8301683Smax.romanov@nginx.com 8311683Smax.romanov@nginx.com self.log_in(resp) 8321683Smax.romanov@nginx.com 8331683Smax.romanov@nginx.com resp = self._resp_to_dict(resp) 8341683Smax.romanov@nginx.com 8351683Smax.romanov@nginx.com assert resp['status'] == 200, 'status' 8361683Smax.romanov@nginx.com 8371683Smax.romanov@nginx.com threads.add(resp['headers']['X-Thread']) 8381683Smax.romanov@nginx.com 8391683Smax.romanov@nginx.com assert resp['headers']['Wsgi-Multithread'] == 'True', 'multithread' 8401683Smax.romanov@nginx.com 8411683Smax.romanov@nginx.com sock.close() 8421683Smax.romanov@nginx.com 8431683Smax.romanov@nginx.com assert len(socks) == len(threads), 'threads differs' 844