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