1# -*- coding: utf-8 -*- 2import unittest 3 4from unit.applications.proto import TestApplicationProto 5 6 7class TestRouting(TestApplicationProto): 8 prerequisites = {'modules': {'python': 'any'}} 9 10 def setUp(self): 11 super().setUp() 12 13 self.assertIn( 14 'success', 15 self.conf( 16 { 17 "listeners": {"*:7080": {"pass": "routes"}}, 18 "routes": [ 19 { 20 "match": {"method": "GET"}, 21 "action": {"return": 200}, 22 } 23 ], 24 "applications": {}, 25 } 26 ), 27 'routing configure', 28 ) 29 30 def route(self, route): 31 return self.conf([route], 'routes') 32 33 def route_match(self, match): 34 self.assertIn( 35 'success', 36 self.route({"match": match, "action": {"return": 200}}), 37 'route match configure', 38 ) 39 40 def route_match_invalid(self, match): 41 self.assertIn( 42 'error', 43 self.route({"match": match, "action": {"return": 200}}), 44 'route match configure invalid', 45 ) 46 47 def host(self, host, status): 48 self.assertEqual( 49 self.get(headers={'Host': host, 'Connection': 'close'})[ 50 'status' 51 ], 52 status, 53 'match host', 54 ) 55 56 def cookie(self, cookie, status): 57 self.assertEqual( 58 self.get( 59 headers={ 60 'Host': 'localhost', 61 'Cookie': cookie, 62 'Connection': 'close', 63 }, 64 )['status'], 65 status, 66 'match cookie', 67 ) 68 69 def test_routes_match_method_positive(self): 70 self.assertEqual(self.get()['status'], 200, 'GET') 71 self.assertEqual(self.post()['status'], 404, 'POST') 72 73 def test_routes_match_method_positive_many(self): 74 self.route_match({"method": ["GET", "POST"]}) 75 76 self.assertEqual(self.get()['status'], 200, 'GET') 77 self.assertEqual(self.post()['status'], 200, 'POST') 78 self.assertEqual(self.delete()['status'], 404, 'DELETE') 79 80 def test_routes_match_method_negative(self): 81 self.route_match({"method": "!GET"}) 82 83 self.assertEqual(self.get()['status'], 404, 'GET') 84 self.assertEqual(self.post()['status'], 200, 'POST') 85 86 def test_routes_match_method_negative_many(self): 87 self.route_match({"method": ["!GET", "!POST"]}) 88 89 self.assertEqual(self.get()['status'], 404, 'GET') 90 self.assertEqual(self.post()['status'], 404, 'POST') 91 self.assertEqual(self.delete()['status'], 200, 'DELETE') 92 93 def test_routes_match_method_wildcard_left(self): 94 self.route_match({"method": "*ET"}) 95 96 self.assertEqual(self.get()['status'], 200, 'GET') 97 self.assertEqual(self.post()['status'], 404, 'POST') 98 99 def test_routes_match_method_wildcard_right(self): 100 self.route_match({"method": "GE*"}) 101 102 self.assertEqual(self.get()['status'], 200, 'GET') 103 self.assertEqual(self.post()['status'], 404, 'POST') 104 105 def test_routes_match_method_wildcard_left_right(self): 106 self.route_match({"method": "*GET*"}) 107 108 self.assertEqual(self.get()['status'], 200, 'GET') 109 self.assertEqual(self.post()['status'], 404, 'POST') 110 111 def test_routes_match_method_wildcard(self): 112 self.route_match({"method": "*"}) 113 114 self.assertEqual(self.get()['status'], 200, 'GET') 115 116 def test_routes_match_invalid(self): 117 self.route_match_invalid({"method": "**"}) 118 self.route_match_invalid({"method": "blah**"}) 119 self.route_match_invalid({"host": "*blah*blah"}) 120 self.route_match_invalid({"host": "blah*blah*blah"}) 121 self.route_match_invalid({"host": "blah*blah*"}) 122 123 def test_routes_match_wildcard_middle(self): 124 self.route_match({"host": "ex*le"}) 125 126 self.host('example', 200) 127 self.host('www.example', 404) 128 self.host('example.com', 404) 129 self.host('exampl', 404) 130 131 def test_routes_match_method_case_insensitive(self): 132 self.route_match({"method": "get"}) 133 134 self.assertEqual(self.get()['status'], 200, 'GET') 135 136 def test_routes_match_wildcard_left_case_insensitive(self): 137 self.route_match({"method": "*get"}) 138 self.assertEqual(self.get()['status'], 200, 'GET') 139 140 self.route_match({"method": "*et"}) 141 self.assertEqual(self.get()['status'], 200, 'GET') 142 143 def test_routes_match_wildcard_middle_case_insensitive(self): 144 self.route_match({"method": "g*t"}) 145 146 self.assertEqual(self.get()['status'], 200, 'GET') 147 148 def test_routes_match_wildcard_right_case_insensitive(self): 149 self.route_match({"method": "get*"}) 150 self.assertEqual(self.get()['status'], 200, 'GET') 151 152 self.route_match({"method": "ge*"}) 153 self.assertEqual(self.get()['status'], 200, 'GET') 154 155 def test_routes_match_wildcard_substring_case_insensitive(self): 156 self.route_match({"method": "*et*"}) 157 158 self.assertEqual(self.get()['status'], 200, 'GET') 159 160 def test_routes_match_wildcard_left_case_sensitive(self): 161 self.route_match({"uri": "*blah"}) 162 163 self.assertEqual(self.get(url='/blah')['status'], 200, '/blah') 164 self.assertEqual(self.get(url='/BLAH')['status'], 404, '/BLAH') 165 166 def test_routes_match_wildcard_middle_case_sensitive(self): 167 self.route_match({"uri": "/b*h"}) 168 169 self.assertEqual(self.get(url='/blah')['status'], 200, '/blah') 170 self.assertEqual(self.get(url='/BLAH')['status'], 404, '/BLAH') 171 172 def test_routes_match_wildcard_right_case_sensitive(self): 173 self.route_match({"uri": "/bla*"}) 174 175 self.assertEqual(self.get(url='/blah')['status'], 200, '/blah') 176 self.assertEqual(self.get(url='/BLAH')['status'], 404, '/BLAH') 177 178 def test_routes_match_wildcard_substring_case_sensitive(self): 179 self.route_match({"uri": "*bla*"}) 180 181 self.assertEqual(self.get(url='/blah')['status'], 200, '/blah') 182 self.assertEqual(self.get(url='/BLAH')['status'], 404, '/BLAH') 183 184 def test_routes_absent(self): 185 self.conf( 186 { 187 "listeners": {"*:7081": {"pass": "applications/empty"}}, 188 "applications": { 189 "empty": { 190 "type": "python", 191 "processes": {"spare": 0}, 192 "path": self.current_dir + '/python/empty', 193 "working_directory": self.current_dir 194 + '/python/empty', 195 "module": "wsgi", 196 } 197 }, 198 } 199 ) 200 201 self.assertEqual(self.get(port=7081)['status'], 200, 'routes absent') 202 203 def test_routes_pass_invalid(self): 204 self.assertIn( 205 'error', 206 self.conf({"pass": "routes/blah"}, 'listeners/*:7080'), 207 'routes invalid', 208 ) 209 210 def test_route_empty(self): 211 self.assertIn( 212 'success', 213 self.conf( 214 { 215 "listeners": {"*:7080": {"pass": "routes/main"}}, 216 "routes": {"main": []}, 217 "applications": {}, 218 } 219 ), 220 'route empty configure', 221 ) 222 223 self.assertEqual(self.get()['status'], 404, 'route empty') 224 225 def test_routes_route_empty(self): 226 self.assertIn( 227 'success', 228 self.conf({}, 'listeners'), 229 'routes empty listeners configure', 230 ) 231 232 self.assertIn( 233 'success', self.conf({}, 'routes'), 'routes empty configure' 234 ) 235 236 def test_routes_route_match_absent(self): 237 self.assertIn( 238 'success', 239 self.conf([{"action": {"return": 200}}], 'routes'), 240 'route match absent configure', 241 ) 242 243 self.assertEqual(self.get()['status'], 200, 'route match absent') 244 245 def test_routes_route_action_absent(self): 246 self.skip_alerts.append(r'failed to apply new conf') 247 248 self.assertIn( 249 'error', 250 self.conf([{"match": {"method": "GET"}}], 'routes'), 251 'route pass absent configure', 252 ) 253 254 def test_routes_route_pass_absent(self): 255 self.assertIn( 256 'error', 257 self.conf([{"match": {"method": "GET"}, "action": {}}], 'routes'), 258 'route pass absent configure', 259 ) 260 261 def test_routes_action_unique(self): 262 self.assertIn( 263 'success', 264 self.conf( 265 { 266 "listeners": { 267 "*:7080": {"pass": "routes"}, 268 "*:7081": {"pass": "applications/app"}, 269 }, 270 "routes": [{"action": {"proxy": "http://127.0.0.1:7081"}}], 271 "applications": { 272 "app": { 273 "type": "python", 274 "processes": {"spare": 0}, 275 "path": "/app", 276 "module": "wsgi", 277 } 278 }, 279 } 280 ), 281 ) 282 283 self.assertIn( 284 'error', 285 self.conf( 286 {"proxy": "http://127.0.0.1:7081", "share": self.testdir}, 287 'routes/0/action', 288 ), 289 'proxy share', 290 ) 291 self.assertIn( 292 'error', 293 self.conf( 294 { 295 "proxy": "http://127.0.0.1:7081", 296 "pass": "applications/app", 297 }, 298 'routes/0/action', 299 ), 300 'proxy pass', 301 ) 302 self.assertIn( 303 'error', 304 self.conf( 305 {"share": self.testdir, "pass": "applications/app"}, 306 'routes/0/action', 307 ), 308 'share pass', 309 ) 310 311 def test_routes_rules_two(self): 312 self.assertIn( 313 'success', 314 self.conf( 315 [ 316 {"match": {"method": "GET"}, "action": {"return": 200}}, 317 {"match": {"method": "POST"}, "action": {"return": 201}}, 318 ], 319 'routes', 320 ), 321 'rules two configure', 322 ) 323 324 self.assertEqual(self.get()['status'], 200, 'rules two match first') 325 self.assertEqual(self.post()['status'], 201, 'rules two match second') 326 327 def test_routes_two(self): 328 self.assertIn( 329 'success', 330 self.conf( 331 { 332 "listeners": {"*:7080": {"pass": "routes/first"}}, 333 "routes": { 334 "first": [ 335 { 336 "match": {"method": "GET"}, 337 "action": {"pass": "routes/second"}, 338 } 339 ], 340 "second": [ 341 { 342 "match": {"host": "localhost"}, 343 "action": {"return": 200}, 344 } 345 ], 346 }, 347 "applications": {}, 348 } 349 ), 350 'routes two configure', 351 ) 352 353 self.assertEqual(self.get()['status'], 200, 'routes two') 354 355 def test_routes_match_host_positive(self): 356 self.route_match({"host": "localhost"}) 357 358 self.assertEqual(self.get()['status'], 200, 'localhost') 359 self.host('localhost.', 200) 360 self.host('localhost.', 200) 361 self.host('.localhost', 404) 362 self.host('www.localhost', 404) 363 self.host('localhost1', 404) 364 365 @unittest.skip('not yet') 366 def test_routes_match_host_absent(self): 367 self.route_match({"host": "localhost"}) 368 369 self.assertEqual( 370 self.get(headers={'Connection': 'close'})['status'], 371 400, 372 'match host absent', 373 ) 374 375 def test_routes_match_host_ipv4(self): 376 self.route_match({"host": "127.0.0.1"}) 377 378 self.host('127.0.0.1', 200) 379 self.host('127.0.0.1:7080', 200) 380 381 def test_routes_match_host_ipv6(self): 382 self.route_match({"host": "[::1]"}) 383 384 self.host('[::1]', 200) 385 self.host('[::1]:7080', 200) 386 387 def test_routes_match_host_positive_many(self): 388 self.route_match({"host": ["localhost", "example.com"]}) 389 390 self.assertEqual(self.get()['status'], 200, 'localhost') 391 self.host('example.com', 200) 392 393 def test_routes_match_host_positive_and_negative(self): 394 self.route_match({"host": ["*example.com", "!www.example.com"]}) 395 396 self.assertEqual(self.get()['status'], 404, 'localhost') 397 self.host('example.com', 200) 398 self.host('www.example.com', 404) 399 self.host('!www.example.com', 200) 400 401 def test_routes_match_host_positive_and_negative_wildcard(self): 402 self.route_match({"host": ["*example*", "!www.example*"]}) 403 404 self.host('example.com', 200) 405 self.host('www.example.com', 404) 406 407 def test_routes_match_host_case_insensitive(self): 408 self.route_match({"host": "Example.com"}) 409 410 self.host('example.com', 200) 411 self.host('EXAMPLE.COM', 200) 412 413 def test_routes_match_host_port(self): 414 self.route_match({"host": "example.com"}) 415 416 self.host('example.com:7080', 200) 417 418 def test_routes_match_host_empty(self): 419 self.route_match({"host": ""}) 420 421 self.host('', 200) 422 self.assertEqual( 423 self.get(http_10=True, headers={})['status'], 424 200, 425 'match host empty 2', 426 ) 427 self.assertEqual(self.get()['status'], 404, 'match host empty 3') 428 429 def test_routes_match_uri_positive(self): 430 self.route_match({"uri": ["/blah", "/slash/"]}) 431 432 self.assertEqual(self.get()['status'], 404, '/') 433 self.assertEqual(self.get(url='/blah')['status'], 200, '/blah') 434 self.assertEqual(self.get(url='/blah#foo')['status'], 200, '/blah#foo') 435 self.assertEqual(self.get(url='/blah?var')['status'], 200, '/blah?var') 436 self.assertEqual(self.get(url='//blah')['status'], 200, '//blah') 437 self.assertEqual( 438 self.get(url='/slash/foo/../')['status'], 200, 'relative' 439 ) 440 self.assertEqual(self.get(url='/slash/./')['status'], 200, '/slash/./') 441 self.assertEqual( 442 self.get(url='/slash//.//')['status'], 200, 'adjacent slashes' 443 ) 444 self.assertEqual(self.get(url='/%')['status'], 400, 'percent') 445 self.assertEqual(self.get(url='/%1')['status'], 400, 'percent digit') 446 self.assertEqual(self.get(url='/%A')['status'], 400, 'percent letter') 447 self.assertEqual( 448 self.get(url='/slash/.?args')['status'], 200, 'dot args' 449 ) 450 self.assertEqual( 451 self.get(url='/slash/.#frag')['status'], 200, 'dot frag' 452 ) 453 self.assertEqual( 454 self.get(url='/slash/foo/..?args')['status'], 455 200, 456 'dot dot args', 457 ) 458 self.assertEqual( 459 self.get(url='/slash/foo/..#frag')['status'], 460 200, 461 'dot dot frag', 462 ) 463 self.assertEqual( 464 self.get(url='/slash/.')['status'], 200, 'trailing dot' 465 ) 466 self.assertEqual( 467 self.get(url='/slash/foo/..')['status'], 468 200, 469 'trailing dot dot', 470 ) 471 472 def test_routes_match_uri_case_sensitive(self): 473 self.route_match({"uri": "/BLAH"}) 474 475 self.assertEqual(self.get(url='/blah')['status'], 404, '/blah') 476 self.assertEqual(self.get(url='/BlaH')['status'], 404, '/BlaH') 477 self.assertEqual(self.get(url='/BLAH')['status'], 200, '/BLAH') 478 479 def test_routes_match_uri_normalize(self): 480 self.route_match({"uri": "/blah"}) 481 482 self.assertEqual( 483 self.get(url='/%62%6c%61%68')['status'], 200, 'normalize' 484 ) 485 486 def test_routes_match_empty_array(self): 487 self.route_match({"uri": []}) 488 489 self.assertEqual(self.get(url='/blah')['status'], 200, 'empty array') 490 491 def test_routes_reconfigure(self): 492 self.assertIn('success', self.conf([], 'routes'), 'redefine') 493 self.assertEqual(self.get()['status'], 404, 'redefine request') 494 495 self.assertIn( 496 'success', 497 self.conf([{"action": {"return": 200}}], 'routes'), 498 'redefine 2', 499 ) 500 self.assertEqual(self.get()['status'], 200, 'redefine request 2') 501 502 self.assertIn('success', self.conf([], 'routes'), 'redefine 3') 503 self.assertEqual(self.get()['status'], 404, 'redefine request 3') 504 505 self.assertIn( 506 'success', 507 self.conf( 508 { 509 "listeners": {"*:7080": {"pass": "routes/main"}}, 510 "routes": {"main": [{"action": {"return": 200}}]}, 511 "applications": {}, 512 } 513 ), 514 'redefine 4', 515 ) 516 self.assertEqual(self.get()['status'], 200, 'redefine request 4') 517 518 self.assertIn( 519 'success', self.conf_delete('routes/main/0'), 'redefine 5' 520 ) 521 self.assertEqual(self.get()['status'], 404, 'redefine request 5') 522 523 self.assertIn( 524 'success', 525 self.conf_post({"action": {"return": 200}}, 'routes/main'), 526 'redefine 6', 527 ) 528 self.assertEqual(self.get()['status'], 200, 'redefine request 6') 529 530 self.assertIn( 531 'error', 532 self.conf({"action": {"return": 200}}, 'routes/main/2'), 533 'redefine 7', 534 ) 535 self.assertIn( 536 'success', 537 self.conf({"action": {"return": 201}}, 'routes/main/1'), 538 'redefine 8', 539 ) 540 541 self.assertEqual( 542 len(self.conf_get('routes/main')), 2, 'redefine conf 8' 543 ) 544 self.assertEqual(self.get()['status'], 200, 'redefine request 8') 545 546 def test_routes_edit(self): 547 self.route_match({"method": "GET"}) 548 549 self.assertEqual(self.get()['status'], 200, 'routes edit GET') 550 self.assertEqual(self.post()['status'], 404, 'routes edit POST') 551 552 self.assertIn( 553 'success', 554 self.conf_post( 555 {"match": {"method": "POST"}, "action": {"return": 200}}, 556 'routes', 557 ), 558 'routes edit configure 2', 559 ) 560 self.assertEqual( 561 'GET', 562 self.conf_get('routes/0/match/method'), 563 'routes edit configure 2 check', 564 ) 565 self.assertEqual( 566 'POST', 567 self.conf_get('routes/1/match/method'), 568 'routes edit configure 2 check 2', 569 ) 570 571 self.assertEqual(self.get()['status'], 200, 'routes edit GET 2') 572 self.assertEqual(self.post()['status'], 200, 'routes edit POST 2') 573 574 self.assertIn( 575 'success', self.conf_delete('routes/0'), 'routes edit configure 3', 576 ) 577 578 self.assertEqual(self.get()['status'], 404, 'routes edit GET 3') 579 self.assertEqual(self.post()['status'], 200, 'routes edit POST 3') 580 581 self.assertIn( 582 'error', 583 self.conf_delete('routes/1'), 584 'routes edit configure invalid', 585 ) 586 self.assertIn( 587 'error', 588 self.conf_delete('routes/-1'), 589 'routes edit configure invalid 2', 590 ) 591 self.assertIn( 592 'error', 593 self.conf_delete('routes/blah'), 594 'routes edit configure invalid 3', 595 ) 596 597 self.assertEqual(self.get()['status'], 404, 'routes edit GET 4') 598 self.assertEqual(self.post()['status'], 200, 'routes edit POST 4') 599 600 self.assertIn( 601 'success', self.conf_delete('routes/0'), 'routes edit configure 5', 602 ) 603 604 self.assertEqual(self.get()['status'], 404, 'routes edit GET 5') 605 self.assertEqual(self.post()['status'], 404, 'routes edit POST 5') 606 607 self.assertIn( 608 'success', 609 self.conf_post( 610 {"match": {"method": "POST"}, "action": {"return": 200}}, 611 'routes', 612 ), 613 'routes edit configure 6', 614 ) 615 616 self.assertEqual(self.get()['status'], 404, 'routes edit GET 6') 617 self.assertEqual(self.post()['status'], 200, 'routes edit POST 6') 618 619 self.assertIn( 620 'success', 621 self.conf( 622 { 623 "listeners": {"*:7080": {"pass": "routes/main"}}, 624 "routes": {"main": [{"action": {"return": 200}}]}, 625 "applications": {}, 626 } 627 ), 628 'route edit configure 7', 629 ) 630 631 self.assertIn( 632 'error', 633 self.conf_delete('routes/0'), 634 'routes edit configure invalid 4', 635 ) 636 self.assertIn( 637 'error', 638 self.conf_delete('routes/main'), 639 'routes edit configure invalid 5', 640 ) 641 642 self.assertEqual(self.get()['status'], 200, 'routes edit GET 7') 643 644 self.assertIn( 645 'success', 646 self.conf_delete('listeners/*:7080'), 647 'route edit configure 8', 648 ) 649 self.assertIn( 650 'success', 651 self.conf_delete('routes/main'), 652 'route edit configure 9', 653 ) 654 655 def test_match_edit(self): 656 self.skip_alerts.append(r'failed to apply new conf') 657 658 self.route_match({"method": ["GET", "POST"]}) 659 660 self.assertEqual(self.get()['status'], 200, 'match edit GET') 661 self.assertEqual(self.post()['status'], 200, 'match edit POST') 662 self.assertEqual(self.put()['status'], 404, 'match edit PUT') 663 664 self.assertIn( 665 'success', 666 self.conf_post('\"PUT\"', 'routes/0/match/method'), 667 'match edit configure 2', 668 ) 669 self.assertListEqual( 670 ['GET', 'POST', 'PUT'], 671 self.conf_get('routes/0/match/method'), 672 'match edit configure 2 check', 673 ) 674 675 self.assertEqual(self.get()['status'], 200, 'match edit GET 2') 676 self.assertEqual(self.post()['status'], 200, 'match edit POST 2') 677 self.assertEqual(self.put()['status'], 200, 'match edit PUT 2') 678 679 self.assertIn( 680 'success', 681 self.conf_delete('routes/0/match/method/1'), 682 'match edit configure 3', 683 ) 684 self.assertListEqual( 685 ['GET', 'PUT'], 686 self.conf_get('routes/0/match/method'), 687 'match edit configure 3 check', 688 ) 689 690 self.assertEqual(self.get()['status'], 200, 'match edit GET 3') 691 self.assertEqual(self.post()['status'], 404, 'match edit POST 3') 692 self.assertEqual(self.put()['status'], 200, 'match edit PUT 3') 693 694 self.assertIn( 695 'success', 696 self.conf_delete('routes/0/match/method/1'), 697 'match edit configure 4', 698 ) 699 self.assertListEqual( 700 ['GET'], 701 self.conf_get('routes/0/match/method'), 702 'match edit configure 4 check', 703 ) 704 705 self.assertEqual(self.get()['status'], 200, 'match edit GET 4') 706 self.assertEqual(self.post()['status'], 404, 'match edit POST 4') 707 self.assertEqual(self.put()['status'], 404, 'match edit PUT 4') 708 709 self.assertIn( 710 'error', 711 self.conf_delete('routes/0/match/method/1'), 712 'match edit configure invalid', 713 ) 714 self.assertIn( 715 'error', 716 self.conf_delete('routes/0/match/method/-1'), 717 'match edit configure invalid 2', 718 ) 719 self.assertIn( 720 'error', 721 self.conf_delete('routes/0/match/method/blah'), 722 'match edit configure invalid 3', 723 ) 724 self.assertListEqual( 725 ['GET'], 726 self.conf_get('routes/0/match/method'), 727 'match edit configure 5 check', 728 ) 729 730 self.assertEqual(self.get()['status'], 200, 'match edit GET 5') 731 self.assertEqual(self.post()['status'], 404, 'match edit POST 5') 732 self.assertEqual(self.put()['status'], 404, 'match edit PUT 5') 733 734 self.assertIn( 735 'success', 736 self.conf_delete('routes/0/match/method/0'), 737 'match edit configure 6', 738 ) 739 self.assertListEqual( 740 [], 741 self.conf_get('routes/0/match/method'), 742 'match edit configure 6 check', 743 ) 744 745 self.assertEqual(self.get()['status'], 200, 'match edit GET 6') 746 self.assertEqual(self.post()['status'], 200, 'match edit POST 6') 747 self.assertEqual(self.put()['status'], 200, 'match edit PUT 6') 748 749 self.assertIn( 750 'success', 751 self.conf('"GET"', 'routes/0/match/method'), 752 'match edit configure 7', 753 ) 754 755 self.assertEqual(self.get()['status'], 200, 'match edit GET 7') 756 self.assertEqual(self.post()['status'], 404, 'match edit POST 7') 757 self.assertEqual(self.put()['status'], 404, 'match edit PUT 7') 758 759 self.assertIn( 760 'error', 761 self.conf_delete('routes/0/match/method/0'), 762 'match edit configure invalid 5', 763 ) 764 self.assertIn( 765 'error', 766 self.conf({}, 'routes/0/action'), 767 'match edit configure invalid 6', 768 ) 769 770 self.assertIn( 771 'success', 772 self.conf({}, 'routes/0/match'), 773 'match edit configure 8', 774 ) 775 776 self.assertEqual(self.get()['status'], 200, 'match edit GET 8') 777 778 def test_routes_match_rules(self): 779 self.route_match({"method": "GET", "host": "localhost", "uri": "/"}) 780 781 self.assertEqual(self.get()['status'], 200, 'routes match rules') 782 783 def test_routes_loop(self): 784 self.assertIn( 785 'success', 786 self.route({"match": {"uri": "/"}, "action": {"pass": "routes"}}), 787 'routes loop configure', 788 ) 789 790 self.assertEqual(self.get()['status'], 500, 'routes loop') 791 792 def test_routes_match_headers(self): 793 self.route_match({"headers": {"host": "localhost"}}) 794 795 self.assertEqual(self.get()['status'], 200, 'match headers') 796 self.host('Localhost', 200) 797 self.host('localhost.com', 404) 798 self.host('llocalhost', 404) 799 self.host('host', 404) 800 801 def test_routes_match_headers_multiple(self): 802 self.route_match({"headers": {"host": "localhost", "x-blah": "test"}}) 803 804 self.assertEqual(self.get()['status'], 404, 'match headers multiple') 805 self.assertEqual( 806 self.get( 807 headers={ 808 "Host": "localhost", 809 "X-blah": "test", 810 "Connection": "close", 811 } 812 )['status'], 813 200, 814 'match headers multiple 2', 815 ) 816 817 self.assertEqual( 818 self.get( 819 headers={ 820 "Host": "localhost", 821 "X-blah": "", 822 "Connection": "close", 823 } 824 )['status'], 825 404, 826 'match headers multiple 3', 827 ) 828 829 def test_routes_match_headers_multiple_values(self): 830 self.route_match({"headers": {"x-blah": "test"}}) 831 832 self.assertEqual( 833 self.get( 834 headers={ 835 "Host": "localhost", 836 "X-blah": ["test", "test", "test"], 837 "Connection": "close", 838 } 839 )['status'], 840 200, 841 'match headers multiple values', 842 ) 843 self.assertEqual( 844 self.get( 845 headers={ 846 "Host": "localhost", 847 "X-blah": ["test", "blah", "test"], 848 "Connection": "close", 849 } 850 )['status'], 851 404, 852 'match headers multiple values 2', 853 ) 854 self.assertEqual( 855 self.get( 856 headers={ 857 "Host": "localhost", 858 "X-blah": ["test", "", "test"], 859 "Connection": "close", 860 } 861 )['status'], 862 404, 863 'match headers multiple values 3', 864 ) 865 866 def test_routes_match_headers_multiple_rules(self): 867 self.route_match({"headers": {"x-blah": ["test", "blah"]}}) 868 869 self.assertEqual( 870 self.get()['status'], 404, 'match headers multiple rules' 871 ) 872 self.assertEqual( 873 self.get( 874 headers={ 875 "Host": "localhost", 876 "X-blah": "test", 877 "Connection": "close", 878 } 879 )['status'], 880 200, 881 'match headers multiple rules 2', 882 ) 883 self.assertEqual( 884 self.get( 885 headers={ 886 "Host": "localhost", 887 "X-blah": "blah", 888 "Connection": "close", 889 } 890 )['status'], 891 200, 892 'match headers multiple rules 3', 893 ) 894 self.assertEqual( 895 self.get( 896 headers={ 897 "Host": "localhost", 898 "X-blah": ["test", "blah", "test"], 899 "Connection": "close", 900 } 901 )['status'], 902 200, 903 'match headers multiple rules 4', 904 ) 905 906 self.assertEqual( 907 self.get( 908 headers={ 909 "Host": "localhost", 910 "X-blah": ["blah", ""], 911 "Connection": "close", 912 } 913 )['status'], 914 404, 915 'match headers multiple rules 5', 916 ) 917 918 def test_routes_match_headers_case_insensitive(self): 919 self.route_match({"headers": {"X-BLAH": "TEST"}}) 920 921 self.assertEqual( 922 self.get( 923 headers={ 924 "Host": "localhost", 925 "x-blah": "test", 926 "Connection": "close", 927 } 928 )['status'], 929 200, 930 'match headers case insensitive', 931 ) 932 933 def test_routes_match_headers_invalid(self): 934 self.route_match_invalid({"headers": ["blah"]}) 935 self.route_match_invalid({"headers": {"foo": ["bar", {}]}}) 936 self.route_match_invalid({"headers": {"": "blah"}}) 937 938 def test_routes_match_headers_empty_rule(self): 939 self.route_match({"headers": {"host": ""}}) 940 941 self.assertEqual(self.get()['status'], 404, 'localhost') 942 self.host('', 200) 943 944 def test_routes_match_headers_empty(self): 945 self.route_match({"headers": {}}) 946 self.assertEqual(self.get()['status'], 200, 'empty') 947 948 self.route_match({"headers": []}) 949 self.assertEqual(self.get()['status'], 200, 'empty 2') 950 951 def test_routes_match_headers_rule_array_empty(self): 952 self.route_match({"headers": {"blah": []}}) 953 954 self.assertEqual(self.get()['status'], 404, 'array empty') 955 self.assertEqual( 956 self.get( 957 headers={ 958 "Host": "localhost", 959 "blah": "foo", 960 "Connection": "close", 961 } 962 )['status'], 200, 'match headers rule array empty 2' 963 ) 964 965 def test_routes_match_headers_array(self): 966 self.route_match( 967 { 968 "headers": [ 969 {"x-header1": "foo*"}, 970 {"x-header2": "bar"}, 971 {"x-header3": ["foo", "bar"]}, 972 {"x-header1": "bar", "x-header4": "foo"}, 973 ] 974 } 975 ) 976 977 self.assertEqual(self.get()['status'], 404, 'match headers array') 978 self.assertEqual( 979 self.get( 980 headers={ 981 "Host": "localhost", 982 "x-header1": "foo123", 983 "Connection": "close", 984 } 985 )['status'], 986 200, 987 'match headers array 2', 988 ) 989 self.assertEqual( 990 self.get( 991 headers={ 992 "Host": "localhost", 993 "x-header2": "bar", 994 "Connection": "close", 995 } 996 )['status'], 997 200, 998 'match headers array 3', 999 ) 1000 self.assertEqual( 1001 self.get( 1002 headers={ 1003 "Host": "localhost", 1004 "x-header3": "bar", 1005 "Connection": "close", 1006 } 1007 )['status'], 1008 200, 1009 'match headers array 4', 1010 ) 1011 self.assertEqual( 1012 self.get( 1013 headers={ 1014 "Host": "localhost", 1015 "x-header1": "bar", 1016 "Connection": "close", 1017 } 1018 )['status'], 1019 404, 1020 'match headers array 5', 1021 ) 1022 self.assertEqual( 1023 self.get( 1024 headers={ 1025 "Host": "localhost", 1026 "x-header1": "bar", 1027 "x-header4": "foo", 1028 "Connection": "close", 1029 } 1030 )['status'], 1031 200, 1032 'match headers array 6', 1033 ) 1034 1035 self.assertIn( 1036 'success', 1037 self.conf_delete('routes/0/match/headers/1'), 1038 'match headers array configure 2', 1039 ) 1040 1041 self.assertEqual( 1042 self.get( 1043 headers={ 1044 "Host": "localhost", 1045 "x-header2": "bar", 1046 "Connection": "close", 1047 } 1048 )['status'], 1049 404, 1050 'match headers array 7', 1051 ) 1052 self.assertEqual( 1053 self.get( 1054 headers={ 1055 "Host": "localhost", 1056 "x-header3": "foo", 1057 "Connection": "close", 1058 } 1059 )['status'], 1060 200, 1061 'match headers array 8', 1062 ) 1063 1064 def test_routes_match_arguments(self): 1065 self.route_match({"arguments": {"foo": "bar"}}) 1066 1067 self.assertEqual(self.get()['status'], 404, 'args') 1068 self.assertEqual(self.get(url='/?foo=bar')['status'], 200, 'args 2') 1069 self.assertEqual(self.get(url='/?foo=bar1')['status'], 404, 'args 3') 1070 self.assertEqual(self.get(url='/?1foo=bar')['status'], 404, 'args 4') 1071 self.assertEqual(self.get(url='/?Foo=bar')['status'], 404, 'case') 1072 self.assertEqual(self.get(url='/?foo=Bar')['status'], 404, 'case 2') 1073 1074 def test_routes_match_arguments_chars(self): 1075 chars = ( 1076 " !\"%23$%25%26'()*%2B,-./0123456789:;<%3D>?@" 1077 "ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" 1078 ) 1079 1080 chars_enc = "" 1081 for h1 in ["2", "3", "4", "5", "6", "7"]: 1082 for h2 in ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", 1083 "B", "C", "D", "E", "F", 1084 ]: 1085 chars_enc += "%" + h1 + h2 1086 chars_enc = chars_enc[:-3] 1087 1088 def check_args(args, query): 1089 self.route_match({"arguments": args}) 1090 self.assertEqual(self.get(url='/?' + query)['status'], 200) 1091 1092 check_args({chars: chars}, chars + '=' + chars) 1093 check_args({chars: chars}, chars + '=' + chars_enc) 1094 check_args({chars: chars}, chars_enc + '=' + chars) 1095 check_args({chars: chars}, chars_enc + '=' + chars_enc) 1096 check_args({chars_enc: chars_enc}, chars + '=' + chars) 1097 check_args({chars_enc: chars_enc}, chars + '=' + chars_enc) 1098 check_args({chars_enc: chars_enc}, chars_enc + '=' + chars) 1099 check_args({chars_enc: chars_enc}, chars_enc + '=' + chars_enc) 1100 1101 def test_routes_match_arguments_empty(self): 1102 self.route_match({"arguments": {}}) 1103 self.assertEqual(self.get()['status'], 200, 'arguments empty') 1104 1105 self.route_match({"arguments": []}) 1106 self.assertEqual(self.get()['status'], 200, 'arguments empty 2') 1107 1108 def test_routes_match_arguments_space(self): 1109 self.route_match({"arguments": {"+fo o%20": "%20b+a r"}}) 1110 self.assertEqual(self.get(url='/? fo o = b a r&')['status'], 200) 1111 self.assertEqual(self.get(url='/?+fo+o+=+b+a+r&')['status'], 200) 1112 self.assertEqual( 1113 self.get(url='/?%20fo%20o%20=%20b%20a%20r&')['status'], 200 1114 ) 1115 1116 self.route_match({"arguments": {"%20foo": " bar"}}) 1117 self.assertEqual(self.get(url='/? foo= bar')['status'], 200) 1118 self.assertEqual(self.get(url='/?+foo=+bar')['status'], 200) 1119 self.assertEqual(self.get(url='/?%20foo=%20bar')['status'], 200) 1120 self.assertEqual(self.get(url='/?+foo= bar')['status'], 200) 1121 self.assertEqual(self.get(url='/?%20foo=+bar')['status'], 200) 1122 1123 def test_routes_match_arguments_equal(self): 1124 self.route_match({"arguments": {"=": "="}}) 1125 self.assertEqual(self.get(url='/?%3D=%3D')['status'], 200) 1126 self.assertEqual(self.get(url='/?%3D==')['status'], 200) 1127 self.assertEqual(self.get(url='/?===')['status'], 404) 1128 self.assertEqual(self.get(url='/?%3D%3D%3D')['status'], 404) 1129 self.assertEqual(self.get(url='/?==%3D')['status'], 404) 1130 1131 def test_routes_match_arguments_enc(self): 1132 self.route_match({"arguments": {"Ю": "н"}}) 1133 self.assertEqual(self.get(url='/?%D0%AE=%D0%BD')['status'], 200) 1134 self.assertEqual(self.get(url='/?%d0%ae=%d0%Bd')['status'], 200) 1135 1136 def test_routes_match_arguments_hash(self): 1137 self.route_match({"arguments": {"#": "#"}}) 1138 self.assertEqual(self.get(url='/?%23=%23')['status'], 200) 1139 self.assertEqual(self.get(url='/?%23=%23#')['status'], 200) 1140 self.assertEqual(self.get(url='/?#=#')['status'], 404) 1141 self.assertEqual(self.get(url='/?%23=#')['status'], 404) 1142 1143 def test_routes_match_arguments_wildcard(self): 1144 self.route_match({"arguments": {"foo": "*"}}) 1145 self.assertEqual(self.get(url='/?foo')['status'], 200) 1146 self.assertEqual(self.get(url='/?foo=')['status'], 200) 1147 self.assertEqual(self.get(url='/?foo=blah')['status'], 200) 1148 self.assertEqual(self.get(url='/?blah=foo')['status'], 404) 1149 1150 self.route_match({"arguments": {"foo": "%25*"}}) 1151 self.assertEqual(self.get(url='/?foo=%xx')['status'], 200) 1152 1153 self.route_match({"arguments": {"foo": "%2A*"}}) 1154 self.assertEqual(self.get(url='/?foo=*xx')['status'], 200) 1155 self.assertEqual(self.get(url='/?foo=xx')['status'], 404) 1156 1157 self.route_match({"arguments": {"foo": "*%2A"}}) 1158 self.assertEqual(self.get(url='/?foo=xx*')['status'], 200) 1159 self.assertEqual(self.get(url='/?foo=xx*x')['status'], 404) 1160 1161 self.route_match({"arguments": {"foo": "1*2"}}) 1162 self.assertEqual(self.get(url='/?foo=12')['status'], 200) 1163 self.assertEqual(self.get(url='/?foo=1blah2')['status'], 200) 1164 self.assertEqual(self.get(url='/?foo=1%2A2')['status'], 200) 1165 self.assertEqual(self.get(url='/?foo=x12')['status'], 404) 1166 1167 self.route_match({"arguments": {"foo": "bar*", "%25": "%25"}}) 1168 self.assertEqual(self.get(url='/?foo=barxx&%=%')['status'], 200) 1169 self.assertEqual(self.get(url='/?foo=barxx&x%=%')['status'], 404) 1170 1171 def test_routes_match_arguments_negative(self): 1172 self.route_match({"arguments": {"foo": "!%25"}}) 1173 self.assertEqual(self.get(url='/?foo=blah')['status'], 200) 1174 self.assertEqual(self.get(url='/?foo=%')['status'], 404) 1175 1176 self.route_match({"arguments": {"foo": "%21blah"}}) 1177 self.assertEqual(self.get(url='/?foo=%21blah')['status'], 200) 1178 self.assertEqual(self.get(url='/?foo=!blah')['status'], 200) 1179 self.assertEqual(self.get(url='/?foo=bar')['status'], 404) 1180 1181 self.route_match({"arguments": {"foo": "!!%21*a"}}) 1182 self.assertEqual(self.get(url='/?foo=blah')['status'], 200) 1183 self.assertEqual(self.get(url='/?foo=!blah')['status'], 200) 1184 self.assertEqual(self.get(url='/?foo=!!a')['status'], 404) 1185 self.assertEqual(self.get(url='/?foo=!!bla')['status'], 404) 1186 1187 def test_routes_match_arguments_percent(self): 1188 self.route_match({"arguments": {"%25": "%25"}}) 1189 self.assertEqual(self.get(url='/?%=%')['status'], 200) 1190 self.assertEqual(self.get(url='/?%25=%25')['status'], 200) 1191 self.assertEqual(self.get(url='/?%25=%')['status'], 200) 1192 1193 self.route_match({"arguments": {"%251": "%252"}}) 1194 self.assertEqual(self.get(url='/?%1=%2')['status'], 200) 1195 self.assertEqual(self.get(url='/?%251=%252')['status'], 200) 1196 self.assertEqual(self.get(url='/?%251=%2')['status'], 200) 1197 1198 self.route_match({"arguments": {"%25%21%251": "%25%24%252"}}) 1199 self.assertEqual(self.get(url='/?%!%1=%$%2')['status'], 200) 1200 self.assertEqual(self.get(url='/?%25!%251=%25$%252')['status'], 200) 1201 self.assertEqual(self.get(url='/?%25!%1=%$%2')['status'], 200) 1202 1203 def test_routes_match_arguments_ampersand(self): 1204 self.route_match({"arguments": {"foo": "&"}}) 1205 self.assertEqual(self.get(url='/?foo=%26')['status'], 200) 1206 self.assertEqual(self.get(url='/?foo=%26&')['status'], 200) 1207 self.assertEqual(self.get(url='/?foo=%26%26')['status'], 404) 1208 self.assertEqual(self.get(url='/?foo=&')['status'], 404) 1209 1210 self.route_match({"arguments": {"&": ""}}) 1211 self.assertEqual(self.get(url='/?%26=')['status'], 200) 1212 self.assertEqual(self.get(url='/?%26=&')['status'], 200) 1213 self.assertEqual(self.get(url='/?%26=%26')['status'], 404) 1214 self.assertEqual(self.get(url='/?&=')['status'], 404) 1215 1216 def test_routes_match_arguments_complex(self): 1217 self.route_match({"arguments": {"foo": ""}}) 1218 1219 self.assertEqual(self.get(url='/?foo')['status'], 200, 'complex') 1220 self.assertEqual( 1221 self.get(url='/?blah=blah&foo=')['status'], 200, 'complex 2' 1222 ) 1223 self.assertEqual( 1224 self.get(url='/?&&&foo&&&')['status'], 200, 'complex 3' 1225 ) 1226 self.assertEqual( 1227 self.get(url='/?foo&foo=bar&foo')['status'], 404, 'complex 4' 1228 ) 1229 self.assertEqual( 1230 self.get(url='/?foo=&foo')['status'], 200, 'complex 5' 1231 ) 1232 self.assertEqual( 1233 self.get(url='/?&=&foo&==&')['status'], 200, 'complex 6' 1234 ) 1235 self.assertEqual( 1236 self.get(url='/?&=&bar&==&')['status'], 404, 'complex 7' 1237 ) 1238 1239 def test_routes_match_arguments_multiple(self): 1240 self.route_match({"arguments": {"foo": "bar", "blah": "test"}}) 1241 1242 self.assertEqual(self.get()['status'], 404, 'multiple') 1243 self.assertEqual( 1244 self.get(url='/?foo=bar&blah=test')['status'], 200, 'multiple 2' 1245 ) 1246 self.assertEqual( 1247 self.get(url='/?foo=bar&blah')['status'], 404, 'multiple 3' 1248 ) 1249 self.assertEqual( 1250 self.get(url='/?foo=bar&blah=tes')['status'], 404, 'multiple 4' 1251 ) 1252 self.assertEqual( 1253 self.get(url='/?foo=b%61r&bl%61h=t%65st')['status'], 1254 200, 1255 'multiple 5', 1256 ) 1257 1258 def test_routes_match_arguments_multiple_rules(self): 1259 self.route_match({"arguments": {"foo": ["bar", "blah"]}}) 1260 1261 self.assertEqual(self.get()['status'], 404, 'rules') 1262 self.assertEqual(self.get(url='/?foo=bar')['status'], 200, 'rules 2') 1263 self.assertEqual(self.get(url='/?foo=blah')['status'], 200, 'rules 3') 1264 self.assertEqual( 1265 self.get(url='/?foo=blah&foo=bar&foo=blah')['status'], 1266 200, 1267 'rules 4', 1268 ) 1269 self.assertEqual( 1270 self.get(url='/?foo=blah&foo=bar&foo=')['status'], 404, 'rules 5' 1271 ) 1272 1273 def test_routes_match_arguments_array(self): 1274 self.route_match( 1275 { 1276 "arguments": [ 1277 {"var1": "val1*"}, 1278 {"var2": "val2"}, 1279 {"var3": ["foo", "bar"]}, 1280 {"var1": "bar", "var4": "foo"}, 1281 ] 1282 } 1283 ) 1284 1285 self.assertEqual(self.get()['status'], 404, 'arr') 1286 self.assertEqual(self.get(url='/?var1=val123')['status'], 200, 'arr 2') 1287 self.assertEqual(self.get(url='/?var2=val2')['status'], 200, 'arr 3') 1288 self.assertEqual(self.get(url='/?var3=bar')['status'], 200, 'arr 4') 1289 self.assertEqual(self.get(url='/?var1=bar')['status'], 404, 'arr 5') 1290 self.assertEqual( 1291 self.get(url='/?var1=bar&var4=foo')['status'], 200, 'arr 6' 1292 ) 1293 1294 self.assertIn( 1295 'success', 1296 self.conf_delete('routes/0/match/arguments/1'), 1297 'match arguments array configure 2', 1298 ) 1299 1300 self.assertEqual(self.get(url='/?var2=val2')['status'], 404, 'arr 7') 1301 self.assertEqual(self.get(url='/?var3=foo')['status'], 200, 'arr 8') 1302 1303 def test_routes_match_arguments_invalid(self): 1304 # TODO remove it after controller fixed 1305 self.skip_alerts.append(r'failed to apply new conf') 1306 1307 self.route_match_invalid({"arguments": ["var"]}) 1308 self.route_match_invalid({"arguments": [{"var1": {}}]}) 1309 self.route_match_invalid({"arguments": {"": "bar"}}) 1310 self.route_match_invalid({"arguments": {"foo": "*ba*r"}}) 1311 self.route_match_invalid({"arguments": {"foo": "%"}}) 1312 self.route_match_invalid({"arguments": {"foo": "%1G"}}) 1313 self.route_match_invalid({"arguments": {"%": "bar"}}) 1314 self.route_match_invalid({"arguments": {"foo": "%0"}}) 1315 self.route_match_invalid({"arguments": {"foo": "%%1F"}}) 1316 self.route_match_invalid({"arguments": {"%%1F": ""}}) 1317 self.route_match_invalid({"arguments": {"%7%F": ""}}) 1318 1319 def test_routes_match_cookies(self): 1320 self.route_match({"cookies": {"foO": "bar"}}) 1321 1322 self.assertEqual(self.get()['status'], 404, 'cookie') 1323 self.cookie('foO=bar', 200) 1324 self.cookie('foO=bar;1', 200) 1325 self.cookie(['foO=bar', 'blah=blah'], 200) 1326 self.cookie('foO=bar; blah=blah', 200) 1327 self.cookie('Foo=bar', 404) 1328 self.cookie('foO=Bar', 404) 1329 self.cookie('foO=bar1', 404) 1330 self.cookie('1foO=bar;', 404) 1331 1332 def test_routes_match_cookies_empty(self): 1333 self.route_match({"cookies": {}}) 1334 self.assertEqual(self.get()['status'], 200, 'cookies empty') 1335 1336 self.route_match({"cookies": []}) 1337 self.assertEqual(self.get()['status'], 200, 'cookies empty 2') 1338 1339 def test_routes_match_cookies_invalid(self): 1340 self.route_match_invalid({"cookies": ["var"]}) 1341 self.route_match_invalid({"cookies": [{"foo": {}}]}) 1342 1343 def test_routes_match_cookies_multiple(self): 1344 self.route_match({"cookies": {"foo": "bar", "blah": "blah"}}) 1345 1346 self.assertEqual(self.get()['status'], 404, 'multiple') 1347 self.cookie('foo=bar; blah=blah', 200) 1348 self.cookie(['foo=bar', 'blah=blah'], 200) 1349 self.cookie(['foo=bar; blah', 'blah'], 404) 1350 self.cookie(['foo=bar; blah=test', 'blah=blah'], 404) 1351 1352 def test_routes_match_cookies_multiple_values(self): 1353 self.route_match({"cookies": {"blah": "blah"}}) 1354 1355 self.cookie(['blah=blah', 'blah=blah', 'blah=blah'], 200) 1356 self.cookie(['blah=blah', 'blah=test', 'blah=blah'], 404) 1357 self.cookie(['blah=blah; blah=', 'blah=blah'], 404) 1358 1359 def test_routes_match_cookies_multiple_rules(self): 1360 self.route_match({"cookies": {"blah": ["test", "blah"]}}) 1361 1362 self.assertEqual(self.get()['status'], 404, 'multiple rules') 1363 self.cookie('blah=test', 200) 1364 self.cookie('blah=blah', 200) 1365 self.cookie(['blah=blah', 'blah=test', 'blah=blah'], 200) 1366 self.cookie(['blah=blah; blah=test', 'blah=blah'], 200) 1367 self.cookie(['blah=blah', 'blah'], 200) # invalid cookie 1368 1369 def test_routes_match_cookies_array(self): 1370 self.route_match( 1371 { 1372 "cookies": [ 1373 {"var1": "val1*"}, 1374 {"var2": "val2"}, 1375 {"var3": ["foo", "bar"]}, 1376 {"var1": "bar", "var4": "foo"}, 1377 ] 1378 } 1379 ) 1380 1381 self.assertEqual(self.get()['status'], 404, 'cookies array') 1382 self.cookie('var1=val123', 200) 1383 self.cookie('var2=val2', 200) 1384 self.cookie(' var2=val2 ', 200) 1385 self.cookie('var3=bar', 200) 1386 self.cookie('var3=bar;', 200) 1387 self.cookie('var1=bar', 404) 1388 self.cookie('var1=bar; var4=foo;', 200) 1389 self.cookie(['var1=bar', 'var4=foo'], 200) 1390 1391 self.assertIn( 1392 'success', 1393 self.conf_delete('routes/0/match/cookies/1'), 1394 'match cookies array configure 2', 1395 ) 1396 1397 self.cookie('var2=val2', 404) 1398 self.cookie('var3=foo', 200) 1399 1400 def test_routes_match_scheme(self): 1401 self.route_match({"scheme": "http"}) 1402 self.route_match({"scheme": "https"}) 1403 self.route_match({"scheme": "HtTp"}) 1404 self.route_match({"scheme": "HtTpS"}) 1405 1406 def test_routes_match_scheme_invalid(self): 1407 self.route_match_invalid({"scheme": ["http"]}) 1408 self.route_match_invalid({"scheme": "ftp"}) 1409 self.route_match_invalid({"scheme": "ws"}) 1410 self.route_match_invalid({"scheme": "*"}) 1411 self.route_match_invalid({"scheme": ""}) 1412 1413 def test_routes_source_port(self): 1414 def sock_port(): 1415 _, sock = self.http(b'', start=True, raw=True, no_recv=True) 1416 port = sock.getsockname()[1] 1417 return (sock, port) 1418 1419 sock, port = sock_port() 1420 sock2, port2 = sock_port() 1421 1422 self.route_match({"source": "127.0.0.1:" + str(port)}) 1423 self.assertEqual(self.get(sock=sock)['status'], 200, 'exact') 1424 self.assertEqual(self.get(sock=sock2)['status'], 404, 'exact 2') 1425 1426 sock, port = sock_port() 1427 sock2, port2 = sock_port() 1428 1429 self.route_match({"source": "!127.0.0.1:" + str(port)}) 1430 self.assertEqual(self.get(sock=sock)['status'], 404, 'negative') 1431 self.assertEqual(self.get(sock=sock2)['status'], 200, 'negative 2') 1432 1433 sock, port = sock_port() 1434 sock2, port2 = sock_port() 1435 1436 self.route_match({"source": ["*:" + str(port), "!127.0.0.1"]}) 1437 self.assertEqual(self.get(sock=sock)['status'], 404, 'negative 3') 1438 self.assertEqual(self.get(sock=sock2)['status'], 404, 'negative 4') 1439 1440 sock, port = sock_port() 1441 sock2, port2 = sock_port() 1442 1443 self.route_match( 1444 {"source": "127.0.0.1:" + str(port) + "-" + str(port)} 1445 ) 1446 self.assertEqual(self.get(sock=sock)['status'], 200, 'range single') 1447 self.assertEqual(self.get(sock=sock2)['status'], 404, 'range single 2') 1448 1449 socks = [ 1450 sock_port(), 1451 sock_port(), 1452 sock_port(), 1453 sock_port(), 1454 sock_port(), 1455 ] 1456 socks.sort(key=lambda sock: sock[1]) 1457 1458 self.route_match( 1459 { 1460 "source": "127.0.0.1:" 1461 + str(socks[1][1]) # second port number 1462 + "-" 1463 + str(socks[3][1]) # fourth port number 1464 } 1465 ) 1466 self.assertEqual(self.get(sock=socks[0][0])['status'], 404, 'range') 1467 self.assertEqual(self.get(sock=socks[1][0])['status'], 200, 'range 2') 1468 self.assertEqual(self.get(sock=socks[2][0])['status'], 200, 'range 3') 1469 self.assertEqual(self.get(sock=socks[3][0])['status'], 200, 'range 4') 1470 self.assertEqual(self.get(sock=socks[4][0])['status'], 404, 'range 5') 1471 1472 socks = [ 1473 sock_port(), 1474 sock_port(), 1475 sock_port(), 1476 ] 1477 socks.sort(key=lambda sock: sock[1]) 1478 1479 self.route_match( 1480 { 1481 "source": [ 1482 "127.0.0.1:" + str(socks[0][1]), 1483 "127.0.0.1:" + str(socks[2][1]), 1484 ] 1485 } 1486 ) 1487 self.assertEqual(self.get(sock=socks[0][0])['status'], 200, 'array') 1488 self.assertEqual(self.get(sock=socks[1][0])['status'], 404, 'array 2') 1489 self.assertEqual(self.get(sock=socks[2][0])['status'], 200, 'array 3') 1490 1491 def test_routes_source_addr(self): 1492 self.assertIn( 1493 'success', 1494 self.conf( 1495 { 1496 "*:7080": {"pass": "routes"}, 1497 "[::1]:7081": {"pass": "routes"}, 1498 }, 1499 'listeners', 1500 ), 1501 'source listeners configure', 1502 ) 1503 1504 def get_ipv6(): 1505 return self.get(sock_type='ipv6', port=7081) 1506 1507 self.route_match({"source": "127.0.0.1"}) 1508 self.assertEqual(self.get()['status'], 200, 'exact') 1509 self.assertEqual(get_ipv6()['status'], 404, 'exact ipv6') 1510 1511 self.route_match({"source": ["127.0.0.1"]}) 1512 self.assertEqual(self.get()['status'], 200, 'exact 2') 1513 self.assertEqual(get_ipv6()['status'], 404, 'exact 2 ipv6') 1514 1515 self.route_match({"source": "!127.0.0.1"}) 1516 self.assertEqual(self.get()['status'], 404, 'exact neg') 1517 self.assertEqual(get_ipv6()['status'], 200, 'exact neg ipv6') 1518 1519 self.route_match({"source": "127.0.0.2"}) 1520 self.assertEqual(self.get()['status'], 404, 'exact 3') 1521 self.assertEqual(get_ipv6()['status'], 404, 'exact 3 ipv6') 1522 1523 self.route_match({"source": "127.0.0.1-127.0.0.1"}) 1524 self.assertEqual(self.get()['status'], 200, 'range single') 1525 self.assertEqual(get_ipv6()['status'], 404, 'range single ipv6') 1526 1527 self.route_match({"source": "127.0.0.2-127.0.0.2"}) 1528 self.assertEqual(self.get()['status'], 404, 'range single 2') 1529 self.assertEqual(get_ipv6()['status'], 404, 'range single 2 ipv6') 1530 1531 self.route_match({"source": "127.0.0.2-127.0.0.3"}) 1532 self.assertEqual(self.get()['status'], 404, 'range') 1533 self.assertEqual(get_ipv6()['status'], 404, 'range ipv6') 1534 1535 self.route_match({"source": "127.0.0.1-127.0.0.2"}) 1536 self.assertEqual(self.get()['status'], 200, 'range 2') 1537 self.assertEqual(get_ipv6()['status'], 404, 'range 2 ipv6') 1538 1539 self.route_match({"source": "127.0.0.0-127.0.0.2"}) 1540 self.assertEqual(self.get()['status'], 200, 'range 3') 1541 self.assertEqual(get_ipv6()['status'], 404, 'range 3 ipv6') 1542 1543 self.route_match({"source": "127.0.0.0-127.0.0.1"}) 1544 self.assertEqual(self.get()['status'], 200, 'range 4') 1545 self.assertEqual(get_ipv6()['status'], 404, 'range 4 ipv6') 1546 1547 self.route_match({"source": "126.0.0.0-127.0.0.0"}) 1548 self.assertEqual(self.get()['status'], 404, 'range 5') 1549 self.assertEqual(get_ipv6()['status'], 404, 'range 5 ipv6') 1550 1551 self.route_match({"source": "126.126.126.126-127.0.0.2"}) 1552 self.assertEqual(self.get()['status'], 200, 'range 6') 1553 self.assertEqual(get_ipv6()['status'], 404, 'range 6 ipv6') 1554 1555 def test_routes_source_ipv6(self): 1556 self.assertIn( 1557 'success', 1558 self.conf( 1559 { 1560 "[::1]:7080": {"pass": "routes"}, 1561 "127.0.0.1:7081": {"pass": "routes"}, 1562 }, 1563 'listeners', 1564 ), 1565 'source listeners configure', 1566 ) 1567 1568 self.route_match({"source": "::1"}) 1569 self.assertEqual(self.get(sock_type='ipv6')['status'], 200, 'exact') 1570 self.assertEqual(self.get(port=7081)['status'], 404, 'exact ipv4') 1571 1572 self.route_match({"source": ["::1"]}) 1573 self.assertEqual(self.get(sock_type='ipv6')['status'], 200, 'exact 2') 1574 self.assertEqual(self.get(port=7081)['status'], 404, 'exact 2 ipv4') 1575 1576 self.route_match({"source": "!::1"}) 1577 self.assertEqual(self.get(sock_type='ipv6')['status'], 404, 'exact neg') 1578 self.assertEqual(self.get(port=7081)['status'], 200, 'exact neg ipv4') 1579 1580 self.route_match({"source": "::2"}) 1581 self.assertEqual(self.get(sock_type='ipv6')['status'], 404, 'exact 3') 1582 self.assertEqual(self.get(port=7081)['status'], 404, 'exact 3 ipv4') 1583 1584 self.route_match({"source": "::1-::1"}) 1585 self.assertEqual(self.get(sock_type='ipv6')['status'], 200, 'range') 1586 self.assertEqual(self.get(port=7081)['status'], 404, 'range ipv4') 1587 1588 self.route_match({"source": "::2-::2"}) 1589 self.assertEqual(self.get(sock_type='ipv6')['status'], 404, 'range 2') 1590 self.assertEqual(self.get(port=7081)['status'], 404, 'range 2 ipv4') 1591 1592 self.route_match({"source": "::2-::3"}) 1593 self.assertEqual(self.get(sock_type='ipv6')['status'], 404, 'range 3') 1594 self.assertEqual(self.get(port=7081)['status'], 404, 'range 3 ipv4') 1595 1596 self.route_match({"source": "::1-::2"}) 1597 self.assertEqual(self.get(sock_type='ipv6')['status'], 200, 'range 4') 1598 self.assertEqual(self.get(port=7081)['status'], 404, 'range 4 ipv4') 1599 1600 self.route_match({"source": "::0-::2"}) 1601 self.assertEqual(self.get(sock_type='ipv6')['status'], 200, 'range 5') 1602 self.assertEqual(self.get(port=7081)['status'], 404, 'range 5 ipv4') 1603 1604 self.route_match({"source": "::0-::1"}) 1605 self.assertEqual(self.get(sock_type='ipv6')['status'], 200, 'range 6') 1606 self.assertEqual(self.get(port=7081)['status'], 404, 'range 6 ipv4') 1607 1608 def test_routes_source_cidr(self): 1609 self.assertIn( 1610 'success', 1611 self.conf( 1612 { 1613 "*:7080": {"pass": "routes"}, 1614 "[::1]:7081": {"pass": "routes"}, 1615 }, 1616 'listeners', 1617 ), 1618 'source listeners configure', 1619 ) 1620 1621 def get_ipv6(): 1622 return self.get(sock_type='ipv6', port=7081) 1623 1624 self.route_match({"source": "127.0.0.1/32"}) 1625 self.assertEqual(self.get()['status'], 200, '32') 1626 self.assertEqual(get_ipv6()['status'], 404, '32 ipv6') 1627 1628 self.route_match({"source": "127.0.0.0/32"}) 1629 self.assertEqual(self.get()['status'], 404, '32 2') 1630 self.assertEqual(get_ipv6()['status'], 404, '32 2 ipv6') 1631 1632 self.route_match({"source": "127.0.0.0/31"}) 1633 self.assertEqual(self.get()['status'], 200, '31') 1634 self.assertEqual(get_ipv6()['status'], 404, '31 ipv6') 1635 1636 self.route_match({"source": "0.0.0.0/1"}) 1637 self.assertEqual(self.get()['status'], 200, '1') 1638 self.assertEqual(get_ipv6()['status'], 404, '1 ipv6') 1639 1640 self.route_match({"source": "0.0.0.0/0"}) 1641 self.assertEqual(self.get()['status'], 200, '0') 1642 self.assertEqual(get_ipv6()['status'], 404, '0 ipv6') 1643 1644 def test_routes_source_cidr_ipv6(self): 1645 self.assertIn( 1646 'success', 1647 self.conf( 1648 { 1649 "[::1]:7080": {"pass": "routes"}, 1650 "127.0.0.1:7081": {"pass": "routes"}, 1651 }, 1652 'listeners', 1653 ), 1654 'source listeners configure', 1655 ) 1656 1657 self.route_match({"source": "::1/128"}) 1658 self.assertEqual(self.get(sock_type='ipv6')['status'], 200, '128') 1659 self.assertEqual(self.get(port=7081)['status'], 404, '128 ipv4') 1660 1661 self.route_match({"source": "::0/128"}) 1662 self.assertEqual(self.get(sock_type='ipv6')['status'], 404, '128 2') 1663 self.assertEqual(self.get(port=7081)['status'], 404, '128 ipv4') 1664 1665 self.route_match({"source": "::0/127"}) 1666 self.assertEqual(self.get(sock_type='ipv6')['status'], 200, '127') 1667 self.assertEqual(self.get(port=7081)['status'], 404, '127 ipv4') 1668 1669 self.route_match({"source": "::0/32"}) 1670 self.assertEqual(self.get(sock_type='ipv6')['status'], 200, '32') 1671 self.assertEqual(self.get(port=7081)['status'], 404, '32 ipv4') 1672 1673 self.route_match({"source": "::0/1"}) 1674 self.assertEqual(self.get(sock_type='ipv6')['status'], 200, '1') 1675 self.assertEqual(self.get(port=7081)['status'], 404, '1 ipv4') 1676 1677 self.route_match({"source": "::/0"}) 1678 self.assertEqual(self.get(sock_type='ipv6')['status'], 200, '0') 1679 self.assertEqual(self.get(port=7081)['status'], 404, '0 ipv4') 1680 1681 def test_routes_source_unix(self): 1682 addr = self.testdir + '/sock' 1683 1684 self.assertIn( 1685 'success', 1686 self.conf({"unix:" + addr: {"pass": "routes"}}, 'listeners'), 1687 'source listeners configure', 1688 ) 1689 1690 self.route_match({"source": "!0.0.0.0/0"}) 1691 self.assertEqual( 1692 self.get(sock_type='unix', addr=addr)['status'], 200, 'unix ipv4' 1693 ) 1694 1695 self.route_match({"source": "!::/0"}) 1696 self.assertEqual( 1697 self.get(sock_type='unix', addr=addr)['status'], 200, 'unix ipv6' 1698 ) 1699 1700 def test_routes_match_source(self): 1701 self.route_match({"source": "::"}) 1702 self.route_match( 1703 { 1704 "source": [ 1705 "127.0.0.1", 1706 "192.168.0.10:8080", 1707 "192.168.0.11:8080-8090", 1708 ] 1709 } 1710 ) 1711 self.route_match( 1712 { 1713 "source": [ 1714 "10.0.0.0/8", 1715 "10.0.0.0/7:1000", 1716 "10.0.0.0/32:8080-8090", 1717 ] 1718 } 1719 ) 1720 self.route_match( 1721 { 1722 "source": [ 1723 "10.0.0.0-10.0.0.1", 1724 "10.0.0.0-11.0.0.0:1000", 1725 "127.0.0.0-127.0.0.255:8080-8090", 1726 ] 1727 } 1728 ) 1729 self.route_match( 1730 {"source": ["2001::", "[2002::]:8000", "[2003::]:8080-8090"]} 1731 ) 1732 self.route_match( 1733 { 1734 "source": [ 1735 "2001::-200f:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 1736 "[fe08::-feff::]:8000", 1737 "[fff0::-fff0::10]:8080-8090", 1738 ] 1739 } 1740 ) 1741 self.route_match( 1742 { 1743 "source": [ 1744 "2001::/16", 1745 "[0ff::/64]:8000", 1746 "[fff0:abcd:ffff:ffff:ffff::/128]:8080-8090", 1747 ] 1748 } 1749 ) 1750 self.route_match({"source": "*:0-65535"}) 1751 self.assertEqual(self.get()['status'], 200, 'source any') 1752 1753 def test_routes_match_source_invalid(self): 1754 self.route_match_invalid({"source": "127"}) 1755 self.route_match_invalid({"source": "256.0.0.1"}) 1756 self.route_match_invalid({"source": "127.0.0."}) 1757 self.route_match_invalid({"source": " 127.0.0.1"}) 1758 self.route_match_invalid({"source": "127.0.0.1:"}) 1759 self.route_match_invalid({"source": "127.0.0.1/"}) 1760 self.route_match_invalid({"source": "11.0.0.0/33"}) 1761 self.route_match_invalid({"source": "11.0.0.0/65536"}) 1762 self.route_match_invalid({"source": "11.0.0.0-10.0.0.0"}) 1763 self.route_match_invalid({"source": "11.0.0.0:3000-2000"}) 1764 self.route_match_invalid({"source": ["11.0.0.0:3000-2000"]}) 1765 self.route_match_invalid({"source": "[2001::]:3000-2000"}) 1766 self.route_match_invalid({"source": "2001::-2000::"}) 1767 self.route_match_invalid({"source": "2001::/129"}) 1768 self.route_match_invalid({"source": "::FFFFF"}) 1769 self.route_match_invalid({"source": "[::1]:"}) 1770 self.route_match_invalid({"source": "[:::]:7080"}) 1771 self.route_match_invalid({"source": "*:"}) 1772 self.route_match_invalid({"source": "*:1-a"}) 1773 self.route_match_invalid({"source": "*:65536"}) 1774 1775 def test_routes_match_destination(self): 1776 self.assertIn( 1777 'success', 1778 self.conf( 1779 {"*:7080": {"pass": "routes"}, "*:7081": {"pass": "routes"}}, 1780 'listeners', 1781 ), 1782 'listeners configure', 1783 ) 1784 1785 self.route_match({"destination": "*:7080"}) 1786 self.assertEqual(self.get()['status'], 200, 'dest') 1787 self.assertEqual(self.get(port=7081)['status'], 404, 'dest 2') 1788 1789 self.route_match({"destination": ["127.0.0.1:7080"]}) 1790 self.assertEqual(self.get()['status'], 200, 'dest 3') 1791 self.assertEqual(self.get(port=7081)['status'], 404, 'dest 4') 1792 1793 self.route_match({"destination": "!*:7080"}) 1794 self.assertEqual(self.get()['status'], 404, 'dest neg') 1795 self.assertEqual(self.get(port=7081)['status'], 200, 'dest neg 2') 1796 1797 self.route_match({"destination": ['!*:7080', '!*:7081']}) 1798 self.assertEqual(self.get()['status'], 404, 'dest neg 3') 1799 self.assertEqual(self.get(port=7081)['status'], 404, 'dest neg 4') 1800 1801 self.route_match({"destination": ['!*:7081', '!*:7082']}) 1802 self.assertEqual(self.get()['status'], 200, 'dest neg 5') 1803 1804 self.route_match({"destination": ['*:7080', '!*:7080']}) 1805 self.assertEqual(self.get()['status'], 404, 'dest neg 6') 1806 1807 self.route_match( 1808 {"destination": ['127.0.0.1:7080', '*:7081', '!*:7080']} 1809 ) 1810 self.assertEqual(self.get()['status'], 404, 'dest neg 7') 1811 self.assertEqual(self.get(port=7081)['status'], 200, 'dest neg 8') 1812 1813 self.route_match({"destination": ['!*:7081', '!*:7082', '*:7083']}) 1814 self.assertEqual(self.get()['status'], 404, 'dest neg 9') 1815 1816 self.route_match( 1817 {"destination": ['*:7081', '!127.0.0.1:7080', '*:7080']} 1818 ) 1819 self.assertEqual(self.get()['status'], 404, 'dest neg 10') 1820 self.assertEqual(self.get(port=7081)['status'], 200, 'dest neg 11') 1821 1822 self.assertIn( 1823 'success', 1824 self.conf_delete('routes/0/match/destination/0'), 1825 'remove destination rule', 1826 ) 1827 self.assertEqual(self.get()['status'], 404, 'dest neg 12') 1828 self.assertEqual(self.get(port=7081)['status'], 404, 'dest neg 13') 1829 1830 self.assertIn( 1831 'success', 1832 self.conf_delete('routes/0/match/destination/0'), 1833 'remove destination rule 2', 1834 ) 1835 self.assertEqual(self.get()['status'], 200, 'dest neg 14') 1836 self.assertEqual(self.get(port=7081)['status'], 404, 'dest neg 15') 1837 1838 self.assertIn( 1839 'success', 1840 self.conf_post("\"!127.0.0.1\"", 'routes/0/match/destination'), 1841 'add destination rule', 1842 ) 1843 self.assertEqual(self.get()['status'], 404, 'dest neg 16') 1844 self.assertEqual(self.get(port=7081)['status'], 404, 'dest neg 17') 1845 1846 def test_routes_match_destination_proxy(self): 1847 self.assertIn( 1848 'success', 1849 self.conf( 1850 { 1851 "listeners": { 1852 "*:7080": {"pass": "routes/first"}, 1853 "*:7081": {"pass": "routes/second"}, 1854 }, 1855 "routes": { 1856 "first": [ 1857 {"action": {"proxy": "http://127.0.0.1:7081"}} 1858 ], 1859 "second": [ 1860 { 1861 "match": {"destination": ["127.0.0.1:7081"]}, 1862 "action": {"return": 200}, 1863 } 1864 ], 1865 }, 1866 "applications": {}, 1867 } 1868 ), 1869 'proxy configure', 1870 ) 1871 1872 self.assertEqual(self.get()['status'], 200, 'proxy') 1873 1874 1875if __name__ == '__main__': 1876 TestRouting.main() 1877