Skip to content

Commit a64e61e

Browse files
authored
added compression to handler response stream (#255)
1 parent 7f28f29 commit a64e61e

File tree

5 files changed

+58
-10
lines changed

5 files changed

+58
-10
lines changed

pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,12 @@
6161
</dependencies>
6262
</dependencyManagement>
6363
<dependencies>
64+
<!-- https://mvnrepository.com/artifact/commons-codec/commons-codec -->
65+
<dependency>
66+
<groupId>commons-codec</groupId>
67+
<artifactId>commons-codec</artifactId>
68+
<version>1.14</version>
69+
</dependency>
6470
<!-- https://mvnrepository.com/artifact/software.amazon.cloudformation/aws-cloudformation-resource-schema -->
6571
<dependency>
6672
<groupId>software.amazon.cloudformation</groupId>

src/main/java/software/amazon/cloudformation/LambdaWrapper.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,8 @@ public void handleRequest(final InputStream inputStream, final OutputStream outp
249249
throw new TerminalException("No request object received");
250250
}
251251

252-
String input = IOUtils.toString(inputStream, StandardCharsets.UTF_8);
252+
String input = this.serializer.decompress(IOUtils.toString(inputStream, StandardCharsets.UTF_8));
253+
253254
JSONObject rawInput = new JSONObject(new JSONTokener(input));
254255

255256
// deserialize incoming payload to modelled request

src/main/java/software/amazon/cloudformation/resource/Serializer.java

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
*/
1515
package software.amazon.cloudformation.resource;
1616

17+
import com.amazonaws.util.IOUtils;
1718
import com.fasterxml.jackson.annotation.JsonInclude;
1819
import com.fasterxml.jackson.core.JsonProcessingException;
1920
import com.fasterxml.jackson.core.type.TypeReference;
@@ -22,14 +23,26 @@
2223
import com.fasterxml.jackson.databind.ObjectMapper;
2324
import com.fasterxml.jackson.databind.SerializationFeature;
2425
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
26+
import java.io.ByteArrayInputStream;
27+
import java.io.ByteArrayOutputStream;
2528
import java.io.IOException;
29+
import java.nio.charset.StandardCharsets;
30+
import java.util.HashMap;
31+
import java.util.Map;
32+
import java.util.zip.GZIPInputStream;
33+
import java.util.zip.GZIPOutputStream;
34+
import org.apache.commons.codec.binary.Base64;
2635
import software.amazon.cloudformation.proxy.aws.AWSServiceSerdeModule;
2736

2837
public class Serializer {
2938

39+
public static final String COMPRESSED = "__COMPRESSED__";
40+
private static final String COMPRESSION_METHOD = "__COMPRESSION_METHOD__";
41+
private static final String COMPRESSION_GZIP_BASE64 = "gzip_base64";
3042
private static final ObjectMapper OBJECT_MAPPER;
31-
3243
private static final ObjectMapper STRICT_OBJECT_MAPPER;
44+
private static final TypeReference<Map<String, Object>> MAP_TYPE_REFERENCE = new TypeReference<Map<String, Object>>() {
45+
};
3346

3447
/**
3548
* Configures the specified ObjectMapper with the (de)serialization behaviours
@@ -76,10 +89,36 @@ public <T> String serialize(final T modelObject) throws JsonProcessingException
7689
return OBJECT_MAPPER.writeValueAsString(modelObject);
7790
}
7891

92+
public <T> String compress(final String modelInput) throws IOException {
93+
final Map<String, String> map = new HashMap<>();
94+
try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
95+
try (GZIPOutputStream gzip = new GZIPOutputStream(byteArrayOutputStream)) {
96+
gzip.write(modelInput.getBytes(StandardCharsets.UTF_8));
97+
}
98+
map.put(COMPRESSED, Base64.encodeBase64String(byteArrayOutputStream.toByteArray()));
99+
map.put(COMPRESSION_METHOD, COMPRESSION_GZIP_BASE64);
100+
}
101+
return OBJECT_MAPPER.writeValueAsString(map);
102+
}
103+
79104
public <T> T deserialize(final String s, final TypeReference<T> reference) throws IOException {
80105
return OBJECT_MAPPER.readValue(s, reference);
81106
}
82107

108+
public String decompress(final String s) throws IOException {
109+
final Map<String, Object> map = deserialize(s, MAP_TYPE_REFERENCE);
110+
111+
if (!map.containsKey(COMPRESSED)) {
112+
return s;
113+
}
114+
115+
final byte[] bytes = Base64.decodeBase64((String) map.get(COMPRESSED));
116+
try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
117+
GZIPInputStream gzipInputStream = new GZIPInputStream(byteArrayInputStream);) {
118+
return new String(IOUtils.toByteArray(gzipInputStream), StandardCharsets.UTF_8);
119+
}
120+
}
121+
83122
public <T> T deserializeStrict(final String s, final TypeReference<T> reference) throws IOException {
84123
return STRICT_OBJECT_MAPPER.readValue(s, reference);
85124
}

src/main/java/software/amazon/cloudformation/scheduler/CloudWatchScheduler.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
*/
1515
package software.amazon.cloudformation.scheduler;
1616

17-
import com.fasterxml.jackson.core.JsonProcessingException;
17+
import java.io.IOException;
1818
import java.util.Objects;
1919
import java.util.UUID;
2020
import lombok.Data;
@@ -101,14 +101,15 @@ public <ResourceT, CallbackT> void rescheduleAfterMinutes(final String functionA
101101
String jsonRequest;
102102
try {
103103
// expect return type to be non-null
104-
jsonRequest = serializer.serialize(handlerRequest);
105-
} catch (JsonProcessingException e) {
104+
jsonRequest = serializer.compress(serializer.serialize(handlerRequest));
105+
} catch (IOException e) {
106106
throw new TerminalException("Unable to serialize the request for callback", e);
107107
}
108108
this.log(String.format("Scheduling re-invoke at %s (%s)%n", cronRule, rescheduleId));
109109

110110
PutRuleRequest putRuleRequest = PutRuleRequest.builder().name(ruleName).scheduleExpression(cronRule)
111111
.state(RuleState.ENABLED).build();
112+
112113
this.client.putRule(putRuleRequest);
113114

114115
Target target = Target.builder().arn(functionArn).id(targetId).input(jsonRequest).build();

src/test/java/software/amazon/cloudformation/scheduler/CloudWatchSchedulerTest.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@
2121
import static org.mockito.Mockito.times;
2222
import static org.mockito.Mockito.verify;
2323
import static org.mockito.Mockito.when;
24-
import java.util.Arrays;
24+
import java.io.IOException;
25+
import java.util.Collections;
2526
import java.util.List;
26-
import org.json.JSONObject;
2727
import org.junit.jupiter.api.Test;
2828
import org.junit.jupiter.api.extension.ExtendWith;
2929
import org.mockito.ArgumentCaptor;
@@ -148,7 +148,7 @@ public void test_cleanupCloudWatchEventsWithErrorDeletingRule() {
148148
}
149149

150150
@Test
151-
public void test_rescheduleAfterMinutes_1MinuteFloor() {
151+
public void test_rescheduleAfterMinutes_1MinuteFloor() throws IOException {
152152
final CloudWatchEventsProvider provider = mock(CloudWatchEventsProvider.class);
153153
final CloudWatchEventsClient client = getCloudWatchEvents();
154154
when(provider.get()).thenReturn(client);
@@ -166,8 +166,9 @@ public void test_rescheduleAfterMinutes_1MinuteFloor() {
166166
verify(requestContext, times(1)).setCloudWatchEventsRuleName(startsWith("reinvoke-handler-"));
167167
verify(requestContext, times(1)).setCloudWatchEventsTargetId(startsWith("reinvoke-target-"));
168168

169-
final List<TargetMatcher> targetMatchers = Arrays
170-
.asList(new TargetMatcher(FUNCTION_ARN, "reinvoke-target-", new JSONObject(request).toString()));
169+
final List<TargetMatcher> targetMatchers = Collections.singletonList(
170+
new TargetMatcher(FUNCTION_ARN, "reinvoke-target-", serializer.compress(serializer.serialize(request))));
171+
171172
verify(client, times(1))
172173
.putTargets(argThat(new PutTargetsRequestMatcher("reinvoke-handler-", new TargetsListMatcher(targetMatchers))));
173174
verify(client, times(1))

0 commit comments

Comments
 (0)