+68
-7
lines changedFilter options
+68
-7
lines changed Original file line number Diff line number Diff line change
@@ -203,6 +203,9 @@
203
203
# maximal amount of data to read at one time in _safe_read
204
204
MAXAMOUNT = 1048576
205
205
206
+
# maximal line length when calling readline().
207
+
_MAXLINE = 65536
208
+
206
209
class HTTPMessage(email.message.Message):
207
210
# XXX The only usage of this method is in
208
211
# http.server.CGIHTTPRequestHandler. Maybe move the code there so
@@ -245,7 +248,9 @@ def parse_headers(fp, _class=HTTPMessage):
245
248
"""
246
249
headers = []
247
250
while True:
248
-
line = fp.readline()
251
+
line = fp.readline(_MAXLINE + 1)
252
+
if len(line) > _MAXLINE:
253
+
raise LineTooLong("header line")
249
254
headers.append(line)
250
255
if line in (b'\r\n', b'\n', b''):
251
256
break
@@ -349,7 +354,10 @@ def begin(self):
349
354
break
350
355
# skip the header from the 100 response
351
356
while True:
352
-
skip = self.fp.readline().strip()
357
+
skip = self.fp.readline(_MAXLINE + 1)
358
+
if len(skip) > _MAXLINE:
359
+
raise LineTooLong("header line")
360
+
skip = skip.strip()
353
361
if not skip:
354
362
break
355
363
if self.debuglevel > 0:
@@ -525,7 +533,9 @@ def _read_chunked(self, amt):
525
533
value = []
526
534
while True:
527
535
if chunk_left is None:
528
-
line = self.fp.readline()
536
+
line = self.fp.readline(_MAXLINE + 1)
537
+
if len(line) > _MAXLINE:
538
+
raise LineTooLong("chunk size")
529
539
i = line.find(b";")
530
540
if i >= 0:
531
541
line = line[:i] # strip chunk-extensions
@@ -560,7 +570,9 @@ def _read_chunked(self, amt):
560
570
# read and discard trailer up to the CRLF terminator
561
571
### note: we shouldn't have any trailers!
562
572
while True:
563
-
line = self.fp.readline()
573
+
line = self.fp.readline(_MAXLINE + 1)
574
+
if len(line) > _MAXLINE:
575
+
raise LineTooLong("trailer line")
564
576
if not line:
565
577
# a vanishingly small number of sites EOF without
566
578
# sending the trailer
@@ -703,7 +715,9 @@ def _tunnel(self):
703
715
raise socket.error("Tunnel connection failed: %d %s" % (code,
704
716
message.strip()))
705
717
while True:
706
-
line = response.fp.readline()
718
+
line = response.fp.readline(_MAXLINE + 1)
719
+
if len(line) > _MAXLINE:
720
+
raise LineTooLong("header line")
707
721
if line == b'\r\n':
708
722
break
709
723
@@ -1133,6 +1147,11 @@ def __init__(self, line):
1133
1147
self.args = line,
1134
1148
self.line = line
1135
1149
1150
+
class LineTooLong(HTTPException):
1151
+
def __init__(self, line_type):
1152
+
HTTPException.__init__(self, "got more than %d bytes when reading %s"
1153
+
% (_MAXLINE, line_type))
1154
+
1136
1155
# for backwards compatibility
1137
1156
error = HTTPException
1138
1157
Original file line number Diff line number Diff line change
@@ -314,8 +314,12 @@ def parse_request(self):
314
314
self.command, self.path, self.request_version = command, path, version
315
315
316
316
# Examine the headers and look for a Connection directive.
317
-
self.headers = http.client.parse_headers(self.rfile,
318
-
_class=self.MessageClass)
317
+
try:
318
+
self.headers = http.client.parse_headers(self.rfile,
319
+
_class=self.MessageClass)
320
+
except http.client.LineTooLong:
321
+
self.send_error(400, "Line too long")
322
+
return False
319
323
320
324
conntype = self.headers.get('Connection', "")
321
325
if conntype.lower() == 'close':
Original file line number Diff line number Diff line change
@@ -303,6 +303,34 @@ def test_epipe(self):
303
303
self.assertEqual("Basic realm=\"example\"",
304
304
resp.getheader("www-authenticate"))
305
305
306
+
# Test lines overflowing the max line size (_MAXLINE in http.client)
307
+
308
+
def test_overflowing_status_line(self):
309
+
self.skipTest("disabled for HTTP 0.9 support")
310
+
body = "HTTP/1.1 200 Ok" + "k" * 65536 + "\r\n"
311
+
resp = client.HTTPResponse(FakeSocket(body))
312
+
self.assertRaises((client.LineTooLong, client.BadStatusLine), resp.begin)
313
+
314
+
def test_overflowing_header_line(self):
315
+
body = (
316
+
'HTTP/1.1 200 OK\r\n'
317
+
'X-Foo: bar' + 'r' * 65536 + '\r\n\r\n'
318
+
)
319
+
resp = client.HTTPResponse(FakeSocket(body))
320
+
self.assertRaises(client.LineTooLong, resp.begin)
321
+
322
+
def test_overflowing_chunked_line(self):
323
+
body = (
324
+
'HTTP/1.1 200 OK\r\n'
325
+
'Transfer-Encoding: chunked\r\n\r\n'
326
+
+ '0' * 65536 + 'a\r\n'
327
+
'hello world\r\n'
328
+
'0\r\n'
329
+
)
330
+
resp = client.HTTPResponse(FakeSocket(body))
331
+
resp.begin()
332
+
self.assertRaises(client.LineTooLong, resp.read)
333
+
306
334
class OfflineTest(TestCase):
307
335
def test_responses(self):
308
336
self.assertEqual(client.responses[client.NOT_FOUND], "Not Found")
Original file line number Diff line number Diff line change
@@ -144,6 +144,13 @@ def test_request_length(self):
144
144
self.assertEqual(result[0], b'HTTP/1.1 414 Request-URI Too Long\r\n')
145
145
self.assertFalse(self.handler.get_called)
146
146
147
+
def test_header_length(self):
148
+
# Issue #6791: same for headers
149
+
result = self.send_typical_request(
150
+
b'GET / HTTP/1.1\r\nX-Foo: bar' + b'r' * 65537 + b'\r\n\r\n')
151
+
self.assertEqual(result[0], b'HTTP/1.1 400 Line too long\r\n')
152
+
self.assertFalse(self.handler.get_called)
153
+
147
154
148
155
class BaseHTTPServerTestCase(BaseTestCase):
149
156
class request_handler(NoLogRequestHandler, BaseHTTPRequestHandler):
Original file line number Diff line number Diff line change
@@ -24,6 +24,9 @@ Core and Builtins
24
24
Library
25
25
-------
26
26
27
+
- Issue #6791: Limit header line length (to 65535 bytes) in http.client
28
+
and http.server, to avoid denial of services from the other party.
29
+
27
30
- Issue #10404: Use ctl-button-1 on OSX for the context menu in Idle.
28
31
29
32
- Issue #4188: Avoid creating dummy thread objects when logging operations
You can’t perform that action at this time.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4