Skip to content

Commit

Permalink
Merge pull request #675 from mbfreder/multipart-form-issue
Browse files Browse the repository at this point in the history
Fix: Multipart files processing when Array of files with same fieldName is sent in request
  • Loading branch information
deki authored Nov 3, 2023
2 parents c517d29 + 3b00e72 commit 8b069b2
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import java.time.ZonedDateTime;
import java.time.format.DateTimeParseException;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class AwsHttpApiV2ProxyHttpServletRequest extends AwsHttpServletRequest {
Expand Down Expand Up @@ -234,16 +235,6 @@ public void logout() throws ServletException {
throw new UnsupportedOperationException();
}

@Override
public Collection<Part> getParts() throws IOException, ServletException {
return getMultipartFormParametersMap().values();
}

@Override
public Part getPart(String s) throws IOException, ServletException {
return getMultipartFormParametersMap().get(s);
}

@Override
public <T extends HttpUpgradeHandler> T upgrade(Class<T> aClass) throws IOException, ServletException {
throw new UnsupportedOperationException();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public abstract class AwsHttpServletRequest implements HttpServletRequest {
private ServletContext servletContext;
private AwsHttpSession session;
private String queryString;
private Map<String, Part> multipartFormParameters;
private Map<String, List<Part>> multipartFormParameters;
private Map<String, List<String>> urlEncodedFormParameters;

protected AwsHttpServletResponse response;
Expand Down Expand Up @@ -510,8 +510,29 @@ protected Map<String, List<String>> getFormUrlEncodedParametersMap() {
return urlEncodedFormParameters;
}

@Override
public Collection<Part> getParts()
throws IOException, ServletException {
List<Part> partList =
getMultipartFormParametersMap().values().stream()
.flatMap(List::stream)
.collect(Collectors.toList());
return partList;
}

@Override
public Part getPart(String s)
throws IOException, ServletException {
// In case there's multiple files with the same fieldName, we return the first one in the list
List<Part> values = getMultipartFormParametersMap().get(s);
if (Objects.isNull(values)) {
return null;
}
return getMultipartFormParametersMap().get(s).get(0);
}

@SuppressFBWarnings({"FILE_UPLOAD_FILENAME", "WEAK_FILENAMEUTILS"})
protected Map<String, Part> getMultipartFormParametersMap() {
protected Map<String, List<Part>> getMultipartFormParametersMap() {
if (multipartFormParameters != null) {
return multipartFormParameters;
}
Expand All @@ -537,7 +558,7 @@ protected Map<String, Part> getMultipartFormParametersMap() {
newPart.addHeader(h, item.getHeaders().getHeader(h));
});

multipartFormParameters.put(item.getFieldName(), newPart);
addPart(multipartFormParameters, item.getFieldName(), newPart);
}
} catch (FileUploadException e) {
Timer.stop("SERVLET_REQUEST_GET_MULTIPART_PARAMS");
Expand All @@ -546,6 +567,14 @@ protected Map<String, Part> getMultipartFormParametersMap() {
Timer.stop("SERVLET_REQUEST_GET_MULTIPART_PARAMS");
return multipartFormParameters;
}
private void addPart(Map<String, List<Part>> params, String fieldName, Part newPart) {
List<Part> partList = params.get(fieldName);
if (Objects.isNull(partList)) {
partList = new ArrayList<>();
params.put(fieldName, partList);
}
partList.add(newPart);
}

protected String[] getQueryParamValues(MultiValuedTreeMap<String, String> qs, String key, boolean isCaseSensitive) {
List<String> value = getQueryParamValuesAsList(qs, key, isCaseSensitive);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -255,21 +255,6 @@ public void logout()
throw new UnsupportedOperationException();
}


@Override
public Collection<Part> getParts()
throws IOException, ServletException {
return getMultipartFormParametersMap().values();
}


@Override
public Part getPart(String s)
throws IOException, ServletException {
return getMultipartFormParametersMap().get(s);
}


@Override
public <T extends HttpUpgradeHandler> T upgrade(Class<T> aClass)
throws IOException, ServletException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
import com.amazonaws.serverless.proxy.model.AwsProxyRequest;
import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder;

import jakarta.servlet.http.Part;
import org.apache.commons.io.IOUtils;
import org.apache.hc.client5.http.entity.mime.MultipartPartBuilder;
import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder;
Expand All @@ -17,10 +19,7 @@

import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Base64;
import java.util.Collections;
import java.util.Map;
import java.util.Random;
import java.util.*;

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

Expand All @@ -31,6 +30,7 @@ public class AwsProxyHttpServletRequestFormTest {
private static final String PART_KEY_2 = "test2";
private static final String PART_VALUE_2 = "value2";
private static final String FILE_KEY = "file_upload_1";
private static final String FILE_KEY_2 = "file_upload_2";
private static final String FILE_NAME = "testImage.jpg";

private static final String ENCODED_VALUE = "test123a%3D1%262@3";
Expand All @@ -41,6 +41,7 @@ public class AwsProxyHttpServletRequestFormTest {
.build();
private static final int FILE_SIZE = 512;
private static byte[] FILE_BYTES = new byte[FILE_SIZE];
private static byte[] FILE_BYTES_2 = new byte[FILE_SIZE];
static {
new Random().nextBytes(FILE_BYTES);
}
Expand All @@ -49,6 +50,10 @@ public class AwsProxyHttpServletRequestFormTest {
.addTextBody(PART_KEY_2, PART_VALUE_2)
.addBinaryBody(FILE_KEY, FILE_BYTES, ContentType.IMAGE_JPEG, FILE_NAME)
.build();
private static final HttpEntity MULTIPART_BINARY_DATA_2 = MultipartEntityBuilder.create()
.addBinaryBody(FILE_KEY, FILE_BYTES, ContentType.IMAGE_JPEG, FILE_NAME)
.addBinaryBody(FILE_KEY, FILE_BYTES_2, ContentType.IMAGE_JPEG, FILE_NAME)
.build();
private static final String ENCODED_FORM_ENTITY = PART_KEY_1 + "=" + ENCODED_VALUE + "&" + PART_KEY_2 + "=" + PART_VALUE_2;

@Test
Expand Down Expand Up @@ -109,6 +114,30 @@ void multipart_getParts_binary() {
}
}

@Test
void multipart_getParts_returnsMultiplePartsWithSameFieldName() {
try {
AwsProxyRequest proxyRequest = new AwsProxyRequestBuilder("/form", "POST")
.header(HttpHeaders.CONTENT_TYPE, MULTIPART_BINARY_DATA_2.getContentType())
.header(HttpHeaders.CONTENT_LENGTH, MULTIPART_BINARY_DATA_2.getContentLength() + "")
.binaryBody(MULTIPART_BINARY_DATA_2.getContent())
.build();

HttpServletRequest request = new AwsProxyHttpServletRequest(proxyRequest, null, null);
assertNotNull(request.getParts());
assertEquals(2, request.getParts().size());
assertNotNull(request.getPart(FILE_KEY));
List<Part> partList = new ArrayList<>(request.getParts());
assertEquals(partList.get(0).getSubmittedFileName(), partList.get(1).getSubmittedFileName());
assertEquals(partList.get(0).getName(), partList.get(1).getName());
assertEquals(FILE_SIZE, request.getPart(FILE_KEY).getSize());
assertEquals(FILE_KEY, request.getPart(FILE_KEY).getName());
assertEquals(FILE_NAME, request.getPart(FILE_KEY).getSubmittedFileName());
} catch (IOException | ServletException e) {
fail(e.getMessage());
}
}

@Test
void postForm_getParamsBase64Encoded_expectAllParams() {
AwsProxyRequest proxyRequest = new AwsProxyRequestBuilder("/form", "POST")
Expand Down

0 comments on commit 8b069b2

Please sign in to comment.