This is an automated email from the ASF dual-hosted git repository. reta pushed a commit to branch 3.6.x-fixes in repository https://gitbox.apache.org/repos/asf/cxf.git
commit d61e808c843b7ba63668528bb21b2c202a9a8692 Author: sstremler <[email protected]> AuthorDate: Fri Apr 10 02:23:34 2026 +0200 [CXF-9207] Add space after semicolon in multipart Content-Disposition headers (#3018) (cherry picked from commit e92e6ebe085113183dacd3286ede8722496f5336) --- .../org/apache/cxf/attachment/AttachmentUtil.java | 2 +- .../cxf/jaxrs/provider/MultipartProvider.java | 24 ++++----- .../cxf/systest/jaxrs/JAXRSMultipartTest.java | 58 ++++++++++++--------- .../cxf/systest/jaxrs/resources/java;test.jpg | Bin 0 -> 1262 bytes 4 files changed, 47 insertions(+), 37 deletions(-) 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 eff623103d..f8fdd69216 100644 --- a/core/src/main/java/org/apache/cxf/attachment/AttachmentUtil.java +++ b/core/src/main/java/org/apache/cxf/attachment/AttachmentUtil.java @@ -557,7 +557,7 @@ public final class AttachmentUtil { if (f.exists() && f.isFile()) { file = f.getName(); } - att.setHeader("Content-Disposition", "attachment;name=\"" + file + "\""); + att.setHeader("Content-Disposition", "attachment; name=\"" + file + "\""); } att.setXOP(isXop); return att; diff --git a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/MultipartProvider.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/MultipartProvider.java index 4a0d74ebd1..2f1d03e2c4 100644 --- a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/MultipartProvider.java +++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/MultipartProvider.java @@ -347,7 +347,7 @@ public class MultipartProvider extends AbstractConfigurableProvider } else if (File.class.isAssignableFrom(obj.getClass())) { File f = (File)obj; ContentDisposition cd = mainMediaType.startsWith(MediaType.MULTIPART_FORM_DATA) - ? new ContentDisposition("form-data;name=file;filename=" + f.getName()) : null; + ? new ContentDisposition("form-data; name=\"file\"; filename=\"" + f.getName() + "\"") : null; return new Attachment(AttachmentUtil.BODY_ATTACHMENT_ID, Files.newInputStream(f.toPath()), cd); } else if (Attachment.class.isAssignableFrom(obj.getClass())) { Attachment att = (Attachment)obj; @@ -358,14 +358,14 @@ public class MultipartProvider extends AbstractConfigurableProvider att.getObject().getClass(), new Annotation[]{}, att.getContentType().toString(), id); MediaType mediaType = httpHeaders.getMediaType(); - Attachment ret = null; + Attachment ret; if (MediaType.MULTIPART_FORM_DATA_TYPE.isCompatible(mediaType) && att.getHeader("Content-Disposition") == null) { ContentDisposition cd = new - ContentDisposition("form-data;name=\"" + ContentDisposition("form-data; name=\"" + att.getContentId() + "\""); - MultivaluedMap<String, String> newHeaders = - new MetadataMap<String, String>(att.getHeaders(), false, true); + MultivaluedMap<String, String> newHeaders = + new MetadataMap<>(att.getHeaders(), false, true); newHeaders.putSingle("Content-Disposition", cd.toString()); ret = new Attachment(att.getContentId(), dh, newHeaders); } else { @@ -451,12 +451,12 @@ public class MultipartProvider extends AbstractConfigurableProvider } private static class MessageBodyWriterDataHandler<T> extends DataHandler { - private MessageBodyWriter<T> writer; - private T obj; - private Class<T> cls; - private Type genericType; - private Annotation[] anns; - private MediaType contentType; + private final MessageBodyWriter<T> writer; + private final T obj; + private final Class<T> cls; + private final Type genericType; + private final Annotation[] anns; + private final MediaType contentType; MessageBodyWriterDataHandler(MessageBodyWriter<T> writer, T obj, Class<T> cls, @@ -476,7 +476,7 @@ public class MultipartProvider extends AbstractConfigurableProvider public void writeTo(OutputStream os) { try { writer.writeTo(obj, cls, genericType, anns, contentType, - new MetadataMap<String, Object>(), os); + new MetadataMap<>(), os); } catch (IOException ex) { throw ExceptionUtils.toInternalServerErrorException(ex, null); } diff --git a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSMultipartTest.java b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSMultipartTest.java index d892dfc0e6..55dddc44f9 100644 --- a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSMultipartTest.java +++ b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSMultipartTest.java @@ -28,6 +28,7 @@ import java.io.InputStream; import java.io.PrintWriter; import java.io.PushbackInputStream; import java.lang.annotation.Annotation; +import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -94,15 +95,15 @@ import static org.junit.Assert.assertTrue; public class JAXRSMultipartTest extends AbstractBusClientServerTestBase { public static final String PORT = MultipartServer.PORT; public static final String PORTINV = allocatePort(JAXRSMultipartTest.class, 1); - + @BeforeClass public static void startServers() throws Exception { assertTrue("server did not launch correctly", launchServer(MultipartServer.class, true)); } - - + + @Test public void testBookAsRootAttachmentStreamSource() throws Exception { String address = "http://localhost:" + PORT + "/bookstore/books/stream"; @@ -526,7 +527,7 @@ public class JAXRSMultipartTest extends AbstractBusClientServerTestBase { } @Test - public void testNullPartProxy() throws Exception { + public void testNullPartProxy() { ByteArrayOutputStream output = new ByteArrayOutputStream(); PrintWriter writer = new PrintWriter(output, true); @@ -536,8 +537,8 @@ public class JAXRSMultipartTest extends AbstractBusClientServerTestBase { JAXRSClientFactory.create("http://localhost:" + PORT, MultipartStore.class); WebClient.getConfig(store).getOutInterceptors().add(out); assertEquals("nobody home2", store.testNullParts("value1", null)); - String expected = "Content-Disposition: form-data;name=\"someid\""; - assertTrue(output.toString().indexOf(expected) != -1); + String expected = "Content-Disposition: form-data; name=\"someid\""; + assertTrue(output.toString().contains(expected)); } @Test @@ -1029,23 +1030,12 @@ public class JAXRSMultipartTest extends AbstractBusClientServerTestBase { @Test public void testUploadImageFromForm2() throws Exception { - File file = - new File(getClass().getResource("/org/apache/cxf/systest/jaxrs/resources/java.jpg") - .toURI().getPath()); - String address = "http://localhost:" + PORT + "/bookstore/books/formimage2"; - WebClient client = WebClient.create(address); - client.type("multipart/form-data").accept("multipart/form-data"); - WebClient.getConfig(client).getRequestContext().put("support.type.as.multipart", - "true"); - MultipartBody body2 = client.post(file, MultipartBody.class); - InputStream is2 = body2.getRootAttachment().getDataHandler().getInputStream(); - byte[] image1 = IOUtils.readBytesFromStream( - getClass().getResourceAsStream("/org/apache/cxf/systest/jaxrs/resources/java.jpg")); - byte[] image2 = IOUtils.readBytesFromStream(is2); - assertArrayEquals(image1, image2); - ContentDisposition cd2 = body2.getRootAttachment().getContentDisposition(); - assertEquals("form-data;name=file;filename=java.jpg", cd2.toString()); - assertEquals("java.jpg", cd2.getParameter("filename")); + this.uploadImageFromForm2("java.jpg"); + } + + @Test + public void testUploadImageFromForm2WithSemicolon() throws Exception { + this.uploadImageFromForm2("java;test.jpg"); } @Test @@ -1165,7 +1155,7 @@ public class JAXRSMultipartTest extends AbstractBusClientServerTestBase { Attachment att = new Attachment(headers, handler, null); try (Response response = client.post(att)) { - assertEquals(response.getStatus(), 200); + assertEquals(200, response.getStatus()); } client.close(); @@ -1277,6 +1267,26 @@ public class JAXRSMultipartTest extends AbstractBusClientServerTestBase { return str; } + private void uploadImageFromForm2(String filename) throws URISyntaxException, IOException { + File file = + new File(getClass().getResource("/org/apache/cxf/systest/jaxrs/resources/" + filename) + .toURI().getPath()); + String address = "http://localhost:" + PORT + "/bookstore/books/formimage2"; + WebClient client = WebClient.create(address); + client.type("multipart/form-data").accept("multipart/form-data"); + WebClient.getConfig(client).getRequestContext().put("support.type.as.multipart", + "true"); + MultipartBody body2 = client.post(file, MultipartBody.class); + InputStream is2 = body2.getRootAttachment().getDataHandler().getInputStream(); + byte[] image1 = IOUtils.readBytesFromStream( + getClass().getResourceAsStream("/org/apache/cxf/systest/jaxrs/resources/" + filename)); + byte[] image2 = IOUtils.readBytesFromStream(is2); + assertArrayEquals(image1, image2); + ContentDisposition cd2 = body2.getRootAttachment().getContentDisposition(); + assertEquals("form-data; name=\"file\"; filename=\"" + filename + "\"", cd2.toString()); + assertEquals(filename, cd2.getParameter("filename")); + } + /** * Windows attachment handling */ diff --git a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/resources/java;test.jpg b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/resources/java;test.jpg new file mode 100644 index 0000000000..dc59bee5e2 Binary files /dev/null and b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/resources/java;test.jpg differ
