Describe the bug
When a part appended to a non-form-data MultipartWriter carries Content-Encoding (gzip/deflate) or Content-Transfer-Encoding (base64/quoted-printable), write() compresses/encodes the part on the wire via MultipartPayloadWriter, but as_bytes() returns the raw, untransformed part content. The result contradicts the part's own headers and does not match the body a server actually receives.
This also breaks digest authentication with qop=auth-int for such bodies: the middleware hashes await body.as_bytes() (client_middleware_digest_auth.py), so the entity hash never matches what is sent, and authentication always fails.
To Reproduce
import asyncio
from aiohttp import MultipartWriter, payload
class Buf:
def __init__(self): self.data = bytearray()
async def write(self, chunk): self.data.extend(chunk)
async def main():
mp = MultipartWriter("mixed", boundary="XBOUND")
part = payload.StringPayload("hello world")
part.headers["Content-Encoding"] = "gzip"
mp.append_payload(part)
buf = Buf()
await mp.write(buf)
print("wire == as_bytes():", bytes(buf.data) == await mp.as_bytes())
asyncio.run(main())
Output: wire == as_bytes(): False — as_bytes() contains the literal hello world after headers declaring Content-Encoding: gzip.
Expected behavior
as_bytes() returns the same bytes write() produces, per its docstring ("bytes representation of the multipart data").
aiohttp Version
Reproduced on 3.14.3 and current master (4.0.0a2.dev0). Introduced with as_bytes() in #11017.
Root cause
MultipartWriter.as_bytes() in aiohttp/multipart.py iterates self._parts ignoring the stored encoding/te_encoding values that write() honors.
Related component
Client
I have a fix ready and will open a PR.
Describe the bug
When a part appended to a non-form-data
MultipartWritercarriesContent-Encoding(gzip/deflate) orContent-Transfer-Encoding(base64/quoted-printable),write()compresses/encodes the part on the wire viaMultipartPayloadWriter, butas_bytes()returns the raw, untransformed part content. The result contradicts the part's own headers and does not match the body a server actually receives.This also breaks digest authentication with
qop=auth-intfor such bodies: the middleware hashesawait body.as_bytes()(client_middleware_digest_auth.py), so the entity hash never matches what is sent, and authentication always fails.To Reproduce
Output:
wire == as_bytes(): False—as_bytes()contains the literalhello worldafter headers declaringContent-Encoding: gzip.Expected behavior
as_bytes()returns the same byteswrite()produces, per its docstring ("bytes representation of the multipart data").aiohttp Version
Reproduced on 3.14.3 and current master (4.0.0a2.dev0). Introduced with
as_bytes()in #11017.Root cause
MultipartWriter.as_bytes()inaiohttp/multipart.pyiteratesself._partsignoring the storedencoding/te_encodingvalues thatwrite()honors.Related component
Client
I have a fix ready and will open a PR.