Skip to content

Commit 0eeb460

Browse files
committed
Preserve MultipartWriter parts headers on write
1 parent 089d1d6 commit 0eeb460

File tree

4 files changed

+34
-14
lines changed

4 files changed

+34
-14
lines changed

CHANGES/3035.bugfix

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Preserve MultipartWriter parts headers on write.

aiohttp/multipart.py

+6-11
Original file line numberDiff line numberDiff line change
@@ -651,7 +651,7 @@ async def _maybe_release_last_part(self) -> None:
651651
self._last_part = None
652652

653653

654-
_Part = Tuple[Payload, 'MultiMapping[str]', str, str]
654+
_Part = Tuple[Payload, str, str]
655655

656656

657657
class MultipartWriter(Payload):
@@ -786,12 +786,7 @@ def append_payload(self, payload: Payload) -> Payload:
786786
if size is not None and not (encoding or te_encoding):
787787
payload.headers[CONTENT_LENGTH] = str(size)
788788

789-
# render headers
790-
headers = ''.join(
791-
[k + ': ' + v + '\r\n' for k, v in payload.headers.items()]
792-
).encode('utf-8') + b'\r\n'
793-
794-
self._parts.append((payload, headers, encoding, te_encoding)) # type: ignore # noqa
789+
self._parts.append((payload, encoding, te_encoding)) # type: ignore
795790
return payload
796791

797792
def append_json(
@@ -832,13 +827,13 @@ def size(self) -> Optional[int]:
832827
return 0
833828

834829
total = 0
835-
for part, headers, encoding, te_encoding in self._parts:
830+
for part, encoding, te_encoding in self._parts:
836831
if encoding or te_encoding or part.size is None:
837832
return None
838833

839834
total += int(
840835
2 + len(self._boundary) + 2 + # b'--'+self._boundary+b'\r\n'
841-
part.size + len(headers) +
836+
part.size + len(part._binary_headers) +
842837
2 # b'\r\n'
843838
)
844839

@@ -851,9 +846,9 @@ async def write(self, writer: Any,
851846
if not self._parts:
852847
return
853848

854-
for part, headers, encoding, te_encoding in self._parts:
849+
for part, encoding, te_encoding in self._parts:
855850
await writer.write(b'--' + self._boundary + b'\r\n')
856-
await writer.write(headers)
851+
await writer.write(part._binary_headers)
857852

858853
if encoding or te_encoding:
859854
w = MultipartPayloadWriter(writer)

aiohttp/payload.py

+8
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,14 @@ def headers(self) -> Optional[_CIMultiDict]:
146146
"""Custom item headers"""
147147
return self._headers
148148

149+
@property
150+
def _binary_headers(self) -> bytes:
151+
if self.headers is None:
152+
return b''
153+
return ''.join(
154+
[k + ': ' + v + '\r\n' for k, v in self.headers.items()]
155+
).encode('utf-8') + b'\r\n'
156+
149157
@property
150158
def encoding(self) -> Optional[str]:
151159
"""Payload encoding"""

tests/test_multipart.py

+19-3
Original file line numberDiff line numberDiff line change
@@ -1005,9 +1005,6 @@ def test_append_multipart(self, writer) -> None:
10051005
part = writer._parts[0][0]
10061006
assert part.headers[CONTENT_TYPE] == 'test/passed'
10071007

1008-
async def test_write(self, writer, stream) -> None:
1009-
await writer.write(stream)
1010-
10111008
def test_with(self) -> None:
10121009
with aiohttp.MultipartWriter(boundary=':') as writer:
10131010
writer.append('foo')
@@ -1030,6 +1027,25 @@ def test_append_none_not_allowed(self) -> None:
10301027
with aiohttp.MultipartWriter(boundary=':') as writer:
10311028
writer.append(None)
10321029

1030+
async def test_write_preserves_content_disposition(
1031+
self, buf, stream
1032+
) -> None:
1033+
with aiohttp.MultipartWriter(boundary=':') as writer:
1034+
part = writer.append(b'foo', headers={CONTENT_TYPE: 'test/passed'})
1035+
part.set_content_disposition('form-data', filename='bug')
1036+
await writer.write(stream)
1037+
1038+
headers, message = bytes(buf).split(b'\r\n\r\n', 1)
1039+
1040+
assert headers == (
1041+
b'--:\r\n'
1042+
b'Content-Type: test/passed\r\n'
1043+
b'Content-Length: 3\r\n'
1044+
b'Content-Disposition:'
1045+
b' form-data; filename="bug"; filename*=utf-8\'\'bug'
1046+
)
1047+
assert message == b'foo\r\n--:--\r\n'
1048+
10331049

10341050
async def test_async_for_reader() -> None:
10351051
data = [

0 commit comments

Comments
 (0)