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