]> git.ipfire.org Git - thirdparty/httpx.git/commitdiff
Fix Content-Length for unicode file contents with multipart (#1537)
authorTom Christie <tom@tomchristie.com>
Thu, 25 Mar 2021 15:35:02 +0000 (15:35 +0000)
committerGitHub <noreply@github.com>
Thu, 25 Mar 2021 15:35:02 +0000 (15:35 +0000)
httpx/_multipart.py
tests/test_multipart.py

index f690afc9ae1c013cca6b71980d48114dfdb1344d..bf75a5663b75c0472c74764fe2ab27d1a101c803 100644 (file)
@@ -40,11 +40,7 @@ class DataField:
 
     def render_data(self) -> bytes:
         if not hasattr(self, "_data"):
-            self._data = (
-                self.value
-                if isinstance(self.value, bytes)
-                else self.value.encode("utf-8")
-            )
+            self._data = to_bytes(self.value)
 
         return self._data
 
@@ -88,7 +84,7 @@ class FileField:
         headers = self.render_headers()
 
         if isinstance(self.file, (str, bytes)):
-            return len(headers) + len(self.file)
+            return len(headers) + len(to_bytes(self.file))
 
         # Let's do our best not to read `file` into memory.
         try:
index 94813932a8ea054b14c1c83c94408236cfe12b8d..199af4b0a5098d737d06cf71d263b939f462ec49 100644 (file)
@@ -133,6 +133,29 @@ def test_multipart_encode(tmp_path: typing.Any) -> None:
         assert content == b"".join(stream)
 
 
+def test_multipart_encode_unicode_file_contents() -> None:
+    files = {"file": ("name.txt", "<Ășnicode string>")}
+
+    with mock.patch("os.urandom", return_value=os.urandom(16)):
+        boundary = os.urandom(16).hex()
+
+        headers, stream = encode_request(files=files)
+        assert isinstance(stream, typing.Iterable)
+
+        content = (
+            '--{0}\r\nContent-Disposition: form-data; name="file";'
+            ' filename="name.txt"\r\n'
+            "Content-Type: text/plain\r\n\r\n<Ășnicode string>\r\n"
+            "--{0}--\r\n"
+            "".format(boundary).encode("utf-8")
+        )
+        assert headers == {
+            "Content-Type": f"multipart/form-data; boundary={boundary}",
+            "Content-Length": str(len(content)),
+        }
+        assert content == b"".join(stream)
+
+
 def test_multipart_encode_files_allows_filenames_as_none() -> None:
     files = {"file": (None, io.BytesIO(b"<file content>"))}
     with mock.patch("os.urandom", return_value=os.urandom(16)):