1import pytest 2from unit.applications.lang.python import TestApplicationPython 3 4 5class TestHTTPHeader(TestApplicationPython): 6 prerequisites = {'modules': {'python': 'any'}} 7 8 def test_http_header_value_leading_sp(self): 9 self.load('custom_header') 10 11 resp = self.get( 12 headers={ 13 'Host': 'localhost', 14 'Custom-Header': ' ,', 15 'Connection': 'close', 16 } 17 ) 18 19 assert resp['status'] == 200, 'value leading sp status' 20 assert ( 21 resp['headers']['Custom-Header'] == ',' 22 ), 'value leading sp custom header' 23 24 def test_http_header_value_leading_htab(self): 25 self.load('custom_header') 26 27 resp = self.get( 28 headers={ 29 'Host': 'localhost', 30 'Custom-Header': '\t,', 31 'Connection': 'close', 32 } 33 ) 34 35 assert resp['status'] == 200, 'value leading htab status' 36 assert ( 37 resp['headers']['Custom-Header'] == ',' 38 ), 'value leading htab custom header' 39 40 def test_http_header_value_trailing_sp(self): 41 self.load('custom_header') 42 43 resp = self.get( 44 headers={ 45 'Host': 'localhost', 46 'Custom-Header': ', ', 47 'Connection': 'close', 48 } 49 ) 50 51 assert resp['status'] == 200, 'value trailing sp status' 52 assert ( 53 resp['headers']['Custom-Header'] == ',' 54 ), 'value trailing sp custom header' 55 56 def test_http_header_value_trailing_htab(self): 57 self.load('custom_header') 58 59 resp = self.get( 60 headers={ 61 'Host': 'localhost', 62 'Custom-Header': ',\t', 63 'Connection': 'close', 64 } 65 ) 66 67 assert resp['status'] == 200, 'value trailing htab status' 68 assert ( 69 resp['headers']['Custom-Header'] == ',' 70 ), 'value trailing htab custom header' 71 72 def test_http_header_value_both_sp(self): 73 self.load('custom_header') 74 75 resp = self.get( 76 headers={ 77 'Host': 'localhost', 78 'Custom-Header': ' , ', 79 'Connection': 'close', 80 } 81 ) 82 83 assert resp['status'] == 200, 'value both sp status' 84 assert ( 85 resp['headers']['Custom-Header'] == ',' 86 ), 'value both sp custom header' 87 88 def test_http_header_value_both_htab(self): 89 self.load('custom_header') 90 91 resp = self.get( 92 headers={ 93 'Host': 'localhost', 94 'Custom-Header': '\t,\t', 95 'Connection': 'close', 96 } 97 ) 98 99 assert resp['status'] == 200, 'value both htab status' 100 assert ( 101 resp['headers']['Custom-Header'] == ',' 102 ), 'value both htab custom header' 103 104 def test_http_header_value_chars(self): 105 self.load('custom_header') 106 107 resp = self.get( 108 headers={ 109 'Host': 'localhost', 110 'Custom-Header': r'(),/:;<=>?@[\]{}\t !#$%&\'*+-.^_`|~', 111 'Connection': 'close', 112 } 113 ) 114 115 assert resp['status'] == 200, 'value chars status' 116 assert ( 117 resp['headers']['Custom-Header'] 118 == r'(),/:;<=>?@[\]{}\t !#$%&\'*+-.^_`|~' 119 ), 'value chars custom header' 120 121 def test_http_header_value_chars_edge(self): 122 self.load('custom_header') 123 124 resp = self.http( 125 b"""GET / HTTP/1.1 126Host: localhost 127Custom-Header: \x20\xFF 128Connection: close 129 130""", 131 raw=True, 132 encoding='latin1', 133 ) 134 135 assert resp['status'] == 200, 'value chars edge status' 136 assert resp['headers']['Custom-Header'] == '\xFF', 'value chars edge' 137 138 def test_http_header_value_chars_below(self): 139 self.load('custom_header') 140 141 resp = self.http( 142 b"""GET / HTTP/1.1 143Host: localhost 144Custom-Header: \x1F 145Connection: close 146 147""", 148 raw=True, 149 ) 150 151 assert resp['status'] == 400, 'value chars below' 152 153 def test_http_header_field_leading_sp(self): 154 self.load('empty') 155 156 assert ( 157 self.get( 158 headers={ 159 'Host': 'localhost', 160 ' Custom-Header': 'blah', 161 'Connection': 'close', 162 } 163 )['status'] 164 == 400 165 ), 'field leading sp' 166 167 def test_http_header_field_leading_htab(self): 168 self.load('empty') 169 170 assert ( 171 self.get( 172 headers={ 173 'Host': 'localhost', 174 '\tCustom-Header': 'blah', 175 'Connection': 'close', 176 } 177 )['status'] 178 == 400 179 ), 'field leading htab' 180 181 def test_http_header_field_trailing_sp(self): 182 self.load('empty') 183 184 assert ( 185 self.get( 186 headers={ 187 'Host': 'localhost', 188 'Custom-Header ': 'blah', 189 'Connection': 'close', 190 } 191 )['status'] 192 == 400 193 ), 'field trailing sp' 194 195 def test_http_header_field_trailing_htab(self): 196 self.load('empty') 197 198 assert ( 199 self.get( 200 headers={ 201 'Host': 'localhost', 202 'Custom-Header\t': 'blah', 203 'Connection': 'close', 204 } 205 )['status'] 206 == 400 207 ), 'field trailing htab' 208 209 def test_http_header_content_length_big(self): 210 self.load('empty') 211 212 assert ( 213 self.post( 214 headers={ 215 'Host': 'localhost', 216 'Content-Length': str(2 ** 64), 217 'Connection': 'close', 218 }, 219 body='X' * 1000, 220 )['status'] 221 == 400 222 ), 'Content-Length big' 223 224 def test_http_header_content_length_negative(self): 225 self.load('empty') 226 227 assert ( 228 self.post( 229 headers={ 230 'Host': 'localhost', 231 'Content-Length': '-100', 232 'Connection': 'close', 233 }, 234 body='X' * 1000, 235 )['status'] 236 == 400 237 ), 'Content-Length negative' 238 239 def test_http_header_content_length_text(self): 240 self.load('empty') 241 242 assert ( 243 self.post( 244 headers={ 245 'Host': 'localhost', 246 'Content-Length': 'blah', 247 'Connection': 'close', 248 }, 249 body='X' * 1000, 250 )['status'] 251 == 400 252 ), 'Content-Length text' 253 254 def test_http_header_content_length_multiple_values(self): 255 self.load('empty') 256 257 assert ( 258 self.post( 259 headers={ 260 'Host': 'localhost', 261 'Content-Length': '41, 42', 262 'Connection': 'close', 263 }, 264 body='X' * 1000, 265 )['status'] 266 == 400 267 ), 'Content-Length multiple value' 268 269 def test_http_header_content_length_multiple_fields(self): 270 self.load('empty') 271 272 assert ( 273 self.post( 274 headers={ 275 'Host': 'localhost', 276 'Content-Length': ['41', '42'], 277 'Connection': 'close', 278 }, 279 body='X' * 1000, 280 )['status'] 281 == 400 282 ), 'Content-Length multiple fields' 283 284 @pytest.mark.skip('not yet') 285 def test_http_header_host_absent(self): 286 self.load('host') 287 288 resp = self.get(headers={'Connection': 'close'}) 289 290 assert resp['status'] == 400, 'Host absent status' 291 292 def test_http_header_host_empty(self): 293 self.load('host') 294 295 resp = self.get(headers={'Host': '', 'Connection': 'close'}) 296 297 assert resp['status'] == 200, 'Host empty status' 298 assert resp['headers']['X-Server-Name'] != '', 'Host empty SERVER_NAME' 299 300 def test_http_header_host_big(self): 301 self.load('empty') 302 303 assert ( 304 self.get(headers={'Host': 'X' * 10000, 'Connection': 'close'})[ 305 'status' 306 ] 307 == 431 308 ), 'Host big' 309 310 def test_http_header_host_port(self): 311 self.load('host') 312 313 resp = self.get( 314 headers={'Host': 'exmaple.com:7080', 'Connection': 'close'} 315 ) 316 317 assert resp['status'] == 200, 'Host port status' 318 assert ( 319 resp['headers']['X-Server-Name'] == 'exmaple.com' 320 ), 'Host port SERVER_NAME' 321 assert ( 322 resp['headers']['X-Http-Host'] == 'exmaple.com:7080' 323 ), 'Host port HTTP_HOST' 324 325 def test_http_header_host_port_empty(self): 326 self.load('host') 327 328 resp = self.get( 329 headers={'Host': 'exmaple.com:', 'Connection': 'close'} 330 ) 331 332 assert resp['status'] == 200, 'Host port empty status' 333 assert ( 334 resp['headers']['X-Server-Name'] == 'exmaple.com' 335 ), 'Host port empty SERVER_NAME' 336 assert ( 337 resp['headers']['X-Http-Host'] == 'exmaple.com:' 338 ), 'Host port empty HTTP_HOST' 339 340 def test_http_header_host_literal(self): 341 self.load('host') 342 343 resp = self.get(headers={'Host': '127.0.0.1', 'Connection': 'close'}) 344 345 assert resp['status'] == 200, 'Host literal status' 346 assert ( 347 resp['headers']['X-Server-Name'] == '127.0.0.1' 348 ), 'Host literal SERVER_NAME' 349 350 def test_http_header_host_literal_ipv6(self): 351 self.load('host') 352 353 resp = self.get(headers={'Host': '[::1]:7080', 'Connection': 'close'}) 354 355 assert resp['status'] == 200, 'Host literal ipv6 status' 356 assert ( 357 resp['headers']['X-Server-Name'] == '[::1]' 358 ), 'Host literal ipv6 SERVER_NAME' 359 assert ( 360 resp['headers']['X-Http-Host'] == '[::1]:7080' 361 ), 'Host literal ipv6 HTTP_HOST' 362 363 def test_http_header_host_trailing_period(self): 364 self.load('host') 365 366 resp = self.get(headers={'Host': '127.0.0.1.', 'Connection': 'close'}) 367 368 assert resp['status'] == 200, 'Host trailing period status' 369 assert ( 370 resp['headers']['X-Server-Name'] == '127.0.0.1' 371 ), 'Host trailing period SERVER_NAME' 372 assert ( 373 resp['headers']['X-Http-Host'] == '127.0.0.1.' 374 ), 'Host trailing period HTTP_HOST' 375 376 def test_http_header_host_trailing_period_2(self): 377 self.load('host') 378 379 resp = self.get( 380 headers={'Host': 'EXAMPLE.COM.', 'Connection': 'close'} 381 ) 382 383 assert resp['status'] == 200, 'Host trailing period 2 status' 384 assert ( 385 resp['headers']['X-Server-Name'] == 'example.com' 386 ), 'Host trailing period 2 SERVER_NAME' 387 assert ( 388 resp['headers']['X-Http-Host'] == 'EXAMPLE.COM.' 389 ), 'Host trailing period 2 HTTP_HOST' 390 391 def test_http_header_host_case_insensitive(self): 392 self.load('host') 393 394 resp = self.get(headers={'Host': 'EXAMPLE.COM', 'Connection': 'close'}) 395 396 assert resp['status'] == 200, 'Host case insensitive' 397 assert ( 398 resp['headers']['X-Server-Name'] == 'example.com' 399 ), 'Host case insensitive SERVER_NAME' 400 401 def test_http_header_host_double_dot(self): 402 self.load('empty') 403 404 assert ( 405 self.get(headers={'Host': '127.0.0..1', 'Connection': 'close'})[ 406 'status' 407 ] 408 == 400 409 ), 'Host double dot' 410 411 def test_http_header_host_slash(self): 412 self.load('empty') 413 414 assert ( 415 self.get(headers={'Host': '/localhost', 'Connection': 'close'})[ 416 'status' 417 ] 418 == 400 419 ), 'Host slash' 420 421 def test_http_header_host_multiple_fields(self): 422 self.load('empty') 423 424 assert ( 425 self.get( 426 headers={ 427 'Host': ['localhost', 'example.com'], 428 'Connection': 'close', 429 } 430 )['status'] 431 == 400 432 ), 'Host multiple fields' 433 434 def test_http_discard_unsafe_fields(self): 435 self.load('header_fields') 436 437 def check_status(header): 438 resp = self.get( 439 headers={ 440 'Host': 'localhost', 441 header: 'blah', 442 'Connection': 'close', 443 } 444 ) 445 446 assert resp['status'] == 200 447 return resp 448 449 resp = check_status("!Custom-Header") 450 assert 'CUSTOM' not in resp['headers']['All-Headers'] 451 452 resp = check_status("Custom_Header") 453 assert 'CUSTOM' not in resp['headers']['All-Headers'] 454 455 assert 'success' in self.conf( 456 {'http': {'discard_unsafe_fields': False}}, 'settings', 457 ) 458 459 resp = check_status("!#$%&'*+.^`|~Custom_Header") 460 assert 'CUSTOM' in resp['headers']['All-Headers'] 461 462 assert 'success' in self.conf( 463 {'http': {'discard_unsafe_fields': True}}, 'settings', 464 ) 465 466 resp = check_status("!Custom-Header") 467 assert 'CUSTOM' not in resp['headers']['All-Headers'] 468 469 resp = check_status("Custom_Header") 470 assert 'CUSTOM' not in resp['headers']['All-Headers'] 471