Deleted Added
1import os
2import re
3import shutil
4import time
5import unittest
6
7from unit.applications.lang.php import TestApplicationPHP
8
9class TestPHPApplication(TestApplicationPHP):
10 prerequisites = {'modules': {'php': 'all'}}
11
12 def before_disable_functions(self):
13 body = self.get()['body']
14
15 self.assertRegex(body, r'time: \d+', 'disable_functions before time')
16 self.assertRegex(body, r'exec: \/\w+', 'disable_functions before exec')
17
18 def set_opcache(self, app, val):
19 self.assertIn(
20 'success',
21 self.conf(
22 {
23 "admin": {
24 "opcache.enable": val,
25 "opcache.enable_cli": val,
26 },
27 },
28 'applications/' + app + '/options',
29 ),
30 )
31
32 opcache = self.get()['headers']['X-OPcache']
33
34 if not opcache or opcache == '-1':
35 print('opcache is not supported')
36 raise unittest.SkipTest()
37
38 self.assertEqual(opcache, val, 'opcache value')
39
40 def test_php_application_variables(self):
41 self.load('variables')
42
43 body = 'Test body string.'
44
45 resp = self.post(
46 headers={
47 'Host': 'localhost',
48 'Content-Type': 'text/html',
49 'Custom-Header': 'blah',
50 'Connection': 'close',
51 },
52 body=body,
53 url='/index.php/blah?var=val'
54 )
55
56 self.assertEqual(resp['status'], 200, 'status')
57 headers = resp['headers']
58 header_server = headers.pop('Server')
59 self.assertRegex(header_server, r'Unit/[\d\.]+', 'server header')
60 self.assertEqual(
61 headers.pop('Server-Software'),
62 header_server,
63 'server software header',
64 )
65
66 date = headers.pop('Date')
67 self.assertEqual(date[-4:], ' GMT', 'date header timezone')
68 self.assertLess(
69 abs(self.date_to_sec_epoch(date) - self.sec_epoch()),
70 5,
71 'date header',
72 )
73
74 if 'X-Powered-By' in headers:
75 headers.pop('X-Powered-By')
76
77 headers.pop('Content-type')
78 self.assertDictEqual(
79 headers,
80 {
81 'Connection': 'close',
82 'Content-Length': str(len(body)),
83 'Request-Method': 'POST',
84 'Path-Info': '/blah',
85 'Request-Uri': '/index.php/blah?var=val',
86 'Http-Host': 'localhost',
87 'Server-Protocol': 'HTTP/1.1',
88 'Custom-Header': 'blah',
89 },
90 'headers',
91 )
92 self.assertEqual(resp['body'], body, 'body')
93
94 def test_php_application_query_string(self):
95 self.load('query_string')
96
97 resp = self.get(url='/?var1=val1&var2=val2')
98
99 self.assertEqual(
100 resp['headers']['Query-String'],
101 'var1=val1&var2=val2',
102 'query string',
103 )
104
105 def test_php_application_query_string_empty(self):
106 self.load('query_string')
107
108 resp = self.get(url='/?')
109
110 self.assertEqual(resp['status'], 200, 'query string empty status')
111 self.assertEqual(
112 resp['headers']['Query-String'], '', 'query string empty'
113 )
114
115 def test_php_application_query_string_absent(self):
116 self.load('query_string')
117
118 resp = self.get()
119
120 self.assertEqual(resp['status'], 200, 'query string absent status')
121 self.assertEqual(
122 resp['headers']['Query-String'], '', 'query string absent'
123 )
124
125 def test_php_application_phpinfo(self):
126 self.load('phpinfo')
127
128 resp = self.get()
129
130 self.assertEqual(resp['status'], 200, 'status')
131 self.assertNotEqual(resp['body'], '', 'body not empty')
132
133 def test_php_application_header_status(self):
134 self.load('header')
135
136 self.assertEqual(
137 self.get(
138 headers={
139 'Host': 'localhost',
140 'Connection': 'close',
141 'X-Header': 'HTTP/1.1 404 Not Found',
142 }
143 )['status'],
144 404,
145 'status',
146 )
147
148 self.assertEqual(
149 self.get(
150 headers={
151 'Host': 'localhost',
152 'Connection': 'close',
153 'X-Header': 'http/1.1 404 Not Found',
154 }
155 )['status'],
156 404,
157 'status case insensitive',
158 )
159
160 self.assertEqual(
161 self.get(
162 headers={
163 'Host': 'localhost',
164 'Connection': 'close',
165 'X-Header': 'HTTP/ 404 Not Found',
166 }
167 )['status'],
168 404,
169 'status version empty',
170 )
171
172
173 def test_php_application_404(self):
174 self.load('404')
175
176 resp = self.get()
177
178 self.assertEqual(resp['status'], 404, '404 status')
179 self.assertRegex(
180 resp['body'], r'<title>404 Not Found</title>', '404 body'
181 )
182
183 def test_php_application_keepalive_body(self):
184 self.load('mirror')
185
186 self.assertEqual(self.get()['status'], 200, 'init')
187
188 body = '0123456789' * 500
189 (resp, sock) = self.post(
190 headers={
191 'Host': 'localhost',
192 'Connection': 'keep-alive',
193 'Content-Type': 'text/html',
194 },
195 start=True,
196 body=body,
197 read_timeout=1,
198 )
199
200 self.assertEqual(resp['body'], body, 'keep-alive 1')
201
202 body = '0123456789'
203 resp = self.post(
204 headers={
205 'Host': 'localhost',
206 'Connection': 'close',
207 'Content-Type': 'text/html',
208 },
209 sock=sock,
210 body=body,
211 )
212
213 self.assertEqual(resp['body'], body, 'keep-alive 2')
214
215 def test_php_application_conditional(self):
216 self.load('conditional')
217
218 self.assertRegex(self.get()['body'], r'True', 'conditional true')
219 self.assertRegex(self.post()['body'], r'False', 'conditional false')
220
221 def test_php_application_get_variables(self):
222 self.load('get_variables')
223
224 resp = self.get(url='/?var1=val1&var2=&var3')
225 self.assertEqual(resp['headers']['X-Var-1'], 'val1', 'GET variables')
226 self.assertEqual(resp['headers']['X-Var-2'], '1', 'GET variables 2')
227 self.assertEqual(resp['headers']['X-Var-3'], '1', 'GET variables 3')
228 self.assertEqual(resp['headers']['X-Var-4'], '', 'GET variables 4')
229
230 def test_php_application_post_variables(self):
231 self.load('post_variables')
232
233 resp = self.post(
234 headers={
235 'Content-Type': 'application/x-www-form-urlencoded',
236 'Host': 'localhost',
237 'Connection': 'close',
238 },
239 body='var1=val1&var2=',
240 )
241 self.assertEqual(resp['headers']['X-Var-1'], 'val1', 'POST variables')
242 self.assertEqual(resp['headers']['X-Var-2'], '1', 'POST variables 2')
243 self.assertEqual(resp['headers']['X-Var-3'], '', 'POST variables 3')
244
245 def test_php_application_cookies(self):
246 self.load('cookies')
247
248 resp = self.get(
249 headers={
250 'Cookie': 'var=val; var2=val2',
251 'Host': 'localhost',
252 'Connection': 'close',
253 }
254 )
255
256 self.assertEqual(resp['headers']['X-Cookie-1'], 'val', 'cookie')
257 self.assertEqual(resp['headers']['X-Cookie-2'], 'val2', 'cookie')
258
259 def test_php_application_ini_precision(self):
260 self.load('ini_precision')
261
262 self.assertNotEqual(
263 self.get()['headers']['X-Precision'], '4', 'ini value default'
264 )
265
266 self.conf(
267 {"file": "ini/php.ini"}, 'applications/ini_precision/options'
268 )
269
270 self.assertEqual(
271 self.get()['headers']['X-File'],
272 self.current_dir + '/php/ini_precision/ini/php.ini',
273 'ini file',
274 )
275 self.assertEqual(
276 self.get()['headers']['X-Precision'], '4', 'ini value'
277 )
278
279 @unittest.skip('not yet')
280 def test_php_application_ini_admin_user(self):
281 self.load('ini_precision')
282
283 self.assertIn(
284 'error',
285 self.conf(
286 {"user": {"precision": "4"}, "admin": {"precision": "5"}},
287 'applications/ini_precision/options',
288 ),
289 'ini admin user',
290 )
291
292 def test_php_application_ini_admin(self):
293 self.load('ini_precision')
294
295 self.conf(
296 {"file": "php.ini", "admin": {"precision": "5"}},
297 'applications/ini_precision/options',
298 )
299
300 self.assertEqual(
301 self.get()['headers']['X-Precision'], '5', 'ini value admin'
302 )
303
304 def test_php_application_ini_user(self):
305 self.load('ini_precision')
306
307 self.conf(
308 {"file": "php.ini", "user": {"precision": "5"}},
309 'applications/ini_precision/options',
310 )
311
312 self.assertEqual(
313 self.get()['headers']['X-Precision'], '5', 'ini value user'
314 )
315
316 def test_php_application_ini_user_2(self):
317 self.load('ini_precision')
318
319 self.conf(
320 {"file": "ini/php.ini"}, 'applications/ini_precision/options'
321 )
322
323 self.assertEqual(
324 self.get()['headers']['X-Precision'], '4', 'ini user file'
325 )
326
327 self.conf(
328 {"precision": "5"}, 'applications/ini_precision/options/user'
329 )
330
331 self.assertEqual(
332 self.get()['headers']['X-Precision'], '5', 'ini value user'
333 )
334
335 def test_php_application_ini_set_admin(self):
336 self.load('ini_precision')
337
338 self.conf(
339 {"admin": {"precision": "5"}}, 'applications/ini_precision/options'
340 )
341
342 self.assertEqual(
343 self.get(url='/?precision=6')['headers']['X-Precision'],
344 '5',
345 'ini set admin',
346 )
347
348 def test_php_application_ini_set_user(self):
349 self.load('ini_precision')
350
351 self.conf(
352 {"user": {"precision": "5"}}, 'applications/ini_precision/options'
353 )
354
355 self.assertEqual(
356 self.get(url='/?precision=6')['headers']['X-Precision'],
357 '6',
358 'ini set user',
359 )
360
361 def test_php_application_ini_repeat(self):
362 self.load('ini_precision')
363
364 self.conf(
365 {"user": {"precision": "5"}}, 'applications/ini_precision/options'
366 )
367
368 self.assertEqual(
369 self.get()['headers']['X-Precision'], '5', 'ini value'
370 )
371
372 self.assertEqual(
373 self.get()['headers']['X-Precision'], '5', 'ini value repeat'
374 )
375
376 def test_php_application_disable_functions_exec(self):
377 self.load('time_exec')
378
379 self.before_disable_functions()
380
381 self.conf(
382 {"admin": {"disable_functions": "exec"}},
383 'applications/time_exec/options',
384 )
385
386 body = self.get()['body']
387
388 self.assertRegex(body, r'time: \d+', 'disable_functions time')
389 self.assertNotRegex(body, r'exec: \/\w+', 'disable_functions exec')
390
391 def test_php_application_disable_functions_comma(self):
392 self.load('time_exec')
393
394 self.before_disable_functions()
395
396 self.conf(
397 {"admin": {"disable_functions": "exec,time"}},
398 'applications/time_exec/options',
399 )
400
401 body = self.get()['body']
402
403 self.assertNotRegex(body, r'time: \d+', 'disable_functions comma time')
404 self.assertNotRegex(
405 body, r'exec: \/\w+', 'disable_functions comma exec'
406 )
407
408 def test_php_application_disable_functions_space(self):
409 self.load('time_exec')
410
411 self.before_disable_functions()
412
413 self.conf(
414 {"admin": {"disable_functions": "exec time"}},
415 'applications/time_exec/options',
416 )
417
418 body = self.get()['body']
419
420 self.assertNotRegex(body, r'time: \d+', 'disable_functions space time')
421 self.assertNotRegex(
422 body, r'exec: \/\w+', 'disable_functions space exec'
423 )
424
425 def test_php_application_disable_functions_user(self):
426 self.load('time_exec')
427
428 self.before_disable_functions()
429
430 self.conf(
431 {"user": {"disable_functions": "exec"}},
432 'applications/time_exec/options',
433 )
434
435 body = self.get()['body']
436
437 self.assertRegex(body, r'time: \d+', 'disable_functions user time')
438 self.assertNotRegex(
439 body, r'exec: \/\w+', 'disable_functions user exec'
440 )
441
442 def test_php_application_disable_functions_nonexistent(self):
443 self.load('time_exec')
444
445 self.before_disable_functions()
446
447 self.conf(
448 {"admin": {"disable_functions": "blah"}},
449 'applications/time_exec/options',
450 )
451
452 body = self.get()['body']
453
454 self.assertRegex(
455 body, r'time: \d+', 'disable_functions nonexistent time'
456 )
457 self.assertRegex(
458 body, r'exec: \/\w+', 'disable_functions nonexistent exec'
459 )
460
461 def test_php_application_disable_classes(self):
462 self.load('date_time')
463
464 self.assertRegex(
465 self.get()['body'], r'012345', 'disable_classes before'
466 )
467
468 self.conf(
469 {"admin": {"disable_classes": "DateTime"}},
470 'applications/date_time/options',
471 )
472
473 self.assertNotRegex(
474 self.get()['body'], r'012345', 'disable_classes before'
475 )
476
477 def test_php_application_disable_classes_user(self):
478 self.load('date_time')
479
480 self.assertRegex(
481 self.get()['body'], r'012345', 'disable_classes before'
482 )
483
484 self.conf(
485 {"user": {"disable_classes": "DateTime"}},
486 'applications/date_time/options',
487 )
488
489 self.assertNotRegex(
490 self.get()['body'], r'012345', 'disable_classes before'
491 )
492
493 def test_php_application_error_log(self):
494 self.load('error_log')
495
496 self.assertEqual(self.get()['status'], 200, 'status')
497
498 time.sleep(1)
499
500 self.assertEqual(self.get()['status'], 200, 'status 2')
501
502 self.stop()
503
504 pattern = r'\d{4}\/\d\d\/\d\d\s\d\d:.+\[notice\].+Error in application'
505
506 self.assertIsNotNone(self.wait_for_record(pattern), 'errors print')
507
508 with open(self.testdir + '/unit.log', 'r', errors='ignore') as f:
509 errs = re.findall(pattern, f.read())
510
511 self.assertEqual(len(errs), 2, 'error_log count')
512
513 date = errs[0].split('[')[0]
514 date2 = errs[1].split('[')[0]
515 self.assertNotEqual(date, date2, 'date diff')
516
517 def test_php_application_script(self):
518 self.assertIn(
519 'success',
520 self.conf(
521 {
522 "listeners": {"*:7080": {"pass": "applications/script"}},
523 "applications": {
524 "script": {
525 "type": "php",
526 "processes": {"spare": 0},
527 "root": self.current_dir + "/php/script",
528 "script": "phpinfo.php",
529 }
530 },
531 }
532 ),
533 'configure script',
534 )
535
536 resp = self.get()
537
538 self.assertEqual(resp['status'], 200, 'status')
539 self.assertNotEqual(resp['body'], '', 'body not empty')
540
541 def test_php_application_index_default(self):
542 self.assertIn(
543 'success',
544 self.conf(
545 {
546 "listeners": {"*:7080": {"pass": "applications/phpinfo"}},
547 "applications": {
548 "phpinfo": {
549 "type": "php",
550 "processes": {"spare": 0},
551 "root": self.current_dir + "/php/phpinfo",
552 }
553 },
554 }
555 ),
556 'configure index default',
557 )
558
559 resp = self.get()
560
561 self.assertEqual(resp['status'], 200, 'status')
562 self.assertNotEqual(resp['body'], '', 'body not empty')
563
564 def test_php_application_extension_check(self):
565 self.load('phpinfo')
566
567 self.assertNotEqual(
568 self.get(url='/index.wrong')['status'], 200, 'status'
569 )
570
571 new_root = self.testdir + "/php"
572 os.mkdir(new_root)
573 shutil.copy(self.current_dir + '/php/phpinfo/index.wrong', new_root)
574
575 self.assertIn(
576 'success',
577 self.conf(
578 {
579 "listeners": {"*:7080": {"pass": "applications/phpinfo"}},
580 "applications": {
581 "phpinfo": {
582 "type": "php",
583 "processes": {"spare": 0},
584 "root": new_root,
585 "working_directory": new_root,
586 }
587 },
588 }
589 ),
590 'configure new root',
591 )
592
593 resp = self.get()
594 self.assertNotEqual(
595 str(resp['status']) + resp['body'], '200', 'status new root'
596 )
597
598 def run_php_application_cwd_root_tests(self):
599 self.assertIn(
600 'success', self.conf_delete('applications/cwd/working_directory')
601 )
602
603 script_cwd = self.current_dir + '/php/cwd'
604
605 resp = self.get()
606 self.assertEqual(resp['status'], 200, 'status ok')
607 self.assertEqual(resp['body'], script_cwd, 'default cwd')
608
609 self.assertIn(
610 'success',
611 self.conf(
612 '"' + self.current_dir + '"',
613 'applications/cwd/working_directory',
614 ),
615 )
616
617 resp = self.get()
618 self.assertEqual(resp['status'], 200, 'status ok')
619 self.assertEqual(resp['body'], script_cwd, 'wdir cwd')
620
621 resp = self.get(url='/?chdir=/')
622 self.assertEqual(resp['status'], 200, 'status ok')
623 self.assertEqual(resp['body'], '/', 'cwd after chdir')
624
625 # cwd must be restored
626
627 resp = self.get()
628 self.assertEqual(resp['status'], 200, 'status ok')
629 self.assertEqual(resp['body'], script_cwd, 'cwd restored')
630
631 resp = self.get(url='/subdir/')
632 self.assertEqual(
633 resp['body'], script_cwd + '/subdir', 'cwd subdir',
634 )
635
636 def test_php_application_cwd_root(self):
637 self.load('cwd')
638 self.run_php_application_cwd_root_tests()
639
640 def test_php_application_cwd_opcache_disabled(self):
641 self.load('cwd')
642 self.set_opcache('cwd', '0')
643 self.run_php_application_cwd_root_tests()
644
645 def test_php_application_cwd_opcache_enabled(self):
646 self.load('cwd')
647 self.set_opcache('cwd', '1')
648 self.run_php_application_cwd_root_tests()
649
650 def run_php_application_cwd_script_tests(self):
651 self.load('cwd')
652
653 script_cwd = self.current_dir + '/php/cwd'
654
655 self.assertIn(
656 'success', self.conf_delete('applications/cwd/working_directory')
657 )
658
659 self.assertIn(
660 'success', self.conf('"index.php"', 'applications/cwd/script')
661 )
662
663 self.assertEqual(
664 self.get()['body'], script_cwd, 'default cwd',
665 )
666
667 self.assertEqual(
668 self.get(url='/?chdir=/')['body'], '/', 'cwd after chdir',
669 )
670
671 # cwd must be restored
672 self.assertEqual(self.get()['body'], script_cwd, 'cwd restored')
673
674 def test_php_application_cwd_script(self):
675 self.load('cwd')
676 self.run_php_application_cwd_script_tests()
677
678 def test_php_application_cwd_script_opcache_disabled(self):
679 self.load('cwd')
680 self.set_opcache('cwd', '0')
681 self.run_php_application_cwd_script_tests()
682
683 def test_php_application_cwd_script_opcache_enabled(self):
684 self.load('cwd')
685 self.set_opcache('cwd', '1')
686 self.run_php_application_cwd_script_tests()
687
688 def test_php_application_path_relative(self):
689 self.load('open')
690
691 self.assertEqual(self.get()['body'], 'test', 'relative path')
692
693 self.assertNotEqual(
694 self.get(url='/?chdir=/')['body'], 'test', 'relative path w/ chdir'
695 )
696
697 self.assertEqual(self.get()['body'], 'test', 'relative path 2')
698
699
700if __name__ == '__main__':
701 TestPHPApplication.main()