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