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