You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by re...@apache.org on 2026/04/10 15:04:16 UTC

(cxf) branch 4.1.x-fixes updated (5f0b4b883f -> bbab5cbd4b)

This is an automated email from the ASF dual-hosted git repository.

reta pushed a change to branch 4.1.x-fixes
in repository https://gitbox.apache.org/repos/asf/cxf.git


    from 5f0b4b883f Update Apache Tomcat to 10.1.54
     new 3309080d23 [CXF-9207] Add space after semicolon in multipart Content-Disposition headers (#3018)
     new bbab5cbd4b Recording .gitmergeinfo Changes

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .gitmergeinfo                                      |   3 +
 .../org/apache/cxf/attachment/AttachmentUtil.java  |   2 +-
 .../cxf/jaxrs/provider/EntityPartProvider.java     |   7 +-
 .../cxf/jaxrs/provider/MultipartProvider.java      |  27 +++----
 .../cxf/systest/jaxrs/JAXRSEntityPartTest.java     |  80 +++++++++++++++------
 .../cxf/systest/jaxrs/JAXRSMultipartTest.java      |  58 ++++++++-------
 .../jaxrs/resources/{java.jpg => java;test.jpg}    | Bin
 7 files changed, 115 insertions(+), 62 deletions(-)
 copy systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/resources/{java.jpg => java;test.jpg} (100%)


(cxf) 01/02: [CXF-9207] Add space after semicolon in multipart Content-Disposition headers (#3018)

Posted by re...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

reta pushed a commit to branch 4.1.x-fixes
in repository https://gitbox.apache.org/repos/asf/cxf.git

commit 3309080d23af36d38e1f6ecbcdf1c5d40ff194e1
Author: sstremler <st...@gmail.com>
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)
    
    # Conflicts:
    #       rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/MultipartProvider.java
---
 .../org/apache/cxf/attachment/AttachmentUtil.java  |   2 +-
 .../cxf/jaxrs/provider/EntityPartProvider.java     |   7 +-
 .../cxf/jaxrs/provider/MultipartProvider.java      |  27 +++----
 .../cxf/systest/jaxrs/JAXRSEntityPartTest.java     |  80 +++++++++++++++------
 .../cxf/systest/jaxrs/JAXRSMultipartTest.java      |  58 ++++++++-------
 .../cxf/systest/jaxrs/resources/java;test.jpg      | Bin 0 -> 1262 bytes
 6 files changed, 112 insertions(+), 62 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 b6cfc56591..96fe01855d 100644
--- a/core/src/main/java/org/apache/cxf/attachment/AttachmentUtil.java
+++ b/core/src/main/java/org/apache/cxf/attachment/AttachmentUtil.java
@@ -556,7 +556,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/EntityPartProvider.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/EntityPartProvider.java
index d02fc8de8c..5fcb68da9a 100644
--- a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/EntityPartProvider.java
+++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/provider/EntityPartProvider.java
@@ -183,7 +183,7 @@ public class EntityPartProvider extends AbstractConfigurableProvider
     }
 
     private static <T> List<Attachment> convertToDataHandlers(final List<EntityPart> parts, final  Class<T> type,
-            final Type genericType, final Annotation[] anns)  throws IOException {
+            final Type genericType, final Annotation[] anns) {
         final List<Attachment> attachments = new ArrayList<>(parts.size());
         for (EntityPart part: parts) {
             attachments.add(createDataHandler(part, type, genericType, anns));
@@ -192,7 +192,7 @@ public class EntityPartProvider extends AbstractConfigurableProvider
     }
 
     private static <T> Attachment createDataHandler(final EntityPart part, final Class<T> type,
-            final Type genericType, final Annotation[] anns) throws IOException {
+            final Type genericType, final Annotation[] anns) {
 
         final String mt = Objects
             .requireNonNullElse(part.getMediaType(), MediaType.APPLICATION_OCTET_STREAM_TYPE)
@@ -205,7 +205,8 @@ public class EntityPartProvider extends AbstractConfigurableProvider
 
         return part.getFileName()
             .map(fileName -> {
-                final ContentDisposition cd = new ContentDisposition("form-data;name=file;filename=" + fileName);
+                final ContentDisposition cd = new ContentDisposition(
+                        "form-data; name=\"file\"; filename=\"" + fileName + "\"");
                 return new Attachment(part.getName(), part.getContent(), cd);
             })
             .orElseGet(() -> {
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 7618e17d60..9322a2cb67 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
@@ -346,7 +346,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;
@@ -357,13 +357,14 @@ public class MultipartProvider extends AbstractConfigurableProvider
                                      att.getObject().getClass(), new Annotation[]{},
                                      att.getContentType().toString(), id);
             MediaType mediaType = httpHeaders.getMediaType();
-            Attachment ret = null;
-            if (MediaType.MULTIPART_FORM_DATA_TYPE.isCompatible(mediaType)) {
+            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 {
@@ -449,12 +450,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,
@@ -474,7 +475,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/JAXRSEntityPartTest.java b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSEntityPartTest.java
index 6506372b72..c7ecd4b1a0 100644
--- a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSEntityPartTest.java
+++ b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSEntityPartTest.java
@@ -19,9 +19,13 @@
 
 package org.apache.cxf.systest.jaxrs;
 
+import java.io.ByteArrayOutputStream;
 import java.io.File;
+import java.io.IOException;
 import java.io.InputStream;
+import java.io.PrintWriter;
 import java.io.SequenceInputStream;
+import java.net.URISyntaxException;
 import java.nio.file.Files;
 import java.util.List;
 
@@ -29,9 +33,9 @@ import jakarta.ws.rs.core.EntityPart;
 import jakarta.ws.rs.core.GenericType;
 import jakarta.ws.rs.core.MediaType;
 import jakarta.ws.rs.core.Response;
+import org.apache.cxf.ext.logging.LoggingOutInterceptor;
 import org.apache.cxf.helpers.IOUtils;
 import org.apache.cxf.jaxrs.client.WebClient;
-import org.apache.cxf.jaxrs.utils.ParameterizedListType;
 import org.apache.cxf.testutil.common.AbstractBusClientServerTestBase;
 
 import org.junit.BeforeClass;
@@ -49,8 +53,7 @@ import static org.junit.Assert.assertTrue;
 
 public class JAXRSEntityPartTest extends AbstractBusClientServerTestBase {
     public static final String PORT = EntityPartServer.PORT;
-    public static final String PORTINV = allocatePort(JAXRSEntityPartTest.class, 1);
-    
+
     @BeforeClass
     public static void startServers() throws Exception {
         assertTrue("server did not launch correctly", launchServer(EntityPartServer.class, true));
@@ -59,33 +62,37 @@ public class JAXRSEntityPartTest extends AbstractBusClientServerTestBase {
 
     @Test
     public void testUploadImage() throws Exception {
+        this.uploadImage("/org/apache/cxf/systest/jaxrs/resources/java.jpg");
+    }
+
+    @Test
+    public void testUploadImageWithSemicolon() throws Exception {
+        this.uploadImage("/org/apache/cxf/systest/jaxrs/resources/java;test.jpg");
+    }
+
+    @Test
+    public void testContentDispositionInRequestIsWhitespaceSeparated() throws URISyntaxException, IOException {
         final File file = new File(getClass().getResource("/org/apache/cxf/systest/jaxrs/resources/java.jpg")
                 .toURI().getPath());
         final String address = "http://localhost:" + PORT + "/bookstore/books/images";
+
+        ByteArrayOutputStream output = new ByteArrayOutputStream();
+        PrintWriter writer = new PrintWriter(output, true);
+        LoggingOutInterceptor out = new LoggingOutInterceptor(writer);
+
         final WebClient client = WebClient.create(address);
+        client.getConfiguration().getOutInterceptors().add(out);
         client.type(MediaType.MULTIPART_FORM_DATA).accept(MediaType.MULTIPART_FORM_DATA);
 
         try (InputStream is = Files.newInputStream(file.toPath())) {
             final EntityPart part = EntityPart
-                .withFileName(file.getName())
-                .content(is)
-                .build();
-
-            try (Response response = client.postCollection(List.of(part), EntityPart.class)) {
-                assertThat(response.getStatus(), equalTo(200));
-
-                @SuppressWarnings("unchecked")
-                final List<EntityPart> parts = (List<EntityPart>) response
-                    .readEntity(new GenericType<>(new ParameterizedListType(EntityPart.class)));
-
-                assertThat(parts, hasSize(1));
-                assertThat(parts.get(0), is(not(nullValue())));
+                    .withFileName(file.getName())
+                    .content(is)
+                    .build();
 
-                assertThat(parts.get(0).getFileName().isPresent(), is(true));
-                assertThat(parts.get(0).getFileName().get(), equalTo(part.getFileName().get()));
-
-                assertArrayEquals(IOUtils.readBytesFromStream(parts.get(0).getContent()), 
-                    IOUtils.readBytesFromStream(Files.newInputStream(file.toPath())));
+            try (Response ignored = client.postCollection(List.of(part), EntityPart.class)) {
+                String expected = "Content-Disposition: form-data; name=\"file\"; filename=\"java.jpg\"";
+                assertTrue(output.toString().contains(expected));
             }
         }
     }
@@ -152,4 +159,35 @@ public class JAXRSEntityPartTest extends AbstractBusClientServerTestBase {
         }
         return str;
     }
+
+    private void uploadImage(String path) throws URISyntaxException, IOException {
+        final File file = new File(getClass().getResource(path)
+                .toURI().getPath());
+        final String address = "http://localhost:" + PORT + "/bookstore/books/images";
+
+        final WebClient client = WebClient.create(address);
+        client.type(MediaType.MULTIPART_FORM_DATA).accept(MediaType.MULTIPART_FORM_DATA);
+
+        try (InputStream is = Files.newInputStream(file.toPath())) {
+            final EntityPart part = EntityPart
+                    .withFileName(file.getName())
+                    .content(is)
+                    .build();
+
+            try (Response response = client.postCollection(List.of(part), EntityPart.class)) {
+                assertThat(response.getStatus(), equalTo(200));
+
+                final List<EntityPart> parts = response.readEntity(new GenericType<>() { });
+
+                assertThat(parts, hasSize(1));
+                assertThat(parts.get(0), is(not(nullValue())));
+
+                assertThat(parts.get(0).getFileName().isPresent(), is(true));
+                assertThat(parts.get(0).getFileName().get(), equalTo(part.getFileName().get()));
+
+                assertArrayEquals(IOUtils.readBytesFromStream(parts.get(0).getContent()),
+                        IOUtils.readBytesFromStream(Files.newInputStream(file.toPath())));
+            }
+        }
+    }
 }
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 706ebdf970..030923dfc4 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,7 +95,7 @@ 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",
@@ -104,8 +105,8 @@ public class JAXRSMultipartTest extends AbstractBusClientServerTestBase {
         bus.setProperty(ProviderFactory.SKIP_JAKARTA_JSON_PROVIDERS_REGISTRATION, true);
     }
 
-    
-    
+
+
     @Test
     public void testBookAsRootAttachmentStreamSource() throws Exception {
         String address = "http://localhost:" + PORT + "/bookstore/books/stream";
@@ -529,7 +530,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);
 
@@ -539,8 +540,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
@@ -1032,23 +1033,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
@@ -1168,7 +1158,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();
@@ -1281,6 +1271,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


(cxf) 02/02: Recording .gitmergeinfo Changes

Posted by re...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

reta pushed a commit to branch 4.1.x-fixes
in repository https://gitbox.apache.org/repos/asf/cxf.git

commit bbab5cbd4b9a7c34071266902ece96dad958c147
Author: Andriy Redko <dr...@gmail.com>
AuthorDate: Fri Apr 10 10:01:08 2026 -0400

    Recording .gitmergeinfo Changes
---
 .gitmergeinfo | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/.gitmergeinfo b/.gitmergeinfo
index f7576e2780..af81bec1ab 100644
--- a/.gitmergeinfo
+++ b/.gitmergeinfo
@@ -18,12 +18,14 @@ M 10780b457e43c232cacfc767d48268a385ae4bc4
 M 14599134aa443a21208fef21c57ed0af4a768df8
 M 14af98e9ae2193603f8fcf82dde271af065cabb4
 M 15ef60633f09b27a131202c303de95f0f88327c2
+M 166f4b3c04c12445b1d17b7d10cedaa2ae3b12c0
 M 16881b7b614f4fede59dd801ec70e04d00668870
 M 17d67e790c59c9f8afc364267657b39ad1e915b3
 M 1821b7ce58a01b2f810995d26918ff2734a855a3
 M 1825796ea7e52d845ce07920358e08559afd36af
 M 1a20d8a78655a26af87c54aabcb521415b3a3cc4
 M 1b2797a6322f264f99342caa42d06719aa9a0bb6
+M 1b5d9a50adb4a13f1e18fe74417a5a987e43c575
 M 1c08d09e491c7c4eb88c0113b9d4799a9267d56a
 M 1dfcfd5871fce66ecbe544239bc65faf95e00d78
 M 1ed2a16caf52eb9559d175245a931ac5c0488e06
@@ -201,6 +203,7 @@ M e40d8dd091a741051d9b4aa7e0d1e3f37b8d36b6
 M e438db25f51db6ece35cc0b74866e57f956314d1
 M e8395ad45d9f65ca3704bc508f73073d9d12f712
 M e920da53d7538ed702746dec94ddae2aa82e3a01
+M e92e6ebe085113183dacd3286ede8722496f5336
 M eaa5a4fea8a04d7889bf8cf037bef0d73e90f876
 M eafbf81a278b1847bc0d1594f93e4631e454c7a8
 M edb3add2a16fcfd7af7c471240eca312056b319a