diff --git a/CHANGES b/CHANGES
index 4d6e8cec..164228ba 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,8 @@
+Unreleased
+----------
+ - Preserve leading and trailing CR/LF bytes in binary SOAP attachments
+ (#1249, #1481).
+
4.3.3 (2026-06-18)
------------------
- Wire up the ``forbid_external`` setting (previously defined but unused
diff --git a/src/zeep/wsdl/attachments.py b/src/zeep/wsdl/attachments.py
index 44e09900..6a4737da 100644
--- a/src/zeep/wsdl/attachments.py
+++ b/src/zeep/wsdl/attachments.py
@@ -74,7 +74,6 @@ def content(self):
if encoding == "base64":
return base64.b64decode(content)
- elif encoding == "binary":
- return content.strip(b"\r\n")
else:
+ # MultipartDecoder has already removed the MIME boundary framing.
return content
diff --git a/tests/test_soap_xop.py b/tests/test_soap_xop.py
index 9c010114..c89ec37b 100644
--- a/tests/test_soap_xop.py
+++ b/tests/test_soap_xop.py
@@ -1,5 +1,6 @@
from io import StringIO
+import pytest
import requests_mock
from lxml import etree
from pretend import stub
@@ -68,7 +69,7 @@ def test_rebuild_xml():
5XJ45-3B2
accident
- Li4uYmluYXJ5IEpQRyBpbWFnZS4uLg==
+ Li4uYmluYXJ5IEpQRyBpbWFnZS4uLg0K
@@ -76,6 +77,33 @@ def test_rebuild_xml():
assert_nodes_equal(etree.tostring(document), expected)
+@pytest.mark.parametrize(
+ "content",
+ [
+ b"binary\r\n",
+ b"\r\nbinary",
+ b"\r\nbinary\r\n",
+ b"\r\n",
+ b"\x00\xff\r\n",
+ b"binary",
+ b"",
+ ],
+)
+def test_binary_attachment_preserves_content(content):
+ data = (
+ b"--MIME_boundary\r\n"
+ b"Content-Type: application/octet-stream\r\n"
+ b"Content-Transfer-Encoding: binary\r\n"
+ b"Content-ID: \r\n\r\n"
+ + content
+ + b"\r\n--MIME_boundary--\r\n"
+ )
+ decoder = MultipartDecoder(data, "multipart/related; boundary=MIME_boundary")
+ assert decoder.parts[0].content == content
+ message_pack = MessagePack(parts=decoder.parts)
+ assert message_pack.attachments[0].content == content
+
+
def get_client_service():
wsdl_main = StringIO(
"""
@@ -249,7 +277,8 @@ def test_xop():
headers={"Content-Type": content_type},
)
result = service.TestOperation2("")
- assert result["_value_1"] == b"BINARYDATA"
+ # The extra CRLF before the MIME boundary belongs to the binary payload.
+ assert result["_value_1"] == b"BINARYDATA\r\n"
m.post(
"http://tests.python-zeep.org/test",
@@ -302,4 +331,4 @@ def test_xop_cid_encoded():
headers={"Content-Type": content_type},
)
result = service.TestOperation2("")
- assert result["_value_1"] == b"BINARYDATA"
+ assert result["_value_1"] == b"BINARYDATA\r\n"