Skip to content

Commit 506dbb1

Browse files
authored
Merge pull request #1085 from npeters/fix/#1084-ServerlessHttpServletRequest.content-decode-base64
#1084: decode body if base64 is enable
2 parents f85319e + 1050e94 commit 506dbb1

File tree

6 files changed

+153
-77
lines changed

6 files changed

+153
-77
lines changed
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package com.amazonaws.serverless.proxy.internal;
2+
3+
import org.apache.commons.io.Charsets;
4+
5+
import java.nio.charset.Charset;
6+
import java.nio.charset.StandardCharsets;
7+
import java.nio.charset.UnsupportedCharsetException;
8+
9+
public final class HttpUtils {
10+
11+
static final String HEADER_KEY_VALUE_SEPARATOR = "=";
12+
static final String HEADER_VALUE_SEPARATOR = ";";
13+
static final String ENCODING_VALUE_KEY = "charset";
14+
15+
16+
static public Charset parseCharacterEncoding(String contentTypeHeader,Charset defaultCharset) {
17+
// we only look at content-type because content-encoding should only be used for
18+
// "binary" requests such as gzip/deflate.
19+
if (contentTypeHeader == null) {
20+
return defaultCharset;
21+
}
22+
23+
String[] contentTypeValues = contentTypeHeader.split(HEADER_VALUE_SEPARATOR);
24+
if (contentTypeValues.length <= 1) {
25+
return defaultCharset;
26+
}
27+
28+
for (String contentTypeValue : contentTypeValues) {
29+
if (contentTypeValue.trim().startsWith(ENCODING_VALUE_KEY)) {
30+
String[] encodingValues = contentTypeValue.split(HEADER_KEY_VALUE_SEPARATOR);
31+
if (encodingValues.length <= 1) {
32+
return defaultCharset;
33+
}
34+
try {
35+
return Charsets.toCharset(encodingValues[1]);
36+
} catch (UnsupportedCharsetException ex) {
37+
return defaultCharset;
38+
}
39+
}
40+
}
41+
return defaultCharset;
42+
}
43+
44+
45+
static public String appendCharacterEncoding(String currentContentType, String newEncoding) {
46+
if (currentContentType == null || currentContentType.trim().isEmpty()) {
47+
return null;
48+
}
49+
50+
if (currentContentType.contains(HEADER_VALUE_SEPARATOR)) {
51+
String[] contentTypeValues = currentContentType.split(HEADER_VALUE_SEPARATOR);
52+
StringBuilder contentType = new StringBuilder(contentTypeValues[0]);
53+
54+
for (int i = 1; i < contentTypeValues.length; i++) {
55+
String contentTypeValue = contentTypeValues[i];
56+
String contentTypeString = HEADER_VALUE_SEPARATOR + " " + contentTypeValue;
57+
if (contentTypeValue.trim().startsWith(ENCODING_VALUE_KEY)) {
58+
contentTypeString = HEADER_VALUE_SEPARATOR + " " + ENCODING_VALUE_KEY + HEADER_KEY_VALUE_SEPARATOR + newEncoding;
59+
}
60+
contentType.append(contentTypeString);
61+
}
62+
63+
return contentType.toString();
64+
} else {
65+
return currentContentType + HEADER_VALUE_SEPARATOR + " " + ENCODING_VALUE_KEY + HEADER_KEY_VALUE_SEPARATOR + newEncoding;
66+
}
67+
}
68+
}

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
*/
1313
package com.amazonaws.serverless.proxy.internal.servlet;
1414

15+
import com.amazonaws.serverless.proxy.internal.HttpUtils;
1516
import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler;
1617
import com.amazonaws.serverless.proxy.internal.SecurityUtils;
1718
import com.amazonaws.serverless.proxy.model.ContainerConfig;
@@ -32,6 +33,7 @@
3233
import java.io.StringReader;
3334
import java.io.UnsupportedEncodingException;
3435
import java.net.URLDecoder;
36+
import java.nio.charset.Charset;
3537
import java.security.Principal;
3638
import java.time.Instant;
3739
import java.time.ZonedDateTime;
@@ -232,7 +234,8 @@ public String getCharacterEncoding() {
232234
if (headers == null) {
233235
return config.getDefaultContentCharset();
234236
}
235-
return parseCharacterEncoding(headers.getFirst(HttpHeaders.CONTENT_TYPE));
237+
Charset charset = HttpUtils.parseCharacterEncoding(headers.getFirst(HttpHeaders.CONTENT_TYPE),null);
238+
return charset != null ? charset.name() : null;
236239
}
237240

238241
@Override
@@ -242,7 +245,7 @@ public void setCharacterEncoding(String s) throws UnsupportedEncodingException {
242245
return;
243246
}
244247
String currentContentType = headers.getFirst(HttpHeaders.CONTENT_TYPE);
245-
headers.putSingle(HttpHeaders.CONTENT_TYPE, appendCharacterEncoding(currentContentType, s));
248+
headers.putSingle(HttpHeaders.CONTENT_TYPE, HttpUtils.appendCharacterEncoding(currentContentType, s));
246249
}
247250

248251
@Override

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

Lines changed: 0 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -369,53 +369,9 @@ protected StringBuffer generateRequestURL(String requestPath) {
369369
return new StringBuffer(getScheme() + "://" + url);
370370
}
371371

372-
protected String parseCharacterEncoding(String contentTypeHeader) {
373-
// we only look at content-type because content-encoding should only be used for
374-
// "binary" requests such as gzip/deflate.
375-
if (contentTypeHeader == null) {
376-
return null;
377-
}
378372

379-
String[] contentTypeValues = contentTypeHeader.split(HEADER_VALUE_SEPARATOR);
380-
if (contentTypeValues.length <= 1) {
381-
return null;
382-
}
383373

384-
for (String contentTypeValue : contentTypeValues) {
385-
if (contentTypeValue.trim().startsWith(ENCODING_VALUE_KEY)) {
386-
String[] encodingValues = contentTypeValue.split(HEADER_KEY_VALUE_SEPARATOR);
387-
if (encodingValues.length <= 1) {
388-
return null;
389-
}
390-
return encodingValues[1];
391-
}
392-
}
393-
return null;
394-
}
395374

396-
protected String appendCharacterEncoding(String currentContentType, String newEncoding) {
397-
if (currentContentType == null || currentContentType.trim().isEmpty()) {
398-
return null;
399-
}
400-
401-
if (currentContentType.contains(HEADER_VALUE_SEPARATOR)) {
402-
String[] contentTypeValues = currentContentType.split(HEADER_VALUE_SEPARATOR);
403-
StringBuilder contentType = new StringBuilder(contentTypeValues[0]);
404-
405-
for (int i = 1; i < contentTypeValues.length; i++) {
406-
String contentTypeValue = contentTypeValues[i];
407-
String contentTypeString = HEADER_VALUE_SEPARATOR + " " + contentTypeValue;
408-
if (contentTypeValue.trim().startsWith(ENCODING_VALUE_KEY)) {
409-
contentTypeString = HEADER_VALUE_SEPARATOR + " " + ENCODING_VALUE_KEY + HEADER_KEY_VALUE_SEPARATOR + newEncoding;
410-
}
411-
contentType.append(contentTypeString);
412-
}
413-
414-
return contentType.toString();
415-
} else {
416-
return currentContentType + HEADER_VALUE_SEPARATOR + " " + ENCODING_VALUE_KEY + HEADER_KEY_VALUE_SEPARATOR + newEncoding;
417-
}
418-
}
419375

420376
protected ServletInputStream bodyStringToInputStream(String body, boolean isBase64Encoded) throws IOException {
421377
if (body == null) {

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
package com.amazonaws.serverless.proxy.internal.servlet;
1414

1515

16+
import com.amazonaws.serverless.proxy.internal.HttpUtils;
1617
import com.amazonaws.serverless.proxy.internal.LambdaContainerHandler;
1718
import com.amazonaws.serverless.proxy.internal.SecurityUtils;
1819
import com.amazonaws.serverless.proxy.model.AwsProxyRequest;
@@ -35,6 +36,7 @@
3536
import java.io.IOException;
3637
import java.io.StringReader;
3738
import java.io.UnsupportedEncodingException;
39+
import java.nio.charset.Charset;
3840
import java.security.Principal;
3941
import java.time.Instant;
4042
import java.time.ZonedDateTime;
@@ -273,7 +275,8 @@ public String getCharacterEncoding() {
273275
if (request.getMultiValueHeaders() == null) {
274276
return config.getDefaultContentCharset();
275277
}
276-
return parseCharacterEncoding(request.getMultiValueHeaders().getFirst(HttpHeaders.CONTENT_TYPE));
278+
Charset charset = HttpUtils.parseCharacterEncoding(request.getMultiValueHeaders().getFirst(HttpHeaders.CONTENT_TYPE),null);
279+
return charset != null ? charset.name() : null;
277280
}
278281

279282

@@ -284,12 +287,12 @@ public void setCharacterEncoding(String s)
284287
request.setMultiValueHeaders(new Headers());
285288
}
286289
String currentContentType = request.getMultiValueHeaders().getFirst(HttpHeaders.CONTENT_TYPE);
287-
if (currentContentType == null || "".equals(currentContentType)) {
290+
if (currentContentType == null || currentContentType.isEmpty()) {
288291
log.debug("Called set character encoding to " + SecurityUtils.crlf(s) + " on a request without a content type. Character encoding will not be set");
289292
return;
290293
}
291294

292-
request.getMultiValueHeaders().putSingle(HttpHeaders.CONTENT_TYPE, appendCharacterEncoding(currentContentType, s));
295+
request.getMultiValueHeaders().putSingle(HttpHeaders.CONTENT_TYPE, HttpUtils.appendCharacterEncoding(currentContentType, s));
293296
}
294297

295298

aws-serverless-java-container-springboot3/src/main/java/com/amazonaws/serverless/proxy/spring/AwsSpringHttpProcessingUtils.java

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,28 @@
11
package com.amazonaws.serverless.proxy.spring;
22

33
import java.io.InputStream;
4+
import java.nio.charset.Charset;
45
import java.nio.charset.StandardCharsets;
5-
import java.util.Iterator;
6+
import java.nio.charset.UnsupportedCharsetException;
7+
import java.util.Base64;
68
import java.util.Map;
79
import java.util.Map.Entry;
8-
import java.util.Set;
910
import java.util.concurrent.CountDownLatch;
1011
import java.util.concurrent.TimeUnit;
1112

13+
import com.amazonaws.serverless.proxy.internal.HttpUtils;
14+
import org.apache.commons.io.Charsets;
1215
import org.apache.commons.logging.Log;
1316
import org.apache.commons.logging.LogFactory;
1417
import org.springframework.cloud.function.serverless.web.ServerlessHttpServletRequest;
1518
import org.springframework.cloud.function.serverless.web.ServerlessMVC;
19+
import org.springframework.http.HttpHeaders;
20+
import org.springframework.http.MediaType;
1621
import org.springframework.util.CollectionUtils;
1722
import org.springframework.util.FileCopyUtils;
1823
import org.springframework.util.MultiValueMapAdapter;
1924
import org.springframework.util.StringUtils;
2025

21-
import com.amazonaws.serverless.proxy.AsyncInitializationWrapper;
2226
import com.amazonaws.serverless.proxy.AwsHttpApiV2SecurityContextWriter;
2327
import com.amazonaws.serverless.proxy.AwsProxySecurityContextWriter;
2428
import com.amazonaws.serverless.proxy.RequestReader;
@@ -120,10 +124,12 @@ private static HttpServletRequest generateRequest1(String request, Context lambd
120124
MultiValueMapAdapter headers = new MultiValueMapAdapter(v1Request.getMultiValueHeaders());
121125
httpRequest.setHeaders(headers);
122126
}
123-
if (StringUtils.hasText(v1Request.getBody())) {
124-
httpRequest.setContentType("application/json");
125-
httpRequest.setContent(v1Request.getBody().getBytes(StandardCharsets.UTF_8));
126-
}
127+
populateContentAndContentType(
128+
v1Request.getBody(),
129+
v1Request.getHeaders().get(HttpHeaders.CONTENT_TYPE),
130+
v1Request.isBase64Encoded(),
131+
httpRequest
132+
);
127133
if (v1Request.getRequestContext() != null) {
128134
httpRequest.setAttribute(RequestReader.API_GATEWAY_CONTEXT_PROPERTY, v1Request.getRequestContext());
129135
httpRequest.setAttribute(RequestReader.ALB_CONTEXT_PROPERTY, v1Request.getRequestContext().getElb());
@@ -149,11 +155,14 @@ private static HttpServletRequest generateRequest2(String request, Context lambd
149155
populateQueryStringparameters(v2Request.getQueryStringParameters(), httpRequest);
150156

151157
v2Request.getHeaders().forEach(httpRequest::setHeader);
152-
153-
if (StringUtils.hasText(v2Request.getBody())) {
154-
httpRequest.setContentType("application/json");
155-
httpRequest.setContent(v2Request.getBody().getBytes(StandardCharsets.UTF_8));
156-
}
158+
159+
populateContentAndContentType(
160+
v2Request.getBody(),
161+
v2Request.getHeaders().get(HttpHeaders.CONTENT_TYPE),
162+
v2Request.isBase64Encoded(),
163+
httpRequest
164+
);
165+
157166
httpRequest.setAttribute(RequestReader.HTTP_API_CONTEXT_PROPERTY, v2Request.getRequestContext());
158167
httpRequest.setAttribute(RequestReader.HTTP_API_STAGE_VARS_PROPERTY, v2Request.getStageVariables());
159168
httpRequest.setAttribute(RequestReader.HTTP_API_EVENT_PROPERTY, v2Request);
@@ -180,4 +189,22 @@ private static <T> T readValue(String json, Class<T> clazz, ObjectMapper mapper)
180189
}
181190
}
182191

192+
private static void populateContentAndContentType(
193+
String body,
194+
String contentType,
195+
boolean base64Encoded,
196+
ServerlessHttpServletRequest httpRequest) {
197+
if (StringUtils.hasText(body)) {
198+
httpRequest.setContentType(contentType == null ? MediaType.APPLICATION_JSON_VALUE : contentType);
199+
if (base64Encoded) {
200+
httpRequest.setContent(Base64.getMimeDecoder().decode(body));
201+
} else {
202+
Charset charseEncoding = HttpUtils.parseCharacterEncoding(contentType,StandardCharsets.UTF_8);
203+
httpRequest.setContent(body.getBytes(charseEncoding));
204+
}
205+
}
206+
}
207+
208+
209+
183210
}

0 commit comments

Comments
 (0)