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