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