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