From fcd5d4dd756613abe59b27a1345892fd8e68c7eb Mon Sep 17 00:00:00 2001 From: John Stark Date: Tue, 23 Apr 2024 22:17:18 +0200 Subject: [PATCH] Handle messages containing only end boundary, fixes #38 --- python_multipart/multipart.py | 24 +++++++++++++++---- tests/test_data/http/empty_message.http | 1 + tests/test_data/http/empty_message.yaml | 2 ++ .../http/empty_message_with_bad_end.http | 1 + .../http/empty_message_with_bad_end.yaml | 3 +++ 5 files changed, 27 insertions(+), 4 deletions(-) create mode 100644 tests/test_data/http/empty_message.http create mode 100644 tests/test_data/http/empty_message.yaml create mode 100644 tests/test_data/http/empty_message_with_bad_end.http create mode 100644 tests/test_data/http/empty_message_with_bad_end.yaml diff --git a/python_multipart/multipart.py b/python_multipart/multipart.py index ace4a8f..ca7feb1 100644 --- a/python_multipart/multipart.py +++ b/python_multipart/multipart.py @@ -130,7 +130,8 @@ class MultipartState(IntEnum): PART_DATA_START = 8 PART_DATA = 9 PART_DATA_END = 10 - END = 11 + END_BOUNDARY = 11 + END = 12 # Flags for the multipart parser. @@ -1120,7 +1121,10 @@ def data_callback(name: CallbackName, end_i: int, remaining: bool = False) -> No # Check to ensure that the last 2 characters in our boundary # are CRLF. if index == len(boundary) - 2: - if c != CR: + if c == HYPHEN: + # Potential empty message. + state = MultipartState.END_BOUNDARY + elif c != CR: # Error! msg = "Did not find CR at end of boundary (%d)" % (i,) self.logger.warning(msg) @@ -1397,6 +1401,18 @@ def data_callback(name: CallbackName, end_i: int, remaining: bool = False) -> No # the start of the boundary itself. i -= 1 + elif state == MultipartState.END_BOUNDARY: + if index == len(boundary) - 2 + 1: + if c != HYPHEN: + msg = "Did not find - at end of boundary (%d)" % (i,) + self.logger.warning(msg) + e = MultipartParseError(msg) + e.offset = i + raise e + index += 1 + self.callback("end") + state = MultipartState.END + elif state == MultipartState.END: # Do nothing and just consume a byte in the end state. if c not in (CR, LF): @@ -1703,8 +1719,8 @@ def on_headers_finished() -> None: def _on_end() -> None: nonlocal writer - assert writer is not None - writer.finalize() + if writer is not None: + writer.finalize() if self.on_end is not None: self.on_end() diff --git a/tests/test_data/http/empty_message.http b/tests/test_data/http/empty_message.http new file mode 100644 index 0000000..baff7d5 --- /dev/null +++ b/tests/test_data/http/empty_message.http @@ -0,0 +1 @@ +----boundary-- diff --git a/tests/test_data/http/empty_message.yaml b/tests/test_data/http/empty_message.yaml new file mode 100644 index 0000000..ab33940 --- /dev/null +++ b/tests/test_data/http/empty_message.yaml @@ -0,0 +1,2 @@ +boundary: --boundary +expected: [] diff --git a/tests/test_data/http/empty_message_with_bad_end.http b/tests/test_data/http/empty_message_with_bad_end.http new file mode 100644 index 0000000..a085714 --- /dev/null +++ b/tests/test_data/http/empty_message_with_bad_end.http @@ -0,0 +1 @@ +----boundary-X diff --git a/tests/test_data/http/empty_message_with_bad_end.yaml b/tests/test_data/http/empty_message_with_bad_end.yaml new file mode 100644 index 0000000..ae920bf --- /dev/null +++ b/tests/test_data/http/empty_message_with_bad_end.yaml @@ -0,0 +1,3 @@ +boundary: --boundary +expected: + error: 13