Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Always rewind files on multipart uploads. #2065

Merged
merged 4 commits into from Feb 4, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 1 addition & 3 deletions httpx/_multipart.py
Expand Up @@ -113,7 +113,6 @@ def __init__(self, name: str, value: FileTypes) -> None:
self.filename = filename
self.file = fileobj
self.headers = headers
self._consumed = False

def get_length(self) -> int:
headers = self.render_headers()
Expand Down Expand Up @@ -158,9 +157,8 @@ def render_data(self) -> typing.Iterator[bytes]:
yield self._data
return

if self._consumed: # pragma: nocover
if hasattr(self.file, "seek"):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is more of a question, so we do seek for the first file upload too, right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup

self.file.seek(0)
self._consumed = True

chunk = self.file.read(self.CHUNK_SIZE)
while chunk:
Expand Down
20 changes: 20 additions & 0 deletions tests/test_multipart.py
@@ -1,6 +1,7 @@
import cgi
import io
import os
import tempfile
import typing
from unittest import mock

Expand Down Expand Up @@ -339,6 +340,25 @@ def data() -> typing.Iterator[bytes]:
assert content == b"".join(stream)


def test_multipart_rewinds_files():
with tempfile.TemporaryFile() as upload:
upload.write(b"Hello, world!")

transport = httpx.MockTransport(echo_request_content)
client = httpx.Client(transport=transport)

files = {"file": upload}
response = client.post("http://127.0.0.1:8000/", files=files)
assert response.status_code == 200
assert b"\r\nHello, world!\r\n" in response.content

# POSTing the same file instance a second time should have the same content.
files = {"file": upload}
response = client.post("http://127.0.0.1:8000/", files=files)
assert response.status_code == 200
assert b"\r\nHello, world!\r\n" in response.content


class TestHeaderParamHTML5Formatting:
def test_unicode(self):
param = format_form_param("filename", "n\u00e4me")
Expand Down