Skip to content

Commit 8b069b2

Browse files
authored
Merge pull request #675 from mbfreder/multipart-form-issue
Fix: Multipart files processing when Array of files with same fieldName is sent in request
2 parents c517d29 + 3b00e72 commit 8b069b2

File tree

4 files changed

+66
-32
lines changed

4 files changed

+66
-32
lines changed

aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsHttpApiV2ProxyHttpServletRequest.java

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import java.time.ZonedDateTime;
3838
import java.time.format.DateTimeParseException;
3939
import java.util.*;
40+
import java.util.stream.Collectors;
4041
import java.util.stream.Stream;
4142

4243
public class AwsHttpApiV2ProxyHttpServletRequest extends AwsHttpServletRequest {
@@ -234,16 +235,6 @@ public void logout() throws ServletException {
234235
throw new UnsupportedOperationException();
235236
}
236237

237-
@Override
238-
public Collection<Part> getParts() throws IOException, ServletException {
239-
return getMultipartFormParametersMap().values();
240-
}
241-
242-
@Override
243-
public Part getPart(String s) throws IOException, ServletException {
244-
return getMultipartFormParametersMap().get(s);
245-
}
246-
247238
@Override
248239
public <T extends HttpUpgradeHandler> T upgrade(Class<T> aClass) throws IOException, ServletException {
249240
throw new UnsupportedOperationException();

aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsHttpServletRequest.java

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ public abstract class AwsHttpServletRequest implements HttpServletRequest {
8686
private ServletContext servletContext;
8787
private AwsHttpSession session;
8888
private String queryString;
89-
private Map<String, Part> multipartFormParameters;
89+
private Map<String, List<Part>> multipartFormParameters;
9090
private Map<String, List<String>> urlEncodedFormParameters;
9191

9292
protected AwsHttpServletResponse response;
@@ -510,8 +510,29 @@ protected Map<String, List<String>> getFormUrlEncodedParametersMap() {
510510
return urlEncodedFormParameters;
511511
}
512512

513+
@Override
514+
public Collection<Part> getParts()
515+
throws IOException, ServletException {
516+
List<Part> partList =
517+
getMultipartFormParametersMap().values().stream()
518+
.flatMap(List::stream)
519+
.collect(Collectors.toList());
520+
return partList;
521+
}
522+
523+
@Override
524+
public Part getPart(String s)
525+
throws IOException, ServletException {
526+
// In case there's multiple files with the same fieldName, we return the first one in the list
527+
List<Part> values = getMultipartFormParametersMap().get(s);
528+
if (Objects.isNull(values)) {
529+
return null;
530+
}
531+
return getMultipartFormParametersMap().get(s).get(0);
532+
}
533+
513534
@SuppressFBWarnings({"FILE_UPLOAD_FILENAME", "WEAK_FILENAMEUTILS"})
514-
protected Map<String, Part> getMultipartFormParametersMap() {
535+
protected Map<String, List<Part>> getMultipartFormParametersMap() {
515536
if (multipartFormParameters != null) {
516537
return multipartFormParameters;
517538
}
@@ -537,7 +558,7 @@ protected Map<String, Part> getMultipartFormParametersMap() {
537558
newPart.addHeader(h, item.getHeaders().getHeader(h));
538559
});
539560

540-
multipartFormParameters.put(item.getFieldName(), newPart);
561+
addPart(multipartFormParameters, item.getFieldName(), newPart);
541562
}
542563
} catch (FileUploadException e) {
543564
Timer.stop("SERVLET_REQUEST_GET_MULTIPART_PARAMS");
@@ -546,6 +567,14 @@ protected Map<String, Part> getMultipartFormParametersMap() {
546567
Timer.stop("SERVLET_REQUEST_GET_MULTIPART_PARAMS");
547568
return multipartFormParameters;
548569
}
570+
private void addPart(Map<String, List<Part>> params, String fieldName, Part newPart) {
571+
List<Part> partList = params.get(fieldName);
572+
if (Objects.isNull(partList)) {
573+
partList = new ArrayList<>();
574+
params.put(fieldName, partList);
575+
}
576+
partList.add(newPart);
577+
}
549578

550579
protected String[] getQueryParamValues(MultiValuedTreeMap<String, String> qs, String key, boolean isCaseSensitive) {
551580
List<String> value = getQueryParamValuesAsList(qs, key, isCaseSensitive);

aws-serverless-java-container-core/src/main/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyHttpServletRequest.java

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -255,21 +255,6 @@ public void logout()
255255
throw new UnsupportedOperationException();
256256
}
257257

258-
259-
@Override
260-
public Collection<Part> getParts()
261-
throws IOException, ServletException {
262-
return getMultipartFormParametersMap().values();
263-
}
264-
265-
266-
@Override
267-
public Part getPart(String s)
268-
throws IOException, ServletException {
269-
return getMultipartFormParametersMap().get(s);
270-
}
271-
272-
273258
@Override
274259
public <T extends HttpUpgradeHandler> T upgrade(Class<T> aClass)
275260
throws IOException, ServletException {

aws-serverless-java-container-core/src/test/java/com/amazonaws/serverless/proxy/internal/servlet/AwsProxyHttpServletRequestFormTest.java

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
import com.amazonaws.serverless.proxy.model.AwsProxyRequest;
55
import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder;
66

7+
import jakarta.servlet.http.Part;
78
import org.apache.commons.io.IOUtils;
9+
import org.apache.hc.client5.http.entity.mime.MultipartPartBuilder;
810
import org.apache.hc.core5.http.ContentType;
911
import org.apache.hc.core5.http.HttpEntity;
1012
import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder;
@@ -17,10 +19,7 @@
1719

1820
import java.io.IOException;
1921
import java.nio.charset.Charset;
20-
import java.util.Base64;
21-
import java.util.Collections;
22-
import java.util.Map;
23-
import java.util.Random;
22+
import java.util.*;
2423

2524
import static org.junit.jupiter.api.Assertions.*;
2625

@@ -31,6 +30,7 @@ public class AwsProxyHttpServletRequestFormTest {
3130
private static final String PART_KEY_2 = "test2";
3231
private static final String PART_VALUE_2 = "value2";
3332
private static final String FILE_KEY = "file_upload_1";
33+
private static final String FILE_KEY_2 = "file_upload_2";
3434
private static final String FILE_NAME = "testImage.jpg";
3535

3636
private static final String ENCODED_VALUE = "test123a%3D1%262@3";
@@ -41,6 +41,7 @@ public class AwsProxyHttpServletRequestFormTest {
4141
.build();
4242
private static final int FILE_SIZE = 512;
4343
private static byte[] FILE_BYTES = new byte[FILE_SIZE];
44+
private static byte[] FILE_BYTES_2 = new byte[FILE_SIZE];
4445
static {
4546
new Random().nextBytes(FILE_BYTES);
4647
}
@@ -49,6 +50,10 @@ public class AwsProxyHttpServletRequestFormTest {
4950
.addTextBody(PART_KEY_2, PART_VALUE_2)
5051
.addBinaryBody(FILE_KEY, FILE_BYTES, ContentType.IMAGE_JPEG, FILE_NAME)
5152
.build();
53+
private static final HttpEntity MULTIPART_BINARY_DATA_2 = MultipartEntityBuilder.create()
54+
.addBinaryBody(FILE_KEY, FILE_BYTES, ContentType.IMAGE_JPEG, FILE_NAME)
55+
.addBinaryBody(FILE_KEY, FILE_BYTES_2, ContentType.IMAGE_JPEG, FILE_NAME)
56+
.build();
5257
private static final String ENCODED_FORM_ENTITY = PART_KEY_1 + "=" + ENCODED_VALUE + "&" + PART_KEY_2 + "=" + PART_VALUE_2;
5358

5459
@Test
@@ -109,6 +114,30 @@ void multipart_getParts_binary() {
109114
}
110115
}
111116

117+
@Test
118+
void multipart_getParts_returnsMultiplePartsWithSameFieldName() {
119+
try {
120+
AwsProxyRequest proxyRequest = new AwsProxyRequestBuilder("/form", "POST")
121+
.header(HttpHeaders.CONTENT_TYPE, MULTIPART_BINARY_DATA_2.getContentType())
122+
.header(HttpHeaders.CONTENT_LENGTH, MULTIPART_BINARY_DATA_2.getContentLength() + "")
123+
.binaryBody(MULTIPART_BINARY_DATA_2.getContent())
124+
.build();
125+
126+
HttpServletRequest request = new AwsProxyHttpServletRequest(proxyRequest, null, null);
127+
assertNotNull(request.getParts());
128+
assertEquals(2, request.getParts().size());
129+
assertNotNull(request.getPart(FILE_KEY));
130+
List<Part> partList = new ArrayList<>(request.getParts());
131+
assertEquals(partList.get(0).getSubmittedFileName(), partList.get(1).getSubmittedFileName());
132+
assertEquals(partList.get(0).getName(), partList.get(1).getName());
133+
assertEquals(FILE_SIZE, request.getPart(FILE_KEY).getSize());
134+
assertEquals(FILE_KEY, request.getPart(FILE_KEY).getName());
135+
assertEquals(FILE_NAME, request.getPart(FILE_KEY).getSubmittedFileName());
136+
} catch (IOException | ServletException e) {
137+
fail(e.getMessage());
138+
}
139+
}
140+
112141
@Test
113142
void postForm_getParamsBase64Encoded_expectAllParams() {
114143
AwsProxyRequest proxyRequest = new AwsProxyRequestBuilder("/form", "POST")

0 commit comments

Comments
 (0)