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 71019Szelenkov@nginx.comfrom unit.applications.lang.python import TestApplicationPython 81730Szelenkov@nginx.comfrom unit.option import option 9484Szelenkov@nginx.com 101017Szelenkov@nginx.com 111019Szelenkov@nginx.comclass TestPythonApplication(TestApplicationPython): 121467Szelenkov@nginx.com prerequisites = {'modules': {'python': 'all'}} 13484Szelenkov@nginx.com 141283Szelenkov@nginx.com def findall(self, pattern): 151654Szelenkov@nginx.com with open(option.temp_dir + '/unit.log', 'r', errors='ignore') as f: 161283Szelenkov@nginx.com return re.findall(pattern, f.read()) 171283Szelenkov@nginx.com 18552Szelenkov@nginx.com def test_python_application_variables(self): 19552Szelenkov@nginx.com self.load('variables') 20484Szelenkov@nginx.com 21484Szelenkov@nginx.com body = 'Test body string.' 22484Szelenkov@nginx.com 231605Smax.romanov@nginx.com resp = self.http( 241605Smax.romanov@nginx.com b"""POST / HTTP/1.1 251605Smax.romanov@nginx.comHost: localhost 261605Smax.romanov@nginx.comContent-Length: %d 271605Smax.romanov@nginx.comCustom-Header: blah 281605Smax.romanov@nginx.comCustom-hEader: Blah 291605Smax.romanov@nginx.comContent-Type: text/html 301605Smax.romanov@nginx.comConnection: close 311605Smax.romanov@nginx.comcustom-header: BLAH 321605Smax.romanov@nginx.com 331605Smax.romanov@nginx.com%s""" % (len(body), body.encode()), 341605Smax.romanov@nginx.com raw=True, 351017Szelenkov@nginx.com ) 36484Szelenkov@nginx.com 371596Szelenkov@nginx.com assert resp['status'] == 200, 'status' 38505Szelenkov@nginx.com headers = resp['headers'] 39674Szelenkov@nginx.com header_server = headers.pop('Server') 401596Szelenkov@nginx.com assert re.search(r'Unit/[\d\.]+', header_server), 'server header' 411596Szelenkov@nginx.com assert ( 421596Szelenkov@nginx.com headers.pop('Server-Software') == header_server 431596Szelenkov@nginx.com ), 'server software header' 44599Szelenkov@nginx.com 45599Szelenkov@nginx.com date = headers.pop('Date') 461596Szelenkov@nginx.com assert date[-4:] == ' GMT', 'date header timezone' 471596Szelenkov@nginx.com assert ( 481596Szelenkov@nginx.com abs(self.date_to_sec_epoch(date) - self.sec_epoch()) < 5 491596Szelenkov@nginx.com ), 'date header' 50599Szelenkov@nginx.com 511596Szelenkov@nginx.com assert headers == { 521596Szelenkov@nginx.com 'Connection': 'close', 531596Szelenkov@nginx.com 'Content-Length': str(len(body)), 541596Szelenkov@nginx.com 'Content-Type': 'text/html', 551596Szelenkov@nginx.com 'Request-Method': 'POST', 561596Szelenkov@nginx.com 'Request-Uri': '/', 571596Szelenkov@nginx.com 'Http-Host': 'localhost', 581596Szelenkov@nginx.com 'Server-Protocol': 'HTTP/1.1', 591605Smax.romanov@nginx.com 'Custom-Header': 'blah, Blah, BLAH', 601596Szelenkov@nginx.com 'Wsgi-Version': '(1, 0)', 611596Szelenkov@nginx.com 'Wsgi-Url-Scheme': 'http', 621596Szelenkov@nginx.com 'Wsgi-Multithread': 'False', 631596Szelenkov@nginx.com 'Wsgi-Multiprocess': 'True', 641596Szelenkov@nginx.com 'Wsgi-Run-Once': 'False', 651596Szelenkov@nginx.com }, 'headers' 661596Szelenkov@nginx.com assert resp['body'] == body, 'body' 67484Szelenkov@nginx.com 68497Szelenkov@nginx.com def test_python_application_query_string(self): 69552Szelenkov@nginx.com self.load('query_string') 70497Szelenkov@nginx.com 71505Szelenkov@nginx.com resp = self.get(url='/?var1=val1&var2=val2') 72497Szelenkov@nginx.com 731596Szelenkov@nginx.com assert ( 741596Szelenkov@nginx.com resp['headers']['Query-String'] == 'var1=val1&var2=val2' 751596Szelenkov@nginx.com ), 'Query-String header' 76497Szelenkov@nginx.com 771171Svbart@nginx.com def test_python_application_query_string_space(self): 781171Svbart@nginx.com self.load('query_string') 791171Svbart@nginx.com 801171Svbart@nginx.com resp = self.get(url='/ ?var1=val1&var2=val2') 811596Szelenkov@nginx.com assert ( 821596Szelenkov@nginx.com resp['headers']['Query-String'] == 'var1=val1&var2=val2' 831596Szelenkov@nginx.com ), 'Query-String space' 841171Svbart@nginx.com 851171Svbart@nginx.com resp = self.get(url='/ %20?var1=val1&var2=val2') 861596Szelenkov@nginx.com assert ( 871596Szelenkov@nginx.com resp['headers']['Query-String'] == 'var1=val1&var2=val2' 881596Szelenkov@nginx.com ), 'Query-String space 2' 891171Svbart@nginx.com 901171Svbart@nginx.com resp = self.get(url='/ %20 ?var1=val1&var2=val2') 911596Szelenkov@nginx.com assert ( 921596Szelenkov@nginx.com resp['headers']['Query-String'] == 'var1=val1&var2=val2' 931596Szelenkov@nginx.com ), 'Query-String space 3' 941171Svbart@nginx.com 951171Svbart@nginx.com resp = self.get(url='/blah %20 blah? var1= val1 & var2=val2') 961596Szelenkov@nginx.com assert ( 971596Szelenkov@nginx.com resp['headers']['Query-String'] == ' var1= val1 & var2=val2' 981596Szelenkov@nginx.com ), 'Query-String space 4' 991171Svbart@nginx.com 100894Szelenkov@nginx.com def test_python_application_query_string_empty(self): 101894Szelenkov@nginx.com self.load('query_string') 102894Szelenkov@nginx.com 103894Szelenkov@nginx.com resp = self.get(url='/?') 104894Szelenkov@nginx.com 1051596Szelenkov@nginx.com assert resp['status'] == 200, 'query string empty status' 1061596Szelenkov@nginx.com assert resp['headers']['Query-String'] == '', 'query string empty' 107894Szelenkov@nginx.com 108894Szelenkov@nginx.com def test_python_application_query_string_absent(self): 109894Szelenkov@nginx.com self.load('query_string') 110894Szelenkov@nginx.com 111894Szelenkov@nginx.com resp = self.get() 112894Szelenkov@nginx.com 1131596Szelenkov@nginx.com assert resp['status'] == 200, 'query string absent status' 1141596Szelenkov@nginx.com assert resp['headers']['Query-String'] == '', 'query string absent' 115894Szelenkov@nginx.com 1161596Szelenkov@nginx.com @pytest.mark.skip('not yet') 117495Szelenkov@nginx.com def test_python_application_server_port(self): 118552Szelenkov@nginx.com self.load('server_port') 119495Szelenkov@nginx.com 1201596Szelenkov@nginx.com assert ( 1211596Szelenkov@nginx.com self.get()['headers']['Server-Port'] == '7080' 1221596Szelenkov@nginx.com ), 'Server-Port header' 123484Szelenkov@nginx.com 1241596Szelenkov@nginx.com @pytest.mark.skip('not yet') 1251250Szelenkov@nginx.com def test_python_application_working_directory_invalid(self): 1261250Szelenkov@nginx.com self.load('empty') 1271250Szelenkov@nginx.com 1281596Szelenkov@nginx.com assert 'success' in self.conf( 1291596Szelenkov@nginx.com '"/blah"', 'applications/empty/working_directory' 1301596Szelenkov@nginx.com ), 'configure invalid working_directory' 1311250Szelenkov@nginx.com 1321596Szelenkov@nginx.com assert self.get()['status'] == 500, 'status' 1331250Szelenkov@nginx.com 134496Szelenkov@nginx.com def test_python_application_204_transfer_encoding(self): 135552Szelenkov@nginx.com self.load('204_no_content') 136496Szelenkov@nginx.com 1371596Szelenkov@nginx.com assert ( 1381596Szelenkov@nginx.com 'Transfer-Encoding' not in self.get()['headers'] 1391596Szelenkov@nginx.com ), '204 header transfer encoding' 140484Szelenkov@nginx.com 141602Szelenkov@nginx.com def test_python_application_ctx_iter_atexit(self): 142602Szelenkov@nginx.com self.load('ctx_iter_atexit') 143602Szelenkov@nginx.com 1441017Szelenkov@nginx.com resp = self.post( 1451017Szelenkov@nginx.com headers={ 1461017Szelenkov@nginx.com 'Host': 'localhost', 1471017Szelenkov@nginx.com 'Connection': 'close', 1481017Szelenkov@nginx.com 'Content-Type': 'text/html', 1491017Szelenkov@nginx.com }, 1501017Szelenkov@nginx.com body='0123456789', 1511017Szelenkov@nginx.com ) 152602Szelenkov@nginx.com 1531596Szelenkov@nginx.com assert resp['status'] == 200, 'ctx iter status' 1541596Szelenkov@nginx.com assert resp['body'] == '0123456789', 'ctx iter body' 155602Szelenkov@nginx.com 156*1775Szelenkov@nginx.com assert 'success' in self.conf({"listeners": {}, "applications": {}}) 157602Szelenkov@nginx.com 1581596Szelenkov@nginx.com assert ( 1591596Szelenkov@nginx.com self.wait_for_record(r'RuntimeError') is not None 1601596Szelenkov@nginx.com ), 'ctx iter atexit' 161602Szelenkov@nginx.com 162603Szelenkov@nginx.com def test_python_keepalive_body(self): 163603Szelenkov@nginx.com self.load('mirror') 164603Szelenkov@nginx.com 1651596Szelenkov@nginx.com assert self.get()['status'] == 200, 'init' 1661029Szelenkov@nginx.com 1671453Szelenkov@nginx.com body = '0123456789' * 500 1681017Szelenkov@nginx.com (resp, sock) = self.post( 1691017Szelenkov@nginx.com headers={ 1701017Szelenkov@nginx.com 'Host': 'localhost', 1711017Szelenkov@nginx.com 'Connection': 'keep-alive', 1721017Szelenkov@nginx.com 'Content-Type': 'text/html', 1731017Szelenkov@nginx.com }, 1741017Szelenkov@nginx.com start=True, 1751453Szelenkov@nginx.com body=body, 1761029Szelenkov@nginx.com read_timeout=1, 1771017Szelenkov@nginx.com ) 178603Szelenkov@nginx.com 1791596Szelenkov@nginx.com assert resp['body'] == body, 'keep-alive 1' 180603Szelenkov@nginx.com 1811453Szelenkov@nginx.com body = '0123456789' 1821017Szelenkov@nginx.com resp = self.post( 1831017Szelenkov@nginx.com headers={ 1841017Szelenkov@nginx.com 'Host': 'localhost', 1851017Szelenkov@nginx.com 'Connection': 'close', 1861017Szelenkov@nginx.com 'Content-Type': 'text/html', 1871017Szelenkov@nginx.com }, 1881017Szelenkov@nginx.com sock=sock, 1891453Szelenkov@nginx.com body=body, 1901017Szelenkov@nginx.com ) 191603Szelenkov@nginx.com 1921596Szelenkov@nginx.com assert resp['body'] == body, 'keep-alive 2' 193603Szelenkov@nginx.com 194684Szelenkov@nginx.com def test_python_keepalive_reconfigure(self): 195684Szelenkov@nginx.com self.load('mirror') 196684Szelenkov@nginx.com 1971596Szelenkov@nginx.com assert self.get()['status'] == 200, 'init' 1981029Szelenkov@nginx.com 199684Szelenkov@nginx.com body = '0123456789' 200684Szelenkov@nginx.com conns = 3 201684Szelenkov@nginx.com socks = [] 202684Szelenkov@nginx.com 203684Szelenkov@nginx.com for i in range(conns): 2041017Szelenkov@nginx.com (resp, sock) = self.post( 2051017Szelenkov@nginx.com headers={ 2061017Szelenkov@nginx.com 'Host': 'localhost', 2071017Szelenkov@nginx.com 'Connection': 'keep-alive', 2081017Szelenkov@nginx.com 'Content-Type': 'text/html', 2091017Szelenkov@nginx.com }, 2101017Szelenkov@nginx.com start=True, 2111017Szelenkov@nginx.com body=body, 2121029Szelenkov@nginx.com read_timeout=1, 2131017Szelenkov@nginx.com ) 214684Szelenkov@nginx.com 2151596Szelenkov@nginx.com assert resp['body'] == body, 'keep-alive open' 2161695Smax.romanov@nginx.com 2171695Smax.romanov@nginx.com self.load('mirror', processes=i + 1) 218684Szelenkov@nginx.com 219684Szelenkov@nginx.com socks.append(sock) 220684Szelenkov@nginx.com 221684Szelenkov@nginx.com for i in range(conns): 2221017Szelenkov@nginx.com (resp, sock) = self.post( 2231017Szelenkov@nginx.com headers={ 2241017Szelenkov@nginx.com 'Host': 'localhost', 2251017Szelenkov@nginx.com 'Connection': 'keep-alive', 2261017Szelenkov@nginx.com 'Content-Type': 'text/html', 2271017Szelenkov@nginx.com }, 2281017Szelenkov@nginx.com start=True, 2291017Szelenkov@nginx.com sock=socks[i], 2301017Szelenkov@nginx.com body=body, 2311029Szelenkov@nginx.com read_timeout=1, 2321017Szelenkov@nginx.com ) 233684Szelenkov@nginx.com 2341596Szelenkov@nginx.com assert resp['body'] == body, 'keep-alive request' 2351695Smax.romanov@nginx.com 2361695Smax.romanov@nginx.com self.load('mirror', processes=i + 1) 237684Szelenkov@nginx.com 238684Szelenkov@nginx.com for i in range(conns): 2391017Szelenkov@nginx.com resp = self.post( 2401017Szelenkov@nginx.com headers={ 2411017Szelenkov@nginx.com 'Host': 'localhost', 2421017Szelenkov@nginx.com 'Connection': 'close', 2431017Szelenkov@nginx.com 'Content-Type': 'text/html', 2441017Szelenkov@nginx.com }, 2451017Szelenkov@nginx.com sock=socks[i], 2461017Szelenkov@nginx.com body=body, 2471017Szelenkov@nginx.com ) 248684Szelenkov@nginx.com 2491596Szelenkov@nginx.com assert resp['body'] == body, 'keep-alive close' 2501695Smax.romanov@nginx.com 2511695Smax.romanov@nginx.com self.load('mirror', processes=i + 1) 252684Szelenkov@nginx.com 253750Szelenkov@nginx.com def test_python_keepalive_reconfigure_2(self): 254750Szelenkov@nginx.com self.load('mirror') 255750Szelenkov@nginx.com 2561596Szelenkov@nginx.com assert self.get()['status'] == 200, 'init' 2571029Szelenkov@nginx.com 258750Szelenkov@nginx.com body = '0123456789' 259750Szelenkov@nginx.com 2601017Szelenkov@nginx.com (resp, sock) = self.post( 2611017Szelenkov@nginx.com headers={ 2621017Szelenkov@nginx.com 'Host': 'localhost', 2631017Szelenkov@nginx.com 'Connection': 'keep-alive', 2641017Szelenkov@nginx.com 'Content-Type': 'text/html', 2651017Szelenkov@nginx.com }, 2661017Szelenkov@nginx.com start=True, 2671017Szelenkov@nginx.com body=body, 2681029Szelenkov@nginx.com read_timeout=1, 2691017Szelenkov@nginx.com ) 270750Szelenkov@nginx.com 2711596Szelenkov@nginx.com assert resp['body'] == body, 'reconfigure 2 keep-alive 1' 272750Szelenkov@nginx.com 273750Szelenkov@nginx.com self.load('empty') 274750Szelenkov@nginx.com 2751596Szelenkov@nginx.com assert self.get()['status'] == 200, 'init' 2761029Szelenkov@nginx.com 2771017Szelenkov@nginx.com (resp, sock) = self.post( 2781017Szelenkov@nginx.com headers={ 2791017Szelenkov@nginx.com 'Host': 'localhost', 2801017Szelenkov@nginx.com 'Connection': 'close', 2811017Szelenkov@nginx.com 'Content-Type': 'text/html', 2821017Szelenkov@nginx.com }, 2831017Szelenkov@nginx.com start=True, 2841017Szelenkov@nginx.com sock=sock, 2851017Szelenkov@nginx.com body=body, 2861017Szelenkov@nginx.com ) 287750Szelenkov@nginx.com 2881596Szelenkov@nginx.com assert resp['status'] == 200, 'reconfigure 2 keep-alive 2' 2891596Szelenkov@nginx.com assert resp['body'] == '', 'reconfigure 2 keep-alive 2 body' 290750Szelenkov@nginx.com 2911596Szelenkov@nginx.com assert 'success' in self.conf( 2921596Szelenkov@nginx.com {"listeners": {}, "applications": {}} 2931596Szelenkov@nginx.com ), 'reconfigure 2 clear configuration' 294750Szelenkov@nginx.com 295750Szelenkov@nginx.com resp = self.get(sock=sock) 296750Szelenkov@nginx.com 2971596Szelenkov@nginx.com assert resp == {}, 'reconfigure 2 keep-alive 3' 298750Szelenkov@nginx.com 299750Szelenkov@nginx.com def test_python_keepalive_reconfigure_3(self): 300750Szelenkov@nginx.com self.load('empty') 301750Szelenkov@nginx.com 3021596Szelenkov@nginx.com assert self.get()['status'] == 200, 'init' 3031029Szelenkov@nginx.com 3041453Szelenkov@nginx.com (_, sock) = self.http( 3051017Szelenkov@nginx.com b"""GET / HTTP/1.1 3061017Szelenkov@nginx.com""", 3071017Szelenkov@nginx.com start=True, 3081017Szelenkov@nginx.com raw=True, 3091453Szelenkov@nginx.com no_recv=True, 3101017Szelenkov@nginx.com ) 311750Szelenkov@nginx.com 3121596Szelenkov@nginx.com assert self.get()['status'] == 200 3131453Szelenkov@nginx.com 3141596Szelenkov@nginx.com assert 'success' in self.conf( 3151596Szelenkov@nginx.com {"listeners": {}, "applications": {}} 3161596Szelenkov@nginx.com ), 'reconfigure 3 clear configuration' 317750Szelenkov@nginx.com 3181017Szelenkov@nginx.com resp = self.http( 3191017Szelenkov@nginx.com b"""Host: localhost 320750Szelenkov@nginx.comConnection: close 321750Szelenkov@nginx.com 3221017Szelenkov@nginx.com""", 3231017Szelenkov@nginx.com sock=sock, 3241017Szelenkov@nginx.com raw=True, 3251017Szelenkov@nginx.com ) 326750Szelenkov@nginx.com 3271596Szelenkov@nginx.com assert resp['status'] == 200, 'reconfigure 3' 328750Szelenkov@nginx.com 329603Szelenkov@nginx.com def test_python_atexit(self): 330603Szelenkov@nginx.com self.load('atexit') 331603Szelenkov@nginx.com 332603Szelenkov@nginx.com self.get() 333603Szelenkov@nginx.com 334*1775Szelenkov@nginx.com assert 'success' in self.conf({"listeners": {}, "applications": {}}) 335603Szelenkov@nginx.com 3361596Szelenkov@nginx.com assert self.wait_for_record(r'At exit called\.') is not None, 'atexit' 337603Szelenkov@nginx.com 3381454Szelenkov@nginx.com def test_python_process_switch(self): 3391695Smax.romanov@nginx.com self.load('delayed', processes=2) 3401454Szelenkov@nginx.com 3411596Szelenkov@nginx.com self.get( 3421596Szelenkov@nginx.com headers={ 3431596Szelenkov@nginx.com 'Host': 'localhost', 3441596Szelenkov@nginx.com 'Content-Length': '0', 3451596Szelenkov@nginx.com 'X-Delay': '5', 3461596Szelenkov@nginx.com 'Connection': 'close', 3471596Szelenkov@nginx.com }, 3481596Szelenkov@nginx.com no_recv=True, 3491596Szelenkov@nginx.com ) 3501454Szelenkov@nginx.com 3511454Szelenkov@nginx.com headers_delay_1 = { 3521454Szelenkov@nginx.com 'Connection': 'close', 3531454Szelenkov@nginx.com 'Host': 'localhost', 3541454Szelenkov@nginx.com 'Content-Length': '0', 3551454Szelenkov@nginx.com 'X-Delay': '1', 3561454Szelenkov@nginx.com } 3571454Szelenkov@nginx.com 3581454Szelenkov@nginx.com self.get(headers=headers_delay_1, no_recv=True) 3591454Szelenkov@nginx.com 3601454Szelenkov@nginx.com time.sleep(0.5) 3611454Szelenkov@nginx.com 3621454Szelenkov@nginx.com for _ in range(10): 3631454Szelenkov@nginx.com self.get(headers=headers_delay_1, no_recv=True) 3641454Szelenkov@nginx.com 3651454Szelenkov@nginx.com self.get(headers=headers_delay_1) 3661454Szelenkov@nginx.com 3671596Szelenkov@nginx.com @pytest.mark.skip('not yet') 368603Szelenkov@nginx.com def test_python_application_start_response_exit(self): 369603Szelenkov@nginx.com self.load('start_response_exit') 370603Szelenkov@nginx.com 3711596Szelenkov@nginx.com assert self.get()['status'] == 500, 'start response exit' 372603Szelenkov@nginx.com 373603Szelenkov@nginx.com def test_python_application_input_iter(self): 374603Szelenkov@nginx.com self.load('input_iter') 375603Szelenkov@nginx.com 3761400Smax.romanov@nginx.com body = '''0123456789 3771400Smax.romanov@nginx.comnext line 3781400Smax.romanov@nginx.com 3791400Smax.romanov@nginx.comlast line''' 3801400Smax.romanov@nginx.com 3811400Smax.romanov@nginx.com resp = self.post(body=body) 3821596Szelenkov@nginx.com assert resp['body'] == body, 'input iter' 3831596Szelenkov@nginx.com assert resp['headers']['X-Lines-Count'] == '4', 'input iter lines' 3841400Smax.romanov@nginx.com 3851400Smax.romanov@nginx.com def test_python_application_input_readline(self): 3861400Smax.romanov@nginx.com self.load('input_readline') 3871400Smax.romanov@nginx.com 3881400Smax.romanov@nginx.com body = '''0123456789 3891400Smax.romanov@nginx.comnext line 3901400Smax.romanov@nginx.com 3911400Smax.romanov@nginx.comlast line''' 3921400Smax.romanov@nginx.com 3931400Smax.romanov@nginx.com resp = self.post(body=body) 3941596Szelenkov@nginx.com assert resp['body'] == body, 'input readline' 3951596Szelenkov@nginx.com assert resp['headers']['X-Lines-Count'] == '4', 'input readline lines' 3961400Smax.romanov@nginx.com 3971400Smax.romanov@nginx.com def test_python_application_input_readline_size(self): 3981400Smax.romanov@nginx.com self.load('input_readline_size') 3991400Smax.romanov@nginx.com 4001400Smax.romanov@nginx.com body = '''0123456789 4011400Smax.romanov@nginx.comnext line 4021400Smax.romanov@nginx.com 4031400Smax.romanov@nginx.comlast line''' 404603Szelenkov@nginx.com 4051596Szelenkov@nginx.com assert self.post(body=body)['body'] == body, 'input readline size' 4061596Szelenkov@nginx.com assert ( 4071596Szelenkov@nginx.com self.post(body='0123')['body'] == '0123' 4081596Szelenkov@nginx.com ), 'input readline size less' 4091400Smax.romanov@nginx.com 4101400Smax.romanov@nginx.com def test_python_application_input_readlines(self): 4111400Smax.romanov@nginx.com self.load('input_readlines') 4121400Smax.romanov@nginx.com 4131400Smax.romanov@nginx.com body = '''0123456789 4141400Smax.romanov@nginx.comnext line 4151400Smax.romanov@nginx.com 4161400Smax.romanov@nginx.comlast line''' 4171400Smax.romanov@nginx.com 4181400Smax.romanov@nginx.com resp = self.post(body=body) 4191596Szelenkov@nginx.com assert resp['body'] == body, 'input readlines' 4201596Szelenkov@nginx.com assert resp['headers']['X-Lines-Count'] == '4', 'input readlines lines' 4211400Smax.romanov@nginx.com 4221400Smax.romanov@nginx.com def test_python_application_input_readlines_huge(self): 4231400Smax.romanov@nginx.com self.load('input_readlines') 4241400Smax.romanov@nginx.com 4251400Smax.romanov@nginx.com body = ( 4261400Smax.romanov@nginx.com '''0123456789 abcdefghi 4271400Smax.romanov@nginx.comnext line: 0123456789 abcdefghi 4281400Smax.romanov@nginx.com 4291400Smax.romanov@nginx.comlast line: 987654321 4301400Smax.romanov@nginx.com''' 4311400Smax.romanov@nginx.com * 512 4321400Smax.romanov@nginx.com ) 4331400Smax.romanov@nginx.com 4341596Szelenkov@nginx.com assert ( 4351596Szelenkov@nginx.com self.post(body=body, read_buffer_size=16384)['body'] == body 4361596Szelenkov@nginx.com ), 'input readlines huge' 437603Szelenkov@nginx.com 438603Szelenkov@nginx.com def test_python_application_input_read_length(self): 439603Szelenkov@nginx.com self.load('input_read_length') 440603Szelenkov@nginx.com 441603Szelenkov@nginx.com body = '0123456789' 442603Szelenkov@nginx.com 4431017Szelenkov@nginx.com resp = self.post( 4441017Szelenkov@nginx.com headers={ 4451017Szelenkov@nginx.com 'Host': 'localhost', 4461017Szelenkov@nginx.com 'Input-Length': '5', 4471017Szelenkov@nginx.com 'Connection': 'close', 4481017Szelenkov@nginx.com }, 4491017Szelenkov@nginx.com body=body, 4501017Szelenkov@nginx.com ) 451603Szelenkov@nginx.com 4521596Szelenkov@nginx.com assert resp['body'] == body[:5], 'input read length lt body' 453603Szelenkov@nginx.com 4541017Szelenkov@nginx.com resp = self.post( 4551017Szelenkov@nginx.com headers={ 4561017Szelenkov@nginx.com 'Host': 'localhost', 4571017Szelenkov@nginx.com 'Input-Length': '15', 4581017Szelenkov@nginx.com 'Connection': 'close', 4591017Szelenkov@nginx.com }, 4601017Szelenkov@nginx.com body=body, 4611017Szelenkov@nginx.com ) 462603Szelenkov@nginx.com 4631596Szelenkov@nginx.com assert resp['body'] == body, 'input read length gt body' 464603Szelenkov@nginx.com 4651017Szelenkov@nginx.com resp = self.post( 4661017Szelenkov@nginx.com headers={ 4671017Szelenkov@nginx.com 'Host': 'localhost', 4681017Szelenkov@nginx.com 'Input-Length': '0', 4691017Szelenkov@nginx.com 'Connection': 'close', 4701017Szelenkov@nginx.com }, 4711017Szelenkov@nginx.com body=body, 4721017Szelenkov@nginx.com ) 473603Szelenkov@nginx.com 4741596Szelenkov@nginx.com assert resp['body'] == '', 'input read length zero' 475603Szelenkov@nginx.com 4761017Szelenkov@nginx.com resp = self.post( 4771017Szelenkov@nginx.com headers={ 4781017Szelenkov@nginx.com 'Host': 'localhost', 4791017Szelenkov@nginx.com 'Input-Length': '-1', 4801017Szelenkov@nginx.com 'Connection': 'close', 4811017Szelenkov@nginx.com }, 4821017Szelenkov@nginx.com body=body, 4831017Szelenkov@nginx.com ) 484603Szelenkov@nginx.com 4851596Szelenkov@nginx.com assert resp['body'] == body, 'input read length negative' 486603Szelenkov@nginx.com 4871596Szelenkov@nginx.com @pytest.mark.skip('not yet') 488603Szelenkov@nginx.com def test_python_application_errors_write(self): 489603Szelenkov@nginx.com self.load('errors_write') 490603Szelenkov@nginx.com 491603Szelenkov@nginx.com self.get() 492603Szelenkov@nginx.com 4931596Szelenkov@nginx.com assert ( 4941596Szelenkov@nginx.com self.wait_for_record(r'\[error\].+Error in application\.') 4951596Szelenkov@nginx.com is not None 4961596Szelenkov@nginx.com ), 'errors write' 497603Szelenkov@nginx.com 498603Szelenkov@nginx.com def test_python_application_body_array(self): 499603Szelenkov@nginx.com self.load('body_array') 500603Szelenkov@nginx.com 5011596Szelenkov@nginx.com assert self.get()['body'] == '0123456789', 'body array' 502603Szelenkov@nginx.com 503603Szelenkov@nginx.com def test_python_application_body_io(self): 504603Szelenkov@nginx.com self.load('body_io') 505603Szelenkov@nginx.com 5061596Szelenkov@nginx.com assert self.get()['body'] == '0123456789', 'body io' 507603Szelenkov@nginx.com 508603Szelenkov@nginx.com def test_python_application_body_io_file(self): 509603Szelenkov@nginx.com self.load('body_io_file') 510603Szelenkov@nginx.com 5111596Szelenkov@nginx.com assert self.get()['body'] == 'body\n', 'body io file' 512603Szelenkov@nginx.com 5131596Szelenkov@nginx.com @pytest.mark.skip('not yet') 5141736Szelenkov@nginx.com def test_python_application_syntax_error(self, skip_alert): 5151596Szelenkov@nginx.com skip_alert(r'Python failed to import module "wsgi"') 516603Szelenkov@nginx.com self.load('syntax_error') 517603Szelenkov@nginx.com 5181596Szelenkov@nginx.com assert self.get()['status'] == 500, 'syntax error' 519603Szelenkov@nginx.com 5201736Szelenkov@nginx.com def test_python_application_loading_error(self, skip_alert): 5211596Szelenkov@nginx.com skip_alert(r'Python failed to import module "blah"') 5221568Szelenkov@nginx.com 5231695Smax.romanov@nginx.com self.load('empty', module="blah") 5241568Szelenkov@nginx.com 5251596Szelenkov@nginx.com assert self.get()['status'] == 503, 'loading error' 5261568Szelenkov@nginx.com 527603Szelenkov@nginx.com def test_python_application_close(self): 528603Szelenkov@nginx.com self.load('close') 529603Szelenkov@nginx.com 530603Szelenkov@nginx.com self.get() 531603Szelenkov@nginx.com 5321596Szelenkov@nginx.com assert self.wait_for_record(r'Close called\.') is not None, 'close' 533603Szelenkov@nginx.com 534603Szelenkov@nginx.com def test_python_application_close_error(self): 535603Szelenkov@nginx.com self.load('close_error') 536603Szelenkov@nginx.com 537603Szelenkov@nginx.com self.get() 538603Szelenkov@nginx.com 5391596Szelenkov@nginx.com assert ( 5401596Szelenkov@nginx.com self.wait_for_record(r'Close called\.') is not None 5411596Szelenkov@nginx.com ), 'close error' 542603Szelenkov@nginx.com 543617Szelenkov@nginx.com def test_python_application_not_iterable(self): 544617Szelenkov@nginx.com self.load('not_iterable') 545617Szelenkov@nginx.com 546665Szelenkov@nginx.com self.get() 547617Szelenkov@nginx.com 5481596Szelenkov@nginx.com assert ( 5491028Szelenkov@nginx.com self.wait_for_record( 5501017Szelenkov@nginx.com r'\[error\].+the application returned not an iterable object' 5511596Szelenkov@nginx.com ) 5521596Szelenkov@nginx.com is not None 5531596Szelenkov@nginx.com ), 'not iterable' 554617Szelenkov@nginx.com 555664Szelenkov@nginx.com def test_python_application_write(self): 556664Szelenkov@nginx.com self.load('write') 557664Szelenkov@nginx.com 5581596Szelenkov@nginx.com assert self.get()['body'] == '0123456789', 'write' 559664Szelenkov@nginx.com 5601261Szelenkov@nginx.com def test_python_application_threading(self): 5611261Szelenkov@nginx.com """wait_for_record() timeouts after 5s while every thread works at 5621261Szelenkov@nginx.com least 3s. So without releasing GIL test should fail. 5631261Szelenkov@nginx.com """ 5641261Szelenkov@nginx.com 5651261Szelenkov@nginx.com self.load('threading') 5661261Szelenkov@nginx.com 5671261Szelenkov@nginx.com for _ in range(10): 5681261Szelenkov@nginx.com self.get(no_recv=True) 5691261Szelenkov@nginx.com 5701596Szelenkov@nginx.com assert ( 5711596Szelenkov@nginx.com self.wait_for_record(r'\(5\) Thread: 100') is not None 5721596Szelenkov@nginx.com ), 'last thread finished' 5731017Szelenkov@nginx.com 5741283Szelenkov@nginx.com def test_python_application_iter_exception(self): 5751283Szelenkov@nginx.com self.load('iter_exception') 5761283Szelenkov@nginx.com 5771283Szelenkov@nginx.com # Default request doesn't lead to the exception. 5781283Szelenkov@nginx.com 5791283Szelenkov@nginx.com resp = self.get( 5801283Szelenkov@nginx.com headers={ 5811283Szelenkov@nginx.com 'Host': 'localhost', 5821283Szelenkov@nginx.com 'X-Skip': '9', 5831283Szelenkov@nginx.com 'X-Chunked': '1', 5841283Szelenkov@nginx.com 'Connection': 'close', 5851283Szelenkov@nginx.com } 5861283Szelenkov@nginx.com ) 5871596Szelenkov@nginx.com assert resp['status'] == 200, 'status' 5881596Szelenkov@nginx.com assert resp['body'] == 'XXXXXXX', 'body' 5891283Szelenkov@nginx.com 5901283Szelenkov@nginx.com # Exception before start_response(). 5911283Szelenkov@nginx.com 5921596Szelenkov@nginx.com assert self.get()['status'] == 503, 'error' 5931283Szelenkov@nginx.com 5941596Szelenkov@nginx.com assert self.wait_for_record(r'Traceback') is not None, 'traceback' 5951596Szelenkov@nginx.com assert ( 5961596Szelenkov@nginx.com self.wait_for_record(r'raise Exception\(\'first exception\'\)') 5971596Szelenkov@nginx.com is not None 5981596Szelenkov@nginx.com ), 'first exception raise' 5991596Szelenkov@nginx.com assert len(self.findall(r'Traceback')) == 1, 'traceback count 1' 6001283Szelenkov@nginx.com 6011283Szelenkov@nginx.com # Exception after start_response(), before first write(). 6021283Szelenkov@nginx.com 6031596Szelenkov@nginx.com assert ( 6041283Szelenkov@nginx.com self.get( 6051283Szelenkov@nginx.com headers={ 6061283Szelenkov@nginx.com 'Host': 'localhost', 6071283Szelenkov@nginx.com 'X-Skip': '1', 6081283Szelenkov@nginx.com 'Connection': 'close', 6091283Szelenkov@nginx.com } 6101596Szelenkov@nginx.com )['status'] 6111596Szelenkov@nginx.com == 503 6121596Szelenkov@nginx.com ), 'error 2' 6131283Szelenkov@nginx.com 6141596Szelenkov@nginx.com assert ( 6151596Szelenkov@nginx.com self.wait_for_record(r'raise Exception\(\'second exception\'\)') 6161596Szelenkov@nginx.com is not None 6171596Szelenkov@nginx.com ), 'exception raise second' 6181596Szelenkov@nginx.com assert len(self.findall(r'Traceback')) == 2, 'traceback count 2' 6191283Szelenkov@nginx.com 6201283Szelenkov@nginx.com # Exception after first write(), before first __next__(). 6211283Szelenkov@nginx.com 6221283Szelenkov@nginx.com _, sock = self.get( 6231283Szelenkov@nginx.com headers={ 6241283Szelenkov@nginx.com 'Host': 'localhost', 6251283Szelenkov@nginx.com 'X-Skip': '2', 6261283Szelenkov@nginx.com 'Connection': 'keep-alive', 6271283Szelenkov@nginx.com }, 6281283Szelenkov@nginx.com start=True, 6291283Szelenkov@nginx.com ) 6301283Szelenkov@nginx.com 6311596Szelenkov@nginx.com assert ( 6321596Szelenkov@nginx.com self.wait_for_record(r'raise Exception\(\'third exception\'\)') 6331596Szelenkov@nginx.com is not None 6341596Szelenkov@nginx.com ), 'exception raise third' 6351596Szelenkov@nginx.com assert len(self.findall(r'Traceback')) == 3, 'traceback count 3' 6361283Szelenkov@nginx.com 6371596Szelenkov@nginx.com assert self.get(sock=sock) == {}, 'closed connection' 6381283Szelenkov@nginx.com 6391283Szelenkov@nginx.com # Exception after first write(), before first __next__(), 6401283Szelenkov@nginx.com # chunked (incomplete body). 6411283Szelenkov@nginx.com 6421283Szelenkov@nginx.com resp = self.get( 6431283Szelenkov@nginx.com headers={ 6441283Szelenkov@nginx.com 'Host': 'localhost', 6451283Szelenkov@nginx.com 'X-Skip': '2', 6461283Szelenkov@nginx.com 'X-Chunked': '1', 6471283Szelenkov@nginx.com 'Connection': 'close', 6481295St.nateldemoura@f5.com }, 6491596Szelenkov@nginx.com raw_resp=True, 6501283Szelenkov@nginx.com ) 6511295St.nateldemoura@f5.com if resp: 6521596Szelenkov@nginx.com assert resp[-5:] != '0\r\n\r\n', 'incomplete body' 6531596Szelenkov@nginx.com assert len(self.findall(r'Traceback')) == 4, 'traceback count 4' 6541283Szelenkov@nginx.com 6551283Szelenkov@nginx.com # Exception in __next__(). 6561283Szelenkov@nginx.com 6571283Szelenkov@nginx.com _, sock = self.get( 6581283Szelenkov@nginx.com headers={ 6591283Szelenkov@nginx.com 'Host': 'localhost', 6601283Szelenkov@nginx.com 'X-Skip': '3', 6611283Szelenkov@nginx.com 'Connection': 'keep-alive', 6621283Szelenkov@nginx.com }, 6631283Szelenkov@nginx.com start=True, 6641283Szelenkov@nginx.com ) 6651283Szelenkov@nginx.com 6661596Szelenkov@nginx.com assert ( 6671596Szelenkov@nginx.com self.wait_for_record(r'raise Exception\(\'next exception\'\)') 6681596Szelenkov@nginx.com is not None 6691596Szelenkov@nginx.com ), 'exception raise next' 6701596Szelenkov@nginx.com assert len(self.findall(r'Traceback')) == 5, 'traceback count 5' 6711283Szelenkov@nginx.com 6721596Szelenkov@nginx.com assert self.get(sock=sock) == {}, 'closed connection 2' 6731283Szelenkov@nginx.com 6741283Szelenkov@nginx.com # Exception in __next__(), chunked (incomplete body). 6751283Szelenkov@nginx.com 6761283Szelenkov@nginx.com resp = self.get( 6771283Szelenkov@nginx.com headers={ 6781283Szelenkov@nginx.com 'Host': 'localhost', 6791283Szelenkov@nginx.com 'X-Skip': '3', 6801283Szelenkov@nginx.com 'X-Chunked': '1', 6811283Szelenkov@nginx.com 'Connection': 'close', 6821295St.nateldemoura@f5.com }, 6831596Szelenkov@nginx.com raw_resp=True, 6841283Szelenkov@nginx.com ) 6851295St.nateldemoura@f5.com if resp: 6861596Szelenkov@nginx.com assert resp[-5:] != '0\r\n\r\n', 'incomplete body 2' 6871596Szelenkov@nginx.com assert len(self.findall(r'Traceback')) == 6, 'traceback count 6' 6881283Szelenkov@nginx.com 6891283Szelenkov@nginx.com # Exception before start_response() and in close(). 6901283Szelenkov@nginx.com 6911596Szelenkov@nginx.com assert ( 6921283Szelenkov@nginx.com self.get( 6931283Szelenkov@nginx.com headers={ 6941283Szelenkov@nginx.com 'Host': 'localhost', 6951283Szelenkov@nginx.com 'X-Not-Skip-Close': '1', 6961283Szelenkov@nginx.com 'Connection': 'close', 6971283Szelenkov@nginx.com } 6981596Szelenkov@nginx.com )['status'] 6991596Szelenkov@nginx.com == 503 7001596Szelenkov@nginx.com ), 'error' 7011283Szelenkov@nginx.com 7021596Szelenkov@nginx.com assert ( 7031596Szelenkov@nginx.com self.wait_for_record(r'raise Exception\(\'close exception\'\)') 7041596Szelenkov@nginx.com is not None 7051596Szelenkov@nginx.com ), 'exception raise close' 7061596Szelenkov@nginx.com assert len(self.findall(r'Traceback')) == 8, 'traceback count 8' 7071283Szelenkov@nginx.com 7081596Szelenkov@nginx.com def test_python_user_group(self, is_su): 7091596Szelenkov@nginx.com if not is_su: 7101596Szelenkov@nginx.com pytest.skip('requires root') 7111304St.nateldemoura@f5.com 7121304St.nateldemoura@f5.com nobody_uid = pwd.getpwnam('nobody').pw_uid 7131304St.nateldemoura@f5.com 7141304St.nateldemoura@f5.com group = 'nobody' 7151304St.nateldemoura@f5.com 7161304St.nateldemoura@f5.com try: 7171304St.nateldemoura@f5.com group_id = grp.getgrnam(group).gr_gid 7181706Smax.romanov@nginx.com except KeyError: 7191304St.nateldemoura@f5.com group = 'nogroup' 7201304St.nateldemoura@f5.com group_id = grp.getgrnam(group).gr_gid 7211304St.nateldemoura@f5.com 7221304St.nateldemoura@f5.com self.load('user_group') 7231304St.nateldemoura@f5.com 7241304St.nateldemoura@f5.com obj = self.getjson()['body'] 7251596Szelenkov@nginx.com assert obj['UID'] == nobody_uid, 'nobody uid' 7261596Szelenkov@nginx.com assert obj['GID'] == group_id, 'nobody gid' 7271304St.nateldemoura@f5.com 7281304St.nateldemoura@f5.com self.load('user_group', user='nobody') 7291304St.nateldemoura@f5.com 7301304St.nateldemoura@f5.com obj = self.getjson()['body'] 7311596Szelenkov@nginx.com assert obj['UID'] == nobody_uid, 'nobody uid user=nobody' 7321596Szelenkov@nginx.com assert obj['GID'] == group_id, 'nobody gid user=nobody' 7331304St.nateldemoura@f5.com 7341304St.nateldemoura@f5.com self.load('user_group', user='nobody', group=group) 7351304St.nateldemoura@f5.com 7361304St.nateldemoura@f5.com obj = self.getjson()['body'] 7371596Szelenkov@nginx.com assert obj['UID'] == nobody_uid, ( 7381596Szelenkov@nginx.com 'nobody uid user=nobody group=%s' % group 7391304St.nateldemoura@f5.com ) 7401304St.nateldemoura@f5.com 7411596Szelenkov@nginx.com assert obj['GID'] == group_id, ( 7421596Szelenkov@nginx.com 'nobody gid user=nobody group=%s' % group 7431304St.nateldemoura@f5.com ) 7441304St.nateldemoura@f5.com 7451304St.nateldemoura@f5.com self.load('user_group', group=group) 7461304St.nateldemoura@f5.com 7471304St.nateldemoura@f5.com obj = self.getjson()['body'] 7481596Szelenkov@nginx.com assert obj['UID'] == nobody_uid, 'nobody uid group=%s' % group 7491304St.nateldemoura@f5.com 7501596Szelenkov@nginx.com assert obj['GID'] == group_id, 'nobody gid group=%s' % group 7511304St.nateldemoura@f5.com 7521304St.nateldemoura@f5.com self.load('user_group', user='root') 7531304St.nateldemoura@f5.com 7541304St.nateldemoura@f5.com obj = self.getjson()['body'] 7551596Szelenkov@nginx.com assert obj['UID'] == 0, 'root uid user=root' 7561596Szelenkov@nginx.com assert obj['GID'] == 0, 'root gid user=root' 7571304St.nateldemoura@f5.com 7581304St.nateldemoura@f5.com group = 'root' 7591304St.nateldemoura@f5.com 7601304St.nateldemoura@f5.com try: 7611304St.nateldemoura@f5.com grp.getgrnam(group) 7621304St.nateldemoura@f5.com group = True 7631706Smax.romanov@nginx.com except KeyError: 7641304St.nateldemoura@f5.com group = False 7651304St.nateldemoura@f5.com 7661304St.nateldemoura@f5.com if group: 7671304St.nateldemoura@f5.com self.load('user_group', user='root', group='root') 7681304St.nateldemoura@f5.com 7691304St.nateldemoura@f5.com obj = self.getjson()['body'] 7701596Szelenkov@nginx.com assert obj['UID'] == 0, 'root uid user=root group=root' 7711596Szelenkov@nginx.com assert obj['GID'] == 0, 'root gid user=root group=root' 7721304St.nateldemoura@f5.com 7731304St.nateldemoura@f5.com self.load('user_group', group='root') 7741304St.nateldemoura@f5.com 7751304St.nateldemoura@f5.com obj = self.getjson()['body'] 7761596Szelenkov@nginx.com assert obj['UID'] == nobody_uid, 'root uid group=root' 7771596Szelenkov@nginx.com assert obj['GID'] == 0, 'root gid group=root' 7781604Smax.romanov@nginx.com 7791736Szelenkov@nginx.com def test_python_application_callable(self, skip_alert): 7801604Smax.romanov@nginx.com skip_alert(r'Python failed to get "blah" from module') 7811604Smax.romanov@nginx.com self.load('callable') 7821604Smax.romanov@nginx.com 7831604Smax.romanov@nginx.com assert self.get()['status'] == 204, 'default application response' 7841604Smax.romanov@nginx.com 7851695Smax.romanov@nginx.com self.load('callable', callable="app") 7861604Smax.romanov@nginx.com 7871604Smax.romanov@nginx.com assert self.get()['status'] == 200, 'callable response' 7881604Smax.romanov@nginx.com 7891695Smax.romanov@nginx.com self.load('callable', callable="blah") 7901604Smax.romanov@nginx.com 7911604Smax.romanov@nginx.com assert self.get()['status'] not in [200, 204], 'callable response inv' 7921604Smax.romanov@nginx.com 7931683Smax.romanov@nginx.com def test_python_application_threads(self): 7941695Smax.romanov@nginx.com self.load('threads', threads=4) 7951683Smax.romanov@nginx.com 7961683Smax.romanov@nginx.com socks = [] 7971683Smax.romanov@nginx.com 7981683Smax.romanov@nginx.com for i in range(4): 7991683Smax.romanov@nginx.com (_, sock) = self.get( 8001683Smax.romanov@nginx.com headers={ 8011683Smax.romanov@nginx.com 'Host': 'localhost', 8021683Smax.romanov@nginx.com 'X-Delay': '2', 8031683Smax.romanov@nginx.com 'Connection': 'close', 8041683Smax.romanov@nginx.com }, 8051683Smax.romanov@nginx.com no_recv=True, 8061683Smax.romanov@nginx.com start=True, 8071683Smax.romanov@nginx.com ) 8081683Smax.romanov@nginx.com 8091683Smax.romanov@nginx.com socks.append(sock) 8101683Smax.romanov@nginx.com 8111683Smax.romanov@nginx.com threads = set() 8121683Smax.romanov@nginx.com 8131683Smax.romanov@nginx.com for sock in socks: 8141683Smax.romanov@nginx.com resp = self.recvall(sock).decode('utf-8') 8151683Smax.romanov@nginx.com 8161683Smax.romanov@nginx.com self.log_in(resp) 8171683Smax.romanov@nginx.com 8181683Smax.romanov@nginx.com resp = self._resp_to_dict(resp) 8191683Smax.romanov@nginx.com 8201683Smax.romanov@nginx.com assert resp['status'] == 200, 'status' 8211683Smax.romanov@nginx.com 8221683Smax.romanov@nginx.com threads.add(resp['headers']['X-Thread']) 8231683Smax.romanov@nginx.com 8241683Smax.romanov@nginx.com assert resp['headers']['Wsgi-Multithread'] == 'True', 'multithread' 8251683Smax.romanov@nginx.com 8261683Smax.romanov@nginx.com sock.close() 8271683Smax.romanov@nginx.com 8281683Smax.romanov@nginx.com assert len(socks) == len(threads), 'threads differs' 829