11304St.nateldemoura@f5.comimport grp
21791Szelenkov@nginx.comimport os
31304St.nateldemoura@f5.comimport pwd
41477Szelenkov@nginx.comimport re
52329Szelenkov@nginx.comimport subprocess
6899Szelenkov@nginx.comimport time
72329Szelenkov@nginx.comimport venv
81477Szelenkov@nginx.com
91635Szelenkov@nginx.comimport pytest
102329Szelenkov@nginx.comfrom packaging import version
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
17552Szelenkov@nginx.com    def test_python_application_variables(self):
18552Szelenkov@nginx.com        self.load('variables')
19484Szelenkov@nginx.com
20484Szelenkov@nginx.com        body = 'Test body string.'
21484Szelenkov@nginx.com
221605Smax.romanov@nginx.com        resp = self.http(
232330Szelenkov@nginx.com            f"""POST / HTTP/1.1
241605Smax.romanov@nginx.comHost: localhost
252330Szelenkov@nginx.comContent-Length: {len(body)}
261605Smax.romanov@nginx.comCustom-Header: blah
271605Smax.romanov@nginx.comCustom-hEader: Blah
281605Smax.romanov@nginx.comContent-Type: text/html
291605Smax.romanov@nginx.comConnection: close
301605Smax.romanov@nginx.comcustom-header: BLAH
311605Smax.romanov@nginx.com
322330Szelenkov@nginx.com{body}""".encode(),
331605Smax.romanov@nginx.com            raw=True,
341017Szelenkov@nginx.com        )
35484Szelenkov@nginx.com
361596Szelenkov@nginx.com        assert resp['status'] == 200, 'status'
37505Szelenkov@nginx.com        headers = resp['headers']
38674Szelenkov@nginx.com        header_server = headers.pop('Server')
391596Szelenkov@nginx.com        assert re.search(r'Unit/[\d\.]+', header_server), 'server header'
401596Szelenkov@nginx.com        assert (
411596Szelenkov@nginx.com            headers.pop('Server-Software') == header_server
421596Szelenkov@nginx.com        ), 'server software header'
43599Szelenkov@nginx.com
44599Szelenkov@nginx.com        date = headers.pop('Date')
451596Szelenkov@nginx.com        assert date[-4:] == ' GMT', 'date header timezone'
461596Szelenkov@nginx.com        assert (
471596Szelenkov@nginx.com            abs(self.date_to_sec_epoch(date) - self.sec_epoch()) < 5
481596Szelenkov@nginx.com        ), 'date header'
49599Szelenkov@nginx.com
501596Szelenkov@nginx.com        assert headers == {
511596Szelenkov@nginx.com            'Connection': 'close',
521596Szelenkov@nginx.com            'Content-Length': str(len(body)),
531596Szelenkov@nginx.com            'Content-Type': 'text/html',
541596Szelenkov@nginx.com            'Request-Method': 'POST',
551596Szelenkov@nginx.com            'Request-Uri': '/',
561596Szelenkov@nginx.com            'Http-Host': 'localhost',
571596Szelenkov@nginx.com            'Server-Protocol': 'HTTP/1.1',
581605Smax.romanov@nginx.com            'Custom-Header': 'blah, Blah, BLAH',
591596Szelenkov@nginx.com            'Wsgi-Version': '(1, 0)',
601596Szelenkov@nginx.com            'Wsgi-Url-Scheme': 'http',
611596Szelenkov@nginx.com            'Wsgi-Multithread': 'False',
621596Szelenkov@nginx.com            'Wsgi-Multiprocess': 'True',
631596Szelenkov@nginx.com            'Wsgi-Run-Once': 'False',
641596Szelenkov@nginx.com        }, 'headers'
651596Szelenkov@nginx.com        assert resp['body'] == body, 'body'
66484Szelenkov@nginx.com
67497Szelenkov@nginx.com    def test_python_application_query_string(self):
68552Szelenkov@nginx.com        self.load('query_string')
69497Szelenkov@nginx.com
70505Szelenkov@nginx.com        resp = self.get(url='/?var1=val1&var2=val2')
71497Szelenkov@nginx.com
721596Szelenkov@nginx.com        assert (
731596Szelenkov@nginx.com            resp['headers']['Query-String'] == 'var1=val1&var2=val2'
741596Szelenkov@nginx.com        ), 'Query-String header'
75497Szelenkov@nginx.com
761171Svbart@nginx.com    def test_python_application_query_string_space(self):
771171Svbart@nginx.com        self.load('query_string')
781171Svbart@nginx.com
791171Svbart@nginx.com        resp = self.get(url='/ ?var1=val1&var2=val2')
801596Szelenkov@nginx.com        assert (
811596Szelenkov@nginx.com            resp['headers']['Query-String'] == 'var1=val1&var2=val2'
821596Szelenkov@nginx.com        ), 'Query-String space'
831171Svbart@nginx.com
841171Svbart@nginx.com        resp = self.get(url='/ %20?var1=val1&var2=val2')
851596Szelenkov@nginx.com        assert (
861596Szelenkov@nginx.com            resp['headers']['Query-String'] == 'var1=val1&var2=val2'
871596Szelenkov@nginx.com        ), 'Query-String space 2'
881171Svbart@nginx.com
891171Svbart@nginx.com        resp = self.get(url='/ %20 ?var1=val1&var2=val2')
901596Szelenkov@nginx.com        assert (
911596Szelenkov@nginx.com            resp['headers']['Query-String'] == 'var1=val1&var2=val2'
921596Szelenkov@nginx.com        ), 'Query-String space 3'
931171Svbart@nginx.com
941171Svbart@nginx.com        resp = self.get(url='/blah %20 blah? var1= val1 & var2=val2')
951596Szelenkov@nginx.com        assert (
961596Szelenkov@nginx.com            resp['headers']['Query-String'] == ' var1= val1 & var2=val2'
971596Szelenkov@nginx.com        ), 'Query-String space 4'
981171Svbart@nginx.com
992273Sjeff.iadarola@gmail.com    def test_python_application_prefix(self):
1002273Sjeff.iadarola@gmail.com        self.load('prefix', prefix='/api/rest')
1012273Sjeff.iadarola@gmail.com
1022273Sjeff.iadarola@gmail.com        def set_prefix(prefix):
1032330Szelenkov@nginx.com            self.conf(f'"{prefix}"', 'applications/prefix/prefix')
1042273Sjeff.iadarola@gmail.com
1052273Sjeff.iadarola@gmail.com        def check_prefix(url, script_name, path_info):
1062273Sjeff.iadarola@gmail.com            resp = self.get(url=url)
1072273Sjeff.iadarola@gmail.com            assert resp['status'] == 200
1082273Sjeff.iadarola@gmail.com            assert resp['headers']['Script-Name'] == script_name
1092273Sjeff.iadarola@gmail.com            assert resp['headers']['Path-Info'] == path_info
1102273Sjeff.iadarola@gmail.com
1112273Sjeff.iadarola@gmail.com        check_prefix('/ap', 'NULL', '/ap')
1122273Sjeff.iadarola@gmail.com        check_prefix('/api', 'NULL', '/api')
1132273Sjeff.iadarola@gmail.com        check_prefix('/api/', 'NULL', '/api/')
1142273Sjeff.iadarola@gmail.com        check_prefix('/api/res', 'NULL', '/api/res')
1152273Sjeff.iadarola@gmail.com        check_prefix('/api/restful', 'NULL', '/api/restful')
1162273Sjeff.iadarola@gmail.com        check_prefix('/api/rest', '/api/rest', '')
1172273Sjeff.iadarola@gmail.com        check_prefix('/api/rest/', '/api/rest', '/')
1182273Sjeff.iadarola@gmail.com        check_prefix('/api/rest/get', '/api/rest', '/get')
1192273Sjeff.iadarola@gmail.com        check_prefix('/api/rest/get/blah', '/api/rest', '/get/blah')
1202273Sjeff.iadarola@gmail.com
1212273Sjeff.iadarola@gmail.com        set_prefix('/api/rest/')
1222273Sjeff.iadarola@gmail.com        check_prefix('/api/rest', '/api/rest', '')
1232273Sjeff.iadarola@gmail.com        check_prefix('/api/restful', 'NULL', '/api/restful')
1242273Sjeff.iadarola@gmail.com        check_prefix('/api/rest/', '/api/rest', '/')
1252273Sjeff.iadarola@gmail.com        check_prefix('/api/rest/blah', '/api/rest', '/blah')
1262273Sjeff.iadarola@gmail.com
1272273Sjeff.iadarola@gmail.com        set_prefix('/app')
1282273Sjeff.iadarola@gmail.com        check_prefix('/ap', 'NULL', '/ap')
1292273Sjeff.iadarola@gmail.com        check_prefix('/app', '/app', '')
1302273Sjeff.iadarola@gmail.com        check_prefix('/app/', '/app', '/')
1312273Sjeff.iadarola@gmail.com        check_prefix('/application/', 'NULL', '/application/')
1322273Sjeff.iadarola@gmail.com
1332273Sjeff.iadarola@gmail.com        set_prefix('/')
1342273Sjeff.iadarola@gmail.com        check_prefix('/', 'NULL', '/')
1352273Sjeff.iadarola@gmail.com        check_prefix('/app', 'NULL', '/app')
1362273Sjeff.iadarola@gmail.com
137894Szelenkov@nginx.com    def test_python_application_query_string_empty(self):
138894Szelenkov@nginx.com        self.load('query_string')
139894Szelenkov@nginx.com
140894Szelenkov@nginx.com        resp = self.get(url='/?')
141894Szelenkov@nginx.com
1421596Szelenkov@nginx.com        assert resp['status'] == 200, 'query string empty status'
1431596Szelenkov@nginx.com        assert resp['headers']['Query-String'] == '', 'query string empty'
144894Szelenkov@nginx.com
145894Szelenkov@nginx.com    def test_python_application_query_string_absent(self):
146894Szelenkov@nginx.com        self.load('query_string')
147894Szelenkov@nginx.com
148894Szelenkov@nginx.com        resp = self.get()
149894Szelenkov@nginx.com
1501596Szelenkov@nginx.com        assert resp['status'] == 200, 'query string absent status'
1511596Szelenkov@nginx.com        assert resp['headers']['Query-String'] == '', 'query string absent'
152894Szelenkov@nginx.com
1531596Szelenkov@nginx.com    @pytest.mark.skip('not yet')
154495Szelenkov@nginx.com    def test_python_application_server_port(self):
155552Szelenkov@nginx.com        self.load('server_port')
156495Szelenkov@nginx.com
1571596Szelenkov@nginx.com        assert (
1581596Szelenkov@nginx.com            self.get()['headers']['Server-Port'] == '7080'
1591596Szelenkov@nginx.com        ), 'Server-Port header'
160484Szelenkov@nginx.com
1611596Szelenkov@nginx.com    @pytest.mark.skip('not yet')
1621250Szelenkov@nginx.com    def test_python_application_working_directory_invalid(self):
1631250Szelenkov@nginx.com        self.load('empty')
1641250Szelenkov@nginx.com
1651596Szelenkov@nginx.com        assert 'success' in self.conf(
1661596Szelenkov@nginx.com            '"/blah"', 'applications/empty/working_directory'
1671596Szelenkov@nginx.com        ), 'configure invalid working_directory'
1681250Szelenkov@nginx.com
1691596Szelenkov@nginx.com        assert self.get()['status'] == 500, 'status'
1701250Szelenkov@nginx.com
171496Szelenkov@nginx.com    def test_python_application_204_transfer_encoding(self):
172552Szelenkov@nginx.com        self.load('204_no_content')
173496Szelenkov@nginx.com
1741596Szelenkov@nginx.com        assert (
1751596Szelenkov@nginx.com            'Transfer-Encoding' not in self.get()['headers']
1761596Szelenkov@nginx.com        ), '204 header transfer encoding'
177484Szelenkov@nginx.com
178602Szelenkov@nginx.com    def test_python_application_ctx_iter_atexit(self):
179602Szelenkov@nginx.com        self.load('ctx_iter_atexit')
180602Szelenkov@nginx.com
1812190Szelenkov@nginx.com        resp = self.post(body='0123456789')
182602Szelenkov@nginx.com
1831596Szelenkov@nginx.com        assert resp['status'] == 200, 'ctx iter status'
1841596Szelenkov@nginx.com        assert resp['body'] == '0123456789', 'ctx iter body'
185602Szelenkov@nginx.com
1861775Szelenkov@nginx.com        assert 'success' in self.conf({"listeners": {}, "applications": {}})
187602Szelenkov@nginx.com
1881596Szelenkov@nginx.com        assert (
1891596Szelenkov@nginx.com            self.wait_for_record(r'RuntimeError') is not None
1901596Szelenkov@nginx.com        ), 'ctx iter atexit'
191602Szelenkov@nginx.com
192603Szelenkov@nginx.com    def test_python_keepalive_body(self):
193603Szelenkov@nginx.com        self.load('mirror')
194603Szelenkov@nginx.com
1951596Szelenkov@nginx.com        assert self.get()['status'] == 200, 'init'
1961029Szelenkov@nginx.com
1971453Szelenkov@nginx.com        body = '0123456789' * 500
1981017Szelenkov@nginx.com        (resp, sock) = self.post(
1991017Szelenkov@nginx.com            headers={
2001017Szelenkov@nginx.com                'Host': 'localhost',
2011017Szelenkov@nginx.com                'Connection': 'keep-alive',
2021017Szelenkov@nginx.com            },
2031017Szelenkov@nginx.com            start=True,
2041453Szelenkov@nginx.com            body=body,
2051029Szelenkov@nginx.com            read_timeout=1,
2061017Szelenkov@nginx.com        )
207603Szelenkov@nginx.com
2081596Szelenkov@nginx.com        assert resp['body'] == body, 'keep-alive 1'
209603Szelenkov@nginx.com
2101453Szelenkov@nginx.com        body = '0123456789'
2112190Szelenkov@nginx.com        resp = self.post(sock=sock, body=body)
212603Szelenkov@nginx.com
2131596Szelenkov@nginx.com        assert resp['body'] == body, 'keep-alive 2'
214603Szelenkov@nginx.com
215684Szelenkov@nginx.com    def test_python_keepalive_reconfigure(self):
216684Szelenkov@nginx.com        self.load('mirror')
217684Szelenkov@nginx.com
2181596Szelenkov@nginx.com        assert self.get()['status'] == 200, 'init'
2191029Szelenkov@nginx.com
220684Szelenkov@nginx.com        body = '0123456789'
221684Szelenkov@nginx.com        conns = 3
222684Szelenkov@nginx.com        socks = []
223684Szelenkov@nginx.com
224684Szelenkov@nginx.com        for i in range(conns):
2251017Szelenkov@nginx.com            (resp, sock) = self.post(
2261017Szelenkov@nginx.com                headers={
2271017Szelenkov@nginx.com                    'Host': 'localhost',
2281017Szelenkov@nginx.com                    'Connection': 'keep-alive',
2291017Szelenkov@nginx.com                },
2301017Szelenkov@nginx.com                start=True,
2311017Szelenkov@nginx.com                body=body,
2321029Szelenkov@nginx.com                read_timeout=1,
2331017Szelenkov@nginx.com            )
234684Szelenkov@nginx.com
2351596Szelenkov@nginx.com            assert resp['body'] == body, 'keep-alive open'
2361695Smax.romanov@nginx.com
2371695Smax.romanov@nginx.com            self.load('mirror', processes=i + 1)
238684Szelenkov@nginx.com
239684Szelenkov@nginx.com            socks.append(sock)
240684Szelenkov@nginx.com
241684Szelenkov@nginx.com        for i in range(conns):
2421017Szelenkov@nginx.com            (resp, sock) = self.post(
2431017Szelenkov@nginx.com                headers={
2441017Szelenkov@nginx.com                    'Host': 'localhost',
2451017Szelenkov@nginx.com                    'Connection': 'keep-alive',
2461017Szelenkov@nginx.com                },
2471017Szelenkov@nginx.com                start=True,
2481017Szelenkov@nginx.com                sock=socks[i],
2491017Szelenkov@nginx.com                body=body,
2501029Szelenkov@nginx.com                read_timeout=1,
2511017Szelenkov@nginx.com            )
252684Szelenkov@nginx.com
2531596Szelenkov@nginx.com            assert resp['body'] == body, 'keep-alive request'
2541695Smax.romanov@nginx.com
2551695Smax.romanov@nginx.com            self.load('mirror', processes=i + 1)
256684Szelenkov@nginx.com
257684Szelenkov@nginx.com        for i in range(conns):
2582190Szelenkov@nginx.com            resp = self.post(sock=socks[i], body=body)
259684Szelenkov@nginx.com
2601596Szelenkov@nginx.com            assert resp['body'] == body, 'keep-alive close'
2611695Smax.romanov@nginx.com
2621695Smax.romanov@nginx.com            self.load('mirror', processes=i + 1)
263684Szelenkov@nginx.com
264750Szelenkov@nginx.com    def test_python_keepalive_reconfigure_2(self):
265750Szelenkov@nginx.com        self.load('mirror')
266750Szelenkov@nginx.com
2671596Szelenkov@nginx.com        assert self.get()['status'] == 200, 'init'
2681029Szelenkov@nginx.com
269750Szelenkov@nginx.com        body = '0123456789'
270750Szelenkov@nginx.com
2711017Szelenkov@nginx.com        (resp, sock) = self.post(
2721017Szelenkov@nginx.com            headers={
2731017Szelenkov@nginx.com                'Host': 'localhost',
2741017Szelenkov@nginx.com                'Connection': 'keep-alive',
2751017Szelenkov@nginx.com            },
2761017Szelenkov@nginx.com            start=True,
2771017Szelenkov@nginx.com            body=body,
2781029Szelenkov@nginx.com            read_timeout=1,
2791017Szelenkov@nginx.com        )
280750Szelenkov@nginx.com
2811596Szelenkov@nginx.com        assert resp['body'] == body, 'reconfigure 2 keep-alive 1'
282750Szelenkov@nginx.com
283750Szelenkov@nginx.com        self.load('empty')
284750Szelenkov@nginx.com
2851596Szelenkov@nginx.com        assert self.get()['status'] == 200, 'init'
2861029Szelenkov@nginx.com
2872190Szelenkov@nginx.com        (resp, sock) = self.post(start=True, sock=sock, body=body)
288750Szelenkov@nginx.com
2891596Szelenkov@nginx.com        assert resp['status'] == 200, 'reconfigure 2 keep-alive 2'
2901596Szelenkov@nginx.com        assert resp['body'] == '', 'reconfigure 2 keep-alive 2 body'
291750Szelenkov@nginx.com
2921596Szelenkov@nginx.com        assert 'success' in self.conf(
2931596Szelenkov@nginx.com            {"listeners": {}, "applications": {}}
2941596Szelenkov@nginx.com        ), 'reconfigure 2 clear configuration'
295750Szelenkov@nginx.com
296750Szelenkov@nginx.com        resp = self.get(sock=sock)
297750Szelenkov@nginx.com
2981596Szelenkov@nginx.com        assert resp == {}, 'reconfigure 2 keep-alive 3'
299750Szelenkov@nginx.com
300603Szelenkov@nginx.com    def test_python_atexit(self):
301603Szelenkov@nginx.com        self.load('atexit')
302603Szelenkov@nginx.com
303603Szelenkov@nginx.com        self.get()
304603Szelenkov@nginx.com
3051775Szelenkov@nginx.com        assert 'success' in self.conf({"listeners": {}, "applications": {}})
306603Szelenkov@nginx.com
3071596Szelenkov@nginx.com        assert self.wait_for_record(r'At exit called\.') is not None, 'atexit'
308603Szelenkov@nginx.com
3091454Szelenkov@nginx.com    def test_python_process_switch(self):
3101695Smax.romanov@nginx.com        self.load('delayed', processes=2)
3111454Szelenkov@nginx.com
3121596Szelenkov@nginx.com        self.get(
3131596Szelenkov@nginx.com            headers={
3141596Szelenkov@nginx.com                'Host': 'localhost',
3151596Szelenkov@nginx.com                'Content-Length': '0',
3161596Szelenkov@nginx.com                'X-Delay': '5',
3171596Szelenkov@nginx.com                'Connection': 'close',
3181596Szelenkov@nginx.com            },
3191596Szelenkov@nginx.com            no_recv=True,
3201596Szelenkov@nginx.com        )
3211454Szelenkov@nginx.com
3221454Szelenkov@nginx.com        headers_delay_1 = {
3231454Szelenkov@nginx.com            'Connection': 'close',
3241454Szelenkov@nginx.com            'Host': 'localhost',
3251454Szelenkov@nginx.com            'Content-Length': '0',
3261454Szelenkov@nginx.com            'X-Delay': '1',
3271454Szelenkov@nginx.com        }
3281454Szelenkov@nginx.com
3291454Szelenkov@nginx.com        self.get(headers=headers_delay_1, no_recv=True)
3301454Szelenkov@nginx.com
3311454Szelenkov@nginx.com        time.sleep(0.5)
3321454Szelenkov@nginx.com
3331454Szelenkov@nginx.com        for _ in range(10):
3341454Szelenkov@nginx.com            self.get(headers=headers_delay_1, no_recv=True)
3351454Szelenkov@nginx.com
3361454Szelenkov@nginx.com        self.get(headers=headers_delay_1)
3371454Szelenkov@nginx.com
3381596Szelenkov@nginx.com    @pytest.mark.skip('not yet')
339603Szelenkov@nginx.com    def test_python_application_start_response_exit(self):
340603Szelenkov@nginx.com        self.load('start_response_exit')
341603Szelenkov@nginx.com
3421596Szelenkov@nginx.com        assert self.get()['status'] == 500, 'start response exit'
343603Szelenkov@nginx.com
344603Szelenkov@nginx.com    def test_python_application_input_iter(self):
345603Szelenkov@nginx.com        self.load('input_iter')
346603Szelenkov@nginx.com
3471400Smax.romanov@nginx.com        body = '''0123456789
3481400Smax.romanov@nginx.comnext line
3491400Smax.romanov@nginx.com
3501400Smax.romanov@nginx.comlast line'''
3511400Smax.romanov@nginx.com
3521400Smax.romanov@nginx.com        resp = self.post(body=body)
3531596Szelenkov@nginx.com        assert resp['body'] == body, 'input iter'
3541596Szelenkov@nginx.com        assert resp['headers']['X-Lines-Count'] == '4', 'input iter lines'
3551400Smax.romanov@nginx.com
3561400Smax.romanov@nginx.com    def test_python_application_input_readline(self):
3571400Smax.romanov@nginx.com        self.load('input_readline')
3581400Smax.romanov@nginx.com
3591400Smax.romanov@nginx.com        body = '''0123456789
3601400Smax.romanov@nginx.comnext line
3611400Smax.romanov@nginx.com
3621400Smax.romanov@nginx.comlast line'''
3631400Smax.romanov@nginx.com
3641400Smax.romanov@nginx.com        resp = self.post(body=body)
3651596Szelenkov@nginx.com        assert resp['body'] == body, 'input readline'
3661596Szelenkov@nginx.com        assert resp['headers']['X-Lines-Count'] == '4', 'input readline lines'
3671400Smax.romanov@nginx.com
3681400Smax.romanov@nginx.com    def test_python_application_input_readline_size(self):
3691400Smax.romanov@nginx.com        self.load('input_readline_size')
3701400Smax.romanov@nginx.com
3711400Smax.romanov@nginx.com        body = '''0123456789
3721400Smax.romanov@nginx.comnext line
3731400Smax.romanov@nginx.com
3741400Smax.romanov@nginx.comlast line'''
375603Szelenkov@nginx.com
3761596Szelenkov@nginx.com        assert self.post(body=body)['body'] == body, 'input readline size'
3771596Szelenkov@nginx.com        assert (
3781596Szelenkov@nginx.com            self.post(body='0123')['body'] == '0123'
3791596Szelenkov@nginx.com        ), 'input readline size less'
3801400Smax.romanov@nginx.com
3811400Smax.romanov@nginx.com    def test_python_application_input_readlines(self):
3821400Smax.romanov@nginx.com        self.load('input_readlines')
3831400Smax.romanov@nginx.com
3841400Smax.romanov@nginx.com        body = '''0123456789
3851400Smax.romanov@nginx.comnext line
3861400Smax.romanov@nginx.com
3871400Smax.romanov@nginx.comlast line'''
3881400Smax.romanov@nginx.com
3891400Smax.romanov@nginx.com        resp = self.post(body=body)
3901596Szelenkov@nginx.com        assert resp['body'] == body, 'input readlines'
3911596Szelenkov@nginx.com        assert resp['headers']['X-Lines-Count'] == '4', 'input readlines lines'
3921400Smax.romanov@nginx.com
3931400Smax.romanov@nginx.com    def test_python_application_input_readlines_huge(self):
3941400Smax.romanov@nginx.com        self.load('input_readlines')
3951400Smax.romanov@nginx.com
3961400Smax.romanov@nginx.com        body = (
3971400Smax.romanov@nginx.com            '''0123456789 abcdefghi
3981400Smax.romanov@nginx.comnext line: 0123456789 abcdefghi
3991400Smax.romanov@nginx.com
4001400Smax.romanov@nginx.comlast line: 987654321
4011400Smax.romanov@nginx.com'''
4021400Smax.romanov@nginx.com            * 512
4031400Smax.romanov@nginx.com        )
4041400Smax.romanov@nginx.com
4051596Szelenkov@nginx.com        assert (
4061596Szelenkov@nginx.com            self.post(body=body, read_buffer_size=16384)['body'] == body
4071596Szelenkov@nginx.com        ), 'input readlines huge'
408603Szelenkov@nginx.com
409603Szelenkov@nginx.com    def test_python_application_input_read_length(self):
410603Szelenkov@nginx.com        self.load('input_read_length')
411603Szelenkov@nginx.com
412603Szelenkov@nginx.com        body = '0123456789'
413603Szelenkov@nginx.com
4141017Szelenkov@nginx.com        resp = self.post(
4151017Szelenkov@nginx.com            headers={
4161017Szelenkov@nginx.com                'Host': 'localhost',
4171017Szelenkov@nginx.com                'Input-Length': '5',
4181017Szelenkov@nginx.com                'Connection': 'close',
4191017Szelenkov@nginx.com            },
4201017Szelenkov@nginx.com            body=body,
4211017Szelenkov@nginx.com        )
422603Szelenkov@nginx.com
4231596Szelenkov@nginx.com        assert resp['body'] == body[:5], 'input read length lt body'
424603Szelenkov@nginx.com
4251017Szelenkov@nginx.com        resp = self.post(
4261017Szelenkov@nginx.com            headers={
4271017Szelenkov@nginx.com                'Host': 'localhost',
4281017Szelenkov@nginx.com                'Input-Length': '15',
4291017Szelenkov@nginx.com                'Connection': 'close',
4301017Szelenkov@nginx.com            },
4311017Szelenkov@nginx.com            body=body,
4321017Szelenkov@nginx.com        )
433603Szelenkov@nginx.com
4341596Szelenkov@nginx.com        assert resp['body'] == body, 'input read length gt body'
435603Szelenkov@nginx.com
4361017Szelenkov@nginx.com        resp = self.post(
4371017Szelenkov@nginx.com            headers={
4381017Szelenkov@nginx.com                'Host': 'localhost',
4391017Szelenkov@nginx.com                'Input-Length': '0',
4401017Szelenkov@nginx.com                'Connection': 'close',
4411017Szelenkov@nginx.com            },
4421017Szelenkov@nginx.com            body=body,
4431017Szelenkov@nginx.com        )
444603Szelenkov@nginx.com
4451596Szelenkov@nginx.com        assert resp['body'] == '', 'input read length zero'
446603Szelenkov@nginx.com
4471017Szelenkov@nginx.com        resp = self.post(
4481017Szelenkov@nginx.com            headers={
4491017Szelenkov@nginx.com                'Host': 'localhost',
4501017Szelenkov@nginx.com                'Input-Length': '-1',
4511017Szelenkov@nginx.com                'Connection': 'close',
4521017Szelenkov@nginx.com            },
4531017Szelenkov@nginx.com            body=body,
4541017Szelenkov@nginx.com        )
455603Szelenkov@nginx.com
4561596Szelenkov@nginx.com        assert resp['body'] == body, 'input read length negative'
457603Szelenkov@nginx.com
4581596Szelenkov@nginx.com    @pytest.mark.skip('not yet')
459603Szelenkov@nginx.com    def test_python_application_errors_write(self):
460603Szelenkov@nginx.com        self.load('errors_write')
461603Szelenkov@nginx.com
462603Szelenkov@nginx.com        self.get()
463603Szelenkov@nginx.com
4641596Szelenkov@nginx.com        assert (
4651596Szelenkov@nginx.com            self.wait_for_record(r'\[error\].+Error in application\.')
4661596Szelenkov@nginx.com            is not None
4671596Szelenkov@nginx.com        ), 'errors write'
468603Szelenkov@nginx.com
469603Szelenkov@nginx.com    def test_python_application_body_array(self):
470603Szelenkov@nginx.com        self.load('body_array')
471603Szelenkov@nginx.com
4721596Szelenkov@nginx.com        assert self.get()['body'] == '0123456789', 'body array'
473603Szelenkov@nginx.com
474603Szelenkov@nginx.com    def test_python_application_body_io(self):
475603Szelenkov@nginx.com        self.load('body_io')
476603Szelenkov@nginx.com
4771596Szelenkov@nginx.com        assert self.get()['body'] == '0123456789', 'body io'
478603Szelenkov@nginx.com
479603Szelenkov@nginx.com    def test_python_application_body_io_file(self):
480603Szelenkov@nginx.com        self.load('body_io_file')
481603Szelenkov@nginx.com
4821596Szelenkov@nginx.com        assert self.get()['body'] == 'body\n', 'body io file'
483603Szelenkov@nginx.com
4841596Szelenkov@nginx.com    @pytest.mark.skip('not yet')
4851736Szelenkov@nginx.com    def test_python_application_syntax_error(self, skip_alert):
4861596Szelenkov@nginx.com        skip_alert(r'Python failed to import module "wsgi"')
487603Szelenkov@nginx.com        self.load('syntax_error')
488603Szelenkov@nginx.com
4891596Szelenkov@nginx.com        assert self.get()['status'] == 500, 'syntax error'
490603Szelenkov@nginx.com
4911736Szelenkov@nginx.com    def test_python_application_loading_error(self, skip_alert):
4921596Szelenkov@nginx.com        skip_alert(r'Python failed to import module "blah"')
4931568Szelenkov@nginx.com
4941695Smax.romanov@nginx.com        self.load('empty', module="blah")
4951568Szelenkov@nginx.com
4961596Szelenkov@nginx.com        assert self.get()['status'] == 503, 'loading error'
4971568Szelenkov@nginx.com
498603Szelenkov@nginx.com    def test_python_application_close(self):
499603Szelenkov@nginx.com        self.load('close')
500603Szelenkov@nginx.com
501603Szelenkov@nginx.com        self.get()
502603Szelenkov@nginx.com
5031596Szelenkov@nginx.com        assert self.wait_for_record(r'Close called\.') is not None, 'close'
504603Szelenkov@nginx.com
505603Szelenkov@nginx.com    def test_python_application_close_error(self):
506603Szelenkov@nginx.com        self.load('close_error')
507603Szelenkov@nginx.com
508603Szelenkov@nginx.com        self.get()
509603Szelenkov@nginx.com
5101596Szelenkov@nginx.com        assert (
5111596Szelenkov@nginx.com            self.wait_for_record(r'Close called\.') is not None
5121596Szelenkov@nginx.com        ), 'close error'
513603Szelenkov@nginx.com
514617Szelenkov@nginx.com    def test_python_application_not_iterable(self):
515617Szelenkov@nginx.com        self.load('not_iterable')
516617Szelenkov@nginx.com
517665Szelenkov@nginx.com        self.get()
518617Szelenkov@nginx.com
5191596Szelenkov@nginx.com        assert (
5201028Szelenkov@nginx.com            self.wait_for_record(
5211017Szelenkov@nginx.com                r'\[error\].+the application returned not an iterable object'
5221596Szelenkov@nginx.com            )
5231596Szelenkov@nginx.com            is not None
5241596Szelenkov@nginx.com        ), 'not iterable'
525617Szelenkov@nginx.com
526664Szelenkov@nginx.com    def test_python_application_write(self):
527664Szelenkov@nginx.com        self.load('write')
528664Szelenkov@nginx.com
5291596Szelenkov@nginx.com        assert self.get()['body'] == '0123456789', 'write'
530664Szelenkov@nginx.com
5312329Szelenkov@nginx.com    def test_python_application_encoding(self):
5322329Szelenkov@nginx.com        self.load('encoding')
5332329Szelenkov@nginx.com
5342329Szelenkov@nginx.com        try:
5352329Szelenkov@nginx.com            locales = (
5362329Szelenkov@nginx.com                subprocess.check_output(
5372329Szelenkov@nginx.com                    ['locale', '-a'],
5382329Szelenkov@nginx.com                    stderr=subprocess.STDOUT,
5392329Szelenkov@nginx.com                )
5402329Szelenkov@nginx.com                .decode()
5412329Szelenkov@nginx.com                .splitlines()
5422329Szelenkov@nginx.com            )
5432329Szelenkov@nginx.com        except (
5442329Szelenkov@nginx.com            FileNotFoundError,
5452329Szelenkov@nginx.com            UnicodeDecodeError,
5462329Szelenkov@nginx.com            subprocess.CalledProcessError,
5472329Szelenkov@nginx.com        ):
5482329Szelenkov@nginx.com            pytest.skip('require locale')
5492329Szelenkov@nginx.com
5502329Szelenkov@nginx.com        to_check = [
5512329Szelenkov@nginx.com            re.compile(r'.*UTF[-_]?8'),
5522329Szelenkov@nginx.com            re.compile(r'.*ISO[-_]?8859[-_]?1'),
5532329Szelenkov@nginx.com        ]
5542329Szelenkov@nginx.com        matches = [
5552329Szelenkov@nginx.com            loc
5562329Szelenkov@nginx.com            for loc in locales
5572329Szelenkov@nginx.com            if any(pattern.match(loc.upper()) for pattern in to_check)
5582329Szelenkov@nginx.com        ]
5592329Szelenkov@nginx.com
5602329Szelenkov@nginx.com        if not matches:
5612329Szelenkov@nginx.com            pytest.skip('no available locales')
5622329Szelenkov@nginx.com
5632329Szelenkov@nginx.com        def unify(str):
5642329Szelenkov@nginx.com            str.upper().replace('-', '').replace('_', '')
5652329Szelenkov@nginx.com
5662329Szelenkov@nginx.com        for loc in matches:
5672329Szelenkov@nginx.com            assert 'success' in self.conf(
5682329Szelenkov@nginx.com                {"LC_CTYPE": loc, "LC_ALL": ""},
5692329Szelenkov@nginx.com                '/config/applications/encoding/environment',
5702329Szelenkov@nginx.com            )
5712329Szelenkov@nginx.com            resp = self.get()
5722329Szelenkov@nginx.com            assert resp['status'] == 200, 'status'
5732329Szelenkov@nginx.com            assert unify(resp['headers']['X-Encoding']) == unify(
5742329Szelenkov@nginx.com                loc.split('.')[-1]
5752329Szelenkov@nginx.com            )
5762329Szelenkov@nginx.com
5772329Szelenkov@nginx.com    def test_python_application_unicode(self, temp_dir):
5782329Szelenkov@nginx.com        try:
5792329Szelenkov@nginx.com            app_type = self.get_application_type()
5802329Szelenkov@nginx.com            v = version.Version(app_type.split()[-1])
5812329Szelenkov@nginx.com            if v.major != 3:
5822329Szelenkov@nginx.com                raise version.InvalidVersion
5832329Szelenkov@nginx.com
5842329Szelenkov@nginx.com        except version.InvalidVersion:
5852329Szelenkov@nginx.com            pytest.skip('require python module version 3')
5862329Szelenkov@nginx.com
5872330Szelenkov@nginx.com        venv_path = f'{temp_dir}/venv'
5882329Szelenkov@nginx.com        venv.create(venv_path)
5892329Szelenkov@nginx.com
5902329Szelenkov@nginx.com        self.load('unicode')
5912329Szelenkov@nginx.com        assert 'success' in self.conf(
5922330Szelenkov@nginx.com            f'"{venv_path}"',
5932329Szelenkov@nginx.com            '/config/applications/unicode/home',
5942329Szelenkov@nginx.com        )
5952329Szelenkov@nginx.com        assert (
5962329Szelenkov@nginx.com            self.get(
5972329Szelenkov@nginx.com                headers={
5982329Szelenkov@nginx.com                    'Host': 'localhost',
5992329Szelenkov@nginx.com                    'Temp-dir': temp_dir,
6002329Szelenkov@nginx.com                    'Connection': 'close',
6012329Szelenkov@nginx.com                }
6022329Szelenkov@nginx.com            )['status']
6032329Szelenkov@nginx.com            == 200
6042329Szelenkov@nginx.com        )
6052329Szelenkov@nginx.com
6061261Szelenkov@nginx.com    def test_python_application_threading(self):
6071261Szelenkov@nginx.com        """wait_for_record() timeouts after 5s while every thread works at
6081261Szelenkov@nginx.com        least 3s.  So without releasing GIL test should fail.
6091261Szelenkov@nginx.com        """
6101261Szelenkov@nginx.com
6111261Szelenkov@nginx.com        self.load('threading')
6121261Szelenkov@nginx.com
6131261Szelenkov@nginx.com        for _ in range(10):
6141261Szelenkov@nginx.com            self.get(no_recv=True)
6151261Szelenkov@nginx.com
6161596Szelenkov@nginx.com        assert (
6171804Szelenkov@nginx.com            self.wait_for_record(r'\(5\) Thread: 100', wait=50) is not None
6181596Szelenkov@nginx.com        ), 'last thread finished'
6191017Szelenkov@nginx.com
6201283Szelenkov@nginx.com    def test_python_application_iter_exception(self):
6211283Szelenkov@nginx.com        self.load('iter_exception')
6221283Szelenkov@nginx.com
6231283Szelenkov@nginx.com        # Default request doesn't lead to the exception.
6241283Szelenkov@nginx.com
6251283Szelenkov@nginx.com        resp = self.get(
6261283Szelenkov@nginx.com            headers={
6271283Szelenkov@nginx.com                'Host': 'localhost',
6281283Szelenkov@nginx.com                'X-Skip': '9',
6291283Szelenkov@nginx.com                'X-Chunked': '1',
6301283Szelenkov@nginx.com                'Connection': 'close',
6311283Szelenkov@nginx.com            }
6321283Szelenkov@nginx.com        )
6331596Szelenkov@nginx.com        assert resp['status'] == 200, 'status'
6341596Szelenkov@nginx.com        assert resp['body'] == 'XXXXXXX', 'body'
6351283Szelenkov@nginx.com
6361283Szelenkov@nginx.com        # Exception before start_response().
6371283Szelenkov@nginx.com
6381596Szelenkov@nginx.com        assert self.get()['status'] == 503, 'error'
6391283Szelenkov@nginx.com
6401596Szelenkov@nginx.com        assert self.wait_for_record(r'Traceback') is not None, 'traceback'
6411596Szelenkov@nginx.com        assert (
6422330Szelenkov@nginx.com            self.wait_for_record(r"raise Exception\('first exception'\)")
6431596Szelenkov@nginx.com            is not None
6441596Szelenkov@nginx.com        ), 'first exception raise'
6451596Szelenkov@nginx.com        assert len(self.findall(r'Traceback')) == 1, 'traceback count 1'
6461283Szelenkov@nginx.com
6471283Szelenkov@nginx.com        # Exception after start_response(), before first write().
6481283Szelenkov@nginx.com
6491596Szelenkov@nginx.com        assert (
6501283Szelenkov@nginx.com            self.get(
6511283Szelenkov@nginx.com                headers={
6521283Szelenkov@nginx.com                    'Host': 'localhost',
6531283Szelenkov@nginx.com                    'X-Skip': '1',
6541283Szelenkov@nginx.com                    'Connection': 'close',
6551283Szelenkov@nginx.com                }
6561596Szelenkov@nginx.com            )['status']
6571596Szelenkov@nginx.com            == 503
6581596Szelenkov@nginx.com        ), 'error 2'
6591283Szelenkov@nginx.com
6601596Szelenkov@nginx.com        assert (
6612330Szelenkov@nginx.com            self.wait_for_record(r"raise Exception\('second exception'\)")
6621596Szelenkov@nginx.com            is not None
6631596Szelenkov@nginx.com        ), 'exception raise second'
6641596Szelenkov@nginx.com        assert len(self.findall(r'Traceback')) == 2, 'traceback count 2'
6651283Szelenkov@nginx.com
6661283Szelenkov@nginx.com        # Exception after first write(), before first __next__().
6671283Szelenkov@nginx.com
6681283Szelenkov@nginx.com        _, sock = self.get(
6691283Szelenkov@nginx.com            headers={
6701283Szelenkov@nginx.com                'Host': 'localhost',
6711283Szelenkov@nginx.com                'X-Skip': '2',
6721283Szelenkov@nginx.com                'Connection': 'keep-alive',
6731283Szelenkov@nginx.com            },
6741283Szelenkov@nginx.com            start=True,
6751283Szelenkov@nginx.com        )
6761283Szelenkov@nginx.com
6771596Szelenkov@nginx.com        assert (
6782330Szelenkov@nginx.com            self.wait_for_record(r"raise Exception\('third exception'\)")
6791596Szelenkov@nginx.com            is not None
6801596Szelenkov@nginx.com        ), 'exception raise third'
6811596Szelenkov@nginx.com        assert len(self.findall(r'Traceback')) == 3, 'traceback count 3'
6821283Szelenkov@nginx.com
6831596Szelenkov@nginx.com        assert self.get(sock=sock) == {}, 'closed connection'
6841283Szelenkov@nginx.com
6851283Szelenkov@nginx.com        # Exception after first write(), before first __next__(),
6861283Szelenkov@nginx.com        # chunked (incomplete body).
6871283Szelenkov@nginx.com
6881283Szelenkov@nginx.com        resp = self.get(
6891283Szelenkov@nginx.com            headers={
6901283Szelenkov@nginx.com                'Host': 'localhost',
6911283Szelenkov@nginx.com                'X-Skip': '2',
6921283Szelenkov@nginx.com                'X-Chunked': '1',
6931283Szelenkov@nginx.com                'Connection': 'close',
6941295St.nateldemoura@f5.com            },
6951596Szelenkov@nginx.com            raw_resp=True,
6961283Szelenkov@nginx.com        )
6971295St.nateldemoura@f5.com        if resp:
6981596Szelenkov@nginx.com            assert resp[-5:] != '0\r\n\r\n', 'incomplete body'
6991596Szelenkov@nginx.com        assert len(self.findall(r'Traceback')) == 4, 'traceback count 4'
7001283Szelenkov@nginx.com
7011283Szelenkov@nginx.com        # Exception in __next__().
7021283Szelenkov@nginx.com
7031283Szelenkov@nginx.com        _, sock = self.get(
7041283Szelenkov@nginx.com            headers={
7051283Szelenkov@nginx.com                'Host': 'localhost',
7061283Szelenkov@nginx.com                'X-Skip': '3',
7071283Szelenkov@nginx.com                'Connection': 'keep-alive',
7081283Szelenkov@nginx.com            },
7091283Szelenkov@nginx.com            start=True,
7101283Szelenkov@nginx.com        )
7111283Szelenkov@nginx.com
7121596Szelenkov@nginx.com        assert (
7132330Szelenkov@nginx.com            self.wait_for_record(r"raise Exception\('next exception'\)")
7141596Szelenkov@nginx.com            is not None
7151596Szelenkov@nginx.com        ), 'exception raise next'
7161596Szelenkov@nginx.com        assert len(self.findall(r'Traceback')) == 5, 'traceback count 5'
7171283Szelenkov@nginx.com
7181596Szelenkov@nginx.com        assert self.get(sock=sock) == {}, 'closed connection 2'
7191283Szelenkov@nginx.com
7201283Szelenkov@nginx.com        # Exception in __next__(), chunked (incomplete body).
7211283Szelenkov@nginx.com
7221283Szelenkov@nginx.com        resp = self.get(
7231283Szelenkov@nginx.com            headers={
7241283Szelenkov@nginx.com                'Host': 'localhost',
7251283Szelenkov@nginx.com                'X-Skip': '3',
7261283Szelenkov@nginx.com                'X-Chunked': '1',
7271283Szelenkov@nginx.com                'Connection': 'close',
7281295St.nateldemoura@f5.com            },
7291596Szelenkov@nginx.com            raw_resp=True,
7301283Szelenkov@nginx.com        )
7311295St.nateldemoura@f5.com        if resp:
7321596Szelenkov@nginx.com            assert resp[-5:] != '0\r\n\r\n', 'incomplete body 2'
7331596Szelenkov@nginx.com        assert len(self.findall(r'Traceback')) == 6, 'traceback count 6'
7341283Szelenkov@nginx.com
7351283Szelenkov@nginx.com        # Exception before start_response() and in close().
7361283Szelenkov@nginx.com
7371596Szelenkov@nginx.com        assert (
7381283Szelenkov@nginx.com            self.get(
7391283Szelenkov@nginx.com                headers={
7401283Szelenkov@nginx.com                    'Host': 'localhost',
7411283Szelenkov@nginx.com                    'X-Not-Skip-Close': '1',
7421283Szelenkov@nginx.com                    'Connection': 'close',
7431283Szelenkov@nginx.com                }
7441596Szelenkov@nginx.com            )['status']
7451596Szelenkov@nginx.com            == 503
7461596Szelenkov@nginx.com        ), 'error'
7471283Szelenkov@nginx.com
7481596Szelenkov@nginx.com        assert (
7492330Szelenkov@nginx.com            self.wait_for_record(r"raise Exception\('close exception'\)")
7501596Szelenkov@nginx.com            is not None
7511596Szelenkov@nginx.com        ), 'exception raise close'
7521596Szelenkov@nginx.com        assert len(self.findall(r'Traceback')) == 8, 'traceback count 8'
7531283Szelenkov@nginx.com
7541596Szelenkov@nginx.com    def test_python_user_group(self, is_su):
7551596Szelenkov@nginx.com        if not is_su:
7561596Szelenkov@nginx.com            pytest.skip('requires root')
7571304St.nateldemoura@f5.com
7581304St.nateldemoura@f5.com        nobody_uid = pwd.getpwnam('nobody').pw_uid
7591304St.nateldemoura@f5.com
7601304St.nateldemoura@f5.com        group = 'nobody'
7611304St.nateldemoura@f5.com
7621304St.nateldemoura@f5.com        try:
7631304St.nateldemoura@f5.com            group_id = grp.getgrnam(group).gr_gid
7641706Smax.romanov@nginx.com        except KeyError:
7651304St.nateldemoura@f5.com            group = 'nogroup'
7661304St.nateldemoura@f5.com            group_id = grp.getgrnam(group).gr_gid
7671304St.nateldemoura@f5.com
7681304St.nateldemoura@f5.com        self.load('user_group')
7691304St.nateldemoura@f5.com
7701304St.nateldemoura@f5.com        obj = self.getjson()['body']
7711596Szelenkov@nginx.com        assert obj['UID'] == nobody_uid, 'nobody uid'
7721596Szelenkov@nginx.com        assert obj['GID'] == group_id, 'nobody gid'
7731304St.nateldemoura@f5.com
7741304St.nateldemoura@f5.com        self.load('user_group', user='nobody')
7751304St.nateldemoura@f5.com
7761304St.nateldemoura@f5.com        obj = self.getjson()['body']
7771596Szelenkov@nginx.com        assert obj['UID'] == nobody_uid, 'nobody uid user=nobody'
7781596Szelenkov@nginx.com        assert obj['GID'] == group_id, 'nobody gid user=nobody'
7791304St.nateldemoura@f5.com
7801304St.nateldemoura@f5.com        self.load('user_group', user='nobody', group=group)
7811304St.nateldemoura@f5.com
7821304St.nateldemoura@f5.com        obj = self.getjson()['body']
7832330Szelenkov@nginx.com        assert obj['UID'] == nobody_uid, f'nobody uid user=nobody group={group}'
7842330Szelenkov@nginx.com        assert obj['GID'] == group_id, f'nobody gid user=nobody group={group}'
7851304St.nateldemoura@f5.com
7861304St.nateldemoura@f5.com        self.load('user_group', group=group)
7871304St.nateldemoura@f5.com
7881304St.nateldemoura@f5.com        obj = self.getjson()['body']
7892330Szelenkov@nginx.com        assert obj['UID'] == nobody_uid, f'nobody uid group={group}'
7902330Szelenkov@nginx.com        assert obj['GID'] == group_id, f'nobody gid group={group}'
7911304St.nateldemoura@f5.com
7921304St.nateldemoura@f5.com        self.load('user_group', user='root')
7931304St.nateldemoura@f5.com
7941304St.nateldemoura@f5.com        obj = self.getjson()['body']
7951596Szelenkov@nginx.com        assert obj['UID'] == 0, 'root uid user=root'
7961596Szelenkov@nginx.com        assert obj['GID'] == 0, 'root gid user=root'
7971304St.nateldemoura@f5.com
7981304St.nateldemoura@f5.com        group = 'root'
7991304St.nateldemoura@f5.com
8001304St.nateldemoura@f5.com        try:
8011304St.nateldemoura@f5.com            grp.getgrnam(group)
8021304St.nateldemoura@f5.com            group = True
8031706Smax.romanov@nginx.com        except KeyError:
8041304St.nateldemoura@f5.com            group = False
8051304St.nateldemoura@f5.com
8061304St.nateldemoura@f5.com        if group:
8071304St.nateldemoura@f5.com            self.load('user_group', user='root', group='root')
8081304St.nateldemoura@f5.com
8091304St.nateldemoura@f5.com            obj = self.getjson()['body']
8101596Szelenkov@nginx.com            assert obj['UID'] == 0, 'root uid user=root group=root'
8111596Szelenkov@nginx.com            assert obj['GID'] == 0, 'root gid user=root group=root'
8121304St.nateldemoura@f5.com
8131304St.nateldemoura@f5.com            self.load('user_group', group='root')
8141304St.nateldemoura@f5.com
8151304St.nateldemoura@f5.com            obj = self.getjson()['body']
8161596Szelenkov@nginx.com            assert obj['UID'] == nobody_uid, 'root uid group=root'
8171596Szelenkov@nginx.com            assert obj['GID'] == 0, 'root gid group=root'
8181604Smax.romanov@nginx.com
8191736Szelenkov@nginx.com    def test_python_application_callable(self, skip_alert):
8201604Smax.romanov@nginx.com        skip_alert(r'Python failed to get "blah" from module')
8211604Smax.romanov@nginx.com        self.load('callable')
8221604Smax.romanov@nginx.com
8231604Smax.romanov@nginx.com        assert self.get()['status'] == 204, 'default application response'
8241604Smax.romanov@nginx.com
8251695Smax.romanov@nginx.com        self.load('callable', callable="app")
8261604Smax.romanov@nginx.com
8271604Smax.romanov@nginx.com        assert self.get()['status'] == 200, 'callable response'
8281604Smax.romanov@nginx.com
8291695Smax.romanov@nginx.com        self.load('callable', callable="blah")
8301604Smax.romanov@nginx.com
8311604Smax.romanov@nginx.com        assert self.get()['status'] not in [200, 204], 'callable response inv'
8321604Smax.romanov@nginx.com
8331791Szelenkov@nginx.com    def test_python_application_path(self):
8341791Szelenkov@nginx.com        self.load('path')
8351791Szelenkov@nginx.com
8361791Szelenkov@nginx.com        def set_path(path):
8371791Szelenkov@nginx.com            assert 'success' in self.conf(path, 'applications/path/path')
8381791Szelenkov@nginx.com
8391791Szelenkov@nginx.com        def get_path():
8401791Szelenkov@nginx.com            return self.get()['body'].split(os.pathsep)
8411791Szelenkov@nginx.com
8421791Szelenkov@nginx.com        default_path = self.conf_get('/config/applications/path/path')
8431791Szelenkov@nginx.com        assert 'success' in self.conf(
8441791Szelenkov@nginx.com            {"PYTHONPATH": default_path},
8451791Szelenkov@nginx.com            '/config/applications/path/environment',
8461791Szelenkov@nginx.com        )
8471791Szelenkov@nginx.com
8481791Szelenkov@nginx.com        self.conf_delete('/config/applications/path/path')
8491791Szelenkov@nginx.com        sys_path = get_path()
8501791Szelenkov@nginx.com
8511791Szelenkov@nginx.com        set_path('"/blah"')
8521791Szelenkov@nginx.com        assert ['/blah', *sys_path] == get_path(), 'check path'
8531791Szelenkov@nginx.com
8541791Szelenkov@nginx.com        set_path('"/new"')
8551791Szelenkov@nginx.com        assert ['/new', *sys_path] == get_path(), 'check path update'
8561791Szelenkov@nginx.com
8571791Szelenkov@nginx.com        set_path('["/blah1", "/blah2"]')
8581848Szelenkov@nginx.com        assert [
8591848Szelenkov@nginx.com            '/blah1',
8601848Szelenkov@nginx.com            '/blah2',
8611848Szelenkov@nginx.com            *sys_path,
8621848Szelenkov@nginx.com        ] == get_path(), 'check path array'
8631791Szelenkov@nginx.com
8641791Szelenkov@nginx.com    def test_python_application_path_invalid(self):
8651791Szelenkov@nginx.com        self.load('path')
8661791Szelenkov@nginx.com
8671791Szelenkov@nginx.com        def check_path(path):
8681791Szelenkov@nginx.com            assert 'error' in self.conf(path, 'applications/path/path')
8691791Szelenkov@nginx.com
8701791Szelenkov@nginx.com        check_path('{}')
8711791Szelenkov@nginx.com        check_path('["/blah", []]')
8721791Szelenkov@nginx.com
8731683Smax.romanov@nginx.com    def test_python_application_threads(self):
8741695Smax.romanov@nginx.com        self.load('threads', threads=4)
8751683Smax.romanov@nginx.com
8761683Smax.romanov@nginx.com        socks = []
8771683Smax.romanov@nginx.com
878*2477Szelenkov@nginx.com        for _ in range(4):
8792212Szelenkov@nginx.com            sock = self.get(
8801683Smax.romanov@nginx.com                headers={
8811683Smax.romanov@nginx.com                    'Host': 'localhost',
8821683Smax.romanov@nginx.com                    'X-Delay': '2',
8831683Smax.romanov@nginx.com                    'Connection': 'close',
8841683Smax.romanov@nginx.com                },
8851683Smax.romanov@nginx.com                no_recv=True,
8861683Smax.romanov@nginx.com            )
8871683Smax.romanov@nginx.com
8881683Smax.romanov@nginx.com            socks.append(sock)
8891683Smax.romanov@nginx.com
8901683Smax.romanov@nginx.com        threads = set()
8911683Smax.romanov@nginx.com
8921683Smax.romanov@nginx.com        for sock in socks:
8931683Smax.romanov@nginx.com            resp = self.recvall(sock).decode('utf-8')
8941683Smax.romanov@nginx.com
8951683Smax.romanov@nginx.com            self.log_in(resp)
8961683Smax.romanov@nginx.com
8971683Smax.romanov@nginx.com            resp = self._resp_to_dict(resp)
8981683Smax.romanov@nginx.com
8991683Smax.romanov@nginx.com            assert resp['status'] == 200, 'status'
9001683Smax.romanov@nginx.com
9011683Smax.romanov@nginx.com            threads.add(resp['headers']['X-Thread'])
9021683Smax.romanov@nginx.com
9031683Smax.romanov@nginx.com            assert resp['headers']['Wsgi-Multithread'] == 'True', 'multithread'
9041683Smax.romanov@nginx.com
9051683Smax.romanov@nginx.com            sock.close()
9061683Smax.romanov@nginx.com
9071683Smax.romanov@nginx.com        assert len(socks) == len(threads), 'threads differs'
908