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