Skip to content

Commit a573690

Browse files
authored
Merge pull request #1082 from HubSpot/unwrap-json-mapping-exception
Unwrap JsonMappingException if cause is LengthLimitingJsonProcessingException
2 parents fa18282 + 259bdc0 commit a573690

5 files changed

Lines changed: 89 additions & 28 deletions

File tree

src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -132,14 +132,21 @@ public JinjavaInterpreter(JinjavaInterpreter orig) {
132132
}
133133

134134
public static void checkOutputSize(String string) {
135+
if (isOutputTooLarge(string)) {
136+
throw new OutputTooBigException(
137+
getCurrent().getConfig().getMaxOutputSize(),
138+
string.length()
139+
);
140+
}
141+
}
142+
143+
public static boolean isOutputTooLarge(String string) {
135144
Optional<Long> maxStringLength = getCurrentMaybe()
136145
.map(interpreter -> interpreter.getConfig().getMaxOutputSize())
137146
.filter(max -> max > 0);
138-
if (
147+
return (
139148
maxStringLength.map(max -> string != null && string.length() > max).orElse(false)
140-
) {
141-
throw new OutputTooBigException(maxStringLength.get(), string.length());
142-
}
149+
);
143150
}
144151

145152
/**

src/main/java/com/hubspot/jinjava/lib/tag/eager/EagerTagDecorator.java

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -38,29 +38,40 @@ public T getTag() {
3838
@Override
3939
public final String interpret(TagNode tagNode, JinjavaInterpreter interpreter) {
4040
try {
41-
String output = innerInterpret(tagNode, interpreter);
42-
JinjavaInterpreter.checkOutputSize(output);
43-
return output;
44-
} catch (DeferredValueException | TemplateSyntaxException | OutputTooBigException e) {
41+
String output;
4542
try {
46-
return EagerReconstructionUtils.wrapInAutoEscapeIfNeeded(
47-
eagerInterpret(
48-
tagNode,
49-
interpreter,
50-
e instanceof InterpretException
51-
? (InterpretException) e
52-
: new InterpretException("Exception with default render", e)
53-
),
54-
interpreter
55-
);
56-
} catch (OutputTooBigException e1) {
57-
throw new DeferredValueException(
58-
String.format("Output too big for eager execution: %s", e1.getMessage())
59-
);
43+
output = innerInterpret(tagNode, interpreter);
44+
} catch (DeferredValueException | TemplateSyntaxException e) {
45+
return wrapEagerInterpret(tagNode, interpreter, e);
6046
}
47+
if (JinjavaInterpreter.isOutputTooLarge(output)) {
48+
return wrapEagerInterpret(tagNode, interpreter, null);
49+
}
50+
return output;
51+
} catch (OutputTooBigException e) {
52+
throw new DeferredValueException(
53+
String.format("Output too big for eager execution: %s", e.getMessage())
54+
);
6155
}
6256
}
6357

58+
private String wrapEagerInterpret(
59+
TagNode tagNode,
60+
JinjavaInterpreter interpreter,
61+
RuntimeException e
62+
) {
63+
return EagerReconstructionUtils.wrapInAutoEscapeIfNeeded(
64+
eagerInterpret(
65+
tagNode,
66+
interpreter,
67+
e instanceof InterpretException
68+
? (InterpretException) e
69+
: new InterpretException("Exception with default render", e)
70+
),
71+
interpreter
72+
);
73+
}
74+
6475
protected String innerInterpret(TagNode tagNode, JinjavaInterpreter interpreter) {
6576
return tag.interpret(tagNode, interpreter);
6677
}

src/main/java/com/hubspot/jinjava/objects/serialization/PyishObjectMapper.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.fasterxml.jackson.core.JsonFactoryBuilder;
44
import com.fasterxml.jackson.core.JsonGenerator;
5+
import com.fasterxml.jackson.databind.JsonMappingException;
56
import com.fasterxml.jackson.databind.JsonSerializer;
67
import com.fasterxml.jackson.databind.ObjectMapper;
78
import com.fasterxml.jackson.databind.ObjectWriter;
@@ -66,11 +67,17 @@ private static String getAsPyishString(Object val, boolean forOutput) {
6667
try {
6768
return getAsPyishStringOrThrow(val, forOutput);
6869
} catch (IOException e) {
69-
if (e instanceof LengthLimitingJsonProcessingException) {
70+
Throwable unwrapped = e;
71+
if (e instanceof JsonMappingException) {
72+
unwrapped = unwrapped.getCause();
73+
}
74+
if (unwrapped instanceof LengthLimitingJsonProcessingException) {
7075
throw new OutputTooBigException(
71-
((LengthLimitingJsonProcessingException) e).getMaxSize(),
72-
((LengthLimitingJsonProcessingException) e).getAttemptedSize()
76+
((LengthLimitingJsonProcessingException) unwrapped).getMaxSize(),
77+
((LengthLimitingJsonProcessingException) unwrapped).getAttemptedSize()
7378
);
79+
} else if (unwrapped instanceof OutputTooBigException) {
80+
throw (OutputTooBigException) unwrapped;
7481
}
7582
return Objects.toString(val, "");
7683
}

src/test/java/com/hubspot/jinjava/lib/tag/eager/EagerTagDecoratorTest.java

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@
22

33
import static org.assertj.core.api.Assertions.assertThat;
44
import static org.assertj.core.api.Assertions.assertThatThrownBy;
5-
import static org.mockito.Mockito.any;
6-
import static org.mockito.Mockito.mock;
7-
import static org.mockito.Mockito.when;
5+
import static org.mockito.Mockito.*;
86

97
import com.hubspot.jinjava.BaseInterpretingTest;
108
import com.hubspot.jinjava.JinjavaConfig;
@@ -15,8 +13,11 @@
1513
import com.hubspot.jinjava.lib.fn.ELFunctionDefinition;
1614
import com.hubspot.jinjava.lib.tag.Tag;
1715
import com.hubspot.jinjava.mode.EagerExecutionMode;
16+
import com.hubspot.jinjava.objects.collections.PyList;
17+
import com.hubspot.jinjava.objects.serialization.PyishSerializable;
1818
import com.hubspot.jinjava.tree.TagNode;
1919
import com.hubspot.jinjava.tree.parse.TagToken;
20+
import java.io.IOException;
2021
import java.util.ArrayList;
2122
import java.util.List;
2223
import org.junit.After;
@@ -157,4 +158,36 @@ public static void addToContext(String key, Object value) {
157158
public static void modifyContext(String key, Object value) {
158159
((List<Object>) JinjavaInterpreter.getCurrent().getContext().get(key)).add(value);
159160
}
161+
162+
static class TooBig extends PyList implements PyishSerializable {
163+
164+
public TooBig(List<Object> list) {
165+
super(list);
166+
}
167+
168+
@Override
169+
public <T extends Appendable & CharSequence> T appendPyishString(T appendable)
170+
throws IOException {
171+
throw new OutputTooBigException(1, 1);
172+
}
173+
}
174+
175+
@Test
176+
public void itDefersNodeWhenOutputTooBigIsThrownWithinInnerInterpret() {
177+
TooBig tooBig = new TooBig(new ArrayList<>());
178+
interpreter =
179+
new JinjavaInterpreter(
180+
jinjava,
181+
context,
182+
JinjavaConfig
183+
.newBuilder()
184+
.withExecutionMode(EagerExecutionMode.instance())
185+
.build()
186+
);
187+
interpreter.getContext().put("too_big", tooBig);
188+
interpreter.render(
189+
"{% for i in range(2) %}{% do too_big.append(deferred) %}{% endfor %}"
190+
);
191+
assertThat(interpreter.getContext().getDeferredNodes()).isNotEmpty();
192+
}
160193
}

src/test/java/com/hubspot/jinjava/objects/serialization/PyishObjectMapperTest.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,10 @@ public void itLimitsDepth() {
8888
assertThatThrownBy(() -> PyishObjectMapper.getAsPyishStringOrThrow(original))
8989
.as("The string to be serialized is larger than the max output size")
9090
.isInstanceOf(JsonMappingException.class)
91+
.hasCauseInstanceOf(LengthLimitingJsonProcessingException.class)
9192
.hasMessageContaining("Max length of 10000 chars reached");
93+
assertThatThrownBy(() -> PyishObjectMapper.getAsPyishString(original))
94+
.isInstanceOf(OutputTooBigException.class);
9295
} finally {
9396
JinjavaInterpreter.popCurrent();
9497
}

0 commit comments

Comments
 (0)