Skip to content

MultipartWriter.as_bytes() does not apply part Content-Encoding / Content-Transfer-Encoding, diverging from write() #13495

Description

@2sumtech

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(): Falseas_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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions