Skip to content

Commit c4fe4d3

Browse files
authored
Don't warn when CRLF is found after last boundary (#193)
1 parent 5b1aed8 commit c4fe4d3

File tree

5 files changed

+36
-2
lines changed

5 files changed

+36
-2
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
## 0.0.19 (2024-11-30)
4+
5+
* Don't warn when CRLF is found after last boundary on `MultipartParser` [#193](https://github.com/Kludex/python-multipart/pull/193).
6+
37
## 0.0.18 (2024-11-28)
48

59
* Hard break if found data after last boundary on `MultipartParser` [#189](https://github.com/Kludex/python-multipart/pull/189).

python_multipart/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
__author__ = "Andrew Dunham"
33
__license__ = "Apache"
44
__copyright__ = "Copyright (c) 2012-2013, Andrew Dunham"
5-
__version__ = "0.0.18"
5+
__version__ = "0.0.19"
66

77
from .multipart import (
88
BaseParser,

python_multipart/multipart.py

+4
Original file line numberDiff line numberDiff line change
@@ -1397,6 +1397,10 @@ def data_callback(name: CallbackName, end_i: int, remaining: bool = False) -> No
13971397
i -= 1
13981398

13991399
elif state == MultipartState.END:
1400+
# Don't do anything if chunk ends with CRLF.
1401+
if c == CR and i + 1 < length and data[i + 1] == LF:
1402+
i += 2
1403+
continue
14001404
# Skip data after the last boundary.
14011405
self.logger.warning("Skipping data after last boundary")
14021406
i = length

scripts/check

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,5 @@ SOURCE_FILES="python_multipart multipart tests"
66

77
uvx ruff format --check --diff $SOURCE_FILES
88
uvx ruff check $SOURCE_FILES
9-
uvx --with types-PyYAML mypy $SOURCE_FILES
9+
uv run mypy $SOURCE_FILES
1010
uvx check-sdist

tests/test_multipart.py

+26
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22

3+
import logging
34
import os
45
import random
56
import sys
@@ -9,6 +10,7 @@
910
from typing import TYPE_CHECKING, cast
1011
from unittest.mock import Mock
1112

13+
import pytest
1214
import yaml
1315

1416
from python_multipart.decoders import Base64Decoder, QuotedPrintableDecoder
@@ -1248,6 +1250,30 @@ def on_file(f: FileProtocol) -> None:
12481250
f = FormParser("multipart/form-data", on_field=Mock(), on_file=on_file, boundary="boundary")
12491251
f.write(data.encode("latin-1"))
12501252

1253+
@pytest.fixture(autouse=True)
1254+
def inject_fixtures(self, caplog: pytest.LogCaptureFixture) -> None:
1255+
self._caplog = caplog
1256+
1257+
def test_multipart_parser_data_end_with_crlf_without_warnings(self) -> None:
1258+
"""This test makes sure that the parser does not handle when the data ends with a CRLF."""
1259+
data = (
1260+
"--boundary\r\n"
1261+
'Content-Disposition: form-data; name="file"; filename="filename.txt"\r\n'
1262+
"Content-Type: text/plain\r\n\r\n"
1263+
"hello\r\n"
1264+
"--boundary--\r\n"
1265+
)
1266+
1267+
files: list[File] = []
1268+
1269+
def on_file(f: FileProtocol) -> None:
1270+
files.append(cast(File, f))
1271+
1272+
f = FormParser("multipart/form-data", on_field=Mock(), on_file=on_file, boundary="boundary")
1273+
with self._caplog.at_level(logging.WARNING):
1274+
f.write(data.encode("latin-1"))
1275+
assert len(self._caplog.records) == 0
1276+
12511277
def test_max_size_multipart(self) -> None:
12521278
# Load test data.
12531279
test_file = "single_field_single_file.http"

0 commit comments

Comments
 (0)