This is an automated email from the ASF dual-hosted git repository. reta pushed a commit to branch 3.4.x-fixes in repository https://gitbox.apache.org/repos/asf/cxf.git
commit 62fc36d08e0d26b4bef16ce656b848832ac1cf09 Author: Andriy Redko <drr...@gmail.com> AuthorDate: Sat Mar 4 11:49:19 2023 -0500 CXF-8669: Multipart annotation not working 3.4.6 onwards (#1078) (cherry picked from commit 5b32eaf34f5a233d0ad8c69d82b30b4ba10eecb6) (cherry picked from commit aaf798079d1504dcd64d0685e84a011c4410adb3) (cherry picked from commit 8ddf1331bab97b07bc40cd1486fbf0196b33688f) --- .../cxf/attachment/AttachmentDataSource.java | 6 +- .../cxf/attachment/AttachmentDeserializer.java | 2 +- .../org/apache/cxf/attachment/AttachmentUtil.java | 23 ++++- .../java/org/apache/cxf/message/MessageUtils.java | 12 +++ .../cxf/attachment/AttachmentDeserializerTest.java | 101 +++++++++++++++++++++ .../apache/cxf/jaxrs/ext/MessageContextImpl.java | 3 +- .../security/wss4j/AttachmentCallbackHandler.java | 13 ++- 7 files changed, 151 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/org/apache/cxf/attachment/AttachmentDataSource.java b/core/src/main/java/org/apache/cxf/attachment/AttachmentDataSource.java index ae9ca10ec2..a50a8ac4f7 100644 --- a/core/src/main/java/org/apache/cxf/attachment/AttachmentDataSource.java +++ b/core/src/main/java/org/apache/cxf/attachment/AttachmentDataSource.java @@ -32,7 +32,6 @@ import org.apache.cxf.io.CachedOutputStream; import org.apache.cxf.message.Message; public class AttachmentDataSource implements DataSource { - private String ct; private CachedOutputStream cache; private InputStream ins; @@ -78,9 +77,10 @@ public class AttachmentDataSource implements DataSource { public String getContentType() { if (StringUtils.isEmpty(ct)) { - ct = "application/octet-stream"; + return "application/octet-stream"; + } else { + return ct; } - return ct; } public InputStream getInputStream() { diff --git a/core/src/main/java/org/apache/cxf/attachment/AttachmentDeserializer.java b/core/src/main/java/org/apache/cxf/attachment/AttachmentDeserializer.java index 0f7f6b890f..53a77c505d 100644 --- a/core/src/main/java/org/apache/cxf/attachment/AttachmentDeserializer.java +++ b/core/src/main/java/org/apache/cxf/attachment/AttachmentDeserializer.java @@ -325,7 +325,7 @@ public class AttachmentDeserializer { this); createCount++; - return AttachmentUtil.createAttachment(partStream, headers); + return AttachmentUtil.createAttachment(partStream, headers, message); } public boolean isLazyLoading() { diff --git a/core/src/main/java/org/apache/cxf/attachment/AttachmentUtil.java b/core/src/main/java/org/apache/cxf/attachment/AttachmentUtil.java index 942c3a9833..802ef87e9c 100644 --- a/core/src/main/java/org/apache/cxf/attachment/AttachmentUtil.java +++ b/core/src/main/java/org/apache/cxf/attachment/AttachmentUtil.java @@ -66,6 +66,10 @@ import org.apache.cxf.message.Message; import org.apache.cxf.message.MessageUtils; public final class AttachmentUtil { + // The default values for {@link AttachmentDataSource} content type in case when + // "Content-Type" header is not present. + public static final String ATTACHMENT_CONTENT_TYPE = "org.apache.cxf.attachment.content-type"; + // The xop:include "href" attribute (https://www.w3.org/TR/xop10/#xop_href) may include // arbitrary URL which we should never follow (unless explicitly allowed). public static final String ATTACHMENT_XOP_FOLLOW_URLS_PROPERTY = "org.apache.cxf.attachment.xop.follow.urls"; @@ -388,14 +392,27 @@ public final class AttachmentUtil { static String getHeader(Map<String, List<String>> headers, String h, String delim) { return getHeaderValue(headers.get(h), delim); } - public static Attachment createAttachment(InputStream stream, Map<String, List<String>> headers) - throws IOException { + + /** + * @deprecated use createAttachment(InputStream stream, Map<String, List<String>> headers, Message message) + */ + public static Attachment createAttachment(InputStream stream, Map<String, List<String>> headers) + throws IOException { + return createAttachment(stream, headers, null /* no Message */); + } + + public static Attachment createAttachment(InputStream stream, Map<String, List<String>> headers, Message message) + throws IOException { String id = cleanContentId(getHeader(headers, "Content-ID")); AttachmentImpl att = new AttachmentImpl(id); - final String ct = getHeader(headers, "Content-Type"); + String ct = getHeader(headers, "Content-Type"); + if (StringUtils.isEmpty(ct)) { + ct = MessageUtils.getContextualString(message, ATTACHMENT_CONTENT_TYPE, "application/octet-stream"); + } + String cd = getHeader(headers, "Content-Disposition"); String fileName = getContentDispositionFileName(cd); diff --git a/core/src/main/java/org/apache/cxf/message/MessageUtils.java b/core/src/main/java/org/apache/cxf/message/MessageUtils.java index 5fd4d3176d..bb356ec26c 100644 --- a/core/src/main/java/org/apache/cxf/message/MessageUtils.java +++ b/core/src/main/java/org/apache/cxf/message/MessageUtils.java @@ -210,6 +210,18 @@ public final class MessageUtils { return defaultValue; } + public static String getContextualString(Message m, String key, String defaultValue) { + if (m != null) { + final Object o = m.getContextualProperty(key); + if (o instanceof String) { + return (String) o; + } else if (o != null) { + return o.toString(); + } + } + return defaultValue; + } + public static Object getContextualProperty(Message m, String propPreferred, String propDefault) { Object prop = null; if (m != null) { diff --git a/core/src/test/java/org/apache/cxf/attachment/AttachmentDeserializerTest.java b/core/src/test/java/org/apache/cxf/attachment/AttachmentDeserializerTest.java index b2d90c1093..96d4d3bdd8 100644 --- a/core/src/test/java/org/apache/cxf/attachment/AttachmentDeserializerTest.java +++ b/core/src/test/java/org/apache/cxf/attachment/AttachmentDeserializerTest.java @@ -786,6 +786,107 @@ public class AttachmentDeserializerTest { assertEquals("passwd", dataSource.getName()); } + @Test + public void testDefaultContentTypeIfNotSet() throws Exception { + StringBuilder sb = new StringBuilder(1000); + + sb.append("SomeHeader: foo\n") + .append("------=_Part_34950_1098328613.1263781527359\n") + .append("Content-Type: text/xml; charset=UTF-8\n") + .append("Content-Transfer-Encoding: binary\n") + .append("Content-Id: <318731183421.1263781527359.IBM.WEBSERVICES@auhpap02>\n") + .append('\n') + .append("<envelope/>\n"); + + sb.append("------=_Part_34950_1098328613.1263781527359\n") + .append("Content-Transfer-Encoding: binary\n") + .append("Content-Id: <b86a5f2d-e7af-4e5e-b71a-9f6f2307cab0>\n") + .append('\n') + .append("<message>\n") + .append("------=_Part_34950_1098328613.1263781527359--\n"); + + msg = new MessageImpl(); + msg.setContent(InputStream.class, new ByteArrayInputStream(sb.toString().getBytes(StandardCharsets.UTF_8))); + msg.put(Message.CONTENT_TYPE, "multipart/related"); + + AttachmentDeserializer ad = new AttachmentDeserializer(msg); + ad.initializeAttachments(); + + // Force it to load the attachments + assertEquals(1, msg.getAttachments().size()); + Attachment attachment = msg.getAttachments().iterator().next(); + AttachmentDataSource dataSource = (AttachmentDataSource)attachment.getDataHandler().getDataSource(); + assertEquals("application/octet-stream", dataSource.getContentType()); + } + + @Test + public void testContentTypeIfNotSet() throws Exception { + StringBuilder sb = new StringBuilder(1000); + + sb.append("SomeHeader: foo\n") + .append("------=_Part_34950_1098328613.1263781527359\n") + .append("Content-Type: text/xml; charset=UTF-8\n") + .append("Content-Transfer-Encoding: binary\n") + .append("Content-Id: <318731183421.1263781527359.IBM.WEBSERVICES@auhpap02>\n") + .append('\n') + .append("<envelope/>\n"); + + sb.append("------=_Part_34950_1098328613.1263781527359\n") + .append("Content-Transfer-Encoding: binary\n") + .append("Content-Id: <b86a5f2d-e7af-4e5e-b71a-9f6f2307cab0>\n") + .append('\n') + .append("<message>\n") + .append("------=_Part_34950_1098328613.1263781527359--\n"); + + msg = new MessageImpl(); + msg.setContent(InputStream.class, new ByteArrayInputStream(sb.toString().getBytes(StandardCharsets.UTF_8))); + msg.put(Message.CONTENT_TYPE, "multipart/related"); + msg.put(AttachmentUtil.ATTACHMENT_CONTENT_TYPE, "text/plain"); + + AttachmentDeserializer ad = new AttachmentDeserializer(msg); + ad.initializeAttachments(); + + // Force it to load the attachments + assertEquals(1, msg.getAttachments().size()); + Attachment attachment = msg.getAttachments().iterator().next(); + AttachmentDataSource dataSource = (AttachmentDataSource)attachment.getDataHandler().getDataSource(); + assertEquals("text/plain", dataSource.getContentType()); + } + + @Test + public void testContentType() throws Exception { + StringBuilder sb = new StringBuilder(1000); + + sb.append("SomeHeader: foo\n") + .append("------=_Part_34950_1098328613.1263781527359\n") + .append("Content-Type: text/xml; charset=UTF-8\n") + .append("Content-Transfer-Encoding: binary\n") + .append("Content-Id: <318731183421.1263781527359.IBM.WEBSERVICES@auhpap02>\n") + .append('\n') + .append("<envelope/>\n"); + + sb.append("------=_Part_34950_1098328613.1263781527359\n") + .append("Content-Transfer-Encoding: binary\n") + .append("Content-Id: <b86a5f2d-e7af-4e5e-b71a-9f6f2307cab0>\n") + .append("Content-Type: text/xml; charset=UTF-8\n") + .append('\n') + .append("<message>\n") + .append("------=_Part_34950_1098328613.1263781527359--\n"); + + msg = new MessageImpl(); + msg.setContent(InputStream.class, new ByteArrayInputStream(sb.toString().getBytes(StandardCharsets.UTF_8))); + msg.put(Message.CONTENT_TYPE, "multipart/related"); + + AttachmentDeserializer ad = new AttachmentDeserializer(msg); + ad.initializeAttachments(); + + // Force it to load the attachments + assertEquals(1, msg.getAttachments().size()); + Attachment attachment = msg.getAttachments().iterator().next(); + AttachmentDataSource dataSource = (AttachmentDataSource)attachment.getDataHandler().getDataSource(); + assertEquals("text/xml; charset=UTF-8", dataSource.getContentType()); + } + @Test public void testCXF8706() { final DataSource ds = AttachmentUtil diff --git a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/MessageContextImpl.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/MessageContextImpl.java index d2bdbd804e..644800adcf 100644 --- a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/MessageContextImpl.java +++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/MessageContextImpl.java @@ -290,7 +290,8 @@ public class MessageContextImpl implements MessageContext { Attachment first = new Attachment(AttachmentUtil.createAttachment( inMessage.getContent(InputStream.class), - headers), + headers, + inMessage), new ProvidersImpl(inMessage)); newAttachments.add(first); } catch (IOException ex) { diff --git a/rt/ws/security/src/main/java/org/apache/cxf/ws/security/wss4j/AttachmentCallbackHandler.java b/rt/ws/security/src/main/java/org/apache/cxf/ws/security/wss4j/AttachmentCallbackHandler.java index 25abcd5b64..b0ecd34200 100644 --- a/rt/ws/security/src/main/java/org/apache/cxf/ws/security/wss4j/AttachmentCallbackHandler.java +++ b/rt/ws/security/src/main/java/org/apache/cxf/ws/security/wss4j/AttachmentCallbackHandler.java @@ -34,8 +34,11 @@ import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.UnsupportedCallbackException; import org.apache.cxf.attachment.AttachmentDataSource; +import org.apache.cxf.attachment.AttachmentUtil; +import org.apache.cxf.common.util.StringUtils; import org.apache.cxf.message.Attachment; import org.apache.cxf.message.Message; +import org.apache.cxf.message.MessageUtils; import org.apache.wss4j.common.ext.AttachmentRemovalCallback; import org.apache.wss4j.common.ext.AttachmentRequestCallback; import org.apache.wss4j.common.ext.AttachmentResultCallback; @@ -46,16 +49,19 @@ import org.apache.wss4j.common.ext.AttachmentResultCallback; public class AttachmentCallbackHandler implements CallbackHandler { private final Collection<org.apache.cxf.message.Attachment> attachments; + private final String defaultMimeType; public AttachmentCallbackHandler(Message message) { if (message.getAttachments() == null) { message.setAttachments(new ArrayList<Attachment>()); } attachments = message.getAttachments(); + defaultMimeType = MessageUtils.getContextualString(message, AttachmentUtil.ATTACHMENT_CONTENT_TYPE, "application/octet-stream"); } public AttachmentCallbackHandler(Collection<org.apache.cxf.message.Attachment> attachments) { this.attachments = attachments; + this.defaultMimeType = null; } @Override @@ -76,12 +82,17 @@ public class AttachmentCallbackHandler implements CallbackHandler { } else if (callback instanceof AttachmentResultCallback) { AttachmentResultCallback attachmentResultCallback = (AttachmentResultCallback) callback; + String mimeType = attachmentResultCallback.getAttachment().getMimeType(); + if (StringUtils.isEmpty(mimeType)) { + mimeType = defaultMimeType; + } + org.apache.cxf.attachment.AttachmentImpl securedAttachment = new org.apache.cxf.attachment.AttachmentImpl( attachmentResultCallback.getAttachmentId(), new DataHandler( new AttachmentDataSource( - attachmentResultCallback.getAttachment().getMimeType(), + mimeType, attachmentResultCallback.getAttachment().getSourceStream()) ) );