xref: /unit/test/test_return.py (revision 1596:b7e2d4d92624)
1import re
2
3from unit.applications.proto import TestApplicationProto
4
5
6class TestReturn(TestApplicationProto):
7    prerequisites = {}
8
9    def setup_method(self):
10        super().setup_method()
11
12        self._load_conf(
13            {
14                "listeners": {"*:7080": {"pass": "routes"}},
15                "routes": [{"action": {"return": 200}}],
16                "applications": {},
17            }
18        )
19
20    def get_resps_sc(self, req=10):
21        to_send = b"""GET / HTTP/1.1
22Host: localhost
23
24""" * (
25            req - 1
26        )
27
28        to_send += b"""GET / HTTP/1.1
29Host: localhost
30Connection: close
31
32"""
33
34        return self.http(to_send, raw_resp=True, raw=True)
35
36    def test_return(self):
37        resp = self.get()
38        assert resp['status'] == 200
39        assert 'Server' in resp['headers']
40        assert 'Date' in resp['headers']
41        assert resp['headers']['Content-Length'] == '0'
42        assert resp['headers']['Connection'] == 'close'
43        assert resp['body'] == '', 'body'
44
45        resp = self.post(body='blah')
46        assert resp['status'] == 200
47        assert resp['body'] == '', 'body'
48
49        resp = self.get_resps_sc()
50        assert len(re.findall('200 OK', resp)) == 10
51        assert len(re.findall('Connection:', resp)) == 1
52        assert len(re.findall('Connection: close', resp)) == 1
53
54        resp = self.get(http_10=True)
55        assert resp['status'] == 200
56        assert 'Server' in resp['headers']
57        assert 'Date' in resp['headers']
58        assert resp['headers']['Content-Length'] == '0'
59        assert 'Connection' not in resp['headers']
60        assert resp['body'] == '', 'body'
61
62    def test_return_update(self):
63        assert 'success' in self.conf('0', 'routes/0/action/return')
64
65        resp = self.get()
66        assert resp['status'] == 0
67        assert resp['body'] == ''
68
69        assert 'success' in self.conf('404', 'routes/0/action/return')
70
71        resp = self.get()
72        assert resp['status'] == 404
73        assert resp['body'] != ''
74
75        assert 'success' in self.conf('598', 'routes/0/action/return')
76
77        resp = self.get()
78        assert resp['status'] == 598
79        assert resp['body'] != ''
80
81        assert 'success' in self.conf('999', 'routes/0/action/return')
82
83        resp = self.get()
84        assert resp['status'] == 999
85        assert resp['body'] == ''
86
87    def test_return_location(self):
88        reserved = ":/?#[]@!$&'()*+,;="
89        unreserved = (
90            "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
91            "0123456789-._~"
92        )
93        unsafe = " \"%<>\\^`{|}"
94        unsafe_enc = "%20%22%25%3C%3E%5C%5E%60%7B%7C%7D"
95
96        def check_location(location, expect=None):
97            if expect is None:
98                expect = location
99
100            assert 'success' in self.conf(
101                {"return": 301, "location": location}, 'routes/0/action'
102            ), 'configure location'
103
104            assert self.get()['headers']['Location'] == expect
105
106        # FAIL: can't specify empty header value.
107        # check_location("")
108
109        check_location(reserved)
110
111        # After first "?" all other "?" encoded.
112        check_location("/?" + reserved, "/?:/%3F#[]@!$&'()*+,;=")
113        check_location("???", "?%3F%3F")
114
115        # After first "#" all other "?" or "#" encoded.
116        check_location("/#" + reserved, "/#:/%3F%23[]@!$&'()*+,;=")
117        check_location("##?#?", "#%23%3F%23%3F")
118
119        # After first "?" next "#" not encoded.
120        check_location("/?#" + reserved, "/?#:/%3F%23[]@!$&'()*+,;=")
121        check_location("??##", "?%3F#%23")
122        check_location("/?##?", "/?#%23%3F")
123
124        # Unreserved never encoded.
125        check_location(unreserved)
126        check_location("/" + unreserved + "?" + unreserved + "#" + unreserved)
127
128        # Unsafe always encoded.
129        check_location(unsafe, unsafe_enc)
130        check_location("?" + unsafe, "?" + unsafe_enc)
131        check_location("#" + unsafe, "#" + unsafe_enc)
132
133        # %00-%20 and %7F-%FF always encoded.
134        check_location(u"\u0000\u0018\u001F\u0020\u0021", "%00%18%1F%20!")
135        check_location(u"\u007F\u0080н\u20BD", "%7F%C2%80%D0%BD%E2%82%BD")
136
137        # Encoded string detection.  If at least one char need to be encoded
138        # then whole string will be encoded.
139        check_location("%20")
140        check_location("/%20?%20#%20")
141        check_location(" %20", "%20%2520")
142        check_location("%20 ", "%2520%20")
143        check_location("/%20?%20#%20 ", "/%2520?%2520#%2520%20")
144
145    def test_return_location_edit(self):
146        assert 'success' in self.conf(
147            {"return": 302, "location": "blah"}, 'routes/0/action'
148        ), 'configure init location'
149        assert self.get()['headers']['Location'] == 'blah'
150
151        assert 'success' in self.conf_delete(
152            'routes/0/action/location'
153        ), 'location delete'
154        assert 'Location' not in self.get()['headers']
155
156        assert 'success' in self.conf(
157            '"blah"', 'routes/0/action/location'
158        ), 'location restore'
159        assert self.get()['headers']['Location'] == 'blah'
160
161        assert 'error' in self.conf_post(
162            '"blah"', 'routes/0/action/location'
163        ), 'location method not allowed'
164        assert self.get()['headers']['Location'] == 'blah'
165
166    def test_return_invalid(self):
167        def check_error(conf):
168            assert 'error' in self.conf(conf, 'routes/0/action')
169
170        check_error({"return": "200"})
171        check_error({"return": []})
172        check_error({"return": 80.1})
173        check_error({"return": 1000})
174        check_error({"return": -1})
175        check_error({"return": 200, "share": "/blah"})
176
177        assert 'error' in self.conf(
178            '001', 'routes/0/action/return'
179        ), 'leading zero'
180
181        check_error({"return": 301, "location": 0})
182        check_error({"return": 301, "location": []})
183