Skip to content

Commit

Permalink
awsGH-754 Fix request parameter parsing
Browse files Browse the repository at this point in the history
While concentrating on REST we somehow ignored standard request parameters. This fix addresses it for both v1 and v2 requests as well as adds tests and additional endpoint to a sample to verify

Resolves aws#754
  • Loading branch information
olegz committed Jan 30, 2024
1 parent 58b296f commit c6fe79f
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,18 @@

import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.function.serverless.web.ServerlessHttpServletRequest;
import org.springframework.cloud.function.serverless.web.ServerlessMVC;
import org.springframework.util.CollectionUtils;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.MultiValueMapAdapter;
import org.springframework.util.StringUtils;
Expand Down Expand Up @@ -109,6 +113,9 @@ private static HttpServletRequest generateRequest1(String request, Context lambd
AwsProxyRequest v1Request = readValue(request, AwsProxyRequest.class, mapper);

ServerlessHttpServletRequest httpRequest = new ServerlessHttpServletRequest(servletContext, v1Request.getHttpMethod(), v1Request.getPath());

populateQueryStringparameters(v1Request.getQueryStringParameters(), httpRequest);

if (v1Request.getMultiValueHeaders() != null) {
MultiValueMapAdapter headers = new MultiValueMapAdapter(v1Request.getMultiValueHeaders());
httpRequest.setHeaders(headers);
Expand All @@ -128,16 +135,21 @@ private static HttpServletRequest generateRequest1(String request, Context lambd
securityWriter.writeSecurityContext(v1Request, lambdaContext));
return httpRequest;
}



@SuppressWarnings({ "rawtypes", "unchecked" })
private static HttpServletRequest generateRequest2(String request, Context lambdaContext,
SecurityContextWriter securityWriter, ObjectMapper mapper, ServletContext servletContext) {
HttpApiV2ProxyRequest v2Request = readValue(request, HttpApiV2ProxyRequest.class, mapper);


ServerlessHttpServletRequest httpRequest = new ServerlessHttpServletRequest(servletContext,
v2Request.getRequestContext().getHttp().getMethod(), v2Request.getRequestContext().getHttp().getPath());
populateQueryStringparameters(v2Request.getQueryStringParameters(), httpRequest);

v2Request.getHeaders().forEach(httpRequest::setHeader);

if (StringUtils.hasText(v2Request.getBody())) {
httpRequest.setContentType("application/json");
httpRequest.setContent(v2Request.getBody().getBytes(StandardCharsets.UTF_8));
Expand All @@ -151,6 +163,14 @@ private static HttpServletRequest generateRequest2(String request, Context lambd
return httpRequest;
}

private static void populateQueryStringparameters(Map<String, String> requestParameters, ServerlessHttpServletRequest httpRequest) {
if (!CollectionUtils.isEmpty(requestParameters)) {
for (Entry<String, String> entry : requestParameters.entrySet()) {
httpRequest.setParameter(entry.getKey(), entry.getValue());
}
}
}

private static <T> T readValue(String json, Class<T> clazz, ObjectMapper mapper) {
try {
return mapper.readValue(json, clazz);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.springframework.cloud.function.serverless.web.ServerlessServletContext;
import org.springframework.util.CollectionUtils;

import com.amazonaws.serverless.proxy.spring.servletapp.MessageData;
Expand Down Expand Up @@ -72,12 +73,16 @@ public class SpringDelegatingLambdaContainerHandlerTests {
+ " },\n"
+ " \"queryStringParameters\": {\n"
+ " \"abc\": \"xyz\",\n"
+ " \"name\": \"Ricky\",\n"
+ " \"foo\": \"baz\"\n"
+ " },\n"
+ " \"multiValueQueryStringParameters\": {\n"
+ " \"abc\": [\n"
+ " \"xyz\"\n"
+ " ],\n"
+ " \"name\": [\n"
+ " \"Ricky\"\n"
+ " ],\n"
+ " \"foo\": [\n"
+ " \"bar\",\n"
+ " \"baz\"\n"
Expand Down Expand Up @@ -124,7 +129,7 @@ public class SpringDelegatingLambdaContainerHandlerTests {
" \"version\": \"2.0\",\n" +
" \"routeKey\": \"$default\",\n" +
" \"rawPath\": \"/my/path\",\n" +
" \"rawQueryString\": \"parameter1=value1&parameter1=value2&parameter2=value\",\n" +
" \"rawQueryString\": \"parameter1=value1&parameter1=value2&name=Ricky&parameter2=value\",\n" +
" \"cookies\": [\n" +
" \"cookie1\",\n" +
" \"cookie2\"\n" +
Expand All @@ -135,6 +140,7 @@ public class SpringDelegatingLambdaContainerHandlerTests {
" },\n" +
" \"queryStringParameters\": {\n" +
" \"parameter1\": \"value1,value2\",\n" +
" \"name\": \"Ricky\",\n" +
" \"parameter2\": \"value\"\n" +
" },\n" +
" \"requestContext\": {\n" +
Expand Down Expand Up @@ -202,6 +208,22 @@ public static Collection<String> data() {
return Arrays.asList(new String[]{API_GATEWAY_EVENT, API_GATEWAY_EVENT_V2});
}

@MethodSource("data")
@ParameterizedTest
public void validateComplesrequest(String jsonEvent) throws Exception {
initServletAppTest();
InputStream targetStream = new ByteArrayInputStream(this.generateHttpRequest(jsonEvent, "POST",
"/foo/male/list/24", "{\"name\":\"bob\"}", null));
ByteArrayOutputStream output = new ByteArrayOutputStream();
handler.handleRequest(targetStream, output, null);
Map result = mapper.readValue(output.toString(StandardCharsets.UTF_8), Map.class);
assertEquals(200, result.get("statusCode"));
String[] respponseBody = ((String) result.get("body")).split("/");
assertEquals("male", respponseBody[0]);
assertEquals("24", respponseBody[1]);
assertEquals("Ricky", respponseBody[2]);
}

@MethodSource("data")
@ParameterizedTest
public void testAsyncPost(String jsonEvent) throws Exception {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package com.amazonaws.serverless.proxy.spring.servletapp;

import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication(exclude = {
org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration.class,
Expand All @@ -14,5 +18,15 @@
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration.class
})
@Import(MessageController.class)
@RestController
public class ServletApplication {

@RequestMapping(path = "/foo/{gender}/list/{age}", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public String complexRequest(
@PathVariable("gender") String gender,
@PathVariable("age") String age,
@RequestParam("name") String name
) {
return gender + "/" + age + "/" + name;
}
}
19 changes: 18 additions & 1 deletion samples/springboot3/alt-pet-store/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,21 @@ PetStoreApi - URL for application https://xxxxxxxxxx.execute-api.us-w
---------------------------------------------------------------------------------------------------------
$ curl https://xxxxxxxxxx.execute-api.us-west-2.amazonaws.com/pets
```
```

You can also try a complex request passing both path and request parameters to complex endpoint such as this:


```
@RequestMapping(path = "/foo/{gender}/bar/{age}", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public String complexRequest(@RequestBody String body,
@PathVariable("gender") String gender,
@PathVariable("age") String age,
@RequestParam("name") String name
)
```
For example.

```
curl -d '{"key1":"value1", "key2":"value2"}' -H "Content-Type: application/json" -X POST https://zuhd709386.execute-api.us-east-2.amazonaws.com/foo/male/bar/25?name=Ricky
```
6 changes: 6 additions & 0 deletions samples/springboot3/alt-pet-store/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@
<artifactId>aws-serverless-java-container-springboot3</artifactId>
<version>2.0.0-SNAPSHOT</version>
</dependency>

<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-annotations</artifactId>
<version>2.2.20</version>
</dependency>
</dependencies>

<profiles>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
import com.amazonaws.serverless.sample.springboot3.model.Pet;
import com.amazonaws.serverless.sample.springboot3.model.PetData;

import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
Expand All @@ -32,6 +34,7 @@
@RestController
@EnableWebMvc
public class PetsController {

@RequestMapping(path = "/pets", method = RequestMethod.POST)
public Pet createPet(@RequestBody Pet newPet) {
if (newPet.getName() == null || newPet.getBreed() == null) {
Expand Down Expand Up @@ -73,5 +76,15 @@ public Pet listPets() {
newPet.setName(PetData.getRandomName());
return newPet;
}

@RequestMapping(path = "/foo/{gender}/bar/{age}", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public String complexRequest(@RequestBody String body,
@PathVariable("gender") String gender,
@PathVariable("age") String age,
@RequestParam("name") String name
) {
System.out.println("Body: " + body + " - " + gender + "/" + age + "/" + name);
return gender + "/" + age + "/" + name;
}

}
2 changes: 1 addition & 1 deletion samples/springboot3/alt-pet-store/template.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Resources:
# AutoPublishAlias: bcn
FunctionName: pet-store-boot-3
Handler: com.amazonaws.serverless.proxy.spring.SpringDelegatingLambdaContainerHandler::handleRequest
Runtime: java21
Runtime: java17
SnapStart:
ApplyOn: PublishedVersions
CodeUri: .
Expand Down

0 comments on commit c6fe79f

Please sign in to comment.