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