xref: /unit/test/test_routing.py (revision 1991:61155eb41a96)
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({
1346            "query": ["foo", "bar"]
1347        })
1348
1349        assert self.get()['status'] == 404, 'arr'
1350        assert self.get(url='/?foo')['status'] == 200, 'arr 1'
1351        assert self.get(url='/?bar')['status'] == 200, 'arr 2'
1352
1353        assert 'success' in self.conf_delete(
1354            'routes/0/match/query/1'
1355        ), 'match query array configure 2'
1356
1357        assert self.get(url='/?bar')['status'] == 404, 'arr 2'
1358
1359    def test_routes_match_query_invalid(self):
1360        self.route_match_invalid({"query": [1]})
1361        self.route_match_invalid({"query": "%"})
1362        self.route_match_invalid({"query": "%1G"})
1363        self.route_match_invalid({"query": "%0"})
1364        self.route_match_invalid({"query": "%%1F"})
1365        self.route_match_invalid({"query": ["foo", "%3D", "%%1F"]})
1366
1367    def test_routes_match_cookies(self):
1368        self.route_match({"cookies": {"foO": "bar"}})
1369
1370        assert self.get()['status'] == 404, 'cookie'
1371        self.cookie('foO=bar', 200)
1372        self.cookie('foO=bar;1', 200)
1373        self.cookie(['foO=bar', 'blah=blah'], 200)
1374        self.cookie('foO=bar; blah=blah', 200)
1375        self.cookie('Foo=bar', 404)
1376        self.cookie('foO=Bar', 404)
1377        self.cookie('foO=bar1', 404)
1378        self.cookie('1foO=bar;', 404)
1379
1380    def test_routes_match_cookies_empty(self):
1381        self.route_match({"cookies": {}})
1382        assert self.get()['status'] == 200, 'cookies empty'
1383
1384        self.route_match({"cookies": []})
1385        assert self.get()['status'] == 200, 'cookies empty 2'
1386
1387    def test_routes_match_cookies_invalid(self):
1388        self.route_match_invalid({"cookies": ["var"]})
1389        self.route_match_invalid({"cookies": [{"foo": {}}]})
1390
1391    def test_routes_match_cookies_multiple(self):
1392        self.route_match({"cookies": {"foo": "bar", "blah": "blah"}})
1393
1394        assert self.get()['status'] == 404, 'multiple'
1395        self.cookie('foo=bar; blah=blah', 200)
1396        self.cookie(['foo=bar', 'blah=blah'], 200)
1397        self.cookie(['foo=bar; blah', 'blah'], 404)
1398        self.cookie(['foo=bar; blah=test', 'blah=blah'], 404)
1399
1400    def test_routes_match_cookies_multiple_values(self):
1401        self.route_match({"cookies": {"blah": "blah"}})
1402
1403        self.cookie(['blah=blah', 'blah=blah', 'blah=blah'], 200)
1404        self.cookie(['blah=blah', 'blah=test', 'blah=blah'], 404)
1405        self.cookie(['blah=blah; blah=', 'blah=blah'], 404)
1406
1407    def test_routes_match_cookies_multiple_rules(self):
1408        self.route_match({"cookies": {"blah": ["test", "blah"]}})
1409
1410        assert self.get()['status'] == 404, 'multiple rules'
1411        self.cookie('blah=test', 200)
1412        self.cookie('blah=blah', 200)
1413        self.cookie(['blah=blah', 'blah=test', 'blah=blah'], 200)
1414        self.cookie(['blah=blah; blah=test', 'blah=blah'], 200)
1415        self.cookie(['blah=blah', 'blah'], 200)  # invalid cookie
1416
1417    def test_routes_match_cookies_array(self):
1418        self.route_match(
1419            {
1420                "cookies": [
1421                    {"var1": "val1*"},
1422                    {"var2": "val2"},
1423                    {"var3": ["foo", "bar"]},
1424                    {"var1": "bar", "var4": "foo"},
1425                ]
1426            }
1427        )
1428
1429        assert self.get()['status'] == 404, 'cookies array'
1430        self.cookie('var1=val123', 200)
1431        self.cookie('var2=val2', 200)
1432        self.cookie(' var2=val2 ', 200)
1433        self.cookie('var3=bar', 200)
1434        self.cookie('var3=bar;', 200)
1435        self.cookie('var1=bar', 404)
1436        self.cookie('var1=bar; var4=foo;', 200)
1437        self.cookie(['var1=bar', 'var4=foo'], 200)
1438
1439        assert 'success' in self.conf_delete(
1440            'routes/0/match/cookies/1'
1441        ), 'match cookies array configure 2'
1442
1443        self.cookie('var2=val2', 404)
1444        self.cookie('var3=foo', 200)
1445
1446    def test_routes_match_scheme(self):
1447        self.route_match({"scheme": "http"})
1448        self.route_match({"scheme": "https"})
1449        self.route_match({"scheme": "HtTp"})
1450        self.route_match({"scheme": "HtTpS"})
1451
1452    def test_routes_match_scheme_invalid(self):
1453        self.route_match_invalid({"scheme": ["http"]})
1454        self.route_match_invalid({"scheme": "ftp"})
1455        self.route_match_invalid({"scheme": "ws"})
1456        self.route_match_invalid({"scheme": "*"})
1457        self.route_match_invalid({"scheme": ""})
1458
1459    def test_routes_source_port(self):
1460        def sock_port():
1461            _, sock = self.http(b'', start=True, raw=True, no_recv=True)
1462            port = sock.getsockname()[1]
1463            return (sock, port)
1464
1465        sock, port = sock_port()
1466        sock2, port2 = sock_port()
1467
1468        self.route_match({"source": "127.0.0.1:" + str(port)})
1469        assert self.get(sock=sock)['status'] == 200, 'exact'
1470        assert self.get(sock=sock2)['status'] == 404, 'exact 2'
1471
1472        sock, port = sock_port()
1473        sock2, port2 = sock_port()
1474
1475        self.route_match({"source": "!127.0.0.1:" + str(port)})
1476        assert self.get(sock=sock)['status'] == 404, 'negative'
1477        assert self.get(sock=sock2)['status'] == 200, 'negative 2'
1478
1479        sock, port = sock_port()
1480        sock2, port2 = sock_port()
1481
1482        self.route_match({"source": ["*:" + str(port), "!127.0.0.1"]})
1483        assert self.get(sock=sock)['status'] == 404, 'negative 3'
1484        assert self.get(sock=sock2)['status'] == 404, 'negative 4'
1485
1486        sock, port = sock_port()
1487        sock2, port2 = sock_port()
1488
1489        self.route_match(
1490            {"source": "127.0.0.1:" + str(port) + "-" + str(port)}
1491        )
1492        assert self.get(sock=sock)['status'] == 200, 'range single'
1493        assert self.get(sock=sock2)['status'] == 404, 'range single 2'
1494
1495        socks = [
1496            sock_port(),
1497            sock_port(),
1498            sock_port(),
1499            sock_port(),
1500            sock_port(),
1501        ]
1502        socks.sort(key=lambda sock: sock[1])
1503
1504        self.route_match(
1505            {
1506                "source": "127.0.0.1:"
1507                + str(socks[1][1])  # second port number
1508                + "-"
1509                + str(socks[3][1])  # fourth port number
1510            }
1511        )
1512        assert self.get(sock=socks[0][0])['status'] == 404, 'range'
1513        assert self.get(sock=socks[1][0])['status'] == 200, 'range 2'
1514        assert self.get(sock=socks[2][0])['status'] == 200, 'range 3'
1515        assert self.get(sock=socks[3][0])['status'] == 200, 'range 4'
1516        assert self.get(sock=socks[4][0])['status'] == 404, 'range 5'
1517
1518        socks = [
1519            sock_port(),
1520            sock_port(),
1521            sock_port(),
1522        ]
1523        socks.sort(key=lambda sock: sock[1])
1524
1525        self.route_match(
1526            {
1527                "source": [
1528                    "127.0.0.1:" + str(socks[0][1]),
1529                    "127.0.0.1:" + str(socks[2][1]),
1530                ]
1531            }
1532        )
1533        assert self.get(sock=socks[0][0])['status'] == 200, 'array'
1534        assert self.get(sock=socks[1][0])['status'] == 404, 'array 2'
1535        assert self.get(sock=socks[2][0])['status'] == 200, 'array 3'
1536
1537    def test_routes_source_addr(self):
1538        assert 'success' in self.conf(
1539            {"*:7080": {"pass": "routes"}, "[::1]:7081": {"pass": "routes"},},
1540            'listeners',
1541        ), 'source listeners configure'
1542
1543        def get_ipv6():
1544            return self.get(sock_type='ipv6', port=7081)
1545
1546        self.route_match({"source": "127.0.0.1"})
1547        assert self.get()['status'] == 200, 'exact'
1548        assert get_ipv6()['status'] == 404, 'exact ipv6'
1549
1550        self.route_match({"source": ["127.0.0.1"]})
1551        assert self.get()['status'] == 200, 'exact 2'
1552        assert get_ipv6()['status'] == 404, 'exact 2 ipv6'
1553
1554        self.route_match({"source": "!127.0.0.1"})
1555        assert self.get()['status'] == 404, 'exact neg'
1556        assert get_ipv6()['status'] == 200, 'exact neg ipv6'
1557
1558        self.route_match({"source": "127.0.0.2"})
1559        assert self.get()['status'] == 404, 'exact 3'
1560        assert get_ipv6()['status'] == 404, 'exact 3 ipv6'
1561
1562        self.route_match({"source": "127.0.0.1-127.0.0.1"})
1563        assert self.get()['status'] == 200, 'range single'
1564        assert get_ipv6()['status'] == 404, 'range single ipv6'
1565
1566        self.route_match({"source": "127.0.0.2-127.0.0.2"})
1567        assert self.get()['status'] == 404, 'range single 2'
1568        assert get_ipv6()['status'] == 404, 'range single 2 ipv6'
1569
1570        self.route_match({"source": "127.0.0.2-127.0.0.3"})
1571        assert self.get()['status'] == 404, 'range'
1572        assert get_ipv6()['status'] == 404, 'range ipv6'
1573
1574        self.route_match({"source": "127.0.0.1-127.0.0.2"})
1575        assert self.get()['status'] == 200, 'range 2'
1576        assert get_ipv6()['status'] == 404, 'range 2 ipv6'
1577
1578        self.route_match({"source": "127.0.0.0-127.0.0.2"})
1579        assert self.get()['status'] == 200, 'range 3'
1580        assert get_ipv6()['status'] == 404, 'range 3 ipv6'
1581
1582        self.route_match({"source": "127.0.0.0-127.0.0.1"})
1583        assert self.get()['status'] == 200, 'range 4'
1584        assert get_ipv6()['status'] == 404, 'range 4 ipv6'
1585
1586        self.route_match({"source": "126.0.0.0-127.0.0.0"})
1587        assert self.get()['status'] == 404, 'range 5'
1588        assert get_ipv6()['status'] == 404, 'range 5 ipv6'
1589
1590        self.route_match({"source": "126.126.126.126-127.0.0.2"})
1591        assert self.get()['status'] == 200, 'range 6'
1592        assert get_ipv6()['status'] == 404, 'range 6 ipv6'
1593
1594    def test_routes_source_ipv6(self):
1595        assert 'success' in self.conf(
1596            {
1597                "[::1]:7080": {"pass": "routes"},
1598                "127.0.0.1:7081": {"pass": "routes"},
1599            },
1600            'listeners',
1601        ), 'source listeners configure'
1602
1603        self.route_match({"source": "::1"})
1604        assert self.get(sock_type='ipv6')['status'] == 200, 'exact'
1605        assert self.get(port=7081)['status'] == 404, 'exact ipv4'
1606
1607        self.route_match({"source": ["::1"]})
1608        assert self.get(sock_type='ipv6')['status'] == 200, 'exact 2'
1609        assert self.get(port=7081)['status'] == 404, 'exact 2 ipv4'
1610
1611        self.route_match({"source": "!::1"})
1612        assert self.get(sock_type='ipv6')['status'] == 404, 'exact neg'
1613        assert self.get(port=7081)['status'] == 200, 'exact neg ipv4'
1614
1615        self.route_match({"source": "::2"})
1616        assert self.get(sock_type='ipv6')['status'] == 404, 'exact 3'
1617        assert self.get(port=7081)['status'] == 404, 'exact 3 ipv4'
1618
1619        self.route_match({"source": "::1-::1"})
1620        assert self.get(sock_type='ipv6')['status'] == 200, 'range'
1621        assert self.get(port=7081)['status'] == 404, 'range ipv4'
1622
1623        self.route_match({"source": "::2-::2"})
1624        assert self.get(sock_type='ipv6')['status'] == 404, 'range 2'
1625        assert self.get(port=7081)['status'] == 404, 'range 2 ipv4'
1626
1627        self.route_match({"source": "::2-::3"})
1628        assert self.get(sock_type='ipv6')['status'] == 404, 'range 3'
1629        assert self.get(port=7081)['status'] == 404, 'range 3 ipv4'
1630
1631        self.route_match({"source": "::1-::2"})
1632        assert self.get(sock_type='ipv6')['status'] == 200, 'range 4'
1633        assert self.get(port=7081)['status'] == 404, 'range 4 ipv4'
1634
1635        self.route_match({"source": "::0-::2"})
1636        assert self.get(sock_type='ipv6')['status'] == 200, 'range 5'
1637        assert self.get(port=7081)['status'] == 404, 'range 5 ipv4'
1638
1639        self.route_match({"source": "::0-::1"})
1640        assert self.get(sock_type='ipv6')['status'] == 200, 'range 6'
1641        assert self.get(port=7081)['status'] == 404, 'range 6 ipv4'
1642
1643    def test_routes_source_cidr(self):
1644        assert 'success' in self.conf(
1645            {"*:7080": {"pass": "routes"}, "[::1]:7081": {"pass": "routes"},},
1646            'listeners',
1647        ), 'source listeners configure'
1648
1649        def get_ipv6():
1650            return self.get(sock_type='ipv6', port=7081)
1651
1652        self.route_match({"source": "127.0.0.1/32"})
1653        assert self.get()['status'] == 200, '32'
1654        assert get_ipv6()['status'] == 404, '32 ipv6'
1655
1656        self.route_match({"source": "127.0.0.0/32"})
1657        assert self.get()['status'] == 404, '32 2'
1658        assert get_ipv6()['status'] == 404, '32 2 ipv6'
1659
1660        self.route_match({"source": "127.0.0.0/31"})
1661        assert self.get()['status'] == 200, '31'
1662        assert get_ipv6()['status'] == 404, '31 ipv6'
1663
1664        self.route_match({"source": "0.0.0.0/1"})
1665        assert self.get()['status'] == 200, '1'
1666        assert get_ipv6()['status'] == 404, '1 ipv6'
1667
1668        self.route_match({"source": "0.0.0.0/0"})
1669        assert self.get()['status'] == 200, '0'
1670        assert get_ipv6()['status'] == 404, '0 ipv6'
1671
1672    def test_routes_source_cidr_ipv6(self):
1673        assert 'success' in self.conf(
1674            {
1675                "[::1]:7080": {"pass": "routes"},
1676                "127.0.0.1:7081": {"pass": "routes"},
1677            },
1678            'listeners',
1679        ), 'source listeners configure'
1680
1681        self.route_match({"source": "::1/128"})
1682        assert self.get(sock_type='ipv6')['status'] == 200, '128'
1683        assert self.get(port=7081)['status'] == 404, '128 ipv4'
1684
1685        self.route_match({"source": "::0/128"})
1686        assert self.get(sock_type='ipv6')['status'] == 404, '128 2'
1687        assert self.get(port=7081)['status'] == 404, '128 ipv4'
1688
1689        self.route_match({"source": "::0/127"})
1690        assert self.get(sock_type='ipv6')['status'] == 200, '127'
1691        assert self.get(port=7081)['status'] == 404, '127 ipv4'
1692
1693        self.route_match({"source": "::0/32"})
1694        assert self.get(sock_type='ipv6')['status'] == 200, '32'
1695        assert self.get(port=7081)['status'] == 404, '32 ipv4'
1696
1697        self.route_match({"source": "::0/1"})
1698        assert self.get(sock_type='ipv6')['status'] == 200, '1'
1699        assert self.get(port=7081)['status'] == 404, '1 ipv4'
1700
1701        self.route_match({"source": "::/0"})
1702        assert self.get(sock_type='ipv6')['status'] == 200, '0'
1703        assert self.get(port=7081)['status'] == 404, '0 ipv4'
1704
1705    def test_routes_source_unix(self, temp_dir):
1706        addr = temp_dir + '/sock'
1707
1708        assert 'success' in self.conf(
1709            {"unix:" + addr: {"pass": "routes"}}, 'listeners'
1710        ), 'source listeners configure'
1711
1712        self.route_match({"source": "!0.0.0.0/0"})
1713        assert (
1714            self.get(sock_type='unix', addr=addr)['status'] == 200
1715        ), 'unix ipv4'
1716
1717        self.route_match({"source": "!::/0"})
1718        assert (
1719            self.get(sock_type='unix', addr=addr)['status'] == 200
1720        ), 'unix ipv6'
1721
1722    def test_routes_match_source(self):
1723        self.route_match({"source": "::"})
1724        self.route_match(
1725            {
1726                "source": [
1727                    "127.0.0.1",
1728                    "192.168.0.10:8080",
1729                    "192.168.0.11:8080-8090",
1730                ]
1731            }
1732        )
1733        self.route_match(
1734            {
1735                "source": [
1736                    "10.0.0.0/8",
1737                    "10.0.0.0/7:1000",
1738                    "10.0.0.0/32:8080-8090",
1739                ]
1740            }
1741        )
1742        self.route_match(
1743            {
1744                "source": [
1745                    "10.0.0.0-10.0.0.1",
1746                    "10.0.0.0-11.0.0.0:1000",
1747                    "127.0.0.0-127.0.0.255:8080-8090",
1748                ]
1749            }
1750        )
1751        self.route_match(
1752            {"source": ["2001::", "[2002::]:8000", "[2003::]:8080-8090"]}
1753        )
1754        self.route_match(
1755            {
1756                "source": [
1757                    "2001::-200f:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
1758                    "[fe08::-feff::]:8000",
1759                    "[fff0::-fff0::10]:8080-8090",
1760                ]
1761            }
1762        )
1763        self.route_match(
1764            {
1765                "source": [
1766                    "2001::/16",
1767                    "[0ff::/64]:8000",
1768                    "[fff0:abcd:ffff:ffff:ffff::/128]:8080-8090",
1769                ]
1770            }
1771        )
1772        self.route_match({"source": "*:0-65535"})
1773        assert self.get()['status'] == 200, 'source any'
1774
1775    def test_routes_match_source_invalid(self):
1776        self.route_match_invalid({"source": "127"})
1777        self.route_match_invalid({"source": "256.0.0.1"})
1778        self.route_match_invalid({"source": "127.0.0."})
1779        self.route_match_invalid({"source": " 127.0.0.1"})
1780        self.route_match_invalid({"source": "127.0.0.1:"})
1781        self.route_match_invalid({"source": "127.0.0.1/"})
1782        self.route_match_invalid({"source": "11.0.0.0/33"})
1783        self.route_match_invalid({"source": "11.0.0.0/65536"})
1784        self.route_match_invalid({"source": "11.0.0.0-10.0.0.0"})
1785        self.route_match_invalid({"source": "11.0.0.0:3000-2000"})
1786        self.route_match_invalid({"source": ["11.0.0.0:3000-2000"]})
1787        self.route_match_invalid({"source": "[2001::]:3000-2000"})
1788        self.route_match_invalid({"source": "2001::-2000::"})
1789        self.route_match_invalid({"source": "2001::/129"})
1790        self.route_match_invalid({"source": "::FFFFF"})
1791        self.route_match_invalid({"source": "[::1]:"})
1792        self.route_match_invalid({"source": "[:::]:7080"})
1793        self.route_match_invalid({"source": "*:"})
1794        self.route_match_invalid({"source": "*:1-a"})
1795        self.route_match_invalid({"source": "*:65536"})
1796
1797    def test_routes_match_source_none(self):
1798        self.route_match({"source": []})
1799        assert self.get()['status'] == 404, 'source none'
1800
1801    def test_routes_match_destination(self):
1802        assert 'success' in self.conf(
1803            {"*:7080": {"pass": "routes"}, "*:7081": {"pass": "routes"}},
1804            'listeners',
1805        ), 'listeners configure'
1806
1807        self.route_match({"destination": "*:7080"})
1808        assert self.get()['status'] == 200, 'dest'
1809        assert self.get(port=7081)['status'] == 404, 'dest 2'
1810
1811        self.route_match({"destination": ["127.0.0.1:7080"]})
1812        assert self.get()['status'] == 200, 'dest 3'
1813        assert self.get(port=7081)['status'] == 404, 'dest 4'
1814
1815        self.route_match({"destination": "!*:7080"})
1816        assert self.get()['status'] == 404, 'dest neg'
1817        assert self.get(port=7081)['status'] == 200, 'dest neg 2'
1818
1819        self.route_match({"destination": ['!*:7080', '!*:7081']})
1820        assert self.get()['status'] == 404, 'dest neg 3'
1821        assert self.get(port=7081)['status'] == 404, 'dest neg 4'
1822
1823        self.route_match({"destination": ['!*:7081', '!*:7082']})
1824        assert self.get()['status'] == 200, 'dest neg 5'
1825
1826        self.route_match({"destination": ['*:7080', '!*:7080']})
1827        assert self.get()['status'] == 404, 'dest neg 6'
1828
1829        self.route_match(
1830            {"destination": ['127.0.0.1:7080', '*:7081', '!*:7080']}
1831        )
1832        assert self.get()['status'] == 404, 'dest neg 7'
1833        assert self.get(port=7081)['status'] == 200, 'dest neg 8'
1834
1835        self.route_match({"destination": ['!*:7081', '!*:7082', '*:7083']})
1836        assert self.get()['status'] == 404, 'dest neg 9'
1837
1838        self.route_match(
1839            {"destination": ['*:7081', '!127.0.0.1:7080', '*:7080']}
1840        )
1841        assert self.get()['status'] == 404, 'dest neg 10'
1842        assert self.get(port=7081)['status'] == 200, 'dest neg 11'
1843
1844        assert 'success' in self.conf_delete(
1845            'routes/0/match/destination/0'
1846        ), 'remove destination rule'
1847        assert self.get()['status'] == 404, 'dest neg 12'
1848        assert self.get(port=7081)['status'] == 404, 'dest neg 13'
1849
1850        assert 'success' in self.conf_delete(
1851            'routes/0/match/destination/0'
1852        ), 'remove destination rule 2'
1853        assert self.get()['status'] == 200, 'dest neg 14'
1854        assert self.get(port=7081)['status'] == 404, 'dest neg 15'
1855
1856        assert 'success' in self.conf_post(
1857            "\"!127.0.0.1\"", 'routes/0/match/destination'
1858        ), 'add destination rule'
1859        assert self.get()['status'] == 404, 'dest neg 16'
1860        assert self.get(port=7081)['status'] == 404, 'dest neg 17'
1861
1862    def test_routes_match_destination_proxy(self):
1863        assert 'success' in self.conf(
1864            {
1865                "listeners": {
1866                    "*:7080": {"pass": "routes/first"},
1867                    "*:7081": {"pass": "routes/second"},
1868                },
1869                "routes": {
1870                    "first": [{"action": {"proxy": "http://127.0.0.1:7081"}}],
1871                    "second": [
1872                        {
1873                            "match": {"destination": ["127.0.0.1:7081"]},
1874                            "action": {"return": 200},
1875                        }
1876                    ],
1877                },
1878                "applications": {},
1879            }
1880        ), 'proxy configure'
1881
1882        assert self.get()['status'] == 200, 'proxy'
1883