Skip to content

Commit

Permalink
Handle lower-case http headers in _Mulitpart_Parser_Protocol (#17545)
Browse files Browse the repository at this point in the history
  • Loading branch information
H-Shay committed Aug 14, 2024
1 parent a308d99 commit b05b2e1
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 11 deletions.
1 change: 1 addition & 0 deletions changelog.d/17545.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Handle lower-case http headers in `_Mulitpart_Parser_Protocol`.
6 changes: 3 additions & 3 deletions synapse/http/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -1057,11 +1057,11 @@ def dataReceived(self, incoming_data: bytes) -> None:
if not self.parser:

def on_header_field(data: bytes, start: int, end: int) -> None:
if data[start:end] == b"Location":
if data[start:end].lower() == b"location":
self.has_redirect = True
if data[start:end] == b"Content-Disposition":
if data[start:end].lower() == b"content-disposition":
self.in_disposition = True
if data[start:end] == b"Content-Type":
if data[start:end].lower() == b"content-type":
self.in_content_type = True

def on_header_value(data: bytes, start: int, end: int) -> None:
Expand Down
42 changes: 34 additions & 8 deletions tests/http/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,11 @@


class ReadMultipartResponseTests(TestCase):
data1 = b"\r\n\r\n--6067d4698f8d40a0a794ea7d7379d53a\r\nContent-Type: application/json\r\n\r\n{}\r\n--6067d4698f8d40a0a794ea7d7379d53a\r\nContent-Type: text/plain\r\nContent-Disposition: inline; filename=test_upload\r\n\r\nfile_"
data2 = b"to_stream\r\n--6067d4698f8d40a0a794ea7d7379d53a--\r\n\r\n"
multipart_response_data1 = b"\r\n\r\n--6067d4698f8d40a0a794ea7d7379d53a\r\nContent-Type: application/json\r\n\r\n{}\r\n--6067d4698f8d40a0a794ea7d7379d53a\r\nContent-Type: text/plain\r\nContent-Disposition: inline; filename=test_upload\r\n\r\nfile_"
multipart_response_data2 = (
b"to_stream\r\n--6067d4698f8d40a0a794ea7d7379d53a--\r\n\r\n"
)
multipart_response_data_cased = b"\r\n\r\n--6067d4698f8d40a0a794ea7d7379d53a\r\ncOntEnt-type: application/json\r\n\r\n{}\r\n--6067d4698f8d40a0a794ea7d7379d53a\r\nContent-tyPe: text/plain\r\nconTent-dispOsition: inline; filename=test_upload\r\n\r\nfile_"

redirect_data = b"\r\n\r\n--6067d4698f8d40a0a794ea7d7379d53a\r\nContent-Type: application/json\r\n\r\n{}\r\n--6067d4698f8d40a0a794ea7d7379d53a\r\nLocation: https://cdn.example.org/ab/c1/2345.txt\r\n\r\n--6067d4698f8d40a0a794ea7d7379d53a--\r\n\r\n"

Expand Down Expand Up @@ -103,8 +106,31 @@ def test_parse_file(self) -> None:
result, deferred, protocol = self._build_multipart_response(249, 250)

# Start sending data.
protocol.dataReceived(self.data1)
protocol.dataReceived(self.data2)
protocol.dataReceived(self.multipart_response_data1)
protocol.dataReceived(self.multipart_response_data2)
# Close the connection.
protocol.connectionLost(Failure(ResponseDone()))

multipart_response: MultipartResponse = deferred.result # type: ignore[assignment]

self.assertEqual(multipart_response.json, b"{}")
self.assertEqual(result.getvalue(), b"file_to_stream")
self.assertEqual(multipart_response.length, len(b"file_to_stream"))
self.assertEqual(multipart_response.content_type, b"text/plain")
self.assertEqual(
multipart_response.disposition, b"inline; filename=test_upload"
)

def test_parse_file_lowercase_headers(self) -> None:
"""
Check that a multipart response containing a file is properly parsed
into the json/file parts, and the json and file are properly captured if the http headers are lowercased
"""
result, deferred, protocol = self._build_multipart_response(249, 250)

# Start sending data.
protocol.dataReceived(self.multipart_response_data_cased)
protocol.dataReceived(self.multipart_response_data2)
# Close the connection.
protocol.connectionLost(Failure(ResponseDone()))

Expand Down Expand Up @@ -143,7 +169,7 @@ def test_too_large(self) -> None:
result, deferred, protocol = self._build_multipart_response(UNKNOWN_LENGTH, 180)

# Start sending data.
protocol.dataReceived(self.data1)
protocol.dataReceived(self.multipart_response_data1)

self.assertEqual(result.getvalue(), b"file_")
self._assert_error(deferred, protocol)
Expand All @@ -154,11 +180,11 @@ def test_additional_data(self) -> None:
result, deferred, protocol = self._build_multipart_response(UNKNOWN_LENGTH, 180)

# Start sending data.
protocol.dataReceived(self.data1)
protocol.dataReceived(self.multipart_response_data1)
self._assert_error(deferred, protocol)

# More data might have come in.
protocol.dataReceived(self.data2)
protocol.dataReceived(self.multipart_response_data2)

self.assertEqual(result.getvalue(), b"file_")
self._assert_error(deferred, protocol)
Expand All @@ -172,7 +198,7 @@ def test_content_length(self) -> None:
self.assertFalse(deferred.called)

# Start sending data.
protocol.dataReceived(self.data1)
protocol.dataReceived(self.multipart_response_data1)
self._assert_error(deferred, protocol)
self._cleanup_error(deferred)

Expand Down

0 comments on commit b05b2e1

Please sign in to comment.