11304St.nateldemoura@f5.comimport grp
2*1596Szelenkov@nginx.comimport pytest
31304St.nateldemoura@f5.comimport pwd
41477Szelenkov@nginx.comimport re
5899Szelenkov@nginx.comimport time
61477Szelenkov@nginx.com
71019Szelenkov@nginx.comfrom unit.applications.lang.python import TestApplicationPython
8*1596Szelenkov@nginx.comfrom conftest import skip_alert
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):
15*1596Szelenkov@nginx.com        with open(self.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
231017Szelenkov@nginx.com        resp = self.post(
241017Szelenkov@nginx.com            headers={
251017Szelenkov@nginx.com                'Host': 'localhost',
261017Szelenkov@nginx.com                'Content-Type': 'text/html',
271017Szelenkov@nginx.com                'Custom-Header': 'blah',
281017Szelenkov@nginx.com                'Connection': 'close',
291017Szelenkov@nginx.com            },
301017Szelenkov@nginx.com            body=body,
311017Szelenkov@nginx.com        )
32484Szelenkov@nginx.com
33*1596Szelenkov@nginx.com        assert resp['status'] == 200, 'status'
34505Szelenkov@nginx.com        headers = resp['headers']
35674Szelenkov@nginx.com        header_server = headers.pop('Server')
36*1596Szelenkov@nginx.com        assert re.search(r'Unit/[\d\.]+', header_server), 'server header'
37*1596Szelenkov@nginx.com        assert (
38*1596Szelenkov@nginx.com            headers.pop('Server-Software') == header_server
39*1596Szelenkov@nginx.com        ), 'server software header'
40599Szelenkov@nginx.com
41599Szelenkov@nginx.com        date = headers.pop('Date')
42*1596Szelenkov@nginx.com        assert date[-4:] == ' GMT', 'date header timezone'
43*1596Szelenkov@nginx.com        assert (
44*1596Szelenkov@nginx.com            abs(self.date_to_sec_epoch(date) - self.sec_epoch()) < 5
45*1596Szelenkov@nginx.com        ), 'date header'
46599Szelenkov@nginx.com
47*1596Szelenkov@nginx.com        assert headers == {
48*1596Szelenkov@nginx.com            'Connection': 'close',
49*1596Szelenkov@nginx.com            'Content-Length': str(len(body)),
50*1596Szelenkov@nginx.com            'Content-Type': 'text/html',
51*1596Szelenkov@nginx.com            'Request-Method': 'POST',
52*1596Szelenkov@nginx.com            'Request-Uri': '/',
53*1596Szelenkov@nginx.com            'Http-Host': 'localhost',
54*1596Szelenkov@nginx.com            'Server-Protocol': 'HTTP/1.1',
55*1596Szelenkov@nginx.com            'Custom-Header': 'blah',
56*1596Szelenkov@nginx.com            'Wsgi-Version': '(1, 0)',
57*1596Szelenkov@nginx.com            'Wsgi-Url-Scheme': 'http',
58*1596Szelenkov@nginx.com            'Wsgi-Multithread': 'False',
59*1596Szelenkov@nginx.com            'Wsgi-Multiprocess': 'True',
60*1596Szelenkov@nginx.com            'Wsgi-Run-Once': 'False',
61*1596Szelenkov@nginx.com        }, 'headers'
62*1596Szelenkov@nginx.com        assert resp['body'] == body, 'body'
63484Szelenkov@nginx.com
64497Szelenkov@nginx.com    def test_python_application_query_string(self):
65552Szelenkov@nginx.com        self.load('query_string')
66497Szelenkov@nginx.com
67505Szelenkov@nginx.com        resp = self.get(url='/?var1=val1&var2=val2')
68497Szelenkov@nginx.com
69*1596Szelenkov@nginx.com        assert (
70*1596Szelenkov@nginx.com            resp['headers']['Query-String'] == 'var1=val1&var2=val2'
71*1596Szelenkov@nginx.com        ), 'Query-String header'
72497Szelenkov@nginx.com
731171Svbart@nginx.com    def test_python_application_query_string_space(self):
741171Svbart@nginx.com        self.load('query_string')
751171Svbart@nginx.com
761171Svbart@nginx.com        resp = self.get(url='/ ?var1=val1&var2=val2')
77*1596Szelenkov@nginx.com        assert (
78*1596Szelenkov@nginx.com            resp['headers']['Query-String'] == 'var1=val1&var2=val2'
79*1596Szelenkov@nginx.com        ), 'Query-String space'
801171Svbart@nginx.com
811171Svbart@nginx.com        resp = self.get(url='/ %20?var1=val1&var2=val2')
82*1596Szelenkov@nginx.com        assert (
83*1596Szelenkov@nginx.com            resp['headers']['Query-String'] == 'var1=val1&var2=val2'
84*1596Szelenkov@nginx.com        ), 'Query-String space 2'
851171Svbart@nginx.com
861171Svbart@nginx.com        resp = self.get(url='/ %20 ?var1=val1&var2=val2')
87*1596Szelenkov@nginx.com        assert (
88*1596Szelenkov@nginx.com            resp['headers']['Query-String'] == 'var1=val1&var2=val2'
89*1596Szelenkov@nginx.com        ), 'Query-String space 3'
901171Svbart@nginx.com
911171Svbart@nginx.com        resp = self.get(url='/blah %20 blah? var1= val1 & var2=val2')
92*1596Szelenkov@nginx.com        assert (
93*1596Szelenkov@nginx.com            resp['headers']['Query-String'] == ' var1= val1 & var2=val2'
94*1596Szelenkov@nginx.com        ), 'Query-String space 4'
951171Svbart@nginx.com
96894Szelenkov@nginx.com    def test_python_application_query_string_empty(self):
97894Szelenkov@nginx.com        self.load('query_string')
98894Szelenkov@nginx.com
99894Szelenkov@nginx.com        resp = self.get(url='/?')
100894Szelenkov@nginx.com
101*1596Szelenkov@nginx.com        assert resp['status'] == 200, 'query string empty status'
102*1596Szelenkov@nginx.com        assert resp['headers']['Query-String'] == '', 'query string empty'
103894Szelenkov@nginx.com
104894Szelenkov@nginx.com    def test_python_application_query_string_absent(self):
105894Szelenkov@nginx.com        self.load('query_string')
106894Szelenkov@nginx.com
107894Szelenkov@nginx.com        resp = self.get()
108894Szelenkov@nginx.com
109*1596Szelenkov@nginx.com        assert resp['status'] == 200, 'query string absent status'
110*1596Szelenkov@nginx.com        assert resp['headers']['Query-String'] == '', 'query string absent'
111894Szelenkov@nginx.com
112*1596Szelenkov@nginx.com    @pytest.mark.skip('not yet')
113495Szelenkov@nginx.com    def test_python_application_server_port(self):
114552Szelenkov@nginx.com        self.load('server_port')
115495Szelenkov@nginx.com
116*1596Szelenkov@nginx.com        assert (
117*1596Szelenkov@nginx.com            self.get()['headers']['Server-Port'] == '7080'
118*1596Szelenkov@nginx.com        ), 'Server-Port header'
119484Szelenkov@nginx.com
120*1596Szelenkov@nginx.com    @pytest.mark.skip('not yet')
1211250Szelenkov@nginx.com    def test_python_application_working_directory_invalid(self):
1221250Szelenkov@nginx.com        self.load('empty')
1231250Szelenkov@nginx.com
124*1596Szelenkov@nginx.com        assert 'success' in self.conf(
125*1596Szelenkov@nginx.com            '"/blah"', 'applications/empty/working_directory'
126*1596Szelenkov@nginx.com        ), 'configure invalid working_directory'
1271250Szelenkov@nginx.com
128*1596Szelenkov@nginx.com        assert self.get()['status'] == 500, 'status'
1291250Szelenkov@nginx.com
130496Szelenkov@nginx.com    def test_python_application_204_transfer_encoding(self):
131552Szelenkov@nginx.com        self.load('204_no_content')
132496Szelenkov@nginx.com
133*1596Szelenkov@nginx.com        assert (
134*1596Szelenkov@nginx.com            'Transfer-Encoding' not in self.get()['headers']
135*1596Szelenkov@nginx.com        ), '204 header transfer encoding'
136484Szelenkov@nginx.com
137602Szelenkov@nginx.com    def test_python_application_ctx_iter_atexit(self):
138602Szelenkov@nginx.com        self.load('ctx_iter_atexit')
139602Szelenkov@nginx.com
1401017Szelenkov@nginx.com        resp = self.post(
1411017Szelenkov@nginx.com            headers={
1421017Szelenkov@nginx.com                'Host': 'localhost',
1431017Szelenkov@nginx.com                'Connection': 'close',
1441017Szelenkov@nginx.com                'Content-Type': 'text/html',
1451017Szelenkov@nginx.com            },
1461017Szelenkov@nginx.com            body='0123456789',
1471017Szelenkov@nginx.com        )
148602Szelenkov@nginx.com
149*1596Szelenkov@nginx.com        assert resp['status'] == 200, 'ctx iter status'
150*1596Szelenkov@nginx.com        assert resp['body'] == '0123456789', 'ctx iter body'
151602Szelenkov@nginx.com
1521017Szelenkov@nginx.com        self.conf({"listeners": {}, "applications": {}})
153602Szelenkov@nginx.com
154602Szelenkov@nginx.com        self.stop()
155602Szelenkov@nginx.com
156*1596Szelenkov@nginx.com        assert (
157*1596Szelenkov@nginx.com            self.wait_for_record(r'RuntimeError') is not None
158*1596Szelenkov@nginx.com        ), 'ctx iter atexit'
159602Szelenkov@nginx.com
160603Szelenkov@nginx.com    def test_python_keepalive_body(self):
161603Szelenkov@nginx.com        self.load('mirror')
162603Szelenkov@nginx.com
163*1596Szelenkov@nginx.com        assert self.get()['status'] == 200, 'init'
1641029Szelenkov@nginx.com
1651453Szelenkov@nginx.com        body = '0123456789' * 500
1661017Szelenkov@nginx.com        (resp, sock) = self.post(
1671017Szelenkov@nginx.com            headers={
1681017Szelenkov@nginx.com                'Host': 'localhost',
1691017Szelenkov@nginx.com                'Connection': 'keep-alive',
1701017Szelenkov@nginx.com                'Content-Type': 'text/html',
1711017Szelenkov@nginx.com            },
1721017Szelenkov@nginx.com            start=True,
1731453Szelenkov@nginx.com            body=body,
1741029Szelenkov@nginx.com            read_timeout=1,
1751017Szelenkov@nginx.com        )
176603Szelenkov@nginx.com
177*1596Szelenkov@nginx.com        assert resp['body'] == body, 'keep-alive 1'
178603Szelenkov@nginx.com
1791453Szelenkov@nginx.com        body = '0123456789'
1801017Szelenkov@nginx.com        resp = self.post(
1811017Szelenkov@nginx.com            headers={
1821017Szelenkov@nginx.com                'Host': 'localhost',
1831017Szelenkov@nginx.com                'Connection': 'close',
1841017Szelenkov@nginx.com                'Content-Type': 'text/html',
1851017Szelenkov@nginx.com            },
1861017Szelenkov@nginx.com            sock=sock,
1871453Szelenkov@nginx.com            body=body,
1881017Szelenkov@nginx.com        )
189603Szelenkov@nginx.com
190*1596Szelenkov@nginx.com        assert resp['body'] == body, 'keep-alive 2'
191603Szelenkov@nginx.com
192684Szelenkov@nginx.com    def test_python_keepalive_reconfigure(self):
193*1596Szelenkov@nginx.com        skip_alert(
194*1596Szelenkov@nginx.com            r'pthread_mutex.+failed',
195*1596Szelenkov@nginx.com            r'failed to apply',
196*1596Szelenkov@nginx.com            r'process \d+ exited on signal',
1971017Szelenkov@nginx.com        )
198684Szelenkov@nginx.com        self.load('mirror')
199684Szelenkov@nginx.com
200*1596Szelenkov@nginx.com        assert self.get()['status'] == 200, 'init'
2011029Szelenkov@nginx.com
202684Szelenkov@nginx.com        body = '0123456789'
203684Szelenkov@nginx.com        conns = 3
204684Szelenkov@nginx.com        socks = []
205684Szelenkov@nginx.com
206684Szelenkov@nginx.com        for i in range(conns):
2071017Szelenkov@nginx.com            (resp, sock) = self.post(
2081017Szelenkov@nginx.com                headers={
2091017Szelenkov@nginx.com                    'Host': 'localhost',
2101017Szelenkov@nginx.com                    'Connection': 'keep-alive',
2111017Szelenkov@nginx.com                    'Content-Type': 'text/html',
2121017Szelenkov@nginx.com                },
2131017Szelenkov@nginx.com                start=True,
2141017Szelenkov@nginx.com                body=body,
2151029Szelenkov@nginx.com                read_timeout=1,
2161017Szelenkov@nginx.com            )
217684Szelenkov@nginx.com
218*1596Szelenkov@nginx.com            assert resp['body'] == body, 'keep-alive open'
219*1596Szelenkov@nginx.com            assert 'success' in self.conf(
220*1596Szelenkov@nginx.com                str(i + 1), 'applications/mirror/processes'
221*1596Szelenkov@nginx.com            ), 'reconfigure'
222684Szelenkov@nginx.com
223684Szelenkov@nginx.com            socks.append(sock)
224684Szelenkov@nginx.com
225684Szelenkov@nginx.com        for i in range(conns):
2261017Szelenkov@nginx.com            (resp, sock) = self.post(
2271017Szelenkov@nginx.com                headers={
2281017Szelenkov@nginx.com                    'Host': 'localhost',
2291017Szelenkov@nginx.com                    'Connection': 'keep-alive',
2301017Szelenkov@nginx.com                    'Content-Type': 'text/html',
2311017Szelenkov@nginx.com                },
2321017Szelenkov@nginx.com                start=True,
2331017Szelenkov@nginx.com                sock=socks[i],
2341017Szelenkov@nginx.com                body=body,
2351029Szelenkov@nginx.com                read_timeout=1,
2361017Szelenkov@nginx.com            )
237684Szelenkov@nginx.com
238*1596Szelenkov@nginx.com            assert resp['body'] == body, 'keep-alive request'
239*1596Szelenkov@nginx.com            assert 'success' in self.conf(
240*1596Szelenkov@nginx.com                str(i + 1), 'applications/mirror/processes'
241*1596Szelenkov@nginx.com            ), 'reconfigure 2'
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
254*1596Szelenkov@nginx.com            assert resp['body'] == body, 'keep-alive close'
255*1596Szelenkov@nginx.com            assert 'success' in self.conf(
256*1596Szelenkov@nginx.com                str(i + 1), 'applications/mirror/processes'
257*1596Szelenkov@nginx.com            ), 'reconfigure 3'
258684Szelenkov@nginx.com
259750Szelenkov@nginx.com    def test_python_keepalive_reconfigure_2(self):
260750Szelenkov@nginx.com        self.load('mirror')
261750Szelenkov@nginx.com
262*1596Szelenkov@nginx.com        assert self.get()['status'] == 200, 'init'
2631029Szelenkov@nginx.com
264750Szelenkov@nginx.com        body = '0123456789'
265750Szelenkov@nginx.com
2661017Szelenkov@nginx.com        (resp, sock) = self.post(
2671017Szelenkov@nginx.com            headers={
2681017Szelenkov@nginx.com                'Host': 'localhost',
2691017Szelenkov@nginx.com                'Connection': 'keep-alive',
2701017Szelenkov@nginx.com                'Content-Type': 'text/html',
2711017Szelenkov@nginx.com            },
2721017Szelenkov@nginx.com            start=True,
2731017Szelenkov@nginx.com            body=body,
2741029Szelenkov@nginx.com            read_timeout=1,
2751017Szelenkov@nginx.com        )
276750Szelenkov@nginx.com
277*1596Szelenkov@nginx.com        assert resp['body'] == body, 'reconfigure 2 keep-alive 1'
278750Szelenkov@nginx.com
279750Szelenkov@nginx.com        self.load('empty')
280750Szelenkov@nginx.com
281*1596Szelenkov@nginx.com        assert self.get()['status'] == 200, 'init'
2821029Szelenkov@nginx.com
2831017Szelenkov@nginx.com        (resp, sock) = self.post(
2841017Szelenkov@nginx.com            headers={
2851017Szelenkov@nginx.com                'Host': 'localhost',
2861017Szelenkov@nginx.com                'Connection': 'close',
2871017Szelenkov@nginx.com                'Content-Type': 'text/html',
2881017Szelenkov@nginx.com            },
2891017Szelenkov@nginx.com            start=True,
2901017Szelenkov@nginx.com            sock=sock,
2911017Szelenkov@nginx.com            body=body,
2921017Szelenkov@nginx.com        )
293750Szelenkov@nginx.com
294*1596Szelenkov@nginx.com        assert resp['status'] == 200, 'reconfigure 2 keep-alive 2'
295*1596Szelenkov@nginx.com        assert resp['body'] == '', 'reconfigure 2 keep-alive 2 body'
296750Szelenkov@nginx.com
297*1596Szelenkov@nginx.com        assert 'success' in self.conf(
298*1596Szelenkov@nginx.com            {"listeners": {}, "applications": {}}
299*1596Szelenkov@nginx.com        ), 'reconfigure 2 clear configuration'
300750Szelenkov@nginx.com
301750Szelenkov@nginx.com        resp = self.get(sock=sock)
302750Szelenkov@nginx.com
303*1596Szelenkov@nginx.com        assert resp == {}, 'reconfigure 2 keep-alive 3'
304750Szelenkov@nginx.com
305750Szelenkov@nginx.com    def test_python_keepalive_reconfigure_3(self):
306750Szelenkov@nginx.com        self.load('empty')
307750Szelenkov@nginx.com
308*1596Szelenkov@nginx.com        assert self.get()['status'] == 200, 'init'
3091029Szelenkov@nginx.com
3101453Szelenkov@nginx.com        (_, sock) = self.http(
3111017Szelenkov@nginx.com            b"""GET / HTTP/1.1
3121017Szelenkov@nginx.com""",
3131017Szelenkov@nginx.com            start=True,
3141017Szelenkov@nginx.com            raw=True,
3151453Szelenkov@nginx.com            no_recv=True,
3161017Szelenkov@nginx.com        )
317750Szelenkov@nginx.com
318*1596Szelenkov@nginx.com        assert self.get()['status'] == 200
3191453Szelenkov@nginx.com
320*1596Szelenkov@nginx.com        assert 'success' in self.conf(
321*1596Szelenkov@nginx.com            {"listeners": {}, "applications": {}}
322*1596Szelenkov@nginx.com        ), 'reconfigure 3 clear configuration'
323750Szelenkov@nginx.com
3241017Szelenkov@nginx.com        resp = self.http(
3251017Szelenkov@nginx.com            b"""Host: localhost
326750Szelenkov@nginx.comConnection: close
327750Szelenkov@nginx.com
3281017Szelenkov@nginx.com""",
3291017Szelenkov@nginx.com            sock=sock,
3301017Szelenkov@nginx.com            raw=True,
3311017Szelenkov@nginx.com        )
332750Szelenkov@nginx.com
333*1596Szelenkov@nginx.com        assert resp['status'] == 200, 'reconfigure 3'
334750Szelenkov@nginx.com
335603Szelenkov@nginx.com    def test_python_atexit(self):
336603Szelenkov@nginx.com        self.load('atexit')
337603Szelenkov@nginx.com
338603Szelenkov@nginx.com        self.get()
339603Szelenkov@nginx.com
3401017Szelenkov@nginx.com        self.conf({"listeners": {}, "applications": {}})
341603Szelenkov@nginx.com
342603Szelenkov@nginx.com        self.stop()
343603Szelenkov@nginx.com
344*1596Szelenkov@nginx.com        assert self.wait_for_record(r'At exit called\.') is not None, 'atexit'
345603Szelenkov@nginx.com
3461454Szelenkov@nginx.com    def test_python_process_switch(self):
3471454Szelenkov@nginx.com        self.load('delayed')
3481454Szelenkov@nginx.com
349*1596Szelenkov@nginx.com        assert 'success' in self.conf(
350*1596Szelenkov@nginx.com            '2', 'applications/delayed/processes'
351*1596Szelenkov@nginx.com        ), 'configure 2 processes'
3521454Szelenkov@nginx.com
353*1596Szelenkov@nginx.com        self.get(
354*1596Szelenkov@nginx.com            headers={
355*1596Szelenkov@nginx.com                'Host': 'localhost',
356*1596Szelenkov@nginx.com                'Content-Length': '0',
357*1596Szelenkov@nginx.com                'X-Delay': '5',
358*1596Szelenkov@nginx.com                'Connection': 'close',
359*1596Szelenkov@nginx.com            },
360*1596Szelenkov@nginx.com            no_recv=True,
361*1596Szelenkov@nginx.com        )
3621454Szelenkov@nginx.com
3631454Szelenkov@nginx.com        headers_delay_1 = {
3641454Szelenkov@nginx.com            'Connection': 'close',
3651454Szelenkov@nginx.com            'Host': 'localhost',
3661454Szelenkov@nginx.com            'Content-Length': '0',
3671454Szelenkov@nginx.com            'X-Delay': '1',
3681454Szelenkov@nginx.com        }
3691454Szelenkov@nginx.com
3701454Szelenkov@nginx.com        self.get(headers=headers_delay_1, no_recv=True)
3711454Szelenkov@nginx.com
3721454Szelenkov@nginx.com        time.sleep(0.5)
3731454Szelenkov@nginx.com
3741454Szelenkov@nginx.com        for _ in range(10):
3751454Szelenkov@nginx.com            self.get(headers=headers_delay_1, no_recv=True)
3761454Szelenkov@nginx.com
3771454Szelenkov@nginx.com        self.get(headers=headers_delay_1)
3781454Szelenkov@nginx.com
379*1596Szelenkov@nginx.com    @pytest.mark.skip('not yet')
380603Szelenkov@nginx.com    def test_python_application_start_response_exit(self):
381603Szelenkov@nginx.com        self.load('start_response_exit')
382603Szelenkov@nginx.com
383*1596Szelenkov@nginx.com        assert self.get()['status'] == 500, 'start response exit'
384603Szelenkov@nginx.com
385603Szelenkov@nginx.com    def test_python_application_input_iter(self):
386603Szelenkov@nginx.com        self.load('input_iter')
387603Szelenkov@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)
394*1596Szelenkov@nginx.com        assert resp['body'] == body, 'input iter'
395*1596Szelenkov@nginx.com        assert resp['headers']['X-Lines-Count'] == '4', 'input iter lines'
3961400Smax.romanov@nginx.com
3971400Smax.romanov@nginx.com    def test_python_application_input_readline(self):
3981400Smax.romanov@nginx.com        self.load('input_readline')
3991400Smax.romanov@nginx.com
4001400Smax.romanov@nginx.com        body = '''0123456789
4011400Smax.romanov@nginx.comnext line
4021400Smax.romanov@nginx.com
4031400Smax.romanov@nginx.comlast line'''
4041400Smax.romanov@nginx.com
4051400Smax.romanov@nginx.com        resp = self.post(body=body)
406*1596Szelenkov@nginx.com        assert resp['body'] == body, 'input readline'
407*1596Szelenkov@nginx.com        assert resp['headers']['X-Lines-Count'] == '4', 'input readline lines'
4081400Smax.romanov@nginx.com
4091400Smax.romanov@nginx.com    def test_python_application_input_readline_size(self):
4101400Smax.romanov@nginx.com        self.load('input_readline_size')
4111400Smax.romanov@nginx.com
4121400Smax.romanov@nginx.com        body = '''0123456789
4131400Smax.romanov@nginx.comnext line
4141400Smax.romanov@nginx.com
4151400Smax.romanov@nginx.comlast line'''
416603Szelenkov@nginx.com
417*1596Szelenkov@nginx.com        assert self.post(body=body)['body'] == body, 'input readline size'
418*1596Szelenkov@nginx.com        assert (
419*1596Szelenkov@nginx.com            self.post(body='0123')['body'] == '0123'
420*1596Szelenkov@nginx.com        ), 'input readline size less'
4211400Smax.romanov@nginx.com
4221400Smax.romanov@nginx.com    def test_python_application_input_readlines(self):
4231400Smax.romanov@nginx.com        self.load('input_readlines')
4241400Smax.romanov@nginx.com
4251400Smax.romanov@nginx.com        body = '''0123456789
4261400Smax.romanov@nginx.comnext line
4271400Smax.romanov@nginx.com
4281400Smax.romanov@nginx.comlast line'''
4291400Smax.romanov@nginx.com
4301400Smax.romanov@nginx.com        resp = self.post(body=body)
431*1596Szelenkov@nginx.com        assert resp['body'] == body, 'input readlines'
432*1596Szelenkov@nginx.com        assert resp['headers']['X-Lines-Count'] == '4', 'input readlines lines'
4331400Smax.romanov@nginx.com
4341400Smax.romanov@nginx.com    def test_python_application_input_readlines_huge(self):
4351400Smax.romanov@nginx.com        self.load('input_readlines')
4361400Smax.romanov@nginx.com
4371400Smax.romanov@nginx.com        body = (
4381400Smax.romanov@nginx.com            '''0123456789 abcdefghi
4391400Smax.romanov@nginx.comnext line: 0123456789 abcdefghi
4401400Smax.romanov@nginx.com
4411400Smax.romanov@nginx.comlast line: 987654321
4421400Smax.romanov@nginx.com'''
4431400Smax.romanov@nginx.com            * 512
4441400Smax.romanov@nginx.com        )
4451400Smax.romanov@nginx.com
446*1596Szelenkov@nginx.com        assert (
447*1596Szelenkov@nginx.com            self.post(body=body, read_buffer_size=16384)['body'] == body
448*1596Szelenkov@nginx.com        ), 'input readlines huge'
449603Szelenkov@nginx.com
450603Szelenkov@nginx.com    def test_python_application_input_read_length(self):
451603Szelenkov@nginx.com        self.load('input_read_length')
452603Szelenkov@nginx.com
453603Szelenkov@nginx.com        body = '0123456789'
454603Szelenkov@nginx.com
4551017Szelenkov@nginx.com        resp = self.post(
4561017Szelenkov@nginx.com            headers={
4571017Szelenkov@nginx.com                'Host': 'localhost',
4581017Szelenkov@nginx.com                'Input-Length': '5',
4591017Szelenkov@nginx.com                'Connection': 'close',
4601017Szelenkov@nginx.com            },
4611017Szelenkov@nginx.com            body=body,
4621017Szelenkov@nginx.com        )
463603Szelenkov@nginx.com
464*1596Szelenkov@nginx.com        assert resp['body'] == body[:5], 'input read length lt body'
465603Szelenkov@nginx.com
4661017Szelenkov@nginx.com        resp = self.post(
4671017Szelenkov@nginx.com            headers={
4681017Szelenkov@nginx.com                'Host': 'localhost',
4691017Szelenkov@nginx.com                'Input-Length': '15',
4701017Szelenkov@nginx.com                'Connection': 'close',
4711017Szelenkov@nginx.com            },
4721017Szelenkov@nginx.com            body=body,
4731017Szelenkov@nginx.com        )
474603Szelenkov@nginx.com
475*1596Szelenkov@nginx.com        assert resp['body'] == body, 'input read length gt body'
476603Szelenkov@nginx.com
4771017Szelenkov@nginx.com        resp = self.post(
4781017Szelenkov@nginx.com            headers={
4791017Szelenkov@nginx.com                'Host': 'localhost',
4801017Szelenkov@nginx.com                'Input-Length': '0',
4811017Szelenkov@nginx.com                'Connection': 'close',
4821017Szelenkov@nginx.com            },
4831017Szelenkov@nginx.com            body=body,
4841017Szelenkov@nginx.com        )
485603Szelenkov@nginx.com
486*1596Szelenkov@nginx.com        assert resp['body'] == '', 'input read length zero'
487603Szelenkov@nginx.com
4881017Szelenkov@nginx.com        resp = self.post(
4891017Szelenkov@nginx.com            headers={
4901017Szelenkov@nginx.com                'Host': 'localhost',
4911017Szelenkov@nginx.com                'Input-Length': '-1',
4921017Szelenkov@nginx.com                'Connection': 'close',
4931017Szelenkov@nginx.com            },
4941017Szelenkov@nginx.com            body=body,
4951017Szelenkov@nginx.com        )
496603Szelenkov@nginx.com
497*1596Szelenkov@nginx.com        assert resp['body'] == body, 'input read length negative'
498603Szelenkov@nginx.com
499*1596Szelenkov@nginx.com    @pytest.mark.skip('not yet')
500603Szelenkov@nginx.com    def test_python_application_errors_write(self):
501603Szelenkov@nginx.com        self.load('errors_write')
502603Szelenkov@nginx.com
503603Szelenkov@nginx.com        self.get()
504603Szelenkov@nginx.com
505603Szelenkov@nginx.com        self.stop()
506603Szelenkov@nginx.com
507*1596Szelenkov@nginx.com        assert (
508*1596Szelenkov@nginx.com            self.wait_for_record(r'\[error\].+Error in application\.')
509*1596Szelenkov@nginx.com            is not None
510*1596Szelenkov@nginx.com        ), 'errors write'
511603Szelenkov@nginx.com
512603Szelenkov@nginx.com    def test_python_application_body_array(self):
513603Szelenkov@nginx.com        self.load('body_array')
514603Szelenkov@nginx.com
515*1596Szelenkov@nginx.com        assert self.get()['body'] == '0123456789', 'body array'
516603Szelenkov@nginx.com
517603Szelenkov@nginx.com    def test_python_application_body_io(self):
518603Szelenkov@nginx.com        self.load('body_io')
519603Szelenkov@nginx.com
520*1596Szelenkov@nginx.com        assert self.get()['body'] == '0123456789', 'body io'
521603Szelenkov@nginx.com
522603Szelenkov@nginx.com    def test_python_application_body_io_file(self):
523603Szelenkov@nginx.com        self.load('body_io_file')
524603Szelenkov@nginx.com
525*1596Szelenkov@nginx.com        assert self.get()['body'] == 'body\n', 'body io file'
526603Szelenkov@nginx.com
527*1596Szelenkov@nginx.com    @pytest.mark.skip('not yet')
528603Szelenkov@nginx.com    def test_python_application_syntax_error(self):
529*1596Szelenkov@nginx.com        skip_alert(r'Python failed to import module "wsgi"')
530603Szelenkov@nginx.com        self.load('syntax_error')
531603Szelenkov@nginx.com
532*1596Szelenkov@nginx.com        assert self.get()['status'] == 500, 'syntax error'
533603Szelenkov@nginx.com
5341568Szelenkov@nginx.com    def test_python_application_loading_error(self):
535*1596Szelenkov@nginx.com        skip_alert(r'Python failed to import module "blah"')
5361568Szelenkov@nginx.com
5371568Szelenkov@nginx.com        self.load('empty')
5381568Szelenkov@nginx.com
539*1596Szelenkov@nginx.com        assert 'success' in self.conf('"blah"', 'applications/empty/module')
5401568Szelenkov@nginx.com
541*1596Szelenkov@nginx.com        assert self.get()['status'] == 503, 'loading error'
5421568Szelenkov@nginx.com
543603Szelenkov@nginx.com    def test_python_application_close(self):
544603Szelenkov@nginx.com        self.load('close')
545603Szelenkov@nginx.com
546603Szelenkov@nginx.com        self.get()
547603Szelenkov@nginx.com
548603Szelenkov@nginx.com        self.stop()
549603Szelenkov@nginx.com
550*1596Szelenkov@nginx.com        assert self.wait_for_record(r'Close called\.') is not None, 'close'
551603Szelenkov@nginx.com
552603Szelenkov@nginx.com    def test_python_application_close_error(self):
553603Szelenkov@nginx.com        self.load('close_error')
554603Szelenkov@nginx.com
555603Szelenkov@nginx.com        self.get()
556603Szelenkov@nginx.com
557603Szelenkov@nginx.com        self.stop()
558603Szelenkov@nginx.com
559*1596Szelenkov@nginx.com        assert (
560*1596Szelenkov@nginx.com            self.wait_for_record(r'Close called\.') is not None
561*1596Szelenkov@nginx.com        ), 'close error'
562603Szelenkov@nginx.com
563617Szelenkov@nginx.com    def test_python_application_not_iterable(self):
564617Szelenkov@nginx.com        self.load('not_iterable')
565617Szelenkov@nginx.com
566665Szelenkov@nginx.com        self.get()
567617Szelenkov@nginx.com
568617Szelenkov@nginx.com        self.stop()
569617Szelenkov@nginx.com
570*1596Szelenkov@nginx.com        assert (
5711028Szelenkov@nginx.com            self.wait_for_record(
5721017Szelenkov@nginx.com                r'\[error\].+the application returned not an iterable object'
573*1596Szelenkov@nginx.com            )
574*1596Szelenkov@nginx.com            is not None
575*1596Szelenkov@nginx.com        ), 'not iterable'
576617Szelenkov@nginx.com
577664Szelenkov@nginx.com    def test_python_application_write(self):
578664Szelenkov@nginx.com        self.load('write')
579664Szelenkov@nginx.com
580*1596Szelenkov@nginx.com        assert self.get()['body'] == '0123456789', 'write'
581664Szelenkov@nginx.com
5821261Szelenkov@nginx.com    def test_python_application_threading(self):
5831261Szelenkov@nginx.com        """wait_for_record() timeouts after 5s while every thread works at
5841261Szelenkov@nginx.com        least 3s.  So without releasing GIL test should fail.
5851261Szelenkov@nginx.com        """
5861261Szelenkov@nginx.com
5871261Szelenkov@nginx.com        self.load('threading')
5881261Szelenkov@nginx.com
5891261Szelenkov@nginx.com        for _ in range(10):
5901261Szelenkov@nginx.com            self.get(no_recv=True)
5911261Szelenkov@nginx.com
592*1596Szelenkov@nginx.com        assert (
593*1596Szelenkov@nginx.com            self.wait_for_record(r'\(5\) Thread: 100') is not None
594*1596Szelenkov@nginx.com        ), 'last thread finished'
5951017Szelenkov@nginx.com
5961283Szelenkov@nginx.com    def test_python_application_iter_exception(self):
5971283Szelenkov@nginx.com        self.load('iter_exception')
5981283Szelenkov@nginx.com
5991283Szelenkov@nginx.com        # Default request doesn't lead to the exception.
6001283Szelenkov@nginx.com
6011283Szelenkov@nginx.com        resp = self.get(
6021283Szelenkov@nginx.com            headers={
6031283Szelenkov@nginx.com                'Host': 'localhost',
6041283Szelenkov@nginx.com                'X-Skip': '9',
6051283Szelenkov@nginx.com                'X-Chunked': '1',
6061283Szelenkov@nginx.com                'Connection': 'close',
6071283Szelenkov@nginx.com            }
6081283Szelenkov@nginx.com        )
609*1596Szelenkov@nginx.com        assert resp['status'] == 200, 'status'
610*1596Szelenkov@nginx.com        assert resp['body'] == 'XXXXXXX', 'body'
6111283Szelenkov@nginx.com
6121283Szelenkov@nginx.com        # Exception before start_response().
6131283Szelenkov@nginx.com
614*1596Szelenkov@nginx.com        assert self.get()['status'] == 503, 'error'
6151283Szelenkov@nginx.com
616*1596Szelenkov@nginx.com        assert self.wait_for_record(r'Traceback') is not None, 'traceback'
617*1596Szelenkov@nginx.com        assert (
618*1596Szelenkov@nginx.com            self.wait_for_record(r'raise Exception\(\'first exception\'\)')
619*1596Szelenkov@nginx.com            is not None
620*1596Szelenkov@nginx.com        ), 'first exception raise'
621*1596Szelenkov@nginx.com        assert len(self.findall(r'Traceback')) == 1, 'traceback count 1'
6221283Szelenkov@nginx.com
6231283Szelenkov@nginx.com        # Exception after start_response(), before first write().
6241283Szelenkov@nginx.com
625*1596Szelenkov@nginx.com        assert (
6261283Szelenkov@nginx.com            self.get(
6271283Szelenkov@nginx.com                headers={
6281283Szelenkov@nginx.com                    'Host': 'localhost',
6291283Szelenkov@nginx.com                    'X-Skip': '1',
6301283Szelenkov@nginx.com                    'Connection': 'close',
6311283Szelenkov@nginx.com                }
632*1596Szelenkov@nginx.com            )['status']
633*1596Szelenkov@nginx.com            == 503
634*1596Szelenkov@nginx.com        ), 'error 2'
6351283Szelenkov@nginx.com
636*1596Szelenkov@nginx.com        assert (
637*1596Szelenkov@nginx.com            self.wait_for_record(r'raise Exception\(\'second exception\'\)')
638*1596Szelenkov@nginx.com            is not None
639*1596Szelenkov@nginx.com        ), 'exception raise second'
640*1596Szelenkov@nginx.com        assert len(self.findall(r'Traceback')) == 2, 'traceback count 2'
6411283Szelenkov@nginx.com
6421283Szelenkov@nginx.com        # Exception after first write(), before first __next__().
6431283Szelenkov@nginx.com
6441283Szelenkov@nginx.com        _, sock = self.get(
6451283Szelenkov@nginx.com            headers={
6461283Szelenkov@nginx.com                'Host': 'localhost',
6471283Szelenkov@nginx.com                'X-Skip': '2',
6481283Szelenkov@nginx.com                'Connection': 'keep-alive',
6491283Szelenkov@nginx.com            },
6501283Szelenkov@nginx.com            start=True,
6511283Szelenkov@nginx.com        )
6521283Szelenkov@nginx.com
653*1596Szelenkov@nginx.com        assert (
654*1596Szelenkov@nginx.com            self.wait_for_record(r'raise Exception\(\'third exception\'\)')
655*1596Szelenkov@nginx.com            is not None
656*1596Szelenkov@nginx.com        ), 'exception raise third'
657*1596Szelenkov@nginx.com        assert len(self.findall(r'Traceback')) == 3, 'traceback count 3'
6581283Szelenkov@nginx.com
659*1596Szelenkov@nginx.com        assert self.get(sock=sock) == {}, 'closed connection'
6601283Szelenkov@nginx.com
6611283Szelenkov@nginx.com        # Exception after first write(), before first __next__(),
6621283Szelenkov@nginx.com        # chunked (incomplete body).
6631283Szelenkov@nginx.com
6641283Szelenkov@nginx.com        resp = self.get(
6651283Szelenkov@nginx.com            headers={
6661283Szelenkov@nginx.com                'Host': 'localhost',
6671283Szelenkov@nginx.com                'X-Skip': '2',
6681283Szelenkov@nginx.com                'X-Chunked': '1',
6691283Szelenkov@nginx.com                'Connection': 'close',
6701295St.nateldemoura@f5.com            },
671*1596Szelenkov@nginx.com            raw_resp=True,
6721283Szelenkov@nginx.com        )
6731295St.nateldemoura@f5.com        if resp:
674*1596Szelenkov@nginx.com            assert resp[-5:] != '0\r\n\r\n', 'incomplete body'
675*1596Szelenkov@nginx.com        assert len(self.findall(r'Traceback')) == 4, 'traceback count 4'
6761283Szelenkov@nginx.com
6771283Szelenkov@nginx.com        # Exception in __next__().
6781283Szelenkov@nginx.com
6791283Szelenkov@nginx.com        _, sock = self.get(
6801283Szelenkov@nginx.com            headers={
6811283Szelenkov@nginx.com                'Host': 'localhost',
6821283Szelenkov@nginx.com                'X-Skip': '3',
6831283Szelenkov@nginx.com                'Connection': 'keep-alive',
6841283Szelenkov@nginx.com            },
6851283Szelenkov@nginx.com            start=True,
6861283Szelenkov@nginx.com        )
6871283Szelenkov@nginx.com
688*1596Szelenkov@nginx.com        assert (
689*1596Szelenkov@nginx.com            self.wait_for_record(r'raise Exception\(\'next exception\'\)')
690*1596Szelenkov@nginx.com            is not None
691*1596Szelenkov@nginx.com        ), 'exception raise next'
692*1596Szelenkov@nginx.com        assert len(self.findall(r'Traceback')) == 5, 'traceback count 5'
6931283Szelenkov@nginx.com
694*1596Szelenkov@nginx.com        assert self.get(sock=sock) == {}, 'closed connection 2'
6951283Szelenkov@nginx.com
6961283Szelenkov@nginx.com        # Exception in __next__(), chunked (incomplete body).
6971283Szelenkov@nginx.com
6981283Szelenkov@nginx.com        resp = self.get(
6991283Szelenkov@nginx.com            headers={
7001283Szelenkov@nginx.com                'Host': 'localhost',
7011283Szelenkov@nginx.com                'X-Skip': '3',
7021283Szelenkov@nginx.com                'X-Chunked': '1',
7031283Szelenkov@nginx.com                'Connection': 'close',
7041295St.nateldemoura@f5.com            },
705*1596Szelenkov@nginx.com            raw_resp=True,
7061283Szelenkov@nginx.com        )
7071295St.nateldemoura@f5.com        if resp:
708*1596Szelenkov@nginx.com            assert resp[-5:] != '0\r\n\r\n', 'incomplete body 2'
709*1596Szelenkov@nginx.com        assert len(self.findall(r'Traceback')) == 6, 'traceback count 6'
7101283Szelenkov@nginx.com
7111283Szelenkov@nginx.com        # Exception before start_response() and in close().
7121283Szelenkov@nginx.com
713*1596Szelenkov@nginx.com        assert (
7141283Szelenkov@nginx.com            self.get(
7151283Szelenkov@nginx.com                headers={
7161283Szelenkov@nginx.com                    'Host': 'localhost',
7171283Szelenkov@nginx.com                    'X-Not-Skip-Close': '1',
7181283Szelenkov@nginx.com                    'Connection': 'close',
7191283Szelenkov@nginx.com                }
720*1596Szelenkov@nginx.com            )['status']
721*1596Szelenkov@nginx.com            == 503
722*1596Szelenkov@nginx.com        ), 'error'
7231283Szelenkov@nginx.com
724*1596Szelenkov@nginx.com        assert (
725*1596Szelenkov@nginx.com            self.wait_for_record(r'raise Exception\(\'close exception\'\)')
726*1596Szelenkov@nginx.com            is not None
727*1596Szelenkov@nginx.com        ), 'exception raise close'
728*1596Szelenkov@nginx.com        assert len(self.findall(r'Traceback')) == 8, 'traceback count 8'
7291283Szelenkov@nginx.com
730*1596Szelenkov@nginx.com    def test_python_user_group(self, is_su):
731*1596Szelenkov@nginx.com        if not is_su:
732*1596Szelenkov@nginx.com            pytest.skip('requires root')
7331304St.nateldemoura@f5.com
7341304St.nateldemoura@f5.com        nobody_uid = pwd.getpwnam('nobody').pw_uid
7351304St.nateldemoura@f5.com
7361304St.nateldemoura@f5.com        group = 'nobody'
7371304St.nateldemoura@f5.com
7381304St.nateldemoura@f5.com        try:
7391304St.nateldemoura@f5.com            group_id = grp.getgrnam(group).gr_gid
7401304St.nateldemoura@f5.com        except:
7411304St.nateldemoura@f5.com            group = 'nogroup'
7421304St.nateldemoura@f5.com            group_id = grp.getgrnam(group).gr_gid
7431304St.nateldemoura@f5.com
7441304St.nateldemoura@f5.com        self.load('user_group')
7451304St.nateldemoura@f5.com
7461304St.nateldemoura@f5.com        obj = self.getjson()['body']
747*1596Szelenkov@nginx.com        assert obj['UID'] == nobody_uid, 'nobody uid'
748*1596Szelenkov@nginx.com        assert obj['GID'] == group_id, 'nobody gid'
7491304St.nateldemoura@f5.com
7501304St.nateldemoura@f5.com        self.load('user_group', user='nobody')
7511304St.nateldemoura@f5.com
7521304St.nateldemoura@f5.com        obj = self.getjson()['body']
753*1596Szelenkov@nginx.com        assert obj['UID'] == nobody_uid, 'nobody uid user=nobody'
754*1596Szelenkov@nginx.com        assert obj['GID'] == group_id, 'nobody gid user=nobody'
7551304St.nateldemoura@f5.com
7561304St.nateldemoura@f5.com        self.load('user_group', user='nobody', group=group)
7571304St.nateldemoura@f5.com
7581304St.nateldemoura@f5.com        obj = self.getjson()['body']
759*1596Szelenkov@nginx.com        assert obj['UID'] == nobody_uid, (
760*1596Szelenkov@nginx.com            'nobody uid user=nobody group=%s' % group
7611304St.nateldemoura@f5.com        )
7621304St.nateldemoura@f5.com
763*1596Szelenkov@nginx.com        assert obj['GID'] == group_id, (
764*1596Szelenkov@nginx.com            'nobody gid user=nobody group=%s' % group
7651304St.nateldemoura@f5.com        )
7661304St.nateldemoura@f5.com
7671304St.nateldemoura@f5.com        self.load('user_group', group=group)
7681304St.nateldemoura@f5.com
7691304St.nateldemoura@f5.com        obj = self.getjson()['body']
770*1596Szelenkov@nginx.com        assert obj['UID'] == nobody_uid, 'nobody uid group=%s' % group
7711304St.nateldemoura@f5.com
772*1596Szelenkov@nginx.com        assert obj['GID'] == group_id, 'nobody gid group=%s' % group
7731304St.nateldemoura@f5.com
7741304St.nateldemoura@f5.com        self.load('user_group', user='root')
7751304St.nateldemoura@f5.com
7761304St.nateldemoura@f5.com        obj = self.getjson()['body']
777*1596Szelenkov@nginx.com        assert obj['UID'] == 0, 'root uid user=root'
778*1596Szelenkov@nginx.com        assert obj['GID'] == 0, 'root gid user=root'
7791304St.nateldemoura@f5.com
7801304St.nateldemoura@f5.com        group = 'root'
7811304St.nateldemoura@f5.com
7821304St.nateldemoura@f5.com        try:
7831304St.nateldemoura@f5.com            grp.getgrnam(group)
7841304St.nateldemoura@f5.com            group = True
7851304St.nateldemoura@f5.com        except:
7861304St.nateldemoura@f5.com            group = False
7871304St.nateldemoura@f5.com
7881304St.nateldemoura@f5.com        if group:
7891304St.nateldemoura@f5.com            self.load('user_group', user='root', group='root')
7901304St.nateldemoura@f5.com
7911304St.nateldemoura@f5.com            obj = self.getjson()['body']
792*1596Szelenkov@nginx.com            assert obj['UID'] == 0, 'root uid user=root group=root'
793*1596Szelenkov@nginx.com            assert obj['GID'] == 0, 'root gid user=root group=root'
7941304St.nateldemoura@f5.com
7951304St.nateldemoura@f5.com            self.load('user_group', group='root')
7961304St.nateldemoura@f5.com
7971304St.nateldemoura@f5.com            obj = self.getjson()['body']
798*1596Szelenkov@nginx.com            assert obj['UID'] == nobody_uid, 'root uid group=root'
799*1596Szelenkov@nginx.com            assert obj['GID'] == 0, 'root gid group=root'
800