This is an automated email from the ASF dual-hosted git repository.
reta pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cxf.git
The following commit(s) were added to refs/heads/main by this push:
new e92e6ebe08 [CXF-9207] Add space after semicolon in multipart
Content-Disposition headers (#3018)
e92e6ebe08 is described below
commit e92e6ebe085113183dacd3286ede8722496f5336
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)
---
.../org/apache/cxf/attachment/AttachmentUtil.java | 2 +-
.../cxf/jaxrs/provider/EntityPartProvider.java | 7 +-
.../cxf/jaxrs/provider/MultipartProvider.java | 24 +++----
.../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, 110 insertions(+), 61 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 039189385c..42cffc8c02 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 319bacf54f..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,14 +357,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 {
@@ -450,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,
@@ -475,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