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