+67
-8
lines changedFilter options
+67
-8
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
@@ -299,7 +304,9 @@ def __init__(self, sock, debuglevel=0, strict=_strict_sentinel, method=None, url
299
304
self.will_close = _UNKNOWN # conn will close at end of response
300
305
301
306
def _read_status(self):
302
-
line = str(self.fp.readline(), "iso-8859-1")
307
+
line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1")
308
+
if len(line) > _MAXLINE:
309
+
raise LineTooLong("status line")
303
310
if self.debuglevel > 0:
304
311
print("reply:", repr(line))
305
312
if not line:
@@ -340,7 +347,10 @@ def begin(self):
340
347
break
341
348
# skip the header from the 100 response
342
349
while True:
343
-
skip = self.fp.readline().strip()
350
+
skip = self.fp.readline(_MAXLINE + 1)
351
+
if len(skip) > _MAXLINE:
352
+
raise LineTooLong("header line")
353
+
skip = skip.strip()
344
354
if not skip:
345
355
break
346
356
if self.debuglevel > 0:
@@ -508,7 +518,9 @@ def _read_chunked(self, amt):
508
518
value = []
509
519
while True:
510
520
if chunk_left is None:
511
-
line = self.fp.readline()
521
+
line = self.fp.readline(_MAXLINE + 1)
522
+
if len(line) > _MAXLINE:
523
+
raise LineTooLong("chunk size")
512
524
i = line.find(b";")
513
525
if i >= 0:
514
526
line = line[:i] # strip chunk-extensions
@@ -543,7 +555,9 @@ def _read_chunked(self, amt):
543
555
# read and discard trailer up to the CRLF terminator
544
556
### note: we shouldn't have any trailers!
545
557
while True:
546
-
line = self.fp.readline()
558
+
line = self.fp.readline(_MAXLINE + 1)
559
+
if len(line) > _MAXLINE:
560
+
raise LineTooLong("trailer line")
547
561
if not line:
548
562
# a vanishingly small number of sites EOF without
549
563
# sending the trailer
@@ -692,7 +706,9 @@ def _tunnel(self):
692
706
raise socket.error("Tunnel connection failed: %d %s" % (code,
693
707
message.strip()))
694
708
while True:
695
-
line = response.fp.readline()
709
+
line = response.fp.readline(_MAXLINE + 1)
710
+
if len(line) > _MAXLINE:
711
+
raise LineTooLong("header line")
696
712
if line == b'\r\n':
697
713
break
698
714
@@ -1137,5 +1153,10 @@ def __init__(self, line):
1137
1153
self.args = line,
1138
1154
self.line = line
1139
1155
1156
+
class LineTooLong(HTTPException):
1157
+
def __init__(self, line_type):
1158
+
HTTPException.__init__(self, "got more than %d bytes when reading %s"
1159
+
% (_MAXLINE, line_type))
1160
+
1140
1161
# for backwards compatibility
1141
1162
error = HTTPException
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
@@ -317,6 +317,33 @@ def test_epipe(self):
317
317
self.assertEqual("Basic realm=\"example\"",
318
318
resp.getheader("www-authenticate"))
319
319
320
+
# Test lines overflowing the max line size (_MAXLINE in http.client)
321
+
322
+
def test_overflowing_status_line(self):
323
+
body = "HTTP/1.1 200 Ok" + "k" * 65536 + "\r\n"
324
+
resp = client.HTTPResponse(FakeSocket(body))
325
+
self.assertRaises((client.LineTooLong, client.BadStatusLine), resp.begin)
326
+
327
+
def test_overflowing_header_line(self):
328
+
body = (
329
+
'HTTP/1.1 200 OK\r\n'
330
+
'X-Foo: bar' + 'r' * 65536 + '\r\n\r\n'
331
+
)
332
+
resp = client.HTTPResponse(FakeSocket(body))
333
+
self.assertRaises(client.LineTooLong, resp.begin)
334
+
335
+
def test_overflowing_chunked_line(self):
336
+
body = (
337
+
'HTTP/1.1 200 OK\r\n'
338
+
'Transfer-Encoding: chunked\r\n\r\n'
339
+
+ '0' * 65536 + 'a\r\n'
340
+
'hello world\r\n'
341
+
'0\r\n'
342
+
)
343
+
resp = client.HTTPResponse(FakeSocket(body))
344
+
resp.begin()
345
+
self.assertRaises(client.LineTooLong, resp.read)
346
+
320
347
class OfflineTest(TestCase):
321
348
def test_responses(self):
322
349
self.assertEqual(client.responses[client.NOT_FOUND], "Not Found")
Original file line number Diff line number Diff line change
@@ -573,6 +573,13 @@ def test_request_length(self):
573
573
self.assertEqual(result[0], b'HTTP/1.1 414 Request-URI Too Long\r\n')
574
574
self.assertFalse(self.handler.get_called)
575
575
576
+
def test_header_length(self):
577
+
# Issue #6791: same for headers
578
+
result = self.send_typical_request(
579
+
b'GET / HTTP/1.1\r\nX-Foo: bar' + b'r' * 65537 + b'\r\n\r\n')
580
+
self.assertEqual(result[0], b'HTTP/1.1 400 Line too long\r\n')
581
+
self.assertFalse(self.handler.get_called)
582
+
576
583
class SimpleHTTPRequestHandlerTestCase(unittest.TestCase):
577
584
""" Test url parsing """
578
585
def setUp(self):
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