Skip to content

Commit

Permalink
Merge pull request #616 from valerena/change-parameters
Browse files Browse the repository at this point in the history
Modify algorithm for parameter transformation logic
  • Loading branch information
deki authored Oct 19, 2023
2 parents 32eb54a + 1a0906f commit c15f931
Show file tree
Hide file tree
Showing 2 changed files with 179 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
import java.nio.charset.StandardCharsets;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;


/**
Expand Down Expand Up @@ -546,39 +548,61 @@ protected Map<String, Part> getMultipartFormParametersMap() {
}

protected String[] getQueryParamValues(MultiValuedTreeMap<String, String> qs, String key, boolean isCaseSensitive) {
List<String> value = getQueryParamValuesAsList(qs, key, isCaseSensitive);
if (value == null){
return null;
}
return value.toArray(new String[0]);
}

protected List<String> getQueryParamValuesAsList(MultiValuedTreeMap<String, String> qs, String key, boolean isCaseSensitive) {
if (qs != null) {
if (isCaseSensitive) {
return qs.get(key).toArray(new String[0]);
return qs.get(key);
}

for (String k : qs.keySet()) {
if (k.toLowerCase(Locale.getDefault()).equals(key.toLowerCase(Locale.getDefault()))) {
return qs.get(k).toArray(new String[0]);
return qs.get(k);
}
}
}

return new String[0];
return Collections.emptyList();
}

protected Map<String, String[]> generateParameterMap(MultiValuedTreeMap<String, String> qs, ContainerConfig config) {
Map<String, String[]> output = new HashMap<>();
Map<String, String[]> output;

Map<String, List<String>> params = getFormUrlEncodedParametersMap();
params.entrySet().stream().parallel().forEach(e -> {
output.put(e.getKey(), e.getValue().toArray(new String[0]));
});
Map<String, List<String>> formEncodedParams = getFormUrlEncodedParametersMap();

if (qs == null) {
// Just transform the List<String> values to String[]
output = formEncodedParams.entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, (e) -> e.getValue().toArray(new String[0])));
} else {
Map<String, List<String>> queryStringParams;
if (config.isQueryStringCaseSensitive()) {
queryStringParams = qs;
} else {
// If it's case insensitive, we check the entire map on every parameter
queryStringParams = qs.entrySet().stream().parallel().collect(
Collectors.toMap(
Map.Entry::getKey,
e -> getQueryParamValuesAsList(qs, e.getKey(), false)
));
}

// Merge formEncodedParams and queryStringParams Maps
output = Stream.of(formEncodedParams, queryStringParams).flatMap(m -> m.entrySet().stream())
.collect(
Collectors.toMap(
Map.Entry::getKey,
e -> e.getValue().toArray(new String[0]),
// If a parameter is in both Maps, we merge the list of values (and ultimately transform to String[])
(formParam, queryParam) -> Stream.of(formParam, queryParam).flatMap(Stream::of).toArray(String[]::new)
));

if (qs != null) {
qs.keySet().stream().parallel().forEach(e -> {
List<String> newValues = new ArrayList<>();
if (output.containsKey(e)) {
String[] values = output.get(e);
newValues.addAll(Arrays.asList(values));
}
newValues.addAll(Arrays.asList(getQueryParamValues(qs, e, config.isQueryStringCaseSensitive())));
output.put(e, newValues.toArray(new String[0]));
});
}

return output;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.amazonaws.serverless.proxy.internal.servlet;

import com.amazonaws.serverless.proxy.model.AwsProxyRequest;
import com.amazonaws.serverless.proxy.model.MultiValuedTreeMap;
import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder;
import com.amazonaws.serverless.proxy.internal.testutils.MockLambdaContext;
import com.amazonaws.serverless.proxy.model.ContainerConfig;
Expand All @@ -15,6 +16,8 @@

import java.util.Base64;
import java.util.List;
import java.util.Map;
import java.util.Arrays;


public class AwsHttpServletRequestTest {
Expand All @@ -33,6 +36,14 @@ public class AwsHttpServletRequestTest {
.queryString("one", "two").queryString("json", "{\"name\":\"faisal\"}").build();
private static final AwsProxyRequest multipleParams = new AwsProxyRequestBuilder("/test", "GET")
.queryString("one", "two").queryString("one", "three").queryString("json", "{\"name\":\"faisal\"}").build();
private static final AwsProxyRequest formEncodedAndQueryString = new AwsProxyRequestBuilder("/test", "POST")
.queryString("one", "two").queryString("one", "three")
.queryString("five", "six")
.form("one", "four")
.form("seven", "eight").build();
private static final AwsProxyRequest differentCasing = new AwsProxyRequestBuilder("/test", "POST")
.queryString("one", "two").queryString("one", "three")
.queryString("ONE", "four").build();

private static final MockLambdaContext mockContext = new MockLambdaContext();

Expand Down Expand Up @@ -182,4 +193,130 @@ void queryStringWithMultipleValues_generateQueryString_validQuery() {
assertTrue(parsedString.contains("json=%7B%22name%22%3A%22faisal%22%7D"));
assertTrue(parsedString.contains("&") && parsedString.indexOf("&") > 0 && parsedString.indexOf("&") < parsedString.length());
}

@Test
void parameterMap_generateParameterMap_validQuery() {
AwsProxyHttpServletRequest request = new AwsProxyHttpServletRequest(queryString, mockContext, null, config);

Map<String, String[]> paramMap = null;
try {
paramMap = request.generateParameterMap(request.getAwsProxyRequest().getMultiValueQueryStringParameters(), config);
} catch (Exception e) {
e.printStackTrace();
fail("Could not generate parameter map");
}
assertArrayEquals(new String[]{"two"}, paramMap.get("one"));
assertArrayEquals(new String[]{"four"}, paramMap.get("three"));
assertTrue(paramMap.size() == 2);
}

@Test
void parameterMap_generateParameterMap_nullParameter() {
AwsProxyHttpServletRequest request = new AwsProxyHttpServletRequest(queryStringNullValue, mockContext, null, config);
Map<String, String[]> paramMap = null;
try {
paramMap = request.generateParameterMap(request.getAwsProxyRequest().getMultiValueQueryStringParameters(), config);
} catch (Exception e) {
e.printStackTrace();
fail("Could not generate parameter map");
}

assertArrayEquals(new String[]{"two"}, paramMap.get("one"));
assertArrayEquals(new String[]{null}, paramMap.get("three"));
assertTrue(paramMap.size() == 2);
}

@Test
void parameterMapWithEncodedParams_generateParameterMap_validQuery() {
AwsProxyHttpServletRequest request = new AwsProxyHttpServletRequest(encodedQueryString, mockContext, null, config);

Map<String, String[]> paramMap = null;
try {
paramMap = request.generateParameterMap(request.getAwsProxyRequest().getMultiValueQueryStringParameters(), config);
} catch (Exception e) {
e.printStackTrace();
fail("Could not generate parameter map");
}

assertArrayEquals(new String[]{"two"}, paramMap.get("one"));
assertArrayEquals(new String[]{"{\"name\":\"faisal\"}"}, paramMap.get("json"));
assertTrue(paramMap.size() == 2);
}

@Test
void parameterMapWithMultipleValues_generateParameterMap_validQuery() {
AwsProxyHttpServletRequest request = new AwsProxyHttpServletRequest(multipleParams, mockContext, null, config);

Map<String, String[]> paramMap = null;
try {
paramMap = request.generateParameterMap(request.getAwsProxyRequest().getMultiValueQueryStringParameters(), config);
} catch (Exception e) {
e.printStackTrace();
fail("Could not generate parameter map");
}
assertArrayEquals(new String[]{"two", "three"}, paramMap.get("one"));
assertArrayEquals(new String[]{"{\"name\":\"faisal\"}"}, paramMap.get("json"));
assertTrue(paramMap.size() == 2);
}

@Test
void parameterMap_generateParameterMap_formEncodedAndQueryString() {
AwsProxyHttpServletRequest request = new AwsProxyHttpServletRequest(formEncodedAndQueryString, mockContext, null, config);

Map<String, String[]> paramMap = null;
try {
paramMap = request.generateParameterMap(request.getAwsProxyRequest().getMultiValueQueryStringParameters(), config);
} catch (Exception e) {
e.printStackTrace();
fail("Could not generate parameter map");
}
// Combines form encoded parameters (one=four) with query string (one=two,three)
// The order between them is not officially guaranteed (it could be four,two,three or two,three,four)
// Current implementation gives form encoded parameters first
assertArrayEquals(new String[]{"four", "two", "three"}, paramMap.get("one"));
assertArrayEquals(new String[]{"six"}, paramMap.get("five"));
assertArrayEquals(new String[]{"eight"}, paramMap.get("seven"));
assertTrue(paramMap.size() == 3);
}

@Test
void parameterMap_generateParameterMap_differentCasing() {
AwsProxyHttpServletRequest request = new AwsProxyHttpServletRequest(differentCasing, mockContext, null, config);

Map<String, String[]> paramMap = null;
try {
paramMap = request.generateParameterMap(request.getAwsProxyRequest().getMultiValueQueryStringParameters(), config);
} catch (Exception e) {
e.printStackTrace();
fail("Could not generate parameter map");
}
// If a parameter is duplicated but with a different casing, it's replaced with only one of them
assertArrayEquals(paramMap.get("one"), paramMap.get("ONE"));
assertTrue(paramMap.size() == 2);
}

@Test
void queryParamValues_getQueryParamValues() {
AwsProxyHttpServletRequest request = new AwsProxyHttpServletRequest(new AwsProxyRequest(), mockContext, null);
MultiValuedTreeMap<String, String> map = new MultiValuedTreeMap<>();
map.add("test", "test");
map.add("test", "test2");
String[] result1 = request.getQueryParamValues(map, "test", true);
assertArrayEquals(new String[]{"test", "test2"}, result1);
String[] result2 = request.getQueryParamValues(map, "TEST", true);
assertNull(result2);
}

@Test
void queryParamValues_getQueryParamValues_caseInsensitive() {
AwsProxyHttpServletRequest request = new AwsProxyHttpServletRequest(new AwsProxyRequest(), mockContext, null);
MultiValuedTreeMap<String, String> map = new MultiValuedTreeMap<>();
map.add("test", "test");
map.add("test", "test2");
String[] result1 = request.getQueryParamValues(map, "test", false);
assertArrayEquals(new String[]{"test", "test2"}, result1);
String[] result2 = request.getQueryParamValues(map, "TEST", false);
assertArrayEquals(new String[]{"test", "test2"}, result2);
}

}

0 comments on commit c15f931

Please sign in to comment.