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