test_routing.py (1875:6e77d607c7a3) test_routing.py (1930:fb8b29aac7a1)
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 def check_headers(hds):
1045 hds = dict({"Host": "localhost", "Connection": "close"}, **hds)
1046 assert (
1047 self.get(headers=hds)['status'] == 200
1048 ), 'headers array match'
1049
1050 def check_headers_404(hds):
1051 hds = dict({"Host": "localhost", "Connection": "close"}, **hds)
1052 assert (
1053 self.get(headers=hds)['status'] == 404
1054 ), 'headers array no match'
1055
1056 assert self.get()['status'] == 404, 'match headers array'
1057 check_headers({"x-header1": "foo123"})
1058 check_headers({"x-header2": "bar"})
1059 check_headers({"x-header3": "bar"})
1060 check_headers_404({"x-header1": "bar"})
1061 check_headers({"x-header1": "bar", "x-header4": "foo"})
1062
1063 assert 'success' in self.conf_delete(
1064 'routes/0/match/headers/1'
1065 ), 'match headers array configure 2'
1066
1067 check_headers_404({"x-header2": "bar"})
1068 check_headers({"x-header3": "foo"})
1069
1070 def test_routes_match_arguments(self):
1071 self.route_match({"arguments": {"foo": "bar"}})
1072
1073 assert self.get()['status'] == 404, 'args'
1074 assert self.get(url='/?foo=bar')['status'] == 200, 'args 2'
1075 assert self.get(url='/?foo=bar1')['status'] == 404, 'args 3'
1076 assert self.get(url='/?1foo=bar')['status'] == 404, 'args 4'
1077 assert self.get(url='/?Foo=bar')['status'] == 404, 'case'
1078 assert self.get(url='/?foo=Bar')['status'] == 404, 'case 2'
1079
1080 def test_routes_match_arguments_chars(self):
1081 chars = (
1082 " !\"%23$%25%26'()*%2B,-./0123456789:;<%3D>?@"
1083 "ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
1084 )
1085
1086 chars_enc = ""
1087 for h1 in ["2", "3", "4", "5", "6", "7"]:
1088 for h2 in [
1089 "0",
1090 "1",
1091 "2",
1092 "3",
1093 "4",
1094 "5",
1095 "6",
1096 "7",
1097 "8",
1098 "9",
1099 "A",
1100 "B",
1101 "C",
1102 "D",
1103 "E",
1104 "F",
1105 ]:
1106 chars_enc += "%" + h1 + h2
1107 chars_enc = chars_enc[:-3]
1108
1109 def check_args(args, query):
1110 self.route_match({"arguments": args})
1111 assert self.get(url='/?' + query)['status'] == 200
1112
1113 check_args({chars: chars}, chars + '=' + chars)
1114 check_args({chars: chars}, chars + '=' + chars_enc)
1115 check_args({chars: chars}, chars_enc + '=' + chars)
1116 check_args({chars: chars}, chars_enc + '=' + chars_enc)
1117 check_args({chars_enc: chars_enc}, chars + '=' + chars)
1118 check_args({chars_enc: chars_enc}, chars + '=' + chars_enc)
1119 check_args({chars_enc: chars_enc}, chars_enc + '=' + chars)
1120 check_args({chars_enc: chars_enc}, chars_enc + '=' + chars_enc)
1121
1122 def test_routes_match_arguments_empty(self):
1123 self.route_match({"arguments": {}})
1124 assert self.get()['status'] == 200, 'arguments empty'
1125
1126 self.route_match({"arguments": []})
1127 assert self.get()['status'] == 200, 'arguments empty 2'
1128
1129 def test_routes_match_arguments_space(self):
1130 self.route_match({"arguments": {"+fo o%20": "%20b+a r"}})
1131 assert self.get(url='/? fo o = b a r&')['status'] == 200
1132 assert self.get(url='/?+fo+o+=+b+a+r&')['status'] == 200
1133 assert self.get(url='/?%20fo%20o%20=%20b%20a%20r&')['status'] == 200
1134
1135 self.route_match({"arguments": {"%20foo": " bar"}})
1136 assert self.get(url='/? foo= bar')['status'] == 200
1137 assert self.get(url='/?+foo=+bar')['status'] == 200
1138 assert self.get(url='/?%20foo=%20bar')['status'] == 200
1139 assert self.get(url='/?+foo= bar')['status'] == 200
1140 assert self.get(url='/?%20foo=+bar')['status'] == 200
1141
1142 def test_routes_match_arguments_equal(self):
1143 self.route_match({"arguments": {"=": "="}})
1144 assert self.get(url='/?%3D=%3D')['status'] == 200
1145 assert self.get(url='/?%3D==')['status'] == 200
1146 assert self.get(url='/?===')['status'] == 404
1147 assert self.get(url='/?%3D%3D%3D')['status'] == 404
1148 assert self.get(url='/?==%3D')['status'] == 404
1149
1150 def test_routes_match_arguments_enc(self):
1151 self.route_match({"arguments": {"Ю": "н"}})
1152 assert self.get(url='/?%D0%AE=%D0%BD')['status'] == 200
1153 assert self.get(url='/?%d0%ae=%d0%Bd')['status'] == 200
1154
1155 def test_routes_match_arguments_hash(self):
1156 self.route_match({"arguments": {"#": "#"}})
1157 assert self.get(url='/?%23=%23')['status'] == 200
1158 assert self.get(url='/?%23=%23#')['status'] == 200
1159 assert self.get(url='/?#=#')['status'] == 404
1160 assert self.get(url='/?%23=#')['status'] == 404
1161
1162 def test_routes_match_arguments_wildcard(self):
1163 self.route_match({"arguments": {"foo": "*"}})
1164 assert self.get(url='/?foo')['status'] == 200
1165 assert self.get(url='/?foo=')['status'] == 200
1166 assert self.get(url='/?foo=blah')['status'] == 200
1167 assert self.get(url='/?blah=foo')['status'] == 404
1168
1169 self.route_match({"arguments": {"foo": "%25*"}})
1170 assert self.get(url='/?foo=%xx')['status'] == 200
1171
1172 self.route_match({"arguments": {"foo": "%2A*"}})
1173 assert self.get(url='/?foo=*xx')['status'] == 200
1174 assert self.get(url='/?foo=xx')['status'] == 404
1175
1176 self.route_match({"arguments": {"foo": "*%2A"}})
1177 assert self.get(url='/?foo=xx*')['status'] == 200
1178 assert self.get(url='/?foo=xx*x')['status'] == 404
1179
1180 self.route_match({"arguments": {"foo": "1*2"}})
1181 assert self.get(url='/?foo=12')['status'] == 200
1182 assert self.get(url='/?foo=1blah2')['status'] == 200
1183 assert self.get(url='/?foo=1%2A2')['status'] == 200
1184 assert self.get(url='/?foo=x12')['status'] == 404
1185
1186 self.route_match({"arguments": {"foo": "bar*", "%25": "%25"}})
1187 assert self.get(url='/?foo=barxx&%=%')['status'] == 200
1188 assert self.get(url='/?foo=barxx&x%=%')['status'] == 404
1189
1190 def test_routes_match_arguments_negative(self):
1191 self.route_match({"arguments": {"foo": "!"}})
1192 assert self.get(url='/?bar')['status'] == 404
1193 assert self.get(url='/?foo')['status'] == 404
1194 assert self.get(url='/?foo=')['status'] == 404
1195 assert self.get(url='/?foo=%25')['status'] == 200
1196
1197 self.route_match({"arguments": {"foo": "!*"}})
1198 assert self.get(url='/?bar')['status'] == 404
1199 assert self.get(url='/?foo')['status'] == 404
1200 assert self.get(url='/?foo=')['status'] == 404
1201 assert self.get(url='/?foo=blah')['status'] == 404
1202
1203 self.route_match({"arguments": {"foo": "!%25"}})
1204 assert self.get(url='/?foo=blah')['status'] == 200
1205 assert self.get(url='/?foo=%')['status'] == 404
1206
1207 self.route_match({"arguments": {"foo": "%21blah"}})
1208 assert self.get(url='/?foo=%21blah')['status'] == 200
1209 assert self.get(url='/?foo=!blah')['status'] == 200
1210 assert self.get(url='/?foo=bar')['status'] == 404
1211
1212 self.route_match({"arguments": {"foo": "!!%21*a"}})
1213 assert self.get(url='/?foo=blah')['status'] == 200
1214 assert self.get(url='/?foo=!blah')['status'] == 200
1215 assert self.get(url='/?foo=!!a')['status'] == 404
1216 assert self.get(url='/?foo=!!bla')['status'] == 404
1217
1218 def test_routes_match_arguments_percent(self):
1219 self.route_match({"arguments": {"%25": "%25"}})
1220 assert self.get(url='/?%=%')['status'] == 200
1221 assert self.get(url='/?%25=%25')['status'] == 200
1222 assert self.get(url='/?%25=%')['status'] == 200
1223
1224 self.route_match({"arguments": {"%251": "%252"}})
1225 assert self.get(url='/?%1=%2')['status'] == 200
1226 assert self.get(url='/?%251=%252')['status'] == 200
1227 assert self.get(url='/?%251=%2')['status'] == 200
1228
1229 self.route_match({"arguments": {"%25%21%251": "%25%24%252"}})
1230 assert self.get(url='/?%!%1=%$%2')['status'] == 200
1231 assert self.get(url='/?%25!%251=%25$%252')['status'] == 200
1232 assert self.get(url='/?%25!%1=%$%2')['status'] == 200
1233
1234 def test_routes_match_arguments_ampersand(self):
1235 self.route_match({"arguments": {"foo": "&"}})
1236 assert self.get(url='/?foo=%26')['status'] == 200
1237 assert self.get(url='/?foo=%26&')['status'] == 200
1238 assert self.get(url='/?foo=%26%26')['status'] == 404
1239 assert self.get(url='/?foo=&')['status'] == 404
1240
1241 self.route_match({"arguments": {"&": ""}})
1242 assert self.get(url='/?%26=')['status'] == 200
1243 assert self.get(url='/?%26=&')['status'] == 200
1244 assert self.get(url='/?%26=%26')['status'] == 404
1245 assert self.get(url='/?&=')['status'] == 404
1246
1247 def test_routes_match_arguments_complex(self):
1248 self.route_match({"arguments": {"foo": ""}})
1249
1250 assert self.get(url='/?foo')['status'] == 200, 'complex'
1251 assert self.get(url='/?blah=blah&foo=')['status'] == 200, 'complex 2'
1252 assert self.get(url='/?&&&foo&&&')['status'] == 200, 'complex 3'
1253 assert self.get(url='/?foo&foo=bar&foo')['status'] == 404, 'complex 4'
1254 assert self.get(url='/?foo=&foo')['status'] == 200, 'complex 5'
1255 assert self.get(url='/?&=&foo&==&')['status'] == 200, 'complex 6'
1256 assert self.get(url='/?&=&bar&==&')['status'] == 404, 'complex 7'
1257
1258 def test_routes_match_arguments_multiple(self):
1259 self.route_match({"arguments": {"foo": "bar", "blah": "test"}})
1260
1261 assert self.get()['status'] == 404, 'multiple'
1262 assert (
1263 self.get(url='/?foo=bar&blah=test')['status'] == 200
1264 ), 'multiple 2'
1265 assert self.get(url='/?foo=bar&blah')['status'] == 404, 'multiple 3'
1266 assert (
1267 self.get(url='/?foo=bar&blah=tes')['status'] == 404
1268 ), 'multiple 4'
1269 assert (
1270 self.get(url='/?foo=b%61r&bl%61h=t%65st')['status'] == 200
1271 ), 'multiple 5'
1272
1273 def test_routes_match_arguments_multiple_rules(self):
1274 self.route_match({"arguments": {"foo": ["bar", "blah"]}})
1275
1276 assert self.get()['status'] == 404, 'rules'
1277 assert self.get(url='/?foo=bar')['status'] == 200, 'rules 2'
1278 assert self.get(url='/?foo=blah')['status'] == 200, 'rules 3'
1279 assert (
1280 self.get(url='/?foo=blah&foo=bar&foo=blah')['status'] == 200
1281 ), 'rules 4'
1282 assert (
1283 self.get(url='/?foo=blah&foo=bar&foo=')['status'] == 404
1284 ), 'rules 5'
1285
1286 def test_routes_match_arguments_array(self):
1287 self.route_match(
1288 {
1289 "arguments": [
1290 {"var1": "val1*"},
1291 {"var2": "val2"},
1292 {"var3": ["foo", "bar"]},
1293 {"var1": "bar", "var4": "foo"},
1294 ]
1295 }
1296 )
1297
1298 assert self.get()['status'] == 404, 'arr'
1299 assert self.get(url='/?var1=val123')['status'] == 200, 'arr 2'
1300 assert self.get(url='/?var2=val2')['status'] == 200, 'arr 3'
1301 assert self.get(url='/?var3=bar')['status'] == 200, 'arr 4'
1302 assert self.get(url='/?var1=bar')['status'] == 404, 'arr 5'
1303 assert self.get(url='/?var1=bar&var4=foo')['status'] == 200, 'arr 6'
1304
1305 assert 'success' in self.conf_delete(
1306 'routes/0/match/arguments/1'
1307 ), 'match arguments array configure 2'
1308
1309 assert self.get(url='/?var2=val2')['status'] == 404, 'arr 7'
1310 assert self.get(url='/?var3=foo')['status'] == 200, 'arr 8'
1311
1312 def test_routes_match_arguments_invalid(self):
1313 self.route_match_invalid({"arguments": ["var"]})
1314 self.route_match_invalid({"arguments": [{"var1": {}}]})
1315 self.route_match_invalid({"arguments": {"": "bar"}})
1316 self.route_match_invalid({"arguments": {"foo": "%"}})
1317 self.route_match_invalid({"arguments": {"foo": "%1G"}})
1318 self.route_match_invalid({"arguments": {"%": "bar"}})
1319 self.route_match_invalid({"arguments": {"foo": "%0"}})
1320 self.route_match_invalid({"arguments": {"foo": "%%1F"}})
1321 self.route_match_invalid({"arguments": {"%%1F": ""}})
1322 self.route_match_invalid({"arguments": {"%7%F": ""}})
1323
1324 def test_routes_match_cookies(self):
1325 self.route_match({"cookies": {"foO": "bar"}})
1326
1327 assert self.get()['status'] == 404, 'cookie'
1328 self.cookie('foO=bar', 200)
1329 self.cookie('foO=bar;1', 200)
1330 self.cookie(['foO=bar', 'blah=blah'], 200)
1331 self.cookie('foO=bar; blah=blah', 200)
1332 self.cookie('Foo=bar', 404)
1333 self.cookie('foO=Bar', 404)
1334 self.cookie('foO=bar1', 404)
1335 self.cookie('1foO=bar;', 404)
1336
1337 def test_routes_match_cookies_empty(self):
1338 self.route_match({"cookies": {}})
1339 assert self.get()['status'] == 200, 'cookies empty'
1340
1341 self.route_match({"cookies": []})
1342 assert self.get()['status'] == 200, 'cookies empty 2'
1343
1344 def test_routes_match_cookies_invalid(self):
1345 self.route_match_invalid({"cookies": ["var"]})
1346 self.route_match_invalid({"cookies": [{"foo": {}}]})
1347
1348 def test_routes_match_cookies_multiple(self):
1349 self.route_match({"cookies": {"foo": "bar", "blah": "blah"}})
1350
1351 assert self.get()['status'] == 404, 'multiple'
1352 self.cookie('foo=bar; blah=blah', 200)
1353 self.cookie(['foo=bar', 'blah=blah'], 200)
1354 self.cookie(['foo=bar; blah', 'blah'], 404)
1355 self.cookie(['foo=bar; blah=test', 'blah=blah'], 404)
1356
1357 def test_routes_match_cookies_multiple_values(self):
1358 self.route_match({"cookies": {"blah": "blah"}})
1359
1360 self.cookie(['blah=blah', 'blah=blah', 'blah=blah'], 200)
1361 self.cookie(['blah=blah', 'blah=test', 'blah=blah'], 404)
1362 self.cookie(['blah=blah; blah=', 'blah=blah'], 404)
1363
1364 def test_routes_match_cookies_multiple_rules(self):
1365 self.route_match({"cookies": {"blah": ["test", "blah"]}})
1366
1367 assert self.get()['status'] == 404, 'multiple rules'
1368 self.cookie('blah=test', 200)
1369 self.cookie('blah=blah', 200)
1370 self.cookie(['blah=blah', 'blah=test', 'blah=blah'], 200)
1371 self.cookie(['blah=blah; blah=test', 'blah=blah'], 200)
1372 self.cookie(['blah=blah', 'blah'], 200) # invalid cookie
1373
1374 def test_routes_match_cookies_array(self):
1375 self.route_match(
1376 {
1377 "cookies": [
1378 {"var1": "val1*"},
1379 {"var2": "val2"},
1380 {"var3": ["foo", "bar"]},
1381 {"var1": "bar", "var4": "foo"},
1382 ]
1383 }
1384 )
1385
1386 assert self.get()['status'] == 404, 'cookies array'
1387 self.cookie('var1=val123', 200)
1388 self.cookie('var2=val2', 200)
1389 self.cookie(' var2=val2 ', 200)
1390 self.cookie('var3=bar', 200)
1391 self.cookie('var3=bar;', 200)
1392 self.cookie('var1=bar', 404)
1393 self.cookie('var1=bar; var4=foo;', 200)
1394 self.cookie(['var1=bar', 'var4=foo'], 200)
1395
1396 assert 'success' in self.conf_delete(
1397 'routes/0/match/cookies/1'
1398 ), 'match cookies array configure 2'
1399
1400 self.cookie('var2=val2', 404)
1401 self.cookie('var3=foo', 200)
1402
1403 def test_routes_match_scheme(self):
1404 self.route_match({"scheme": "http"})
1405 self.route_match({"scheme": "https"})
1406 self.route_match({"scheme": "HtTp"})
1407 self.route_match({"scheme": "HtTpS"})
1408
1409 def test_routes_match_scheme_invalid(self):
1410 self.route_match_invalid({"scheme": ["http"]})
1411 self.route_match_invalid({"scheme": "ftp"})
1412 self.route_match_invalid({"scheme": "ws"})
1413 self.route_match_invalid({"scheme": "*"})
1414 self.route_match_invalid({"scheme": ""})
1415
1416 def test_routes_source_port(self):
1417 def sock_port():
1418 _, sock = self.http(b'', start=True, raw=True, no_recv=True)
1419 port = sock.getsockname()[1]
1420 return (sock, port)
1421
1422 sock, port = sock_port()
1423 sock2, port2 = sock_port()
1424
1425 self.route_match({"source": "127.0.0.1:" + str(port)})
1426 assert self.get(sock=sock)['status'] == 200, 'exact'
1427 assert self.get(sock=sock2)['status'] == 404, 'exact 2'
1428
1429 sock, port = sock_port()
1430 sock2, port2 = sock_port()
1431
1432 self.route_match({"source": "!127.0.0.1:" + str(port)})
1433 assert self.get(sock=sock)['status'] == 404, 'negative'
1434 assert self.get(sock=sock2)['status'] == 200, 'negative 2'
1435
1436 sock, port = sock_port()
1437 sock2, port2 = sock_port()
1438
1439 self.route_match({"source": ["*:" + str(port), "!127.0.0.1"]})
1440 assert self.get(sock=sock)['status'] == 404, 'negative 3'
1441 assert self.get(sock=sock2)['status'] == 404, 'negative 4'
1442
1443 sock, port = sock_port()
1444 sock2, port2 = sock_port()
1445
1446 self.route_match(
1447 {"source": "127.0.0.1:" + str(port) + "-" + str(port)}
1448 )
1449 assert self.get(sock=sock)['status'] == 200, 'range single'
1450 assert self.get(sock=sock2)['status'] == 404, 'range single 2'
1451
1452 socks = [
1453 sock_port(),
1454 sock_port(),
1455 sock_port(),
1456 sock_port(),
1457 sock_port(),
1458 ]
1459 socks.sort(key=lambda sock: sock[1])
1460
1461 self.route_match(
1462 {
1463 "source": "127.0.0.1:"
1464 + str(socks[1][1]) # second port number
1465 + "-"
1466 + str(socks[3][1]) # fourth port number
1467 }
1468 )
1469 assert self.get(sock=socks[0][0])['status'] == 404, 'range'
1470 assert self.get(sock=socks[1][0])['status'] == 200, 'range 2'
1471 assert self.get(sock=socks[2][0])['status'] == 200, 'range 3'
1472 assert self.get(sock=socks[3][0])['status'] == 200, 'range 4'
1473 assert self.get(sock=socks[4][0])['status'] == 404, 'range 5'
1474
1475 socks = [
1476 sock_port(),
1477 sock_port(),
1478 sock_port(),
1479 ]
1480 socks.sort(key=lambda sock: sock[1])
1481
1482 self.route_match(
1483 {
1484 "source": [
1485 "127.0.0.1:" + str(socks[0][1]),
1486 "127.0.0.1:" + str(socks[2][1]),
1487 ]
1488 }
1489 )
1490 assert self.get(sock=socks[0][0])['status'] == 200, 'array'
1491 assert self.get(sock=socks[1][0])['status'] == 404, 'array 2'
1492 assert self.get(sock=socks[2][0])['status'] == 200, 'array 3'
1493
1494 def test_routes_source_addr(self):
1495 assert 'success' in self.conf(
1496 {"*:7080": {"pass": "routes"}, "[::1]:7081": {"pass": "routes"},},
1497 'listeners',
1498 ), 'source listeners configure'
1499
1500 def get_ipv6():
1501 return self.get(sock_type='ipv6', port=7081)
1502
1503 self.route_match({"source": "127.0.0.1"})
1504 assert self.get()['status'] == 200, 'exact'
1505 assert get_ipv6()['status'] == 404, 'exact ipv6'
1506
1507 self.route_match({"source": ["127.0.0.1"]})
1508 assert self.get()['status'] == 200, 'exact 2'
1509 assert get_ipv6()['status'] == 404, 'exact 2 ipv6'
1510
1511 self.route_match({"source": "!127.0.0.1"})
1512 assert self.get()['status'] == 404, 'exact neg'
1513 assert get_ipv6()['status'] == 200, 'exact neg ipv6'
1514
1515 self.route_match({"source": "127.0.0.2"})
1516 assert self.get()['status'] == 404, 'exact 3'
1517 assert get_ipv6()['status'] == 404, 'exact 3 ipv6'
1518
1519 self.route_match({"source": "127.0.0.1-127.0.0.1"})
1520 assert self.get()['status'] == 200, 'range single'
1521 assert get_ipv6()['status'] == 404, 'range single ipv6'
1522
1523 self.route_match({"source": "127.0.0.2-127.0.0.2"})
1524 assert self.get()['status'] == 404, 'range single 2'
1525 assert get_ipv6()['status'] == 404, 'range single 2 ipv6'
1526
1527 self.route_match({"source": "127.0.0.2-127.0.0.3"})
1528 assert self.get()['status'] == 404, 'range'
1529 assert get_ipv6()['status'] == 404, 'range ipv6'
1530
1531 self.route_match({"source": "127.0.0.1-127.0.0.2"})
1532 assert self.get()['status'] == 200, 'range 2'
1533 assert get_ipv6()['status'] == 404, 'range 2 ipv6'
1534
1535 self.route_match({"source": "127.0.0.0-127.0.0.2"})
1536 assert self.get()['status'] == 200, 'range 3'
1537 assert get_ipv6()['status'] == 404, 'range 3 ipv6'
1538
1539 self.route_match({"source": "127.0.0.0-127.0.0.1"})
1540 assert self.get()['status'] == 200, 'range 4'
1541 assert get_ipv6()['status'] == 404, 'range 4 ipv6'
1542
1543 self.route_match({"source": "126.0.0.0-127.0.0.0"})
1544 assert self.get()['status'] == 404, 'range 5'
1545 assert get_ipv6()['status'] == 404, 'range 5 ipv6'
1546
1547 self.route_match({"source": "126.126.126.126-127.0.0.2"})
1548 assert self.get()['status'] == 200, 'range 6'
1549 assert get_ipv6()['status'] == 404, 'range 6 ipv6'
1550
1551 def test_routes_source_ipv6(self):
1552 assert 'success' in self.conf(
1553 {
1554 "[::1]:7080": {"pass": "routes"},
1555 "127.0.0.1:7081": {"pass": "routes"},
1556 },
1557 'listeners',
1558 ), 'source listeners configure'
1559
1560 self.route_match({"source": "::1"})
1561 assert self.get(sock_type='ipv6')['status'] == 200, 'exact'
1562 assert self.get(port=7081)['status'] == 404, 'exact ipv4'
1563
1564 self.route_match({"source": ["::1"]})
1565 assert self.get(sock_type='ipv6')['status'] == 200, 'exact 2'
1566 assert self.get(port=7081)['status'] == 404, 'exact 2 ipv4'
1567
1568 self.route_match({"source": "!::1"})
1569 assert self.get(sock_type='ipv6')['status'] == 404, 'exact neg'
1570 assert self.get(port=7081)['status'] == 200, 'exact neg ipv4'
1571
1572 self.route_match({"source": "::2"})
1573 assert self.get(sock_type='ipv6')['status'] == 404, 'exact 3'
1574 assert self.get(port=7081)['status'] == 404, 'exact 3 ipv4'
1575
1576 self.route_match({"source": "::1-::1"})
1577 assert self.get(sock_type='ipv6')['status'] == 200, 'range'
1578 assert self.get(port=7081)['status'] == 404, 'range ipv4'
1579
1580 self.route_match({"source": "::2-::2"})
1581 assert self.get(sock_type='ipv6')['status'] == 404, 'range 2'
1582 assert self.get(port=7081)['status'] == 404, 'range 2 ipv4'
1583
1584 self.route_match({"source": "::2-::3"})
1585 assert self.get(sock_type='ipv6')['status'] == 404, 'range 3'
1586 assert self.get(port=7081)['status'] == 404, 'range 3 ipv4'
1587
1588 self.route_match({"source": "::1-::2"})
1589 assert self.get(sock_type='ipv6')['status'] == 200, 'range 4'
1590 assert self.get(port=7081)['status'] == 404, 'range 4 ipv4'
1591
1592 self.route_match({"source": "::0-::2"})
1593 assert self.get(sock_type='ipv6')['status'] == 200, 'range 5'
1594 assert self.get(port=7081)['status'] == 404, 'range 5 ipv4'
1595
1596 self.route_match({"source": "::0-::1"})
1597 assert self.get(sock_type='ipv6')['status'] == 200, 'range 6'
1598 assert self.get(port=7081)['status'] == 404, 'range 6 ipv4'
1599
1600 def test_routes_source_cidr(self):
1601 assert 'success' in self.conf(
1602 {"*:7080": {"pass": "routes"}, "[::1]:7081": {"pass": "routes"},},
1603 'listeners',
1604 ), 'source listeners configure'
1605
1606 def get_ipv6():
1607 return self.get(sock_type='ipv6', port=7081)
1608
1609 self.route_match({"source": "127.0.0.1/32"})
1610 assert self.get()['status'] == 200, '32'
1611 assert get_ipv6()['status'] == 404, '32 ipv6'
1612
1613 self.route_match({"source": "127.0.0.0/32"})
1614 assert self.get()['status'] == 404, '32 2'
1615 assert get_ipv6()['status'] == 404, '32 2 ipv6'
1616
1617 self.route_match({"source": "127.0.0.0/31"})
1618 assert self.get()['status'] == 200, '31'
1619 assert get_ipv6()['status'] == 404, '31 ipv6'
1620
1621 self.route_match({"source": "0.0.0.0/1"})
1622 assert self.get()['status'] == 200, '1'
1623 assert get_ipv6()['status'] == 404, '1 ipv6'
1624
1625 self.route_match({"source": "0.0.0.0/0"})
1626 assert self.get()['status'] == 200, '0'
1627 assert get_ipv6()['status'] == 404, '0 ipv6'
1628
1629 def test_routes_source_cidr_ipv6(self):
1630 assert 'success' in self.conf(
1631 {
1632 "[::1]:7080": {"pass": "routes"},
1633 "127.0.0.1:7081": {"pass": "routes"},
1634 },
1635 'listeners',
1636 ), 'source listeners configure'
1637
1638 self.route_match({"source": "::1/128"})
1639 assert self.get(sock_type='ipv6')['status'] == 200, '128'
1640 assert self.get(port=7081)['status'] == 404, '128 ipv4'
1641
1642 self.route_match({"source": "::0/128"})
1643 assert self.get(sock_type='ipv6')['status'] == 404, '128 2'
1644 assert self.get(port=7081)['status'] == 404, '128 ipv4'
1645
1646 self.route_match({"source": "::0/127"})
1647 assert self.get(sock_type='ipv6')['status'] == 200, '127'
1648 assert self.get(port=7081)['status'] == 404, '127 ipv4'
1649
1650 self.route_match({"source": "::0/32"})
1651 assert self.get(sock_type='ipv6')['status'] == 200, '32'
1652 assert self.get(port=7081)['status'] == 404, '32 ipv4'
1653
1654 self.route_match({"source": "::0/1"})
1655 assert self.get(sock_type='ipv6')['status'] == 200, '1'
1656 assert self.get(port=7081)['status'] == 404, '1 ipv4'
1657
1658 self.route_match({"source": "::/0"})
1659 assert self.get(sock_type='ipv6')['status'] == 200, '0'
1660 assert self.get(port=7081)['status'] == 404, '0 ipv4'
1661
1662 def test_routes_source_unix(self, temp_dir):
1663 addr = temp_dir + '/sock'
1664
1665 assert 'success' in self.conf(
1666 {"unix:" + addr: {"pass": "routes"}}, 'listeners'
1667 ), 'source listeners configure'
1668
1669 self.route_match({"source": "!0.0.0.0/0"})
1670 assert (
1671 self.get(sock_type='unix', addr=addr)['status'] == 200
1672 ), 'unix ipv4'
1673
1674 self.route_match({"source": "!::/0"})
1675 assert (
1676 self.get(sock_type='unix', addr=addr)['status'] == 200
1677 ), 'unix ipv6'
1678
1679 def test_routes_match_source(self):
1680 self.route_match({"source": "::"})
1681 self.route_match(
1682 {
1683 "source": [
1684 "127.0.0.1",
1685 "192.168.0.10:8080",
1686 "192.168.0.11:8080-8090",
1687 ]
1688 }
1689 )
1690 self.route_match(
1691 {
1692 "source": [
1693 "10.0.0.0/8",
1694 "10.0.0.0/7:1000",
1695 "10.0.0.0/32:8080-8090",
1696 ]
1697 }
1698 )
1699 self.route_match(
1700 {
1701 "source": [
1702 "10.0.0.0-10.0.0.1",
1703 "10.0.0.0-11.0.0.0:1000",
1704 "127.0.0.0-127.0.0.255:8080-8090",
1705 ]
1706 }
1707 )
1708 self.route_match(
1709 {"source": ["2001::", "[2002::]:8000", "[2003::]:8080-8090"]}
1710 )
1711 self.route_match(
1712 {
1713 "source": [
1714 "2001::-200f:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
1715 "[fe08::-feff::]:8000",
1716 "[fff0::-fff0::10]:8080-8090",
1717 ]
1718 }
1719 )
1720 self.route_match(
1721 {
1722 "source": [
1723 "2001::/16",
1724 "[0ff::/64]:8000",
1725 "[fff0:abcd:ffff:ffff:ffff::/128]:8080-8090",
1726 ]
1727 }
1728 )
1729 self.route_match({"source": "*:0-65535"})
1730 assert self.get()['status'] == 200, 'source any'
1731
1732 def test_routes_match_source_invalid(self):
1733 self.route_match_invalid({"source": "127"})
1734 self.route_match_invalid({"source": "256.0.0.1"})
1735 self.route_match_invalid({"source": "127.0.0."})
1736 self.route_match_invalid({"source": " 127.0.0.1"})
1737 self.route_match_invalid({"source": "127.0.0.1:"})
1738 self.route_match_invalid({"source": "127.0.0.1/"})
1739 self.route_match_invalid({"source": "11.0.0.0/33"})
1740 self.route_match_invalid({"source": "11.0.0.0/65536"})
1741 self.route_match_invalid({"source": "11.0.0.0-10.0.0.0"})
1742 self.route_match_invalid({"source": "11.0.0.0:3000-2000"})
1743 self.route_match_invalid({"source": ["11.0.0.0:3000-2000"]})
1744 self.route_match_invalid({"source": "[2001::]:3000-2000"})
1745 self.route_match_invalid({"source": "2001::-2000::"})
1746 self.route_match_invalid({"source": "2001::/129"})
1747 self.route_match_invalid({"source": "::FFFFF"})
1748 self.route_match_invalid({"source": "[::1]:"})
1749 self.route_match_invalid({"source": "[:::]:7080"})
1750 self.route_match_invalid({"source": "*:"})
1751 self.route_match_invalid({"source": "*:1-a"})
1752 self.route_match_invalid({"source": "*:65536"})
1753
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 def check_headers(hds):
1045 hds = dict({"Host": "localhost", "Connection": "close"}, **hds)
1046 assert (
1047 self.get(headers=hds)['status'] == 200
1048 ), 'headers array match'
1049
1050 def check_headers_404(hds):
1051 hds = dict({"Host": "localhost", "Connection": "close"}, **hds)
1052 assert (
1053 self.get(headers=hds)['status'] == 404
1054 ), 'headers array no match'
1055
1056 assert self.get()['status'] == 404, 'match headers array'
1057 check_headers({"x-header1": "foo123"})
1058 check_headers({"x-header2": "bar"})
1059 check_headers({"x-header3": "bar"})
1060 check_headers_404({"x-header1": "bar"})
1061 check_headers({"x-header1": "bar", "x-header4": "foo"})
1062
1063 assert 'success' in self.conf_delete(
1064 'routes/0/match/headers/1'
1065 ), 'match headers array configure 2'
1066
1067 check_headers_404({"x-header2": "bar"})
1068 check_headers({"x-header3": "foo"})
1069
1070 def test_routes_match_arguments(self):
1071 self.route_match({"arguments": {"foo": "bar"}})
1072
1073 assert self.get()['status'] == 404, 'args'
1074 assert self.get(url='/?foo=bar')['status'] == 200, 'args 2'
1075 assert self.get(url='/?foo=bar1')['status'] == 404, 'args 3'
1076 assert self.get(url='/?1foo=bar')['status'] == 404, 'args 4'
1077 assert self.get(url='/?Foo=bar')['status'] == 404, 'case'
1078 assert self.get(url='/?foo=Bar')['status'] == 404, 'case 2'
1079
1080 def test_routes_match_arguments_chars(self):
1081 chars = (
1082 " !\"%23$%25%26'()*%2B,-./0123456789:;<%3D>?@"
1083 "ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
1084 )
1085
1086 chars_enc = ""
1087 for h1 in ["2", "3", "4", "5", "6", "7"]:
1088 for h2 in [
1089 "0",
1090 "1",
1091 "2",
1092 "3",
1093 "4",
1094 "5",
1095 "6",
1096 "7",
1097 "8",
1098 "9",
1099 "A",
1100 "B",
1101 "C",
1102 "D",
1103 "E",
1104 "F",
1105 ]:
1106 chars_enc += "%" + h1 + h2
1107 chars_enc = chars_enc[:-3]
1108
1109 def check_args(args, query):
1110 self.route_match({"arguments": args})
1111 assert self.get(url='/?' + query)['status'] == 200
1112
1113 check_args({chars: chars}, chars + '=' + chars)
1114 check_args({chars: chars}, chars + '=' + chars_enc)
1115 check_args({chars: chars}, chars_enc + '=' + chars)
1116 check_args({chars: chars}, chars_enc + '=' + chars_enc)
1117 check_args({chars_enc: chars_enc}, chars + '=' + chars)
1118 check_args({chars_enc: chars_enc}, chars + '=' + chars_enc)
1119 check_args({chars_enc: chars_enc}, chars_enc + '=' + chars)
1120 check_args({chars_enc: chars_enc}, chars_enc + '=' + chars_enc)
1121
1122 def test_routes_match_arguments_empty(self):
1123 self.route_match({"arguments": {}})
1124 assert self.get()['status'] == 200, 'arguments empty'
1125
1126 self.route_match({"arguments": []})
1127 assert self.get()['status'] == 200, 'arguments empty 2'
1128
1129 def test_routes_match_arguments_space(self):
1130 self.route_match({"arguments": {"+fo o%20": "%20b+a r"}})
1131 assert self.get(url='/? fo o = b a r&')['status'] == 200
1132 assert self.get(url='/?+fo+o+=+b+a+r&')['status'] == 200
1133 assert self.get(url='/?%20fo%20o%20=%20b%20a%20r&')['status'] == 200
1134
1135 self.route_match({"arguments": {"%20foo": " bar"}})
1136 assert self.get(url='/? foo= bar')['status'] == 200
1137 assert self.get(url='/?+foo=+bar')['status'] == 200
1138 assert self.get(url='/?%20foo=%20bar')['status'] == 200
1139 assert self.get(url='/?+foo= bar')['status'] == 200
1140 assert self.get(url='/?%20foo=+bar')['status'] == 200
1141
1142 def test_routes_match_arguments_equal(self):
1143 self.route_match({"arguments": {"=": "="}})
1144 assert self.get(url='/?%3D=%3D')['status'] == 200
1145 assert self.get(url='/?%3D==')['status'] == 200
1146 assert self.get(url='/?===')['status'] == 404
1147 assert self.get(url='/?%3D%3D%3D')['status'] == 404
1148 assert self.get(url='/?==%3D')['status'] == 404
1149
1150 def test_routes_match_arguments_enc(self):
1151 self.route_match({"arguments": {"Ю": "н"}})
1152 assert self.get(url='/?%D0%AE=%D0%BD')['status'] == 200
1153 assert self.get(url='/?%d0%ae=%d0%Bd')['status'] == 200
1154
1155 def test_routes_match_arguments_hash(self):
1156 self.route_match({"arguments": {"#": "#"}})
1157 assert self.get(url='/?%23=%23')['status'] == 200
1158 assert self.get(url='/?%23=%23#')['status'] == 200
1159 assert self.get(url='/?#=#')['status'] == 404
1160 assert self.get(url='/?%23=#')['status'] == 404
1161
1162 def test_routes_match_arguments_wildcard(self):
1163 self.route_match({"arguments": {"foo": "*"}})
1164 assert self.get(url='/?foo')['status'] == 200
1165 assert self.get(url='/?foo=')['status'] == 200
1166 assert self.get(url='/?foo=blah')['status'] == 200
1167 assert self.get(url='/?blah=foo')['status'] == 404
1168
1169 self.route_match({"arguments": {"foo": "%25*"}})
1170 assert self.get(url='/?foo=%xx')['status'] == 200
1171
1172 self.route_match({"arguments": {"foo": "%2A*"}})
1173 assert self.get(url='/?foo=*xx')['status'] == 200
1174 assert self.get(url='/?foo=xx')['status'] == 404
1175
1176 self.route_match({"arguments": {"foo": "*%2A"}})
1177 assert self.get(url='/?foo=xx*')['status'] == 200
1178 assert self.get(url='/?foo=xx*x')['status'] == 404
1179
1180 self.route_match({"arguments": {"foo": "1*2"}})
1181 assert self.get(url='/?foo=12')['status'] == 200
1182 assert self.get(url='/?foo=1blah2')['status'] == 200
1183 assert self.get(url='/?foo=1%2A2')['status'] == 200
1184 assert self.get(url='/?foo=x12')['status'] == 404
1185
1186 self.route_match({"arguments": {"foo": "bar*", "%25": "%25"}})
1187 assert self.get(url='/?foo=barxx&%=%')['status'] == 200
1188 assert self.get(url='/?foo=barxx&x%=%')['status'] == 404
1189
1190 def test_routes_match_arguments_negative(self):
1191 self.route_match({"arguments": {"foo": "!"}})
1192 assert self.get(url='/?bar')['status'] == 404
1193 assert self.get(url='/?foo')['status'] == 404
1194 assert self.get(url='/?foo=')['status'] == 404
1195 assert self.get(url='/?foo=%25')['status'] == 200
1196
1197 self.route_match({"arguments": {"foo": "!*"}})
1198 assert self.get(url='/?bar')['status'] == 404
1199 assert self.get(url='/?foo')['status'] == 404
1200 assert self.get(url='/?foo=')['status'] == 404
1201 assert self.get(url='/?foo=blah')['status'] == 404
1202
1203 self.route_match({"arguments": {"foo": "!%25"}})
1204 assert self.get(url='/?foo=blah')['status'] == 200
1205 assert self.get(url='/?foo=%')['status'] == 404
1206
1207 self.route_match({"arguments": {"foo": "%21blah"}})
1208 assert self.get(url='/?foo=%21blah')['status'] == 200
1209 assert self.get(url='/?foo=!blah')['status'] == 200
1210 assert self.get(url='/?foo=bar')['status'] == 404
1211
1212 self.route_match({"arguments": {"foo": "!!%21*a"}})
1213 assert self.get(url='/?foo=blah')['status'] == 200
1214 assert self.get(url='/?foo=!blah')['status'] == 200
1215 assert self.get(url='/?foo=!!a')['status'] == 404
1216 assert self.get(url='/?foo=!!bla')['status'] == 404
1217
1218 def test_routes_match_arguments_percent(self):
1219 self.route_match({"arguments": {"%25": "%25"}})
1220 assert self.get(url='/?%=%')['status'] == 200
1221 assert self.get(url='/?%25=%25')['status'] == 200
1222 assert self.get(url='/?%25=%')['status'] == 200
1223
1224 self.route_match({"arguments": {"%251": "%252"}})
1225 assert self.get(url='/?%1=%2')['status'] == 200
1226 assert self.get(url='/?%251=%252')['status'] == 200
1227 assert self.get(url='/?%251=%2')['status'] == 200
1228
1229 self.route_match({"arguments": {"%25%21%251": "%25%24%252"}})
1230 assert self.get(url='/?%!%1=%$%2')['status'] == 200
1231 assert self.get(url='/?%25!%251=%25$%252')['status'] == 200
1232 assert self.get(url='/?%25!%1=%$%2')['status'] == 200
1233
1234 def test_routes_match_arguments_ampersand(self):
1235 self.route_match({"arguments": {"foo": "&"}})
1236 assert self.get(url='/?foo=%26')['status'] == 200
1237 assert self.get(url='/?foo=%26&')['status'] == 200
1238 assert self.get(url='/?foo=%26%26')['status'] == 404
1239 assert self.get(url='/?foo=&')['status'] == 404
1240
1241 self.route_match({"arguments": {"&": ""}})
1242 assert self.get(url='/?%26=')['status'] == 200
1243 assert self.get(url='/?%26=&')['status'] == 200
1244 assert self.get(url='/?%26=%26')['status'] == 404
1245 assert self.get(url='/?&=')['status'] == 404
1246
1247 def test_routes_match_arguments_complex(self):
1248 self.route_match({"arguments": {"foo": ""}})
1249
1250 assert self.get(url='/?foo')['status'] == 200, 'complex'
1251 assert self.get(url='/?blah=blah&foo=')['status'] == 200, 'complex 2'
1252 assert self.get(url='/?&&&foo&&&')['status'] == 200, 'complex 3'
1253 assert self.get(url='/?foo&foo=bar&foo')['status'] == 404, 'complex 4'
1254 assert self.get(url='/?foo=&foo')['status'] == 200, 'complex 5'
1255 assert self.get(url='/?&=&foo&==&')['status'] == 200, 'complex 6'
1256 assert self.get(url='/?&=&bar&==&')['status'] == 404, 'complex 7'
1257
1258 def test_routes_match_arguments_multiple(self):
1259 self.route_match({"arguments": {"foo": "bar", "blah": "test"}})
1260
1261 assert self.get()['status'] == 404, 'multiple'
1262 assert (
1263 self.get(url='/?foo=bar&blah=test')['status'] == 200
1264 ), 'multiple 2'
1265 assert self.get(url='/?foo=bar&blah')['status'] == 404, 'multiple 3'
1266 assert (
1267 self.get(url='/?foo=bar&blah=tes')['status'] == 404
1268 ), 'multiple 4'
1269 assert (
1270 self.get(url='/?foo=b%61r&bl%61h=t%65st')['status'] == 200
1271 ), 'multiple 5'
1272
1273 def test_routes_match_arguments_multiple_rules(self):
1274 self.route_match({"arguments": {"foo": ["bar", "blah"]}})
1275
1276 assert self.get()['status'] == 404, 'rules'
1277 assert self.get(url='/?foo=bar')['status'] == 200, 'rules 2'
1278 assert self.get(url='/?foo=blah')['status'] == 200, 'rules 3'
1279 assert (
1280 self.get(url='/?foo=blah&foo=bar&foo=blah')['status'] == 200
1281 ), 'rules 4'
1282 assert (
1283 self.get(url='/?foo=blah&foo=bar&foo=')['status'] == 404
1284 ), 'rules 5'
1285
1286 def test_routes_match_arguments_array(self):
1287 self.route_match(
1288 {
1289 "arguments": [
1290 {"var1": "val1*"},
1291 {"var2": "val2"},
1292 {"var3": ["foo", "bar"]},
1293 {"var1": "bar", "var4": "foo"},
1294 ]
1295 }
1296 )
1297
1298 assert self.get()['status'] == 404, 'arr'
1299 assert self.get(url='/?var1=val123')['status'] == 200, 'arr 2'
1300 assert self.get(url='/?var2=val2')['status'] == 200, 'arr 3'
1301 assert self.get(url='/?var3=bar')['status'] == 200, 'arr 4'
1302 assert self.get(url='/?var1=bar')['status'] == 404, 'arr 5'
1303 assert self.get(url='/?var1=bar&var4=foo')['status'] == 200, 'arr 6'
1304
1305 assert 'success' in self.conf_delete(
1306 'routes/0/match/arguments/1'
1307 ), 'match arguments array configure 2'
1308
1309 assert self.get(url='/?var2=val2')['status'] == 404, 'arr 7'
1310 assert self.get(url='/?var3=foo')['status'] == 200, 'arr 8'
1311
1312 def test_routes_match_arguments_invalid(self):
1313 self.route_match_invalid({"arguments": ["var"]})
1314 self.route_match_invalid({"arguments": [{"var1": {}}]})
1315 self.route_match_invalid({"arguments": {"": "bar"}})
1316 self.route_match_invalid({"arguments": {"foo": "%"}})
1317 self.route_match_invalid({"arguments": {"foo": "%1G"}})
1318 self.route_match_invalid({"arguments": {"%": "bar"}})
1319 self.route_match_invalid({"arguments": {"foo": "%0"}})
1320 self.route_match_invalid({"arguments": {"foo": "%%1F"}})
1321 self.route_match_invalid({"arguments": {"%%1F": ""}})
1322 self.route_match_invalid({"arguments": {"%7%F": ""}})
1323
1324 def test_routes_match_cookies(self):
1325 self.route_match({"cookies": {"foO": "bar"}})
1326
1327 assert self.get()['status'] == 404, 'cookie'
1328 self.cookie('foO=bar', 200)
1329 self.cookie('foO=bar;1', 200)
1330 self.cookie(['foO=bar', 'blah=blah'], 200)
1331 self.cookie('foO=bar; blah=blah', 200)
1332 self.cookie('Foo=bar', 404)
1333 self.cookie('foO=Bar', 404)
1334 self.cookie('foO=bar1', 404)
1335 self.cookie('1foO=bar;', 404)
1336
1337 def test_routes_match_cookies_empty(self):
1338 self.route_match({"cookies": {}})
1339 assert self.get()['status'] == 200, 'cookies empty'
1340
1341 self.route_match({"cookies": []})
1342 assert self.get()['status'] == 200, 'cookies empty 2'
1343
1344 def test_routes_match_cookies_invalid(self):
1345 self.route_match_invalid({"cookies": ["var"]})
1346 self.route_match_invalid({"cookies": [{"foo": {}}]})
1347
1348 def test_routes_match_cookies_multiple(self):
1349 self.route_match({"cookies": {"foo": "bar", "blah": "blah"}})
1350
1351 assert self.get()['status'] == 404, 'multiple'
1352 self.cookie('foo=bar; blah=blah', 200)
1353 self.cookie(['foo=bar', 'blah=blah'], 200)
1354 self.cookie(['foo=bar; blah', 'blah'], 404)
1355 self.cookie(['foo=bar; blah=test', 'blah=blah'], 404)
1356
1357 def test_routes_match_cookies_multiple_values(self):
1358 self.route_match({"cookies": {"blah": "blah"}})
1359
1360 self.cookie(['blah=blah', 'blah=blah', 'blah=blah'], 200)
1361 self.cookie(['blah=blah', 'blah=test', 'blah=blah'], 404)
1362 self.cookie(['blah=blah; blah=', 'blah=blah'], 404)
1363
1364 def test_routes_match_cookies_multiple_rules(self):
1365 self.route_match({"cookies": {"blah": ["test", "blah"]}})
1366
1367 assert self.get()['status'] == 404, 'multiple rules'
1368 self.cookie('blah=test', 200)
1369 self.cookie('blah=blah', 200)
1370 self.cookie(['blah=blah', 'blah=test', 'blah=blah'], 200)
1371 self.cookie(['blah=blah; blah=test', 'blah=blah'], 200)
1372 self.cookie(['blah=blah', 'blah'], 200) # invalid cookie
1373
1374 def test_routes_match_cookies_array(self):
1375 self.route_match(
1376 {
1377 "cookies": [
1378 {"var1": "val1*"},
1379 {"var2": "val2"},
1380 {"var3": ["foo", "bar"]},
1381 {"var1": "bar", "var4": "foo"},
1382 ]
1383 }
1384 )
1385
1386 assert self.get()['status'] == 404, 'cookies array'
1387 self.cookie('var1=val123', 200)
1388 self.cookie('var2=val2', 200)
1389 self.cookie(' var2=val2 ', 200)
1390 self.cookie('var3=bar', 200)
1391 self.cookie('var3=bar;', 200)
1392 self.cookie('var1=bar', 404)
1393 self.cookie('var1=bar; var4=foo;', 200)
1394 self.cookie(['var1=bar', 'var4=foo'], 200)
1395
1396 assert 'success' in self.conf_delete(
1397 'routes/0/match/cookies/1'
1398 ), 'match cookies array configure 2'
1399
1400 self.cookie('var2=val2', 404)
1401 self.cookie('var3=foo', 200)
1402
1403 def test_routes_match_scheme(self):
1404 self.route_match({"scheme": "http"})
1405 self.route_match({"scheme": "https"})
1406 self.route_match({"scheme": "HtTp"})
1407 self.route_match({"scheme": "HtTpS"})
1408
1409 def test_routes_match_scheme_invalid(self):
1410 self.route_match_invalid({"scheme": ["http"]})
1411 self.route_match_invalid({"scheme": "ftp"})
1412 self.route_match_invalid({"scheme": "ws"})
1413 self.route_match_invalid({"scheme": "*"})
1414 self.route_match_invalid({"scheme": ""})
1415
1416 def test_routes_source_port(self):
1417 def sock_port():
1418 _, sock = self.http(b'', start=True, raw=True, no_recv=True)
1419 port = sock.getsockname()[1]
1420 return (sock, port)
1421
1422 sock, port = sock_port()
1423 sock2, port2 = sock_port()
1424
1425 self.route_match({"source": "127.0.0.1:" + str(port)})
1426 assert self.get(sock=sock)['status'] == 200, 'exact'
1427 assert self.get(sock=sock2)['status'] == 404, 'exact 2'
1428
1429 sock, port = sock_port()
1430 sock2, port2 = sock_port()
1431
1432 self.route_match({"source": "!127.0.0.1:" + str(port)})
1433 assert self.get(sock=sock)['status'] == 404, 'negative'
1434 assert self.get(sock=sock2)['status'] == 200, 'negative 2'
1435
1436 sock, port = sock_port()
1437 sock2, port2 = sock_port()
1438
1439 self.route_match({"source": ["*:" + str(port), "!127.0.0.1"]})
1440 assert self.get(sock=sock)['status'] == 404, 'negative 3'
1441 assert self.get(sock=sock2)['status'] == 404, 'negative 4'
1442
1443 sock, port = sock_port()
1444 sock2, port2 = sock_port()
1445
1446 self.route_match(
1447 {"source": "127.0.0.1:" + str(port) + "-" + str(port)}
1448 )
1449 assert self.get(sock=sock)['status'] == 200, 'range single'
1450 assert self.get(sock=sock2)['status'] == 404, 'range single 2'
1451
1452 socks = [
1453 sock_port(),
1454 sock_port(),
1455 sock_port(),
1456 sock_port(),
1457 sock_port(),
1458 ]
1459 socks.sort(key=lambda sock: sock[1])
1460
1461 self.route_match(
1462 {
1463 "source": "127.0.0.1:"
1464 + str(socks[1][1]) # second port number
1465 + "-"
1466 + str(socks[3][1]) # fourth port number
1467 }
1468 )
1469 assert self.get(sock=socks[0][0])['status'] == 404, 'range'
1470 assert self.get(sock=socks[1][0])['status'] == 200, 'range 2'
1471 assert self.get(sock=socks[2][0])['status'] == 200, 'range 3'
1472 assert self.get(sock=socks[3][0])['status'] == 200, 'range 4'
1473 assert self.get(sock=socks[4][0])['status'] == 404, 'range 5'
1474
1475 socks = [
1476 sock_port(),
1477 sock_port(),
1478 sock_port(),
1479 ]
1480 socks.sort(key=lambda sock: sock[1])
1481
1482 self.route_match(
1483 {
1484 "source": [
1485 "127.0.0.1:" + str(socks[0][1]),
1486 "127.0.0.1:" + str(socks[2][1]),
1487 ]
1488 }
1489 )
1490 assert self.get(sock=socks[0][0])['status'] == 200, 'array'
1491 assert self.get(sock=socks[1][0])['status'] == 404, 'array 2'
1492 assert self.get(sock=socks[2][0])['status'] == 200, 'array 3'
1493
1494 def test_routes_source_addr(self):
1495 assert 'success' in self.conf(
1496 {"*:7080": {"pass": "routes"}, "[::1]:7081": {"pass": "routes"},},
1497 'listeners',
1498 ), 'source listeners configure'
1499
1500 def get_ipv6():
1501 return self.get(sock_type='ipv6', port=7081)
1502
1503 self.route_match({"source": "127.0.0.1"})
1504 assert self.get()['status'] == 200, 'exact'
1505 assert get_ipv6()['status'] == 404, 'exact ipv6'
1506
1507 self.route_match({"source": ["127.0.0.1"]})
1508 assert self.get()['status'] == 200, 'exact 2'
1509 assert get_ipv6()['status'] == 404, 'exact 2 ipv6'
1510
1511 self.route_match({"source": "!127.0.0.1"})
1512 assert self.get()['status'] == 404, 'exact neg'
1513 assert get_ipv6()['status'] == 200, 'exact neg ipv6'
1514
1515 self.route_match({"source": "127.0.0.2"})
1516 assert self.get()['status'] == 404, 'exact 3'
1517 assert get_ipv6()['status'] == 404, 'exact 3 ipv6'
1518
1519 self.route_match({"source": "127.0.0.1-127.0.0.1"})
1520 assert self.get()['status'] == 200, 'range single'
1521 assert get_ipv6()['status'] == 404, 'range single ipv6'
1522
1523 self.route_match({"source": "127.0.0.2-127.0.0.2"})
1524 assert self.get()['status'] == 404, 'range single 2'
1525 assert get_ipv6()['status'] == 404, 'range single 2 ipv6'
1526
1527 self.route_match({"source": "127.0.0.2-127.0.0.3"})
1528 assert self.get()['status'] == 404, 'range'
1529 assert get_ipv6()['status'] == 404, 'range ipv6'
1530
1531 self.route_match({"source": "127.0.0.1-127.0.0.2"})
1532 assert self.get()['status'] == 200, 'range 2'
1533 assert get_ipv6()['status'] == 404, 'range 2 ipv6'
1534
1535 self.route_match({"source": "127.0.0.0-127.0.0.2"})
1536 assert self.get()['status'] == 200, 'range 3'
1537 assert get_ipv6()['status'] == 404, 'range 3 ipv6'
1538
1539 self.route_match({"source": "127.0.0.0-127.0.0.1"})
1540 assert self.get()['status'] == 200, 'range 4'
1541 assert get_ipv6()['status'] == 404, 'range 4 ipv6'
1542
1543 self.route_match({"source": "126.0.0.0-127.0.0.0"})
1544 assert self.get()['status'] == 404, 'range 5'
1545 assert get_ipv6()['status'] == 404, 'range 5 ipv6'
1546
1547 self.route_match({"source": "126.126.126.126-127.0.0.2"})
1548 assert self.get()['status'] == 200, 'range 6'
1549 assert get_ipv6()['status'] == 404, 'range 6 ipv6'
1550
1551 def test_routes_source_ipv6(self):
1552 assert 'success' in self.conf(
1553 {
1554 "[::1]:7080": {"pass": "routes"},
1555 "127.0.0.1:7081": {"pass": "routes"},
1556 },
1557 'listeners',
1558 ), 'source listeners configure'
1559
1560 self.route_match({"source": "::1"})
1561 assert self.get(sock_type='ipv6')['status'] == 200, 'exact'
1562 assert self.get(port=7081)['status'] == 404, 'exact ipv4'
1563
1564 self.route_match({"source": ["::1"]})
1565 assert self.get(sock_type='ipv6')['status'] == 200, 'exact 2'
1566 assert self.get(port=7081)['status'] == 404, 'exact 2 ipv4'
1567
1568 self.route_match({"source": "!::1"})
1569 assert self.get(sock_type='ipv6')['status'] == 404, 'exact neg'
1570 assert self.get(port=7081)['status'] == 200, 'exact neg ipv4'
1571
1572 self.route_match({"source": "::2"})
1573 assert self.get(sock_type='ipv6')['status'] == 404, 'exact 3'
1574 assert self.get(port=7081)['status'] == 404, 'exact 3 ipv4'
1575
1576 self.route_match({"source": "::1-::1"})
1577 assert self.get(sock_type='ipv6')['status'] == 200, 'range'
1578 assert self.get(port=7081)['status'] == 404, 'range ipv4'
1579
1580 self.route_match({"source": "::2-::2"})
1581 assert self.get(sock_type='ipv6')['status'] == 404, 'range 2'
1582 assert self.get(port=7081)['status'] == 404, 'range 2 ipv4'
1583
1584 self.route_match({"source": "::2-::3"})
1585 assert self.get(sock_type='ipv6')['status'] == 404, 'range 3'
1586 assert self.get(port=7081)['status'] == 404, 'range 3 ipv4'
1587
1588 self.route_match({"source": "::1-::2"})
1589 assert self.get(sock_type='ipv6')['status'] == 200, 'range 4'
1590 assert self.get(port=7081)['status'] == 404, 'range 4 ipv4'
1591
1592 self.route_match({"source": "::0-::2"})
1593 assert self.get(sock_type='ipv6')['status'] == 200, 'range 5'
1594 assert self.get(port=7081)['status'] == 404, 'range 5 ipv4'
1595
1596 self.route_match({"source": "::0-::1"})
1597 assert self.get(sock_type='ipv6')['status'] == 200, 'range 6'
1598 assert self.get(port=7081)['status'] == 404, 'range 6 ipv4'
1599
1600 def test_routes_source_cidr(self):
1601 assert 'success' in self.conf(
1602 {"*:7080": {"pass": "routes"}, "[::1]:7081": {"pass": "routes"},},
1603 'listeners',
1604 ), 'source listeners configure'
1605
1606 def get_ipv6():
1607 return self.get(sock_type='ipv6', port=7081)
1608
1609 self.route_match({"source": "127.0.0.1/32"})
1610 assert self.get()['status'] == 200, '32'
1611 assert get_ipv6()['status'] == 404, '32 ipv6'
1612
1613 self.route_match({"source": "127.0.0.0/32"})
1614 assert self.get()['status'] == 404, '32 2'
1615 assert get_ipv6()['status'] == 404, '32 2 ipv6'
1616
1617 self.route_match({"source": "127.0.0.0/31"})
1618 assert self.get()['status'] == 200, '31'
1619 assert get_ipv6()['status'] == 404, '31 ipv6'
1620
1621 self.route_match({"source": "0.0.0.0/1"})
1622 assert self.get()['status'] == 200, '1'
1623 assert get_ipv6()['status'] == 404, '1 ipv6'
1624
1625 self.route_match({"source": "0.0.0.0/0"})
1626 assert self.get()['status'] == 200, '0'
1627 assert get_ipv6()['status'] == 404, '0 ipv6'
1628
1629 def test_routes_source_cidr_ipv6(self):
1630 assert 'success' in self.conf(
1631 {
1632 "[::1]:7080": {"pass": "routes"},
1633 "127.0.0.1:7081": {"pass": "routes"},
1634 },
1635 'listeners',
1636 ), 'source listeners configure'
1637
1638 self.route_match({"source": "::1/128"})
1639 assert self.get(sock_type='ipv6')['status'] == 200, '128'
1640 assert self.get(port=7081)['status'] == 404, '128 ipv4'
1641
1642 self.route_match({"source": "::0/128"})
1643 assert self.get(sock_type='ipv6')['status'] == 404, '128 2'
1644 assert self.get(port=7081)['status'] == 404, '128 ipv4'
1645
1646 self.route_match({"source": "::0/127"})
1647 assert self.get(sock_type='ipv6')['status'] == 200, '127'
1648 assert self.get(port=7081)['status'] == 404, '127 ipv4'
1649
1650 self.route_match({"source": "::0/32"})
1651 assert self.get(sock_type='ipv6')['status'] == 200, '32'
1652 assert self.get(port=7081)['status'] == 404, '32 ipv4'
1653
1654 self.route_match({"source": "::0/1"})
1655 assert self.get(sock_type='ipv6')['status'] == 200, '1'
1656 assert self.get(port=7081)['status'] == 404, '1 ipv4'
1657
1658 self.route_match({"source": "::/0"})
1659 assert self.get(sock_type='ipv6')['status'] == 200, '0'
1660 assert self.get(port=7081)['status'] == 404, '0 ipv4'
1661
1662 def test_routes_source_unix(self, temp_dir):
1663 addr = temp_dir + '/sock'
1664
1665 assert 'success' in self.conf(
1666 {"unix:" + addr: {"pass": "routes"}}, 'listeners'
1667 ), 'source listeners configure'
1668
1669 self.route_match({"source": "!0.0.0.0/0"})
1670 assert (
1671 self.get(sock_type='unix', addr=addr)['status'] == 200
1672 ), 'unix ipv4'
1673
1674 self.route_match({"source": "!::/0"})
1675 assert (
1676 self.get(sock_type='unix', addr=addr)['status'] == 200
1677 ), 'unix ipv6'
1678
1679 def test_routes_match_source(self):
1680 self.route_match({"source": "::"})
1681 self.route_match(
1682 {
1683 "source": [
1684 "127.0.0.1",
1685 "192.168.0.10:8080",
1686 "192.168.0.11:8080-8090",
1687 ]
1688 }
1689 )
1690 self.route_match(
1691 {
1692 "source": [
1693 "10.0.0.0/8",
1694 "10.0.0.0/7:1000",
1695 "10.0.0.0/32:8080-8090",
1696 ]
1697 }
1698 )
1699 self.route_match(
1700 {
1701 "source": [
1702 "10.0.0.0-10.0.0.1",
1703 "10.0.0.0-11.0.0.0:1000",
1704 "127.0.0.0-127.0.0.255:8080-8090",
1705 ]
1706 }
1707 )
1708 self.route_match(
1709 {"source": ["2001::", "[2002::]:8000", "[2003::]:8080-8090"]}
1710 )
1711 self.route_match(
1712 {
1713 "source": [
1714 "2001::-200f:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
1715 "[fe08::-feff::]:8000",
1716 "[fff0::-fff0::10]:8080-8090",
1717 ]
1718 }
1719 )
1720 self.route_match(
1721 {
1722 "source": [
1723 "2001::/16",
1724 "[0ff::/64]:8000",
1725 "[fff0:abcd:ffff:ffff:ffff::/128]:8080-8090",
1726 ]
1727 }
1728 )
1729 self.route_match({"source": "*:0-65535"})
1730 assert self.get()['status'] == 200, 'source any'
1731
1732 def test_routes_match_source_invalid(self):
1733 self.route_match_invalid({"source": "127"})
1734 self.route_match_invalid({"source": "256.0.0.1"})
1735 self.route_match_invalid({"source": "127.0.0."})
1736 self.route_match_invalid({"source": " 127.0.0.1"})
1737 self.route_match_invalid({"source": "127.0.0.1:"})
1738 self.route_match_invalid({"source": "127.0.0.1/"})
1739 self.route_match_invalid({"source": "11.0.0.0/33"})
1740 self.route_match_invalid({"source": "11.0.0.0/65536"})
1741 self.route_match_invalid({"source": "11.0.0.0-10.0.0.0"})
1742 self.route_match_invalid({"source": "11.0.0.0:3000-2000"})
1743 self.route_match_invalid({"source": ["11.0.0.0:3000-2000"]})
1744 self.route_match_invalid({"source": "[2001::]:3000-2000"})
1745 self.route_match_invalid({"source": "2001::-2000::"})
1746 self.route_match_invalid({"source": "2001::/129"})
1747 self.route_match_invalid({"source": "::FFFFF"})
1748 self.route_match_invalid({"source": "[::1]:"})
1749 self.route_match_invalid({"source": "[:::]:7080"})
1750 self.route_match_invalid({"source": "*:"})
1751 self.route_match_invalid({"source": "*:1-a"})
1752 self.route_match_invalid({"source": "*:65536"})
1753
1754 def test_routes_match_source_none(self):
1755 self.route_match({"source": []})
1756 assert self.get()['status'] == 404, 'source none'
1757
1754 def test_routes_match_destination(self):
1755 assert 'success' in self.conf(
1756 {"*:7080": {"pass": "routes"}, "*:7081": {"pass": "routes"}},
1757 'listeners',
1758 ), 'listeners configure'
1759
1760 self.route_match({"destination": "*:7080"})
1761 assert self.get()['status'] == 200, 'dest'
1762 assert self.get(port=7081)['status'] == 404, 'dest 2'
1763
1764 self.route_match({"destination": ["127.0.0.1:7080"]})
1765 assert self.get()['status'] == 200, 'dest 3'
1766 assert self.get(port=7081)['status'] == 404, 'dest 4'
1767
1768 self.route_match({"destination": "!*:7080"})
1769 assert self.get()['status'] == 404, 'dest neg'
1770 assert self.get(port=7081)['status'] == 200, 'dest neg 2'
1771
1772 self.route_match({"destination": ['!*:7080', '!*:7081']})
1773 assert self.get()['status'] == 404, 'dest neg 3'
1774 assert self.get(port=7081)['status'] == 404, 'dest neg 4'
1775
1776 self.route_match({"destination": ['!*:7081', '!*:7082']})
1777 assert self.get()['status'] == 200, 'dest neg 5'
1778
1779 self.route_match({"destination": ['*:7080', '!*:7080']})
1780 assert self.get()['status'] == 404, 'dest neg 6'
1781
1782 self.route_match(
1783 {"destination": ['127.0.0.1:7080', '*:7081', '!*:7080']}
1784 )
1785 assert self.get()['status'] == 404, 'dest neg 7'
1786 assert self.get(port=7081)['status'] == 200, 'dest neg 8'
1787
1788 self.route_match({"destination": ['!*:7081', '!*:7082', '*:7083']})
1789 assert self.get()['status'] == 404, 'dest neg 9'
1790
1791 self.route_match(
1792 {"destination": ['*:7081', '!127.0.0.1:7080', '*:7080']}
1793 )
1794 assert self.get()['status'] == 404, 'dest neg 10'
1795 assert self.get(port=7081)['status'] == 200, 'dest neg 11'
1796
1797 assert 'success' in self.conf_delete(
1798 'routes/0/match/destination/0'
1799 ), 'remove destination rule'
1800 assert self.get()['status'] == 404, 'dest neg 12'
1801 assert self.get(port=7081)['status'] == 404, 'dest neg 13'
1802
1803 assert 'success' in self.conf_delete(
1804 'routes/0/match/destination/0'
1805 ), 'remove destination rule 2'
1806 assert self.get()['status'] == 200, 'dest neg 14'
1807 assert self.get(port=7081)['status'] == 404, 'dest neg 15'
1808
1809 assert 'success' in self.conf_post(
1810 "\"!127.0.0.1\"", 'routes/0/match/destination'
1811 ), 'add destination rule'
1812 assert self.get()['status'] == 404, 'dest neg 16'
1813 assert self.get(port=7081)['status'] == 404, 'dest neg 17'
1814
1815 def test_routes_match_destination_proxy(self):
1816 assert 'success' in self.conf(
1817 {
1818 "listeners": {
1819 "*:7080": {"pass": "routes/first"},
1820 "*:7081": {"pass": "routes/second"},
1821 },
1822 "routes": {
1823 "first": [{"action": {"proxy": "http://127.0.0.1:7081"}}],
1824 "second": [
1825 {
1826 "match": {"destination": ["127.0.0.1:7081"]},
1827 "action": {"return": 200},
1828 }
1829 ],
1830 },
1831 "applications": {},
1832 }
1833 ), 'proxy configure'
1834
1835 assert self.get()['status'] == 200, 'proxy'
1758 def test_routes_match_destination(self):
1759 assert 'success' in self.conf(
1760 {"*:7080": {"pass": "routes"}, "*:7081": {"pass": "routes"}},
1761 'listeners',
1762 ), 'listeners configure'
1763
1764 self.route_match({"destination": "*:7080"})
1765 assert self.get()['status'] == 200, 'dest'
1766 assert self.get(port=7081)['status'] == 404, 'dest 2'
1767
1768 self.route_match({"destination": ["127.0.0.1:7080"]})
1769 assert self.get()['status'] == 200, 'dest 3'
1770 assert self.get(port=7081)['status'] == 404, 'dest 4'
1771
1772 self.route_match({"destination": "!*:7080"})
1773 assert self.get()['status'] == 404, 'dest neg'
1774 assert self.get(port=7081)['status'] == 200, 'dest neg 2'
1775
1776 self.route_match({"destination": ['!*:7080', '!*:7081']})
1777 assert self.get()['status'] == 404, 'dest neg 3'
1778 assert self.get(port=7081)['status'] == 404, 'dest neg 4'
1779
1780 self.route_match({"destination": ['!*:7081', '!*:7082']})
1781 assert self.get()['status'] == 200, 'dest neg 5'
1782
1783 self.route_match({"destination": ['*:7080', '!*:7080']})
1784 assert self.get()['status'] == 404, 'dest neg 6'
1785
1786 self.route_match(
1787 {"destination": ['127.0.0.1:7080', '*:7081', '!*:7080']}
1788 )
1789 assert self.get()['status'] == 404, 'dest neg 7'
1790 assert self.get(port=7081)['status'] == 200, 'dest neg 8'
1791
1792 self.route_match({"destination": ['!*:7081', '!*:7082', '*:7083']})
1793 assert self.get()['status'] == 404, 'dest neg 9'
1794
1795 self.route_match(
1796 {"destination": ['*:7081', '!127.0.0.1:7080', '*:7080']}
1797 )
1798 assert self.get()['status'] == 404, 'dest neg 10'
1799 assert self.get(port=7081)['status'] == 200, 'dest neg 11'
1800
1801 assert 'success' in self.conf_delete(
1802 'routes/0/match/destination/0'
1803 ), 'remove destination rule'
1804 assert self.get()['status'] == 404, 'dest neg 12'
1805 assert self.get(port=7081)['status'] == 404, 'dest neg 13'
1806
1807 assert 'success' in self.conf_delete(
1808 'routes/0/match/destination/0'
1809 ), 'remove destination rule 2'
1810 assert self.get()['status'] == 200, 'dest neg 14'
1811 assert self.get(port=7081)['status'] == 404, 'dest neg 15'
1812
1813 assert 'success' in self.conf_post(
1814 "\"!127.0.0.1\"", 'routes/0/match/destination'
1815 ), 'add destination rule'
1816 assert self.get()['status'] == 404, 'dest neg 16'
1817 assert self.get(port=7081)['status'] == 404, 'dest neg 17'
1818
1819 def test_routes_match_destination_proxy(self):
1820 assert 'success' in self.conf(
1821 {
1822 "listeners": {
1823 "*:7080": {"pass": "routes/first"},
1824 "*:7081": {"pass": "routes/second"},
1825 },
1826 "routes": {
1827 "first": [{"action": {"proxy": "http://127.0.0.1:7081"}}],
1828 "second": [
1829 {
1830 "match": {"destination": ["127.0.0.1:7081"]},
1831 "action": {"return": 200},
1832 }
1833 ],
1834 },
1835 "applications": {},
1836 }
1837 ), 'proxy configure'
1838
1839 assert self.get()['status'] == 200, 'proxy'