From 453384587ac887a280fa93cbbc2ed610119872b4 Mon Sep 17 00:00:00 2001 From: Gyanu Date: Tue, 1 Sep 2026 11:10:50 +0530 Subject: [PATCH] Don't use MEMFILE_MAX as the total multipart memory cap A couple of files that each stay under the spool threshold can still add up past 100 KiB and raise MultipartError. Keep MEMFILE_MAX as the per-part limit and scale the parser-wide cap. --- bottle.py | 3 ++- test/test_environ.py | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/bottle.py b/bottle.py index 9f57c9539..df57c8805 100755 --- a/bottle.py +++ b/bottle.py @@ -1367,7 +1367,8 @@ def POST(self): if not boundary: raise MultipartError("Invalid content type header, missing boundary") parser = _MultipartParser(self.body, boundary, self.content_length, - mem_limit=self.MEMFILE_MAX, memfile_limit=self.MEMFILE_MAX, + memfile_limit=self.MEMFILE_MAX, + mem_limit=self.MEMFILE_MAX * 128, charset=charset) for part in parser.parse(): diff --git a/test/test_environ.py b/test/test_environ.py index c6082ccf8..4e46f9716 100755 --- a/test/test_environ.py +++ b/test/test_environ.py @@ -357,6 +357,23 @@ def test_multipart(self): self.assertEqual(['value2', '万难'], request.forms.getall('field2')) self.assertTrue('field2' not in request.files) + def test_multipart_file_larger_than_memfile_max(self): + content = 'x' * (BaseRequest.MEMFILE_MAX + 50) + e = tools.multipart_environ(fields=[], files=[('file1', 'big.bin', content)]) + request = BaseRequest(e) + uploaded = request.files['file1'] + self.assertEqual(len(uploaded.file.read()), BaseRequest.MEMFILE_MAX + 50) + + def test_multipart_many_small_files_above_memfile_total(self): + chunk = 'x' * (BaseRequest.MEMFILE_MAX // 2 + 20) + e = tools.multipart_environ(fields=[], files=[ + ('file1', 'a.bin', chunk), + ('file2', 'b.bin', chunk), + ]) + request = BaseRequest(e) + self.assertEqual(len(request.files['file1'].file.read()), len(chunk)) + self.assertEqual(len(request.files['file2'].file.read()), len(chunk)) + def test_json_empty(self): """ Environ: Request.json property with empty body. """ self.assertEqual(BaseRequest({}).json, None)