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